"Insufficient permissions" querying with Vue.js to Firestore - google-cloud-firestore

I'm having problems querying Firestore with Vue.js.
This are my rules on Firestore:
service firebase.storage {
match /databases/{database}/documents {
match /users/{userid=**} {
function getRole(role){
return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.roles[role]
}
allow read, write: if getRole('admin') == true
}
}
}
This is my structure, if I have to change it, it doesn't matter. I'm learning, I want to make the best practices:
-users[Collection]
-userID[Document]
-forms[Collection]
-author: userID
-name: "name"
-roles: {
-admin: true
}
and this is the query that I'm trying with Vue.js:
usersRef.doc(`${this.user.uid}`).collection('forms').where('author', '==', true).get().then(snapshot => {
console.log(snapshot)
})

There was a typo. Instead of firebase, is firestore:
service firebase.storage
service firestore.storage.

Related

Firestore Security Rule that allows writing only to user's own document?

I want to restrict documents in certain collections to only have write access to the user whose uid matches the document id. This does not work, however, as it produces an invalid permission error on the client.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function isSignedIn() {
return request.auth != null;
}
function isOwnDocument() {
return request.auth.uid == request.resource.id;
}
match /userSettings/{doc} {
allow write: if isOwnDocument();
allow read: if isSignedIn();
}
}
}
You can pass the docId variable directly in the isOwnDocument() function instead of reading from the request.resource object as shown below:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function isSignedIn() {
return request.auth != null;
}
function isOwnDocument(docId) {
return request.auth.uid == docId;
}
match /userSettings/{docId} {
allow write: if isOwnDocument(docId);
allow read: if isSignedIn();
}
}
}
The resource property might be undefined in explained in this answer.
request.auth.uid == resource.id can be used to be but that'll throw an error if the document does not exist since your rule is works for write ops that includes create and update too). This rule will work only when used with allow create:. But best to pass the ID directly in the function parameters so it'll support both operations.

Firestore security when retrieving document by id denies permission

When I directly retrieve a document using .doc().get() firestore security rues deny permission. What do I need to do to allow CRUD operations for authenticated users of my Ionic app?
I have the following function in my PWA Ionic app
``` async getUserProfile(): Promise<firebase.firestore.DocumentSnapshot> {
const user: firebase.User = await this.authProvider.getUser();
this.currentUser = user;
console.log('found current user');
this.userProfile = firebase.firestore().doc(`userProfile/${user.uid}`);
console.log('returing userprofile', this.userProfile.get());
return this.userProfile.get();
}```
But the call to this.userprofile.get() fails due to security warning from Firestore Security rules
found current user
returing userprofile
ZoneAwarePromise
__zone_symbol__state: 0
__zone_symbol__value: FirebaseError: Missing or insufficient permissions. at new e (http://localhost:8100/vendor.js:92760:23) at http://localhost:8100/vendor.js:102987:28
.
.
.
(http://localhost:8100/polyfills.js:10248:56)
code: "permission-denied"
name: "FirebaseError"
toString: ƒ ()
message: "Missing or insufficient permissions."
stack: "FirebaseError: Missing or insufficient permissions.\n at new e
The Firestore Security Rules are set simply as
service cloud.firestore {
match /databases/{database}/documents {
match /userProfile/{userId}/{documents=**} {
allow read, update, get,delete: if request.auth != null && request.auth.uid == userId;
allow create: if request.auth != null;
}
}
}
What should be the correct security rule to implement to allow authenticated users to CRUD their documents? Or the correct way to retrieve the document from the collection
UPDATE: It works directly from the simulator but not from the app
Some inspiration. I use this very basic role based access to allow read and write where a normal user can read and admins can write:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
function googleProvider() {
return request.auth.token.firebase.sign_in_provider == "google.com";
}
function getUserData() {
return get(/databases/$(database)/documents/users/$(request.auth.uid)).data
}
allow read: if googleProvider() && getUserData().roles['user'] == true;
allow write: if googleProvider() && getUserData().roles['admin'] == true;
}
}
}
What version of security rules do you have?
There is a hint for the version 1:
Security rules use version 1 by default. In version 1, recursive wildcards match one or more path items. They do not match an empty path, so match /cities/{city}/{document=} matches documents in subcollections but not in the cities collection, whereas match /cities/{document=} matches both documents in the cities collection and subcollections.
That could explain why you can't get the data you want.
The version 2 behaves different:
In version 2 of the security rules, recursive wildcards match zero or more path items. match/cities/{city}/{document=**} matches documents in any subcollections as well as documents in the cities collection.
Here you can see the whole docu for it.
The version should be on top of your rules like here:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Matches any document in the cities collection as well as any document
// in a subcollection.
match /cities/{city}/{document=**} {
allow read, write: if <condition>;
}
}
}

