resource.data.cid == request.auth.uid Missing or insufficient permissions - firebase

I get the error code "permission-denied" with error message "Missing or insuffiecient permisions" and I'm not sure why, I'm new to this.
Here is the rule defined in firestore:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function depositRecordAuth() {
return request.auth != null &&
resource.data.cid == request.auth.uid &&
timestamp.value(request.auth.token.expires)>= request.time;
}
match /deposit/{docId} {
allow read , delete: if
depositRecordAuth();
}
}
}
And the only piece of code which throws the error is: resource.data.cid == request.auth.uid
If I remove it, it will work.
An this is the code which throws the error:
fireStoreDocListener = (collection: string, docId: string, CallBack: (data: firebase.firestore.DocumentData | undefined) => void) =>
this.firestore?.collection(collection).doc(docId)
.onSnapshot((doc) => {
const docData = doc.data();
CallBack(docData);
}, (err: IFireBaseError) => {
let errorDescription = "";
if (err.code === 'permission-denied') {
//do somthing
errorDescription = `firebase error on doc listener , error code - ${err.code} , error message - ${err.message}`;
} else {
//do somthing else
errorDescription = `firebase error on doc listener , error code - ${err.code} , error message - ${err.message}'`;
}
})
Updated with logging:
the console.log(collection, docId) has
Collection: deposit
docId: 109003
and the doc in firebase:
doc

Related

Firestore security rules - Property resource is undefined on object

For some reason when I try to use the resource.data I get an error saying that resource is undefined. In this case it occurs in the notUpdating() function. Why is this and how can I fix it?
I'm new to firebase - Thanks for the help!
Error: simulator.rules line [12], column [28]. Property resource is undefined on object.
Rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
function authed() {
return request.auth != null
}
function matchesUser() {
return request.auth.uid == userId
}
function notUpdating(field) {
return !(field in request.resource.data) || resource.data[field] == request.resource.data[field]
}
match /folders/{docId} {
allow read: if authed() && matchesUser()
allow create: if authed() && matchesUser()
}
match /files/{docId} {
allow read: if authed() && matchesUser()
allow create: if authed() && matchesUser()
allow update: if authed() && matchesUser() && notUpdating("userId")
}
}
}
}
My code for adding and updating a file:
function addFile() {
if (!filePathInp.value) return;
const fileName = filePathInp.value.split("\\").pop();
const fileRef = ref(storage, `users/${$user.uid}/files/${fileName}`);
const uploadTask = uploadBytesResumable(fileRef, filePathInp.value);
uploadTask.on(
"state_changed",
(snapshot) => {},
(error) => {
console.log(error);
},
() => {
// upload complete
getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
const files = collection(firestore, `users/${$user.uid}/files`);
const filesQuery = query(
files,
where("name", "==", fileName),
where("userId", "==", $user.uid),
where("parentPath", "==", path)
);
getDocs(filesQuery).then((doc) => {
if (doc.empty) {
addDoc(files, {
url: downloadURL,
name: fileName,
createdAt: new Date(),
parentPath: path,
userId: $user.uid,
});
} else {
updateDoc(doc.docs[0].ref, {
url: downloadURL,
});
}
});
});
}
);
filePathInp.value = "";
}

Cannot query Firestore due to security rules

