0
votes

I have data in Firestore that I update daily with server-side Java code. This has been going on for months without problems. The way I have created a Firestore instance is as follows. (I have a service account in Firestore and its credentials in a json file.)

GoogleCredentials myCredentials = GoogleCredentials.fromStream(new FileInputStream("/myPath/myCredentials.json"));  
    
FirestoreOptions firestoreOptions = FirestoreOptions.getDefaultInstance()
   .toBuilder()
   .setProjectId("myFirestoreProject")
   .setCredentials(myCredentials)
   .build();
Firestore db = firestoreOptions.getService();

Firestore security rules have not been an issue as client-side access to Firestore is not there yet, so I have had all Firestore collections involved unconditionally open for read and write operations. Now, as I only want the server-side access to Firestore, created from the credentials in myCredentials.json, to have permission to write, I set the Firestore security rules as follows.

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /myCollection/{document=**}{
        allow read;
        allow write: if false;
    }
  }         
}

These rules, however, block my access to Firestore as created above, and I also don't know how to set appropriate rules. I understood that it is possible to bypass any Firestore security rules if the Firestore instance is instead created by

FirebaseOptions options = FirebaseOptions.builder()
   .setCredentials(myCredentials)
   .setDatabaseUrl("https://myFirestoreProject.firebaseio.com")
   .build();
        
FirebaseApp app = FirebaseApp.initializeApp(options, "myFirestoreApp");
Firestore db = FirestoreClient.getFirestore(app);

Now, when I create db like this and run my update code, I get the error message

java.lang.IllegalStateException: Firestore client has already been closed

I won't post the precise update code here, as it on the one hand is very long, and on the other hand runs smoothly with the first type of creation of Firestore instances. However, simpler toy code has worked just fine, so I have checked that the security rules have indeed been bypassed with the second type of creation of Firestore.

I use NetBeans, and in both cases above, the maven imports are these:

<dependencies>
   <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>libraries-bom</artifactId>
      <version>8.0.0</version>
      <type>pom</type>
      <scope>import</scope>
   </dependency>
   <dependency>
      <groupId>com.google.firebase</groupId>
      <artifactId>firebase-admin</artifactId>
      <version>7.1.1</version>
   </dependency>
</dependencies>>

This confusion over the difference between these two Firestore instances leads me to two questions:

  1. If I use my initial creation of Firestore, can I set the security rules so as to grant this Firestore instance permission to write, while blocking any client-side write access - for instance from Android apps?

  2. What is a common cause for the above error message concerning the second creation of Firestore, and is there a way to prevent the Firestore client from closing?

2

2 Answers

0
votes

If I create the Firestore instance by

FirestoreOptions firestoreOptions = FirestoreOptions.newBuilder()
                                       .setCredentials(myCredentials)
                                       .setProjectId("myFirestoreProject")
                                       .build();
                       
           
Firestore db = firestoreOptions.getService();

, the security rules are bypassed, and this instance does not close until all data has been written.

0
votes

To answer your first question, as mentioned in the Note in the documentation:

"The server client libraries bypass all Cloud Firestore Security Rules and instead authenticate through Google Application Default Credentials. If you are using the server client libraries or the REST or RPC APIs, make sure to set up Identity and Access Management (IAM) for Cloud Firestore."

So the rules will only apply to the client side. You can deny all client side access by denying all access to the database.

As for your second question, check that your code doesn't initialize or close the database more than once and make sure it follows the example in the documentation.