I just installed FriendlyPix iOS and followed all the steps listed on the readme including deploying the cloud functions and rules. I am getting an error and I think it has to do with the storage rules. The error I get is:
User does not have permission to access gs://friendlypixsk.appspot.com/HS4uNP1BYNNnW2Qf59azZH8i3gp2/full/-LAoHLCCMnlD8RoFhUNd/jpeg.
This happens in the uploadPressed function in FPUploadViewController. It happens in the fullref.downloadURL function call. The closure passed to that function is getting an error.
The Firebase Storage rules I have are as follows:
// Returns true if the given UID matches the signed in UID,
// the uploaded file is an image and its size is below the given number of MB.
// Also allow deletes.
function isImageAndBelowMaxSize(uid, maxSizeMB) {
return request.auth.token.admin == true || (request.auth.uid == uid
&& (request.resource == null // Allow deletes
|| request.resource.size < maxSizeMB * 1024 * 1024 // Max size for the uploaded file
&& request.resource.contentType.matches('image/.*'))) // The file is an image
}
service firebase.storage {
match /b/{bucket}/o {
match /{userId}/thumb/{postId}/{fileName} {
allow read, write: if isImageAndBelowMaxSize(userId, 1);
}
match /{userId}/full/{postId}/{fileName} {
allow read, write: if isImageAndBelowMaxSize(userId, 5);
}
}
}
I have not modified them at all. The image does get uploaded and I can see it in the firebase console. I'm a little new to firebase and not sure exactly how these rules work. I have signed in using google sign in.
Any help would be greatly appreciated!!
Thanks
Related
I have a very simple Firebase Storage structure:
> check-in-station-content
> reports
These are two folders and I am trying to set up a restriction on the check-in-station-content to accept only video/mp4 files, not bigger than 30MB and to be uploaded only by authenticated parties. So, my rules looks like this:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read: if request.auth != null;
}
match /check-in-station-content {
// Allow write files, subject to the constraints:
// 1) File is less than 30MB
// 2) Content type is video/mp4
match /{videoId} {
allow write: if request.auth != null && request.resource.size < 30 * 1024 * 1024 && request.resource.contentType.matches('video/mp4')
}
}
}
}
Then, when I go to the UI for the storage and click the Upload button, nothing is stopping me of uploading whatever file having whatever size
When you use the Firebase console, you actually bypass all the security rules, since you are interacting with Cloud Firestore as the owner of the project (or another role with similar access, e.g. Editor). The rules apply only when you are using one of the Client SDKs or the other Client APIs.
By the way, note that your rule matches the check-in-station-background-content path and no the check-in-station-content one.
In a firestore database, I am using email only as authentication. The database has the following structure:
companies
jobs
users
For sake of readability (and for customer's peace of mind), I am using the email address as the Document ID for the users collection. A user document looks like this:
document id: tbogard#gmail.com
fields
name_first: Terry
name_last: Bogard
jobs_read: ["job_A"]
jobs_readwrite: ["job_B, job_C"]
When I try to grab the request token in the rules, it gives me errors (search for ***):
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Helper functions
function userExists(){
// *** Function not found error: Name: [exists]. ***
return exists(/databases/$(database)/documents/users/$(request.auth.token.email));
}
function userData(){
// *** Function [get] called with malformed path: /databases/(default)/documents/users/ ***
return get(/databases/$(database)/documents/users/$(request.auth.token.email)).data;
}
// For now, let's keep it simple, and enforce the User read/readwrite rules on Jobs.
match /jobs/{jobId}{
allow read: if userExists() && (jobId in userData().jobs_read || jobId in userData().jobs_readwrite);
allow write: if userExists() && jobId in userData().jobs_readwrite;
}
// Only allow Users to see their own profile and companies they belong to.
match /companies/{companyId}{
allow read: if userExists() && userData().email in resource.data.employees;
allow write: if false;
}
match /users/{userId}{
allow read: if userExists() && userData().email == resource.data.email;
allow write: if false;
}
}
}
I'm guessing request.auth.token.email is returning something like an optional? I can't find anything in the documentation explaining how the functions get/exists, which require a path, handle this. Is there a way that I could make the Firestore UID for each user the email address instead of the random string, or can I fix these rules some way?
I am having difficulty trying to diagnose a particular rule in my firestore.rules file. See that question here for context.
Is there a way to debug the firestore.rules file and/or functions? I'm using unit testing and the emulators to test my rules, but I would really love to see exactly what values are being evaluated by the rules engine.
For instance, here is my firestore.rules file:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /organizations/{orgId} {
allow read: if isAdmin();
allow create, update: if isAdmin();
match /classes/{classId} {
allow read: if request.auth.uid != null;
allow create, update: if isAdmin();
match /students/{studentId} {
allow read: if isAdmin() || belongsToCurrentClass();
allow create, update: if isAdmin();
}
}
}
}
}
function isAdmin() {
// removed for security
}
function belongsToCurrentClass() {
// retuns true if the authenticated user is the teacher of the requested class
return get(/databases/$(database)/documents/organizations/$(orgId)/classes/$(classId)).data.teacherUid == request.auth.uid;
}
What I'd love to do is set breakpoints or step through the code. When attempting CRUD operations on a organizations/{orgId}/classes/{classId}/students/{studentId} path I'd love to inspect exactly what values the orgId, classId, and studentId variables are holding, as well as the resource and request parameters. I'd love to inspect exactly which document (if any) is returned by the get request in belongsToCurrentClass and what the return value is.
Does anyone know of any way to do this? I think I'd answer my question referred to above in 10 seconds if I could just see the data being evaluated.
There is a local emulator for Cloud Firestore security rules. This is your best (and really only) tool for digging into security rule execution. There is no step-through debugging, but you can see a lot of debug output in the console.
https://firebase.google.com/docs/rules/emulator-setup
We can add the built-in debug function to rules. As noted in a comment, you'll see an unhelpful message like this in the browser:
Received: [path] Expected: [bool]. for 'list' # L6
On the plus side, we won't forget to remove debug messages. Tail the log file to see the output: tail -f firestore-debug.log
For example, to see which paths are being called:
match /databases/{database}/documents {
match /{document=**} {
allow create, read, update, delete: if debug(request.path);
}
}
I'm trying to secure my Firebase (google cloud storage) files based on user data. In firestore, I use a rule based on getting database content (look up the uid in users table and match a field) and that's working fine. I'm trying to use the same kind of rules in firebase storage but in the simulator I get Error: simulator.rules line [12], column [17]. Function not found error: Name: [get].; Error: Invalid argument provided to call. Function: [get], Argument: ["||invalid_argument||"]. My rules look like this:
match /b/{bucket}/o {
function isAuth() {
return request.auth != null && request.auth.uid != null
}
function isAdmin() {
return isAuth() &&
"admin" in get(/databases/$(database)/documents/users/$(request.auth.uid)).data.roles;
}
function clientMatch(client) { // expects user's "client" field to be ID of client
return isAuth() &&
client == get(/databases/$(database)/documents/users/$(request.auth.uid)).data.client;
}
match /P/Clients/{client}/{allPaths=**} {
allow read, write: if isAdmin() || clientMatch(client);
}
}
}
Line 12 is the one beginning client == get, in clientMatch().
I haven't been able to tell whether these functions are only supported for Firestore (db) rules, or whether they should work for storage as well.
If this doesn't work, what are my options? How do people look up user data for Firebase storage security?
You currently can't reference Firestore documents in Storage rules. If you would like to see that as a feature of Storage rules, please file a feature request.
Consider instead using a Cloud Functions storage trigger to perform some additional checks after the file is uploaded, and remove the file if you find that it's not valid.
This is now possible with cross-service security rules. You have to use firestore. namespace before the get() and exists() functions as shown below:
function isAdmin() {
return isAuth() && "admin" in firestore.get(/databases/$(database)/documents/users/$(request.auth.uid)).data.roles;
}
Do note that you'll be charged for read operations just like in Firestore security rules.
Trying to add security rules to storage. When I add a security rule to make sure only authenticated users should be allowed with read/write, simulator is working. But when I try to add another constraint on the size of a file, I'm encountering an error.
The following is the Security Rule:
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth != null && request.resource.size < 5 * 1024 * 1024;
}
}
}
I have files under files\
I get the following error in simulator:
Simulated read denied
Error details:
Error: simulator.rules line [4], column [29]. Property resource is undefined on object.
Issue happens if I try to simulate write as well.
EDIT1: IMPORTANT
OK! I found this question and tried experimenting a bit on that line and got the simulator allowing read/write! I made the following change:
allow read, write: if request.auth != null && (request != null
|| request.resource.size < 5 * 1024 * 1024);
That's basically I added a null check. So, at the moment, I'm not clear what's going on here!
I was able to work around this with a rule like
match /users/{uid}/{document=**} {
allow read, create, update: if
request.auth != null &&
request.auth.uid == uid &&
(!("resource" in request) || request.resource.data.uid == request.auth.uid);
}
In this case I wanted to ensure that the "uid" property in the update matches the user's uid. However, if in the simulator you don't do "Build Document" first then request.resource is undefined (I think this is a bug; it should be defined but null IMO).
I believe this really only applies in the simulator, and is not a realistic scenario since create/update requests will always contain a document, even an empty one. But I think I'll keep it in my rule just in case.