These are my current security rules:
service cloud.firestore {
match /databases/{database}/documents {
match /tournaments/{tournament=**} {
allow update, delete: if request.auth.uid == resource.data.author;
allow create: if request.auth.uid != null;
allow read: if true;
}
}
}
Everything is working, only updating or deleting does not work due to "Missing or insufficient permissions"
Here is my code for that
mDocRef
.update(key, score)
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "DocumentSnapshot successfully updated!");
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.w(TAG, "Error updating document", e);
}
});
mDocRef refers to a document with a path of "tournaments/tournamentID/aColletion/aDocument"
Here is the code to create mDocRef:
mTournamentDocRef = mTournamentColRef.document();
mDocRef = mTournamentDocRef.collection("aCollection").document();
Some things to note:
the user is during the whole process signed in through firebase
I tried using .where("author", "==", user.uid) but got an "cannot resolve method" error
Thank you for your help!
I checked the documentation, those are the rules you are looking for:
match /users/{userId} {
allow read, update, delete: if request.auth.uid == userId;
allow create: if request.auth.uid != null;
}
And the documentation link as a reference: https://firebase.google.com/docs/firestore/security/rules-conditions?authuser=0
I added the ID of the firebase user to each document that is created to get the wanted results
tournamentData.put("author", user.getUid());
data.put("author", user.getUid());
Related
I wrote the rules for my firestore database that is only authenticated users can read and write the data into documents. The rule is:
service cloud.firestore {
match /databases/{database}/documents {
// Allow the user to access documents in the "cities" collection
// only if they are authenticated.
match /PhoneAuthUsers/{phoneid=**} {
allow read, write: if request.auth.uid != null;
}
}
}
and i published the rule whenever i run the app i got the error line
"Missing Insufficient Permissions"
My authentication method is Phoneauthentication and i have the auth id in authentication tab. This is my db structure :
**- PhoneAuthUsers(Collection)
- Phonenumber(documents)
- Accounts(Collection)
-autoid(Document)**
Client side code :
db.collection("PhoneAuthUsers")
.whereEqualTo("PhoneNumber", mobile)
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
System.out.println("hello user");
if (task.isSuccessful()) {
for (DocumentSnapshot document : task.getResult()) {
Log.d(TAG, document.getId() + " => " + document.getData());
AuthID = document.getId();
System.out.print("AuthIDPhonenumber" + AuthID);
System.out.println("PhoneNumber");
String phone = document.getString("PhoneNumber");
Log.d(TAG, "onSuccess: " + phone);
if (phone.equals(mobile)) {
count = count + 1;
System.out.println("count" + count);
}
}
When trying to execute the query it shows missing insufficient permissions.
For example :
PhoneAuthUsers/+911234567890/Accounts/autoid/Field Values. when getting the documents from the phoneauthusers i am getting the missing insufficient permissions. How to solve the issue.
I'm storing structured data on Firestore and I want to get the data of a specific nested document and it must be in real time because the data is updating in real time.
Here is the structure in Firestore:
So basically, I have the following path :
collection(Users).document(23).collection(orders).document(16)
or
/Users/23/orders/21
..and I want to get the data of document 16 of the "orders" collection.
Here's the code
class FireStoreMapActivity{
DocumentReference doc;
void getData(){
doc = FirebaseFirestore.getInstance()
.collection("Users")
.document(23 + "")
.collection("orders")
.document(21 + "");
doc.addSnapshotListener(this, (documentSnapshot, e) -> {
if (documentSnapshot != null) {
if (documentSnapshot.exists()) {
List<String> images = (List<String>) documentSnapshot.get("images");
Log.e(">>>>>>>>>>> size => ", locHistory.size() + " ");
} else {
Log.e(">>>>>>>> error ", " documentSnapshot != exists");
}
} else {
Log.e(">>>>>>>> error ", " documentSnapshot = null");
}
});
}
}
But every time, the null log appear
Log.e(">>>>>>>> error ", " documentSnapshot = null");
UPDATE
well, after printing the exception it seems to be a permission error!
com.google.firebase.firestore.FirebaseFirestoreException: PERMISSION_DENIED: Missing or insufficient permissions.
Here's my ruleset
service cloud.firestore {
match /databases/{database}/documents {
match /Users/{document=**} {
allow read, write: if request.auth.uid != null;
}
}
}
but how to inform Firestore that I'm in fact an authenticated user.!?
If documentSnapshot is null and you're sure the document exists, then e will have a value. Print it to see what the problem is, which typically will be something about permissions.
From your update it seems your rules requires just an authenticated user, but your listener is rejected by the server based on those rules. This means you're not signed in, or at least not signed in when you try to read from Firestore.
Given that the code you shared shows nothing about authentication, it's hard to say more. If you'd like to verify though, log the current user just before you call addSnapshotListener with something like Log.i("User", FirebaseAuth.getInstance().getCurrentUser()).
Ok I am a running into a weird problem with firestore
I have the following structure
collection1 -> document1- >collection2
I am adding a new document to collection2 with on complete listener( I tried the success listener as well). No errors are shown. The new document is not shown on the console. The listener is not being called. However, when I query, I get ALL the added documents including the new ones. What's going on here?
Map<String, Object> data = new HashMap<>();
data.put("completed", true;
data.put("date_completed", new Date());
data.put("location", "123 main st");
data.put("notes", "");
data.put("work", "");
db.collection("collection1").document(document1).collection("collection2")
.add(data)
.addOnCompleteListener(new OnCompleteListener<DocumentReference>() {
#Override
public void onComplete(#NonNull Task<DocumentReference> task) {
if (task.isSuccessful()) {
DocumentReference documentReference = task.getResult();
Log.d(TAG, "DocumentSnapshot written with ID: " + documentReference.getId());
det.setDocId(documentReference.getId());
addr_arr.add(det);
} else {
Log.w(TAG, "Error adding document", task.getException());
Toast.makeText(EditListActivity.this, "Failed operation - " + task.getException().getMessage(),
Toast.LENGTH_SHORT).show();
hideProgressDialog();
}
}
});
Here is the query I do
CollectionReference collecRef = db.collection("collection1").document(document1).collection("collection2");
collecRef.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (DocumentSnapshot document : task.getResult()) {
// here I do document.get on all the fields, add them to object and add object to array
}
adapter.notifyDataSetChanged();
} else {
Log.d(TAG, "get failed with ", task.getException());
Toast.makeText(EditListActivity.this, "Failed operation - " + task.getException(),
Toast.LENGTH_SHORT).show();
hideProgressDialog();
}
}
});
Your app is acting like it's offline or somehow lacking a working internet connection.
When there is no network connection, the Firestore SDK won't fail on writes. It will store the write locally, and eventually sync that to the server. In the meantime, the local write becomes part of any queries, just like if the data was available on the server. So probably what you're seeing is the result of your local write.
(This is the same behavior you would see with Realtime Database.)
As far why the Firestore SDK doesn't have a connection, I don't know. You might have to troubleshoot that on your own.
private void registerUser(){
String emailId = email.getText().toString().trim().toLowerCase();
String password = pass.getText().toString().trim();
firebaseAuth.createUserWithEmailAndPassword(emailId,password)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
progressDialog.dismiss();
if(task.isSuccessful()){
Toast.makeText(MainActivity.this,"Registration Successful",Toast.LENGTH_SHORT).show();
//show chatroom
finish();
startActivity(new Intent(getApplicationContext(),ProfileActivity.class));
}
else{
Toast.makeText(MainActivity.this,"Registration Failed. Please try again",Toast.LENGTH_SHORT).show();
}
}
});
}
I wish to add a username or display name to it but am unable to do so. I tried a few things but still no result. Kindly help me. I need this functionality for a project submission this week.
This is definitely possibly but just not in the user creation method.
Once you've created your user (possibly in the addOnSuccessListener) you can use something similar to the following code to update the Users DisplayName:
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
UserProfileChangeRequest profileUpdates = new UserProfileChangeRequest.Builder().setDisplayName("John Smith").build();
user.updateProfile(profileUpdates);
Hope this helps!
Edit: I previously said to add the code to the AuthStateListener, however, Frank's suggestion below to put it in the addOnSuccessListener is better/makes more sense so I have updated the answer to reflect this.
I just recently investigated this issue for my own implementation (SDK version 4.4.1). What I've found is that it works perfectly if you are sure to utilize the exact same task.result object from registration/login and not the object from the default instance.
Another work around that helped me is to have an email reference table in your FB DB like this:
{ "EmailRef": { "username1" : "email# domain .com"}, {"username2" : "email2#domain.com"} }
And then to query for the username by the user's email (from auth.CurrentUser.Email) using a method like this:
public static void GetCurrentUserName(Firebase.Auth.FirebaseUser user)
{
string message = "";
DatabaseReference dbRef = FbDbConnection();
FirebaseDatabase.DefaultInstance.GetReference("EmailRef").OrderByValue().EqualTo(user.Email).GetValueAsync().ContinueWith(task =>
{
if (task.IsFaulted)
{
message = "GetCurrentUserName encountered an error: " + task.Exception;
ModalManager.BuildFireBaseDebugModal(message);
Debug.LogError(message);
return;
}
if (task.IsCanceled)
{
message = "GetCurrentUserName was canceled.";
Debug.LogError(message);
return;
}
if (task.IsCompleted)
{
foreach (DataSnapshot ss in task.Result.Children.AsEnumerable())
{
try
{
if (ss.Value != null)
{
if (ss.Value.ToString() == user.Email)
{
message = "GetCurrentUserName was successful -- Email: " + user.Email + " Username: " + user.DisplayName;
Debug.LogError(message);
}
}
return;
}
catch (Exception ex)
{
message = "GetCurrentUserName Exception: " + ex;
Debug.LogError(message);
return;
}
}
}
});
}
i have set up firebase storage for my app, and added the code for anonymous auth on the app and on the firebase console.
it worked at first, but i dont know why it stopped working, saying that the user does not have permission to access the object
Anonymous auth is correctly set up and i did see it working, code is almost like Google Firebase docs
logcat:
D/FirebaseAuth: signInAnonymously:onComplete:true
D/FirebaseAuth:
onAuthStateChanged:signed_in: (Random auth user id)
... When i request the item from firebase
E/StorageUtil: error getting token java.util.concurrent.ExecutionException: com.google.firebase.FirebaseException: An internal error has occured. [Internal error encountered.]
I/DpmTcmClient: RegisterTcmMonitor
from: com.android.okhttp.TcmIdleTimerMonitor W/NetworkRequest: no auth
token for request E/StorageException: StorageException has occurred.
User does not have permission to access this object.
Code: -13021 HttpResult: 403
Can Someone help?
Declaring Variables
private FirebaseAuth mAuth;
private FirebaseAuth.AuthStateListener mAuthListener;
on the OnCreate method
mAuthListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
FirebaseUser user = firebaseAuth.getCurrentUser();
if (user != null) {
// User is signed in
Log.d("FirebaseAuth", "onAuthStateChanged:signed_in:" + user.getUid());
} else {
// User is signed out
Log.d("FirebaseAuth", "onAuthStateChanged:signed_out");
}
// ...
}
};
mAuth.signInAnonymously()
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
Log.d("FirebaseAuth", "signInAnonymously:onComplete:" + task.isSuccessful());
// If sign in fails, display a message to the user. If sign in succeeds
// the auth state listener will be notified and logic to handle the
// signed in user can be handled in the listener.
if (!task.isSuccessful()) {
Log.w("FirebaseAuth", "signInAnonymously", task.getException());
Toast.makeText(SingleMemeEditor.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
}
// ...
}
});
and the method that gets from storage:
Bitmap bmp;
final Context lContext = context; //getting the MainActivity Context
final String lFileName = fileName; //filename to download
final String lCatPath = catPath; //internal categorization folder
FirebaseStorage storage = FirebaseStorage.getInstance();
// Create a storage reference from our app
StorageReference storageRef = storage.getReferenceFromUrl(context.getResources().getString(R.string.firebase_bucket));
// Create a reference with an initial file path and name
StorageReference filesRef = storageRef.child("files/" + fileName);
try
{
final File localFile = File.createTempFile("images", "jpg");
filesRef.getFile(localFile).addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>()
{
#Override
public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot)
{
// Local temp file has been created
File file = new File(getDirectory(lContext)
+ File.separator + lCatPath + File.separator + lFileName);
try
{
Boolean b = file.createNewFile();
if(b)
{
FileInputStream in = new FileInputStream(localFile);
FileOutputStream out = new FileOutputStream(file);
// Transfer bytes from in to out
byte[] buf = new byte[(int)localFile.length()];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
out.close();
}
Drawable.createFromPath(file.getPath())).getBitmap());
}
catch (IOException ex)
{
// Handle any errors
Log.e("CopyingFromTemp", ex.getMessage());
}
}
}).addOnFailureListener(new OnFailureListener()
{
#Override
public void onFailure(#NonNull Exception ex)
{
// Handle any errors
Log.e("FirebaseDownloadError", ex.getMessage());
}
});
}
catch(Exception ex)
{
Log.e("FirebaseDownloadError", ex.getMessage());
}
also i'm using standard security rules:
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
as Benjamin Wulfe hinted, i deleted the App's data on the phone and it worked, which means that some kind of Token data was stored on the phone and Anonymous Auth was getting an old session data.
so i added a sign out code before signInAnonymously
mAuth.signOut();
and done!
Thanks to you all for the help!
EDIT: I found another method which is better than signing out and in again (which lead to hundreds of unused anonymous users on the firebase console, and that because the app is not in production yet, would have been millions).
this is what i did:
if (mAuth.getCurrentUser() != null)
{
mAuth.getCurrentUser().reload();
}
else
{
mAuth.signInAnonymously()
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>()
{
#Override
public void onComplete(#NonNull Task<AuthResult> task)
{
Log.d("FirebaseAuth", "signInAnonymously:onComplete:" + task.isSuccessful());
// If sign in fails, display a message to the user. If sign in succeeds
// the auth state listener will be notified and logic to handle the
// signed in user can be handled in the listener.
if (!task.isSuccessful())
{
Log.w("FirebaseAuth", "signInAnonymously", task.getException());
Toast.makeText(MainActivity.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
}
// ...
}
});
}
this just reloads current authenticated (anonymous) user.
The message " W/NetworkRequest: no auth token for request " is key for debugging this issue.
This log message means that Firebase Storage did not see any login in the current context. This includes anonymous logins. It means that no authorization was passed to the backend and the only way this will be allowed is if you set your rules to be completely open (public access) which is not recommended (see below).
//this sets completely open access to your data
allow read, write;
I would review the code you have for logging in and ensure it successfully completes before any storage operation is done.
If you are sure your auth code is correct, try resetting data on the device so that no saved state might be there to mess up the application's authorization information.
You might have to check your RULES for storage in firebase console. By default it is set to only permit to authenticated user only
like this
allow read, write: if request.auth != null;
Sometime its disconnect from firebase database,So Connect your app with firebase authentication in android studio through firebase assistance tool.