[Firestore Database] 파이어베이스 Caused by: io.grpc.StatusException: PERMISSION_DENIED: Missing or insufficient permissions. 권한 오류 해결하기
파이어베이스에서 제공하는 데이터베이스 중에 Firestore 데이터베이스를 사용해보기 위해
파이어베이스 콘솔에 접근하여 데이터 탭에서 컬렉션 하나를 생성하였다.
그리고 가이드 문서에 따라 개발환경을 구성하였다.
dependencies {
// Import the BoM for the Firebase platform
implementation platform('com.google.firebase:firebase-bom:29.0.0')
// Declare the dependency for the Cloud Firestore library
// When using the BoM, you don't specify versions in Firebase library dependencies
implementation 'com.google.firebase:firebase-firestore'
}
아래 코드처러미 인스턴스를 초기화 하고 데이터를 추가해 보았다.
Cloud Firestore는 컬렉션에 저장되는 문서에 데이터를 저장한다.
문서에 데이터를 처음 추가할 때 Cloud Firestore에서 암시적으로 컬렉션과 문서를 만든다.
컬렉션이나 문서를 명시적으로 만들 필요는 없다.
private FirebaseFirestore db;
final String TAG = "FireStore";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ranking);
setup();
setDocument();
}
public void setup() {
// [START get_firestore_instance]
db = FirebaseFirestore.getInstance();
// [END get_firestore_instance]
// [START set_firestore_settings]
FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder()
.setPersistenceEnabled(true)
.build();
db.setFirestoreSettings(settings);
// [END set_firestore_settings]
}
public void setDocument() {
// [START set_document]
Map<String, Object> todaySteps = new HashMap<>();
todaySteps.put("activateTime", "20");
todaySteps.put("date", "2022.02.06");
todaySteps.put("nickId", "CHK123USA");
todaySteps.put("nickName", "홍길동");
todaySteps.put("step", "860");
// Add a new document with a generated ID
db.collection("ranking")
.add(todaySteps)
.addOnSuccessListener(new OnSuccessListener<DocumentReference>() {
@Override
public void onSuccess(DocumentReference documentReference) {
Log.d(TAG, "DocumentSnapshot added with ID: " + documentReference.getId());
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Log.w(TAG, "Error adding document", e);
}
});
db.collection("ranking").document("document-id")
.set(todaySteps)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "################################ DocumentSnapshot successfully written!");
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Log.w(TAG, "############################# Error writing document", e);
}
});
// [END set_document]
// [START set_with_id]
// db.collection("ranking").document("document-id").set(todaySteps);
// [END set_with_id]
}
그러나 Permission denied 오류가 발생하였다.
역시 접근하려면 로그인이 필요한 것인가?
com.google.firebase.firestore.FirebaseFirestoreException: PERMISSION_DENIED: Missing or insufficient permissions.
at com.google.firebase.firestore.util.Util.exceptionFromStatus(Util.java:117)
at com.google.firebase.firestore.core.SyncEngine.notifyUser(SyncEngine.java:575)
at com.google.firebase.firestore.core.SyncEngine.handleRejectedWrite(SyncEngine.java:457)
at com.google.firebase.firestore.core.MemoryComponentProvider$RemoteStoreCallback.handleRejectedWrite(MemoryComponentProvider.java:114)
at com.google.firebase.firestore.remote.RemoteStore.handleWriteError(RemoteStore.java:729)
at com.google.firebase.firestore.remote.RemoteStore.handleWriteStreamClose(RemoteStore.java:685)
at com.google.firebase.firestore.remote.RemoteStore.access$600(RemoteStore.java:53)
at com.google.firebase.firestore.remote.RemoteStore$2.onClose(RemoteStore.java:206)
at com.google.firebase.firestore.remote.AbstractStream.close(AbstractStream.java:356)
at com.google.firebase.firestore.remote.AbstractStream.handleServerClose(AbstractStream.java:410)
at com.google.firebase.firestore.remote.AbstractStream$StreamObserver.lambda$onClose$3$AbstractStream$StreamObserver(AbstractStream.java:151)
at com.google.firebase.firestore.remote.-$$Lambda$AbstractStream$StreamObserver$CiUxcjPbqaWvp6DcUxZC1He8ANY.run(Unknown Source:4)
at com.google.firebase.firestore.remote.AbstractStream$CloseGuardedRunner.run(AbstractStream.java:67)
at com.google.firebase.firestore.remote.AbstractStream$StreamObserver.onClose(AbstractStream.java:137)
at com.google.firebase.firestore.remote.FirestoreChannel$1.onClose(FirestoreChannel.java:142)
at io.grpc.internal.DelayedClientCall$DelayedListener$3.run(DelayedClientCall.java:463)
at io.grpc.internal.DelayedClientCall$DelayedListener.delayOrExecute(DelayedClientCall.java:427)
at io.grpc.internal.DelayedClientCall$DelayedListener.onClose(DelayedClientCall.java:460)
at io.grpc.internal.ClientCallImpl.closeObserver(ClientCallImpl.java:557)
at io.grpc.internal.ClientCallImpl.access$300(ClientCallImpl.java:69)
at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInternal(ClientCallImpl.java:738)
at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInContext(ClientCallImpl.java:717)
at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:133)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:462)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at com.google.firebase.firestore.util.AsyncQueue$SynchronizedShutdownAwareExecutor$DelayedStartFactory.run(AsyncQueue.java:234)
at java.lang.Thread.run(Thread.java:919)
Caused by: io.grpc.StatusException: PERMISSION_DENIED: Missing or insufficient permissions.
at io.grpc.Status.asException(Status.java:543)
at com.google.firebase.firestore.util.Util.exceptionFromStatus(Util.java:115)
at com.google.firebase.firestore.core.SyncEngine.notifyUser(SyncEngine.java:575)
at com.google.firebase.firestore.core.SyncEngine.handleRejectedWrite(SyncEngine.java:457)
at com.google.firebase.firestore.core.MemoryComponentProvider$RemoteStoreCallback.handleRejectedWrite(MemoryComponentProvider.java:114)
at com.google.firebase.firestore.remote.RemoteStore.handleWriteError(RemoteStore.java:729)
at com.google.firebase.firestore.remote.RemoteStore.handleWriteStreamClose(RemoteStore.java:685)
at com.google.firebase.firestore.remote.RemoteStore.access$600(RemoteStore.java:53)
at com.google.firebase.firestore.remote.RemoteStore$2.onClose(RemoteStore.java:206)
at com.google.firebase.firestore.remote.AbstractStream.close(AbstractStream.java:356)
at com.google.firebase.firestore.remote.AbstractStream.handleServerClose(AbstractStream.java:410)
at com.google.firebase.firestore.remote.AbstractStream$StreamObserver.lambda$onClose$3$AbstractStream$StreamObserver(AbstractStream.java:151)
at com.google.firebase.firestore.remote.-$$Lambda$AbstractStream$StreamObserver$CiUxcjPbqaWvp6DcUxZC1He8ANY.run(Unknown Source:4)
at com.google.firebase.firestore.remote.AbstractStream$CloseGuardedRunner.run(AbstractStream.java:67)
at com.google.firebase.firestore.remote.AbstractStream$StreamObserver.onClose(AbstractStream.java:137)
at com.google.firebase.firestore.remote.FirestoreChannel$1.onClose(FirestoreChannel.java:142)
at io.grpc.internal.DelayedClientCall$DelayedListener$3.run(DelayedClientCall.java:463)
at io.grpc.internal.DelayedClientCall$DelayedListener.delayOrExecute(DelayedClientCall.java:427)
at io.grpc.internal.DelayedClientCall$DelayedListener.onClose(DelayedClientCall.java:460)
at io.grpc.internal.ClientCallImpl.closeObserver(ClientCallImpl.java:557)
at io.grpc.internal.ClientCallImpl.access$300(ClientCallImpl.java:69)
at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInternal(ClientCallImpl.java:738)
at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInContext(ClientCallImpl.java:717)
at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:133)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:462)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at com.google.firebase.firestore.util.AsyncQueue$SynchronizedShutdownAwareExecutor$DelayedStartFactory.run(AsyncQueue.java:234)
at java.lang.Thread.run(Thread.java:919)
데이터 보안
웹, Android 또는 Apple 플랫폼 SDK를 사용하는 경우 Firebase 인증 및 Cloud Firestore 보안 규칙을 사용하여 Cloud Firestore의 데이터에 보안을 적용한다.
다음은 시작하는 데 사용할 수 있는 기본 규칙 세트이다. Console의 규칙 탭에서 보안 규칙을 수정할 수 있다.
// Allow read/write access on all documents to any user signed in to the application
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth != null;
}
}
}
웹, Android 또는 iOS 앱을 프로덕션에 배포하기 전에 앱 클라이언트만 Cloud Firestore 데이터에 액세스할 수 있도록 하는 단계를 수행해야한다. 앱 체크 문서를 참조하자.
서버 SDK 중 하나를 사용하는 경우 Identity and Access Management(IAM)를 사용하여 Cloud Firestore의 데이터를 보호한다.
파이어베이스 콘솔에 로그인하여 나의 규칙탭을 확인해보니 다음과 같은 잠금모드였다.
// Deny read/write access to all users under any conditions
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
}
}
인증없이 아래와 같이 규칙을 테스트모드로 하면 바로 확인이 가능하고 오류도 사라졌다.
// Allow read/write access to all users under any conditions
// Warning: **NEVER** use this rule set in production; it allows
// anyone to overwrite your entire database.
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if true;
}
}
}
[확인결과]
파이어베이스 콘솔 데이텁에서 확인결과 새로운 문서로 추가되었다.
안드로이드 스튜디오에서 로그창 확인결과 해당 문서 id를 리턴 받을 수 있다.
실제 마켓에 등록할때는 인증 규칙으로 변경 후 인증을 요구하면 될 것 같다.
[참고 코드]
- Cloud Firestore 시작하기: https://firebase.google.com/docs/firestore/quickstart#android
- DocSnippets: https://github.com/firebase/snippets-android/blob/8184cba2c40842a180f91dcfb4a216e721cc6ae6/firestore/app/src/main/java/com/google/example/firestore/DocSnippets.java#L134-L134