These are my rules. User can only access accounts associated with that user (account document has field users containing uid)
function isLoggedIn() {
return request.auth != null;
}
function getAccount(accountID) {
return get(/databases/$(database)/documents/accounts/$(accountID)).data;
}
function isBelongTo(accountID) {
return isLoggedIn() && (request.auth.uid in getAccount(accountID).users);
}
match /accounts/{accountID}/{documents=**} {
allow read: if isBelongTo(accountID);
}
This works fine on the rules playground. But when I do a query in firebase javascript sdk in a browser like this
onSnapshot(query(collection(db, "accounts"), where('users', 'array-contains', uid ? uid : '')), (querySnapshot) => {
const docs = [];
// querySnapshot does not have .map, so we have to use .forEach
querySnapshot.forEach((doc) =>
docs.push({ ...doc.data(), id: doc.id })
);
console.log(docs);
});
it returns an empty array (in my larger project but not the demo linked below) and returns a permission error.
if i first get a single document with the below code, then the query above returns the document /accounts/35 in the query snapshot, and also returns a permission error:
onSnapshot(doc(db, "accounts", "35"), (doc) => {
if (doc.exists) {
// console.log("Document data:", doc.data());
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
})
I'm made a sample repo and here is the result:
How can I resolve this?

How to Load Firestore Bundles using Flutter?

As per documentation written in FlutterFire about Firestore bundles, I was unable to create such a thing. It seems documentation lacks something as all I get was error codes: Permission is denied
static const bucket = "felix-3610.appspot.com";
static const storage = "https://storage.cloud.google.com";
final Message msg = Get.find();
Future<QuerySnapshot<Map<String, Object?>>> getEpic() async {
QuerySnapshot<Map<String, Object?>>? snapshot;
const path = "$storage/$bucket/music/bundles/epic_bundles.txt";
Reference httpsReference = FirebaseStorage.instance.refFromURL(path);
try {
String url = await httpsReference.getDownloadURL();
final response = await http.get(Uri.https(url));
Uint8List buffer = Uint8List.fromList(response.body.codeUnits);
LoadBundleTask task = FirebaseFirestore.instance.loadBundle(buffer);
await task.stream.last.then((value) async {
snapshot = await FirebaseFirestore.instance.collection("latest-epic").get(const GetOptions(source: Source.cache));
});
} on FirebaseException catch (e) {
log(e.code); //unauthorized
}
return snapshot!;
}
I could not know where I went wrong. Have people tried to code and load Firestore bundles in Flutter? Please, I do need a good documentation of how to do it correctly.
E/StorageException(24282): StorageException has occurred.
E/StorageException(24282): User does not have permission to access
this object. E/StorageException(24282): Code: -13021 HttpResult: 403
E/StorageException(24282): { "error": { "code": 403, "message":
"Permission denied." }} E/StorageException(24282):
java.io.IOException: { "error": { "code": 403, "message":
"Permission denied." }} E/StorageException(24282): at
com.google.firebase.storage.network.NetworkRequest.parseResponse(NetworkRequest.java:445)
E/StorageException(24282): at
com.google.firebase.storage.network.NetworkRequest.parseErrorResponse(NetworkRequest.java:462)
E/StorageException(24282): at
com.google.firebase.storage.network.NetworkRequest.processResponseStream(NetworkRequest.java:453)
E/StorageException(24282): at
com.google.firebase.storage.network.NetworkRequest.performRequest(NetworkRequest.java:272)
E/StorageException(24282): at
com.google.firebase.storage.network.NetworkRequest.performRequest(NetworkRequest.java:289)
E/StorageException(24282): at
com.google.firebase.storage.internal.ExponentialBackoffSender.sendWithExponentialBackoff(ExponentialBackoffSender.java:76)
E/StorageException(24282): at
com.google.firebase.storage.internal.ExponentialBackoffSender.sendWithExponentialBackoff(ExponentialBackoffSender.java:68)
E/StorageException(24282): at
com.google.firebase.storage.GetDownloadUrlTask.run(GetDownloadUrlTask.java:77)
E/StorageException(24282): at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
E/StorageException(24282): at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
E/StorageException(24282): at java.lang.Thread.run(Thread.java:764)
The way I generated bundle is using Cloud Scheduler, like in this example below:
// #ts-check
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const firestore = admin.firestore();
const fs = require("fs");
// This will create it every month
exports.BundleRelax = functions.pubsub.schedule("0 0 1 * *").retryConfig({
retryCount: 3,
maxRetryDuration: "30s",
}).onRun(async (context) => {
const bundleId = "latest-relax";
const bundle = firestore.bundle(bundleId);
const querySnapshot = await firestore.collection("Music_Relax").where("status.state", "==", 1).get();
// Build the bundle
const bundleBuffer = bundle.add("latest-relax-query", querySnapshot).build();
// Create tmp local file to upload
const bundledFilePath = "/tmp/bundle.txt";
fs.writeFileSync(bundledFilePath, bundleBuffer);
// Upload bundle file to Storage
const bucket = "felix-366510.appspot.com";
const destination = "relax_bundle.txt";
await admin.storage().bucket(bucket).upload(bundledFilePath, {
destination,
public: true,
metadata: {
cacheControl: "public, max-age=3110400",
},
}).then((value) => {
return Promise.resolve("success");
});
});
Security Rules for Cloud Storage:
rules_version = '2';
//Read operation: get, list
//Write operation: create, update, delete
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if isAdmin();
}
match /music {
allow read: if isIDN();
allow get, list, create, update: if isPublisher();
}
match /server {
allow read, create, update: if isUser();
}
match /publishers {
allow read: if isUser();
allow get, list, create, update: if isPublisher();
}
match /clients {
allow read, write: if isFelix();
}
//This is for any subcription inside Felix Apps
//Value 0 is equal to FALSE
//Value 1 is equal to TRUE
//This is for all versions
function isMT() {
return mtIDN() || mtEN() || mtCHN();
}
function isMTtrial() {
return trMT_IDN() || trMT_EN() || trMT_CHN();
}
//IDN version code 1
//Trials here
function trMT_IDN(){
return isIDN() && (request.auth.token.T11 == 1);
}
//Subs here
function mtIDN(){
return isIDN() && (request.auth.token.MT1 == 1);
}
//EN version code 2
//Trials here
function trMT_EN(){
return isEN() && (request.auth.token.T21 == 1);
}
//Subs here
function mtEN(){
return isEN() && (request.auth.token.MT2 == 1);
}
//CHN version code 3
//Trials here
function trMT_CHN(){
return isCHN() && (request.auth.token.T31 == 1);
}
//Subs here
function mtCHN(){
return isCHN() && (request.auth.token.MT3 == 1);
}
function isFelix(){
return isIDN() || isEN() || isCHN();
}
function isIDN(){
return isUser() && request.auth.token.IDN == true;
}
function isEN(){
return isUser() && request.auth.token.EN == true;
}
function isCHN(){
return isUser() && request.auth.token.CHN == true;
}
function isUser() {
return (isClient() || isServer());
}
function isPublisher(){
return (isMusician() || isGross() || isNet() || isFree());
}
function isMusician(){
return isUser() && roleNew();
}
function isNet(){
return isUser() && roleNet();
}
function isGross(){
return isUser() && roleGross();
}
function isFree(){
return isUser() && roleFree();
}
function isAdmin(){
return request.auth.uid != null && request.auth.token.Admin == true;
}
function isServer(){
return request.auth.uid != null && request.auth.token.HaD == true;
}
function isClient(){
return request.auth.uid != null && request.auth.token.Felix == true;
}
//This is for roles claim inside HaD App
function roleNew(){
return request.auth.token.role == "Unregistered";
}
function roleNet(){
return request.auth.token.role == "Net";
}
function roleGross(){
return request.auth.token.role == "Gross";
}
function roleFree(){
return request.auth.token.role == "Free";
}
}
}