Security rules Firestore

I'm trying to secure my data in Firestore. I have read the documentation and watch some videos but I still have some difficulties getting it right.
What I have built is a project app. With a data structure like this:
"School": {
school1:
school2: {
"Users": {
userId: {
"SchoolName": "school2"
}
}
"Projects": {
projectId: {
}
}
}
}
Only authenticated users can read and write to the whole database and only users in the same school can read and write data to that school. For example, only users in school2 can add a project to school2.
I tried something like this but it didn't work
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth.uid != null;
}
match /School/{schoolName} {
allow read, write: if get(/databases/{database}/documents/School/$(schoolName)/Users/{userId}).data.SchoolName[(schoolName)]
}
}
}
Can someone please show me how to do this and maybe some good explanation on how to think about security rules. Thank you very much in advance!
you made just one mistake, replace this line:
allow read, write: if get(/databases/{database}/documents/School/$(schoolName)/Users/{userId}).data.SchoolName[(schoolName)]
with this :
allow read, write: if get(/databases/{database}/documents/School/$(schoolName)/Users/{request.auth.uid}).data.SchoolName == "school2"

Vuexfire / Firestore rules : role-based access to select elements of a collection

I am having trouble implementing a role-based access system using Firestore rules and Vuexfire.
My goal is to be able to bind a list in my app to a Firestore collection, with only part of its elements visible / editable, based on user roles.
When attempting that with my current structure, giving a read/write access to a single element in the collection returns an "insufficient permissions" error when binding the collection and nothing is retrieved.
The behaviour I am trying to achieve is :
When accessing the list in my app, the user will only see the elements his roles collection grant him access to.
An empty list should only occur if either the collection is empty, or the user has no relevant role over any element in the collection.
The current structure can be illustrated as follows :
hotels/
hotelA
name: "hotel-a"
rooms/
roomA
roomB
hotelB
name: "hotel-b"
rooms/
users/
adminA
roles/
hotelA
role: "admin"
Using the following rules :
service cloud.firestore {
match /databases/{database}/documents {
allow read: if request.auth != null;
match /hotels/{hotel} {
function isSignedIn() {
return request.auth != null;
}
function getRole(rsc) {
return get(/databases/$(database)/documents/users/$(request.auth.uid)/roles/$(rsc.id)).data.role;
}
function isOneOfRoles(rsc, array) {
return isSignedIn() && (getRole(rsc) in array);
}
allow read, write: if isOneOfRoles(resource, ["admin"]);
}
}
}
I then use Vuexfire to bind a list of hotels to the hotels collection, like so :
const setListHotelRefFromId = (context) => {
var db = firebase.firestore()
var listHotelRef = db.collection('hotels')
context.dispatch('setListHotelRef', { ref: listHotelRef })
}
const setListHotelRef = firebaseAction(({ bindFirebaseRef, unbindFirebaseRef, commit }, { ref }) => {
commit(types.SET_LOADING, true)
bindFirebaseRef('list.hotel', ref).then(() => {
commit(types.SET_LOADING, false)
})
})
In the environment described above, suppose I log in as a user whose userID is "AdminA", and access the hotel list. I would be expecting to see a list with a single entry, "hotelA", but instead get an "insufficient permissions" error from Firestore.

How to allow only particular fields of a firestore document to be accessed publicly

Suppose I have this structure in a firestore database:
collection[
document: {
x: 'x',
y: 'y'
}
]
and I have this firebase rule in place:
service cloud.firestore {
match /databases/{database}/documents {
match /collection/{document} {
allow read: if true;
}
}
}
But this rule exposes the whole document in the above collection, What I want to do is to expose only x field, is it possible? Thanks
You can't. Security rules only work on a document-level basis, so you can't restrict read access to specific fields.
If you want to do something like you're suggesting, you'll probably need to restructure your data so that your non-public data is in a separate document. Most likely you'll want to do this by putting your private data in a subcollection. So you might end up with something like this...
collection: [
document: {
y: 'y'
private-collection: [
document: {
x: 'x'
}
]
}
]
And then you'd set up your security rules like:
service cloud.firestore {
match /databases/{database}/documents {
match /collection/{document} {
allow read: if true;
match /private-collection/{otherdoc} {
allow read: if someOtherRuleThatYouAddHere();
}
}
}
}
YOU CAN!
not do this for reading, but for updating. This is not what was asked for, but it's probably what many people came here for - including myself.
allow update: request.resource.data.diff(resource.data).affectedKeys().hasOnly(['MyField']);
Docs