So I have a function in my website which allows users to upload a profile photo. The photo is stored in a folder in my Firebase storage under their UID and then once successfully uploaded, the downloadURL is stored under their UID in my Firebase realtime database like so:
database.ref('users/'+firebase.auth().currentUser.uid).update({
profilePhoto: downloadURL
});
Now I have some rules already in place so that only the auth UID can write data to themselves.
{
"rules": {
"users": {
".read": "true",
"$user": {
".read": "true",
".write": "auth.uid == $user",
"profilePhoto": {
".validate": "newData.isString()
},
}
}
}
}
Work great but how can I make it so only URL's coming directly from my Firebase storage are written?
For example, a user with knowledge could simply run the script above and change the data to something outside of the storage or just some random text which breaks the profile image when I fetch it.
One thing I thought of doing is adding the Firebase storage 'base path' as a validation like so:
"profilePhoto": {
".validate": "newData.isString()
&& newData.val().contains('mystorage-12345.appspot.com/')
}
But is there a better way of doing it? Will the 'base path' ever change down the line?
There are no combinations of rules that you could use to prevent a user from linking to a malicious file or entering gibberish. Since you're open to better methods, here's my standard pattern for handling uploads like this.
First, decide on a URL structure for your profile photos where the photo will be stored in your bucket with an easily guessable format. My preference is:
/u/<uid>/profile_<width>x<height>.jpg
Second, provide a signed URL for your user to upload their image to. This might be in a separate bucket that, when a new object appears, triggers a function resize the photo and then moves the resized photos to /u/<uid>/profile_<width>x<height>.jpg with the relevant widths and heights.
Finally, there's no need to store the URL of the profile photo because any user's profile can be displayed like this:
<img src="https://<bucketUrl>/u/<uid>/profile_512x512.jpg" onerror="this.src=' + "/img/default-profile.jpg" + '" />
What's happening here is it first attempts to load the user's profile photo using the guessable /u/<uid>/profile_<width>x<height>.jpg pattern and if it fails to load, for any reason, display a default photo in its place.
Advantages
Lower costs due to fewer database reads, writes and storage.
Simple, fast and reliable.
Built-in failover in case the bucket can't be reached for any reason.
Related
i'll first explain a little bit, i have a store website, with firebase as backend, when a user is logged in, it's all cool, when user is not logged in, i want to let that user add to cart, the problem is, i had to find a way of reference to create a document in firebase, so i added a function and when a non-logged user enters the website, the function calls, the function is creating a clientId and storing it in sessionStorage, when user adds to cart, a collection "guestCarts" gets created and in the collection, a document named the same as clientId, in it having the cart and in the cart are the products, the path would be like this guestCarts > 3dawd213sd > cart > /hereAreProducts/ .
Now, in the firebase rules, i want to let this kind of user create only once and edit/update/delete only the doc that has their clientId.. The problem is, how can i create a reference to the clientId in firebase Rules ?
i would have something like this :
match /guestCarts/{clientId}/{document=**}
\ allow read;
allow update,delete if clientId == clientId\
This is the part that is hard, how can i retrieve the clientId from the sessionStorage and compare it to the id in the match ?
To mention that, my project is made with React, if it has any sense.
I have a firebase realtime db with the following rules structure:
{
"rules": {
"users": {
".read" : true,
"$user_id": {
".write": "$user_id === auth.uid"
}
}
}
}
does it allow anyone to see the db users' emails even if they only sign up using socials (google, fb)?
how can i avoid it in case?
I've set read : true, cause i create dynamic links which on open through routing they show certain data of certain users to an unregistered user and should remain like that.
EDIT: to let you better understand the process, what i'd need to set are rules so that users can sign up, only signed in users can write data, but if they create a link (with one of their items id contained in the link) anyone can open that link and read the item.
Here is the tricky part, at the same time non registered users shouldn't be allowed to read users emails (the email address saved by firebase upon social login, i do not programmatically save email address in the db).
I wrote a simple chat application a while back to help me learn node and socket.io, I have recently been looking into firebase as a platform for an app/website, and I am running into an interesting problem I can't solve. Within the app there will be a chat app, there will be channels and all that good stuff, the hard part is there will be private chat, where 2 users can talk privately. The plan is to store the chat in a firebase database. Now I can easily restrict access to firebase databases based on if a user is authenticated, and even restrict user profile access to authenticated users and the user that owns that profile, but I am trying to figure out how to restrict access to the "private chat" children of the "chat" database, to only the 2 users that are in that conversation.
I am thinking the database would look something like this...
{
"chat": {
"channels": ['topics', 'current', 'blah', 'blah', 'blah'],
"{PRIVATE_CHAT_UID_GOES_HERE}": {
"users": ["{USER_ID_1}", "{USER_ID_2}"],
"messages": [{"from": "{USER_ID}", "message": "Hi there"},{...}]
"createdOn": "DATE GOES HERE"
},
"{PRIVATE_CHAT_UID_GOES_HERE}": {
"users": ["{USER_ID}", "{USER_ID}"],
"messages": [{...}, {...}],
"createdOn": "DATE GOES HERE"
}
}
}
Then I would restrict access to the child(private chat id) to only the users that are in the "users" array. That way no one can read or write to that particular chat, unless they are in that particular chat. I just have no idea how to go about it.
I know you can do things like
".read": "auth !== null && auth.uid = $uid"
But I don't think that would be applicable since it limits usage to the owner of the uid, and the uid would be automatically generated when I add a child to "chat" to start a private chat between users.
Is something like this even possible, or is there some better way to structure the data that would allow an easy restriction of access to only the 2 users that are part of the conversation?
I am trying to avoid having a node.js server sitting around just verifying if a user is in a chat, it seems like a pointless overhead, when you can list the database and handle auth directly from the database. I am more than happy to provide code samples of what I have, though I don't know that they are relevant. Thank you in advance for any help.
Your first problem is that you're storing the users in an array. An array is an ordered collection that can have duplicate values. In your scenario you don't want duplicate values, and most likely the order doesn't matter. In such cases you should use a set data structure, which in Firebase is modeled as:
"users": {
"{USER_ID_1}": true,
"{USER_ID_2}": true
}
Now you can restrict read access to the chat room to its members by:
{
"rules": {
"chat": {
"$roomid": {
".read": "data.child('members').child(auth.uid).exists()
}
}
}
}
Be careful mixing different data types in the same node. It's often best to keep each entity in its own top-level node, relating the different types by their key. For example, I'd separate the room's messages, members, and other metadata:
chat
rooms
<roomid1>
createdOn: ....
.... other metadata for the room
roomUsers
<roomid1>
user_id1: true
user_id2: true
roomMessages
<roomid1>
<message1>: { ... }
<message2>: { ... }
<message3>: { ... }
So I've been using Firebase as a database for my website (this is a web based project, using HTML, CSS and JS) and I'm running into a problem retrieving data from it.
Basically this site allows users to create a profile for a character (they can fill in the name, the characters stats etc...) and when they click submit, it'll save the values they filled out to the database.
The values are saved perfectly fine, but when I go to retrieve the data the command doesn't seem to do anything.
So in order to get the profiles, I've been trying to use this bit of code to get whatever is stored at the specified .ref(path):
var uid = firebase.auth().currentUser.uid;
var getChar = firebase.database().ref('/users/' + uid + '/chars/').orderByKey();
Which according to the Firebase docs should return a list of keys at the path that I specified in .ref(). However whenever I try to access whatever is in the var, it just gives me the string that contains a link to the database that looks like this:
https://#mydatabaseurlhere.firebaseio.com/users/uid/chars
Where #mydatabaseurlhere is the url I created on the Firebase app, and the uid is the authenticated user's ID.
I've been reading the docs, and its telling me that the above code should return a list of whatever is at the path that I specified, but so far it just gives me a link. Is there something I've been missing from the Docs that'll allow me to access whatever data is currently in the database? Because I've tried to take a snapshot using .once() to no avail either. I've also set the rules on /users/ to allow anyone to read/write to the database but I'm still not able to access the data (or maybe I am accessing, I'm just missing how to retrieve it).
Either way, I'm wondering how one can go about accessing this data, as I'm extremely confused as to why I can't seem to retrieve the data that has been successfully written to the database.
You're defining a query. But that doesn't yet retrieve the data.
To retrieve the data, you need to attach a listener. For example:
var uid = firebase.auth().currentUser.uid;
var getChar = firebase.database().ref('/users/' + uid + '/chars/').orderByKey();
getChar.on('value', function(snapshot) {
snapshot.forEach(function(child) {
console.log(child.key, child.val());
});
});
New to Meteor, I'm using the alanning:roles package to handle roles.
I've managed to be able to only publish a collection when a user is logged in, when the page is first refreshed.
Meteor.publish('col', function(){
if (Roles.userIsInRole(this.userId, 'admin')) {
console.log('authed');
return Sessions.find({});
} else {
console.log('not auth');
// user unauthorized
this.stop();
return;
}
});
Logging out kills access to the collection (I'm using mongol to see). Logging back in after logging out, or logging in from a logged out state when the page is first loaded, will not give me access.
The webapp I'm trying to build is something like an ticketing system. I'm trying to be secure, so no unnecessary publishing if the user is not authorized.
What I'm trying to do is, get ticket information submitted from users from a collection, and display it on the client screen (as long as the client is authorized first). Maybe a better way to handle this is to force a refresh (how do I do that?) after a user change so unauthorized users are "kicked" out? And render all relevant data from the private collection right after the user is authorized?
I actually managed to get what I want for now with helpers...
In my ./client/Subs.js file:
Meteor.subscribe('col');
Template.NewTicket.helpers({ // match the template name
// move this into method later, not secure because on client??
isAdmin() {
console.log('is admin.');
console.log(Meteor.userId());
Meteor.subscribe('col');
return Roles.userIsInRole(Meteor.userId(), 'admin');
},
});
and then somewhere in my template file ./client/partials/NewTicket.html:
<template name="NewTicket">
{{#if isAdmin}}
{{/if}}
</template>
to trigger the check? I'm 99% sure theres a better way.