Firebase Rules and flutter : How to check for username availability

Hello I am working with Firestore and flutter. I need to check the username availability when someone creates a new account.
I want to make that when the user is not connected in the app, the field 'username' of the collection "User Data" can be access with get().
However, the code in rules return several errors of 'expected {' but even if I add the '{', it stills does not accept it.
The code in rule that doesn't work and firebase won't allow me to install this rule:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth.uid != null;
}
match /User Data/{User Data} {
allow read: true;
}
}
What I've tried so far :
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth.uid != null;
}
match /User Data/{User Data} {
allow read: request.resource.data == resource.data.username;
}
}
The code in flutter :
Future<bool> checkUsernameAvailability(String val) async {
final result = await Firestore.instance.collection("User Data").where('username', isEqualTo: val).getDocuments();
return result.documents.isEmpty;
}
onPressed: () async {
final valid = await checkUsernameAvailability(_usernameController.text);
if (!valid) {
error = AppLocalizations.of(context)
.translate('this_username_is_not_available');
} else if (_formKey.currentState.validate()) {
setState(() => loading = true);
dynamic result =
await _auth.registerWithEmailAndPassword(
_emailController.text,
_passwordController.text,
_nameController.text,
_usernameController.text);
if (result == null) {
setState(() {
loading = false;
error = AppLocalizations.of(context)
.translate('please_enter_email');
});
}
}
}
All help is welcomed thanks!
You can seperately write security rules for all collections. When you use match /{document=**} expression to allow read and write for authenticated users, it overrides other rules.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /User Data/{User Data} {
allow read: request.resource.data == resource.data.username
allow write: if request.auth.uid != null;
}
}

Firestore security rules email_verified not working

If I create a new user with createUserWithEmailAndPassword, even though I didn't verify the mail yet, that user is already logged in. And his .emailVerified === false, and until here all good.
Now, I go to the mail, verify it using the link, go back to the web app, it is still .emailVerified === false so I refresh the page, now .emailVerified === true.
So I try to reach this doc:
public async getPublicUserDetails() {
const currentUserId = this._angularFireAuth.auth.currentUser.uid;
try {
const docRef = this._angularFirestore.collection("users").doc(currentUserId).ref;
const doc = await docRef.get();
if (!doc.exists) {
return null;
}
return doc.data() as IPublicUserDetailsDto;
}
catch (error) {
console.error("User " + currentUserId + " details get failed! " + JSON.stringify(error));
throw error;
}
}
It catches an exception, saying I don't have the required permissions to access the doc.
The Firestore rules I'm using are:
rules_version = '2';
service cloud.firestore {
function dbDocs() { return /databases/$(database)/documents; }
function isSignedIn() { return request.auth != null && request.auth.uid != null; }
function isEmailVerified() { return isSignedIn() && request.auth.token.email_verified; }
function isCurrUser(uid) { return isSignedIn() && request.auth.uid == uid; }
function userExists(uid) { return exists(/databases/$(database)/documents/users/$(uid)); }
match /databases/{database}/documents {
match /users {
match /{userId} {
allow read: if isEmailVerified();
allow write: if isEmailVerified() && isCurrUser(userId);
}
}
}
}
I can refresh the page infinite times, but it will work only if I signOut & signIn again OR if I replace the allow read line with
match /{userId} {
allow read: if isSignedIn(); // replace this
allow write: if isEmailVerified() && isCurrUser(userId);
}
Conclusion: it seems like the request.auth.token.email_verified does not reflect the value provided inside the FirebaseAuth service, as it seems to get refreshed only if I log out and back in.
Can someone help me, please? Thank you all in advance!

Resources