I'm curious about the method of fire base security rules - firebase

This is my firebase rule.
{
"rules":{
"users":{
".write":"auth.provider == 'anonymous' || auth.token.email.endsWith('dev#email.com')",
".read":"auth.token.email.endsWith('dev#email.com')"
}
}
}
It says unsafe rules.
I wonder why, and there's no way to know how to improve it Help me

Related

Polymerfire - Read document once

I was wondering if there is a way to retrieve a document just once, and avoid every kind of bidirectional sync with the database.
The Polymerfire documentation is poor, and I couldn't find it.
Thanks
No, the firebase-document element does not have a 'once' mode. However, you can easily drop down to the underlying JS SDK if you've already initialized the SDK with firebase-app:
Polymer({
is: 'my-element',
attached: function() {
firebase.database().ref('/path/to/doc')
.once('value').then(snap => this.data = snap.val());
}
});
I found another solution. You can check if the data are already loaded and then call the off() method on firebase reference, which disables all listeners.
Here are the important parts of code:
<firebase-document id="office_document" app-name="doctor-appointment-system" path="/offices/{{profileID}}" data="{{officeData}}">
</firebase-document>
dataNotLoaded: {
type: Boolean,
computed: 'computeDataAvailability(officeData)',
observer: 'disableReference'
}
computeDataAvailability: function (data) {
return Object.keys(data).length === 0;
}
disableReference: function (dataNotLoaded) {
if (!dataNotLoaded) {
this.$.office_document.ref.off();
}
},
I prefer this approach because it allows me to use Polymerfire and I needed to check if the data were loaded to disable paper-spinner so it didn't cause unnecessary complexity in my code.

Firebase Database Rules private nodes

