The task is to make a rule that stops us from reading a user's 'height' - but not their 'color'.
Database:
user: {
ryan: {
color: "red",
height: 170,
},
kirsty: {
color: "yellow",
height: 150,
}
}
Client code:
db.ref('user/ryan/height').on('value', (e) => {
console.log(e.val());
})
Rule:
This seems to have worked - but I don't know why. Does height written here capture all db values named "height"?
{
"rules": {
".write": true,
"height": {
".read": false
}
}
}
You could try this { "rules": { ".read": true, "$color": { ".write": false } } }
Related
I am trying now for too long to get my index right, but to no luck till now..
My database structure is:
(app name)
+ searches
+ -Kw_Eyk6zbCcFklO119u (firebase random key)
+ FlightOffers
+ c6aa0d29-31b7-4113-81ab-8de2e4ee877e (firebase random key)
+ PricingOptions
+ 0
+ Price
In React, I use the following code:
const searchRefOffers = firebase.database().ref('searches/' + this.Store.SearchStore.searchId + '/FlightOffers')
searchRefOffers.orderByChild("PricingOptions/Price").limitToFirst(100).on("value", (snapshot) => {
firesnap = snapshot.val()
writeFirebaseOffersToStore(firesnap)
})
My console keeps saying:
FIREBASE WARNING: Using an unspecified index. Your data will be downloaded and filtered on the client. Consider adding ".indexOn": "PricingOptions/Price" at /searches/-KxoEfnwgJtMxzuIY2FJ/FlightOffers to your security rules for better performance.
I use the following rules:
{
"rules": {
".read": true,
".write": true,
"searches": {
"$searchesid": {
"FlightOffers": {
"$FlightOffersid": {
"PricingOptions": {
"0": {
".indexOn": [
"Price"
]
}
}
}
}
}
}
}
}
Can someone please help me out? Thank a lot!!
If you are using orderByChild() then use $uid
{
"rules": {
".read": true,
".write": true,
"searches": {
"$uid": {
"FlightOffers": {
"$uid": {
"PricingOptions": {
".indexOn": ["Price"]
}
}
}
}
}
}
}
Use Uid for me this way worked if you are fetching orderByValue() then use
{
"rules": {
"scores": {
".indexOn": ".value"
}
}
}
Currently my Firebase Database looks like this below. I have three users :I want to make feature like this, when one user follow another user and the user follow him back, they will be friends.They wont be friends until they are both follower of each other. So far I have managed to make the followers/following like this : Now I am out of clue what to do next.
Small disclaimer: I didn't get a chance to test this, let me know if it does what you want.
Under followers and following you seem to be using push keys to add the userID of the person that's being followed or that is following. Instead, I would simply add the uid as a child and set the value of that node to something random. Then when you want an user's followers you'd get all the keys instead of all the values.
DB Structure: I omitted anything irrelevant for the sake of brevity.
root: {
user_profile: {
$user1: {
followers: {
$user2: true,
$user3: true
},
following: {
$user2: true,
$user3: true
},
friends: {
$user2: true,
$user3: true
}
},
$user2: {
followers: {
$user3: true,
$user1: true
},
following: {
$user3: true,
$user1: true
},
friends: {
$user3: true,
$user1: true
}
},
$user3: : {
followers: {
$user2: true,
$user1: true
},
following: {
$user1: true,
$user2: true
},
friends: {
$user1: true,
$user2: true
}
}
}
}
I think using DB Rules would be the easiest way to do this.
Using these rules, a user would only be able to write to friends/$friend if the two users in question are following each other.
{
"rules": {
"user_profile": {
".read": true,
"$user_profile": {
".write": "$user_profile === auth.uid",
"friends": {
"$friend": {
".write": "(data.parent().parent().child('followers/'+auth.uid).exists() && data.parent().parent().child('following/'+auth.uid).exists()) && $friend === auth.uid"
}
},
"followers" : {
"$follower": {
".write": "$follower === auth.uid"
}
}
}
}
}
}
Small example on how to follow someone.
func follow(uid: String) -> Void {
// Obv you'd want to do some extra checks here such as whether the user is logged in or not, but for the sake of brevity they're omitted.
let dbRef = FIRDatabase.database().reference().child("user_profile")
let userID = FIRAuth.auth()?.currentUser?.uid
dbRef.child("\(userID)/following/\(uid)").setValue(true);
dbRef.child("\(uid)/followers/\(userID)").setValue(true);
dbRef.child("\(userID)/friends/\(uid)").setValue(true); // These should fail if the users aren't following each other.
dbRef.child("\(uid)/friends/\(userID)").setValue(true); // These should fail if the users aren't following each other.
}
And for unfollowing an user you'd do exactly the same only with .remove() instead of .setValue(true).
I created the rule by using the Firebase Bolt compiler.
// ####### Root
path / {
read() = true;
write() = false;
}
// ######## USERS PHOTOS
// Allow anyone to read the list of Photos.
path /users_photos {
read() = true;
}
// All individual Photos are writable by anyone.
path /users_photos/$id is Photos {
write() = isSignedIn();
}
type Photos {
image: String,
user_id: String,
removed: Boolean,
dt_created: InitialTimestamp,
dt_updated: CurrentTimestamp
}
type CurrentTimestamp extends Number {
validate() = this == now;
}
type InitialTimestamp extends Number {
validate() = initial(this, now);
}
//
// Helper Functions
//
isSignedIn() = auth != null;
// Returns true if the value is intialized to init, or retains it's prior
// value, otherwise.
initial(value, init) = value == (prior(value) == null ? init : prior(value));
Ref: https://github.com/firebase/bolt/blob/master/docs/guide.md
My script:
/*Upload*/
VigiApp.controller('UploadController', ['$scope', 'Upload', '$timeout', 'FirebaseURL', function ($scope, Upload, $timeout, FirebaseURL) {
// upload on file select or drop
$scope.upload = function (file, id) {
$('.page-spinner-bar').removeClass('ng-hide hide').addClass('ng-show show');
id = typeof id !== 'undefined' ? id : null;
Upload.base64DataUrl(file).then(function(base64){
//auth
var fbAuth = FirebaseURL.getAuth();
//Ref
var usersPhotosRef = FirebaseURL.child("users_photos");
usersPhotosRef.push({'image': base64,'removed': true, 'user_id': fbAuth.uid}, function(error){
if (error) {
alert('Error: Something went wrong when creating your post please try again');
} else {
var newID = usersPhotosRef.key();
if(id !== null){
$('#'+id).css("background-image", "url('"+base64+"')");
$('#'+id).css("background-size", "100% 100%");
}
}
$('.page-spinner-bar').removeClass('ng-show show').addClass('ng-hide hide');
});
});
}
}]);
Compile ...
>firebase-bolt mmgv-vigiapp.bolt -o rules.json
bolt: Generating rules.json...
And deploy ...
>firebase deploy:rules
=== Deploying to 'vivid-heat-2144'...
i deploying rules
+ Deploy complete!
Dashboard: https://vivid-heat-2144.firebaseio.com
But I'm getting the error:
FIREBASE WARNING: set at /users_photos/-K5VL1m04oF8s2xp8oTf failed: permission_denied
The rules created:
{
"rules": {
".read": "true",
"users_photos": {
".read": "true",
"$id": {
".validate": "newData.hasChildren(['image', 'user_id', 'removed', 'dt_created', 'dt_updated'])",
"image": {
".validate": "newData.isString()"
},
"user_id": {
".validate": "newData.isString()"
},
"removed": {
".validate": "newData.isBoolean()"
},
"dt_created": {
".validate": "newData.isNumber() && newData.val() == (data.val() == null ? now : data.val())"
},
"dt_updated": {
".validate": "newData.isNumber() && newData.val() == now"
},
"$other": {
".validate": "false"
},
".write": "auth != null"
}
}
}
}
When I remove the date, it works.
...
type Photos {
image: String,
user_id: String,
removed: Boolean,
}
...
How can I generate the creation date and update?
Where is my wrong, please?
When you are adding a Photo, you pass this information:
usersPhotosRef.push({'image': base64,'removed': true, 'user_id': fbAuth.uid}
Your security rules, require these properties:
".validate": "newData.hasChildren(['image', 'user_id', 'removed', 'dt_created', 'dt_updated'])",
There is no magic "default value" for dt_created and dt_updated, so you'll need to pass these in from your application code:
usersPhotosRef.push({
'image': base64,
'removed': true,
'user_id': fbAuth.uid,
'dt_created': Firebase.ServerValue.TIMESTAMP,
'dt_updated': Firebase.ServerValue.TIMESTAMP
}
Since this snippet is adding a new record, dt_created and dt_updated are set to the same value. When you update a record, you'll only want to set dt_updated.
I am struggling with security rules in Firebase for anonymously authenticated users. I want users to be able to create, read, update, and delete their own projects using anonymous authentication. When I use the code below, Firebase denies permission to the database: Error: permission_denied: Client doesn't have permission to access the desired data.
Does my Angular code need to first create a users folder of some sort in Firebase even though I'm using anonymous authentication?
[EDIT: I've included my routes and some additional code in case it helps.]
Javascript:
myApp.factory('fbAuth', function($firebaseAuth, $firebase) {
var ref = new Firebase('https://xxxxxxxxxx.firebaseio.com');
var authData = ref.getAuth();
if (authData) {console.log('Authenticated user with uid:', authData.uid); } else {
ref.authAnonymously(function (error, authData) {
if (error) {
console.log('Login Failed!', error);
} else {
console.log('Authenticated successfully with payload:', authData);
}
});
}
return authData;
});
myApp.factory('Projects', function($firebase, fbURL) {
return $firebase(new Firebase(fbURL+'/projects')).$asArray();
});
myApp.factory('Selections', function($firebase, fbURL) {
return $firebase(new Firebase(fbURL+'/services')).$asArray();
});
myApp.controller('ProjectListCtrl', function ProjectListCtrl(Projects) {
var projectList = this;
projectList.projects = Projects;
projectList.total = function(){
var total = 0;
angular.forEach(projectList.projects, function(project) {
total += project.type.cost;
});
return total;
};
});
myApp.controller('SelectionListCtrl', function (Selections) {
var selectionList = this;
selectionList.selections = Selections;
this.selectedServices = Selections;
});
Routes:
myApp.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('selection', {
url: '/',
views: {
'list': {
url: '',
templateUrl: 'views/list.html',
controller: 'ProjectListCtrl as projectList',
}
},
'selectionlist': {
templateUrl: 'views/selectionlist.html',
controller: 'SelectionListCtrl as selectionList',
}
})
Security Rules:
{
"rules": {
"projects": {
"$project_id" : {
"$uid": {
".read": "auth != null && auth.uid === $uid",
".write": "auth !=null && auth.uid === $uid" }
}
},
"$other": {".validate": false }
}
}
Firebase Data Structure
Root
- projects
+ -JiVDL4RUSladYTqqHl6
+ -JiVIdH8QIQ8o8q3iKvf
+ -JiYY44i6AOGzTjPDNVM
This is the structure of my data
{
"projects" : {
"proj1" : {
"-JccS4StrTnJdTficR-u" : {
"name" : "consultation",
"status" : false
},
"-Jd6JlJUfyyZ2U0NTQEs" : {
"name" : "Words",
"status" : false
}
},
"proj2" : {
"-JccS6nwYHRrxvjZtCkt" : {
"name" : "Rework",
"status" : false
},
"-Jd6Jqa7-EPJuWxbtJAV" : {
"name" : "Review",
"status" : false
}
},
"proj3" : {
"-JccSJ5lEBQEg3XCbG-u" : {
"name" : "translation",
"status" : false
}
}
}
}
Here are my rules
{
"rules": {
".read": true,
"projects": {
"proj1": {
".write": "auth != null"
},
"proj2": {
".write": true
},
"proj3": {
".write": true,
".validate": "newData.child('name').isString()
&& newData.child('name').val().length < 10"
}
}
}
}
When I created the database, there was no rule. Now, I've created the rules. I want the length of the name to be less than 10 characters.
When I run the code, I'm getting the following error: FIREBASE WARNING: set at /projects/proj3/-Jd4n6XditTHLWKVmhC6 failed: permission_denied
I can't exactly tell why it's not working. I've also tried just this portion newData.child('name').isString(), but I'm still getting the same error.
Here is the code that push data to FireBase
function saveToFB(taskName, proj) {
// this will save data to Firebase
var project=projTasks.child(proj);
project.push({
name: taskName,
status: false
});
};
Looking at the structure of your data, as well as your rules, it looks like there is a missing level of data between "proj1"/"proj2"/"proj3" and the object that has name and status attributes.
Specifically, the .validate rules you have configured is checking the length of the name attribute for "proj3", not the new element added to the list there.
To get around this, try increasing the depth of the security rules definitions, and make use of the $wildcard functionality, where any security rules key prefixed with $ is treated as a wildcard with a name that you can reuse.
{
"rules": {
".read": true,
"projects": {
"proj1": {
".write": "auth != null"
},
"proj2": {
".write": true
},
"proj3": {
".write": true,
"$someChildElementHere": {
".validate": "newData.child('name').isString()
&& newData.child('name').val().length < 10"
}
}
}
}
}