I have a channel for communication between two users and I use Firebase storage to transfer files between these users.
I don't want a third user to have access to the channel's files.
I'm trying to identify authorized users using 'authfile' and the metadata inside it.
That authfile is uploaded using admin sdk.
Only specific users can download and upload files to specific path in storage where the authfiles metadata contains his uid. still the users are not allowed to touch the authfile.
The metadata inside the authfile looks like this:
metadata: {
AvnYaUFdaJh1j0FMIiKWoIC2MDw1: true, // firebase userId 1
YhFyYRbutlf1PFI4NLKTN4qWRhQ2: true, // firebase userId 2
}
my storage
The rules I've tried:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
// Channel Media
match /media/{channelId}/{allPaths=**} {
function isAllowedUser(uid) {
return /authfile.resource.metadata.users[uid] == true;
}
allow delete: if isAllowedUser(request.auth.uid)
&& request.resource.contentType != 'authfile'
allow read: if isAllowedUser(request.auth.uid)
&& request.resource.contentType != 'authfile'
allow write: if isAllowedUser(request.auth.uid)
&& request.resource.contentType != 'authfile'
&& request.resource.size < 16 * 1024 * 1024;
}
}
}
I am writing Firebase storage rules for the first time and would like advice on how this could be implemented.
I don't think you need 'users' here.
function isAllowedUser(uid) {
return /authfile.resource.metadata.users[uid] == true;
}
This should be valid
resource.metadata[uid] == true;
Additionally, if you need something like checking existence of another file or it's metadata, consider using Cloud functions as mentioned in this answer.
Related
I am trying to upload images with Firebase Storage JS SDK with some rules based on Firestore data.
imageList.forEach(element => {
tasks.push(firebaseStorage.child("Devices").child(dateTime).child(element.name).put(element, { contentType: element.type }))
});
Rules:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /Devices/{deviceId} {
// Authentication is required.
allow create: if request.auth.uid != null &&
// Only Admins are capable of uploading images.
firestore.exists(/databases/(default)/documents/Admins/$(request.auth.uid));
// Authentication is required.
allow read: if request.auth.uid != null &&
// Only Cashiers are capable of reading images.
firestore.exists(/databases/(default)/documents/Cashiers/$(request.auth.uid));
// No update or deletion of files in client (web) for now.
allow update, delete: if false
}
}
}
This is what I get. Any idea what went wrong here? TIA.
Is read and write permission are both required for uploading?
The match /Devices/{deviceId} matches is applied for the files in /Devices directory only. If the rule must be applied to a file within the deviceId sub directory then the path must be:
match /Devices/{deviceId}/{fileName} {
// ...
}
If a rule must be recursively applied to all sub directories, then use a wildcard:
match /Devices/{device=**}
so I have security rules like this:
service firebase.storage {
match /b/{bucket}/o {
match /eventPoster/{imageID} {
allow read: if isSignedIn()
allow create: if isSignedIn() && isImage() && lessThanNMegabytes(0.5)
}
function isSignedIn() {
return request.auth != null;
}
function isBelongTo(userId) {
return request.auth.uid == userId;
}
function lessThanNMegabytes(n) {
return request.resource.size < n * 1024 * 1024;
}
function isImage() {
return request.resource.contentType.matches('image/.*');
}
}
}
as you can see, I set only signed in user that can see/read the image. but if I copy and paste the link from my file in storage like below in incognito browser, I still can see my image
https://firebasestorage.googleapis.com/v0/b/XXXXXXX.appspot.com/o/eventPoster%2F0050370e-a226-4a69-a635-ceccce10007c?alt=media&token=44f6d5a4-ff44-4376-3c7b9dfac465
I expect that I can't see the image via the browser, I test it using google chrome. even though my app is for Android and iOS just for testing I use browser. I assume, I am not signed in if I access it via the browser
The URL you shared there is a download URL. That URL is publicly readable, regardless of security rules. The only way to stop it from working is by revoking its token from the Firebase console.
If you want a file in Firebase Storage to only be accessible in accordance to its security rules, don't generate download URLs for it, or at least don't share the download URL with anyone.
I've added rules to my Cloud Firestore database but still receive these issue messages:
any user can read your entire database
any user can write to your entire database
See the rules below.
Indeed, any user can read and write into "users" collection, otherwise I won't be able to log in/register a user. How to solve the issue?
Thanks
The problem with your current rules is that it allows any user to read/write any other user's user document. You need to restrict it a bit more to look at the userId of the documents. Something like this
service cloud.firestore {
match /databases/{database}/documents {
// Ensure the user is authenticated
function userIsAuthenticated() {
return request.auth != null && request.auth.uid != null
}
// Ensure the user owns the existing resource
function userOwnsResource() {
return userIsAuthenticated() && request.auth.uid == resource.data.userId
}
// Ensure the user owns the new resource
function userOwnsNewResource() {
return userIsAuthenticated() && request.auth.uid == request.resource.data.userId
}
match /users/{userId=**} {
allow read, update, delete: if userOwnsResource();
allow create: if userOwnsNewResource();
}
}
}
userOwnsResource checks that the user can access an existing resource. userOwnsNewResource checks that they can create a new document for their userId. You can obviously reuse these functions in other rules which is handy.
In order for me to get Fastly working with firebase storage I had to add the following permissions on each image and the storage bucket: Entity: User, Name: AllUsers, Access: Reader. Is there a way to avoid this tedious and unscalable method, since its all user uploaded media?
My firebase storage security looks like the following:
service firebase.storage {
match /b/myapp.appspot.com/o {
match /proUsers/{userId}/{allPaths=**} {
allow read, write: if request.auth.uid == userId || request.resource.size < 2 * 1024 * 1024 || request.resource.contentType.matches('image/png') || request.resource.contentType.matches('image/jpeg');
}
}
}
the error I receive on fastly is that: Anonymous users does not have storage.objects.list access to bucket and if I try to access image directly I get the error: Anonymous users does not have storage.objects.get access to object
Where do I allow for anonymous users to have read capabilities? I assumed setting allow read did precisely this.
To allow anonymous users to read from your database (but not write) you can change your rules to this:
service firebase.storage {
match /b/myapp.appspot.com/o {
match /proUsers/{userId}/{allPaths=**} {
allow write: if request.auth.uid == userId || request.resource.size < 2 * 1024 * 1024 || request.resource.contentType.matches('image/png') || request.resource.contentType.matches('image/jpeg');
allow read;
}
}
}
Is there a method/ruleset for Firebase 3 storage to disable file updating or overwriting?
I found data.exists() for Database but no solution for Storage.
TL;DR: In Storage Security Rules, request.resource ~= newData.val() and resource ~= data.val(), so you can use them similarly.
service firebase.storage {
match /b/<bucket>/o {
match /path/to/file {
allow write: if resource == null; // if !data.exists() in DB land
}
}
}