I'm doing a groceryItem app where users can add items and only should see their own items.
I add my items by using childByAutoId and get unique key entries for each item (see picture #1).
Each item hast got 3 properties. addedByUser is the UID of an authenticated user and of course a name and the quantity.
I have problems setting the rules so that only users can see their own items. My current rules look like this (see picture #2).
If I log in with another user he can read all items. So my question is:
How I can make items readable for the users who created them?
I think I have a gap between items and addedByUser because I have the childByAutoId between. If you have any solutions or tips how I can structure this in a better way, pls feel free to help me.
Thanks in advance!
The $addedByUser is probably in the wrong spot in this example, as it is not a "nested child" of the items, but an attribute of a child item. I would suggest (not having tested this) that you would be looking for something along the lines of:
"items": {
"$item": {
".read": "data.child('addedByUser').val() === auth.uid"
}
}
This is lifted from the Firebase docs:
With data:
{
"messages": {
"message0": {
"content": "Hello",
"timestamp": 1405704370369
},
"message1": {
"content": "Goodbye",
"timestamp": 1405704395231
},
...
}
}
The rules could be:
{
"rules": {
"messages": {
"$message": {
// only messages from the last ten minutes can be read
".read": "data.child('timestamp').val() > (now - 600000)",
// new messages must have a string content and a number timestamp
".validate": "newData.hasChildren(['content', 'timestamp']) && newData.child('content').isString() && newData.child('timestamp').isNumber()"
}
}
}
}
This will prevent the incorrect user from reading someone else's (singular) item, and in your example would prevent user SZ825V... from performing a read on "items/-KKTDD...", however if user SZ825V were to perform a read on "items", then the read would fail entirely, because the user is trying to read their own documents and someone else's as well. This is an important concept in firebase with the rules behaving in an atomic way - that is, if one part of the read fails, the entire read does. Another way of putting this is that the rules do not act as a filter.
If this is the functionality you are seeking, it may be required to rearrange your structure. An example could be as follows:
{
"users": {
"abcdef-user-id": {
"items": {
"fjeiwofjewio": {
"name": "apple",
"qty": "23"
}
}
},
"ghijkl-user-id": {
"items": {
"regrewgreh": {
"name": "passionfruit",
"qty": "18"
}
}
}
}
and security rules might be:
.. {
"users": {
"$userId": {
".read": "auth.uid === $userId"
}
}
}
The above approach assumes you only want to allow a user to read the items in their user own data tree, is that no-one else would be able to see any other users' items. If you did want other users to see some of the other users' items, then a different approach again would be required, which would be another SO question.

Run code in specific part of URL in Iron Router

I have an application which makes heavy use of features, separated by specific users, which have different roles.
The problem is that I want to restrict access to some templates, if, for instance, the user is not an Admin.
Currently, I have this:
Router.route('createUser', {
path: '/admin/users/',
onBeforeAction: function() {
if(!isAdmin()) {
Router.go('/');
}
this.next();
}
});
But, specifying that if(isAdmin()) call to every other route is a pain. I want to know if there is any other easy and less error prone way to do it.
Maybe some regex magic would do, but I don't seem to find any examples of use.
First i will recommend you to read this meteor:common-mistakes on the profile editing part
So i will recommend you to better use the alanningroles-meteor package.
Is super easy to use, here is a Online DEMO and the Source Code if you have doubts.
On the router level you can create an onBefore hooks like this.
isAdmin = function(){
var currentUser = Meteor.user()
isUserSuperAdmin = Roles.userIsInRole(currentUser,'Super-Admin'); //using alaning roles.
if(isUserSuperAdmin){ //or use isAdmin();
this.next();
}else{
this.render('accessDenied')
}
}
Router.onBeforeAction('accessDenied', {only: ['admin','otherAdminRoute',etc]});
You can have an onBeforeAction hook combined with only for all routes like so:
var isAdmin = function() {
// Whatever logic you have for checking admin
if (!admin) {
Router.go("/");
}
this.next();
}
Router.onBeforeAction(isAdmin, {
only: ["admin/users"] // Specify other admin templates here
});

Firebase orderByChild security rules

Given the firebase setup with the dinosaur example I would like to add a security rule for .read. If I add .read:true in the hierarchy like the following the query fails to provide any results.
{
"rules": {
"dinosaurs": {
"$dino": {
".read":true
}
}
}
}
If I use the following the queries work as expected.
{
"rules": {
"dinosaurs": {
".read":true
}
}
}
For my use case I need a setup more like the first example so that I can check each item for a condition before allowing a read to that item. To use the new orderByChild("property") style query however it would seems I have to allow access to the entire subtree. Is this the case?

How to restrict a route to an admin user in Meteor?

as an iOS developer primarily, I'm very new to webdev. I'm looking into Meteor and have some questions regarding routing -- my apologies if they are very easy.
I am using the Meteor Router package to create routes, but I would like to have some pages only accessible to the admin user.
Meteor.Router.add({
'/' : 'home',
'/admin' : 'admin'
});
So I have a simple route setup as above, but I'm not sure how to restrict access to the /admin route.
Is it as simple as something like this? What would be a good way to restrict the route to the /admin page and show a warning or perhaps even redirect them back to the / page?
Thank you!
client.html
<head>
<title>My App</title>
</head>
<body>
{{renderPage}}
</body>
<template name="home">
{{greeting}}
</template>
<template name="admin">
{{greeting}}
</template>
client.js
Template.admin.greeting = function () {
var currentUser = Meteor.user();
if (null !== currentUser && 'admin' === currentUser.username) {
return "Hello Admin!";
}
else{
return "Sorry, only admins can see this page";
}
};
The best way to restrict access to a route is with the router itself (rather than pushing the problem to your controller). You have a couple of choices in how you do this:
Routing Function
You could make the /admin route look like:
'/admin': function() {
return {
as: 'admin',
to: function() {
if (Meteor.user() && Meteor.user().username === 'admin') {
return 'admin';
} else {
return 'unauthorized';
}
}
};
}
I'm assuming you have an unauthorized template that renders a 403 page or something informative.
Filter
Alternatively, you can leave your original /admin route as it was and add a filter:
Meteor.Router.filters({
'needsAdmin': function(page) {
if (Meteor.user() && Meteor.user().username === 'admin') {
return page;
} else {
return 'unauthorized';
}
}
});
and use it like so:
Meteor.Router.filter('needsAdmin', {only: 'admin'});
Personally, I like the filter option because it's reusable and it's a little more obvious what's going on.
Another solution is to use Roles package and make sure the user has the 'admin' role before serving data.
$ mrt add roles
Then you can check for roles like with a nice syntax:
if(!Roles.userIsInRole(Meteor.user(), ['admin'])) {
// Redirect...
}
Roles is integrated with the Meteor accounts system and plays nicely with most of the accounts packages.
If you are looking to manage accounts (create/delete Roles and add/remove Roles from a given user) I've created the package Accounts Admin UI. The README has a quickstart and some some notes on how to integrate this with other routing packages.
$ mrt add accounts-admin-ui-bootstrap-3
Use the and parameter:
Meteor.Router.add({
'/admin': { to: 'admin', and: function() {
if (!Meteor.user() || Meteor.user().name != 'admin'){
Meteor.Router.to('/');
}
}}
});
Everyone here has made great points on how to protect an admin panel on the router level. Another possibility is to skip the router all together. I've recently done this with Meteor Candy, a drop-in admin package for Meteor.
The idea is, you could create a Reactive-Dict to hold the state of the admin interface. If you place it into a package, you can ensure that it never collides with your application code. And with the new Dynamic Imports feature, you can virtually keep it off the client until its needed.
Here's how that might work:
<template name="adminPanel">
{{#if show}}
{{> adminPanelUI}}
{{/if}}
</template>
AdminUI = new ReactiveDict();
Meteor.defer(function () {
Blaze.render(Template.MeteorCandy, document.body);
});
Template.adminPanel.helpers({
show: function () {
if (AdminUI.get('show')) {
return true;
}
}
})
On top of that, all you would need is to define the occasion that sets "show" to a truth-y value.

Resources