The decisioning is to permit if the user has required roles. The required roles and the user's current permissions are in JSON format.
Required permissions: (Saved as a attribute)
{
"data": {
"service1": {
"service1.1": true
},
"service2": {
"service2.1: false,
"service2.2": true,
"service2.3": false
}
}
}
User's current permissions:
{
"data": {
"service1": {
"service1.1": true
},
"service2": {
"service2.1: false,
"service2.2": false,
"service2.3": true
}
}
}
To make the decision, we need to check if the user has the services as true similar to required Permissions. In the above example, the user has data.service1.service1.1 as true and data.service2.service2.3 as true where the required roles being data.service1.service1.1 as true and data.service2.service2.2 as true, in this case we deny.
I wrote separate rules to check for each and every service, but that would only be a check of combination of services.
rule service1.1{
permit
condition
(allOf(function[booleanEqual], true, requiredRoles.data.service1.service1.1))
&&
(allOf(function[booleanEqual], true, requiredRoles.data.service1.service1.1))
on permit {
advice reasonForPermit{
reasonAttribute= "Contains Valid services"
}
}
}
Would someone please help on how to write a if.. then check in alfa?
There is no if..then construct in ALFA (or XACML). You use combining algorithms instead. In particular, onPermitApplySecond is the policy combining algorithm that resembles an if..then construct the most.
However, there is usually a simpler way to express what you want if you can make reasonably assumptions on your attribute data. In your example, for instance, if it's always guaranteed that both the required and current permissions contain exactly one boolean value for each available service, then you could write:
rule {
target
clause requiredRoles_service1_1 == false or permitted_service1_1 == true
clause requiredRoles_service2_1 == false or permitted_service2_1 == true
...
permit
}
Remember that in a target, clauses are AND'ed together. This rule then checks that, for every service, the role is either not required or is given in the current permissions.
If instead it may happen than any of those attributes is not present (i.e. there are no values for the attribute), then you have to guard against that case. You can do that using a condition like the following one, but there are other ways too:
rule {
permit
condition
(not(booleanIsIn(true, requiredRoles_service1_1)) || booleanIsIn(true, permitted_service1_1))
&&
(not(booleanIsIn(true, requiredRoles_service2_1)) || booleanIsIn(true, permitted_service2_1))
&&
...
}
All in all, there are usually simpler ways to express a policy if you can massage attribute data into other forms. Having a pair of attributes per service, like in the examples above, may not be necessary.
If you could gather all required roles and current permissions in one attribute each, then the policy can be expressed much more compactly. Let's say you have two attributes, requiredRoles and permittedRoles whose values list the service roles required and permitted for a given user, respectively. In your example, this would mean that requiredRoles has value, say, ["service1.1", "service2.2"] and permittedRoles has value ["service1.1", "service2.3"]. Then you can write a rule like this:
rule {
permit
condition stringSubSet(requiredRoles, permittedRoles)
}
I was able to do this by creating separate attributes for each service and wrote a rule with the target clause with the service from required roles and the condition will be if the service in the permitted role is true. I combined all the rules as below in the policy using permitunlessDeny algorithm
rule rule1 {
target clause requiredRoles.service1_1 == true
deny
condition
not(permittedRoles.service1_1 == true)
on permit {
advice reasonForPermit {
reasonAttribute= "User has valid services"
}
}
}
Thank you for the suggestion Pablo.
Related
I want my user to be able to read and write only his own data but. When I test the rules, I get the message "Simulator read denied". Please tell where i am wrong? Thanks for your time.
Here the some information :
RULES
service firebase.storage {
// Only a user can upload their file, but anyone can view it
match /users/{userId}/{fileName} {
allow read ;
allow write: if request.auth != null && request.auth.uid == userId;
}
}
LOCATION
/b/online-notepad-d43d2.appspot.com/o/WkqtgpdUYRUGOBaAfmCByXtVPoT2/file1.txt
PROVIDER
"password"
FIREBASE UID
WkqtgpdUYRUGOBaAfmCByXtVPoT2
EMAIL
hgaur701#gmail.com
EMAIL VERIFIED
true
Authentication payload
{
"uid": "WkqtgpdUYRUGOBaAfmCByXtVPoT2",
"token": {
"sub": "WkqtgpdUYRUGOBaAfmCByXtVPoT2",
"aud": "online-notepad-d43d2",
"email": "hgaur701#gmail.com",
"email_verified": true,
"firebase": {
"sign_in_provider": "password"
}
}
}
SCREENSHOT
exact location i am using : "/WkqtgpdUYRUGOBaAfmCByXtVPoT2/file1.txt"
and one more thing i upload this folder and file from upload button.
not from POST request.
STILL GETTING SAME RESULT :(
Here you define two completely separate clauses:
allow read ;
allow write: if request.auth != null && request.auth.uid == userId;
So you have an empty read clause, and a non-empty write clause.
You probably want to use:
allow read, write: if request.auth != null && request.auth.uid == userId;
Edit for second problem: as you said in your edit, you are accessing the path /WkqtgpdUYRUGOBaAfmCByXtVPoT2/file1.txt, but your rules only define access permissions for match /users/{userId}/{fileName}. Since you are only allowing access in /users/... and the path you try to use is not in /users, the access is denied by the rules.
I'm checking if any of the user's roles are part of a document's allowed roles. The following should match since the user is an admin+editor, and the post allows reading by publisher+editor:
service cloud.firestore {
match /databases/{database}/documents {
match /group/{groupId} {
match /posts/{postId} {
allow read: if 'admin,editor'.matches('((,|^)(publisher|editor)(,|$))');
}
}
}
}
Here's it working in a regex tester: https://regex101.com/r/bDXMg3/2/
But this fails to match, any ideas?
Our docs could be clearer on this, but it looks like the whole string needs to match. Give (.*,|^)(publisher|editor)(,.*|$) a try.
I mean I know what those are but I am a bit confuse about the security rules, for example:
service cloud.firestore {
match /databases/{database}/documents {
// This is probably a mistake
match /spaceships { // <= what is this a collection or a document?
allow read;
// In spite of the above line, a user can't read any document within the
// spaceship collection.
}
}
}
Firebase documentation says:
Rules for collections don't apply to documents within that collection. It's unusual (and probably an error) to have a security rule that is written at the collection level instead of the document level.
That means that this match /spaceships {... is a collection right?
But later on we have this:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**}{ // <= what is this a document or a collection?
allow read, write: if true;
}
}
}
I don't understand is this match /{document=**}{... a document? or a collection? I mean is at the collection level.
Paths in Firestore are alternating collections and documents: /collection/document/subcollection/subdocument
For example:
// Matches kaylee, the mechanic on serenity
/spaceships/serenity/crew/kaylee/...
When using security rules, you can specify wildcards:
// This will match any spaceship, and any crewmember
/spaceships/{spaceshipId}/crew/{crewmemberId}/...
Now imagine that you have another subcollection under spaceships:
/spaceships/{spaceshipId}/stowaways/{stowawayId}/...
If you want to write rules against multiple subcollections, you need to either:
// You could use multiple single wildcards
/spaceships/{spaceshipId}/{crewOrStowaway}/{crewOrStowawayId}/...
// Or you could use a multi-segment wildcard
/spaceships/{spaceshipId}/{allShipInformation=**}
This returns allShipInformation as a path, which will match all documents and collections at and below that path. Note that it's one or more path segment, rather than zero or more.
You can read more about this in the docs
In your first example /spaceships is at the collection level. As was stated in the quote you referenced, placing a rule here is not helpful as it won't be applied to any of the documents in the collection.
In your second example /{document=**} is at the collection level but is using a recursive wildcard. In short, what this does is apply the rule to the documents within this collection and to any documents within any subcollections of this collection.
This allows you to write:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**}{
allow read, write: if true;
}
}
}
Instead of:
service cloud.firestore {
match /databases/{database}/documents {
match /spaceships/{shipId} {
allow read, write: if true;
}
match /spaceships/{shipId}/crew/{crewMemberId} {
allow read, write: if true;
}
}
}
All:
I am pretty new to AngularFire, when I follow tutorial to do a simple todo app, I wonder how can I only allow user to add new todo, but not delete. Basically I do not want client side hack to destroy my data. Like user opens console and manually call $remove API, how can I prevent that?
Thanks
To prevent clients from performing unwanted CRUD operations on your Firebase database, you need to define database rules.
Rules are defined in a JSON structure and can reference the current auth information. For example, if you are storing user information in database under /users/<uid>, the following rule would grant each user read/write access to their own information and read access to other users' information:
{
"rules": {
"users": {
".read": "auth != null"
"$uid": {
".write": "auth != null && auth.uid == $uid"
}
}
}
}
Rules can also reference data that's already in the database, so it's possible to allow writes only if there is no existing data at a particular key - that is, you can prevent updates.
Similarly, rules can reference the new data that is to be stored, so it's possible to check that new data has been specified - that is, you can prevent deletes.
A rule like this would allow todos (stored under /todos/<uid>/<todoId>) to be created, but neither updated nor deleted:
...
"todos": {
"$uid": {
".read": "auth != null && auth.uid == $uid",
"$todoId": {
".write": "auth != null && auth.uid == $uid && data.val() == null && newData.val() != null"
}
}
}
...
Maintaining rule definitions in the JSON structure can become difficult; you might find Firebase's Bolt tool useful for defining more complicated rules.
We have used RBAC to implement simple role based permissions for CRUD, but now we need to also add a 'visibility' functionality which makes it possible to limit content visibility (R) to only registered users or only the content owners.
So, how can we limit content visibility on different levels, for example
PUBLIC: anybody can see the content, including anonymous
INTERNAL: only registered users can see the content
PRIVATE: only the creator can see the content
What would be the best way to implement this, it looks like RBAC does not have a straightforward way of dealing with this.
I think that the problem can be solved by using defaultScope in models. Thus, before giving the content, we can check the current role of the user data and give the necessary conditions.
public static function find()
{
$userRoleArray = \Yii::$app->authManager->getRolesByUser(Yii::$app->user->getId());
$userRole = current($userRoleArray)->name;
if ($userRole == 'admin') {
return parent::find()->where("Your condition");
} elseif ($userRole == 'moderator') {
return parent::find()->where("Your condition");
}
}
you can make a permission function and run in each function that will take user role as argument and returns true or redirect to not allowed page.
Here is something I tried but you can modify according to your need.
public function allowUser($min_level) {
//-1 no login required 0..3: admin level
$userRole = //get user role;
$current_level = -1;
if (Yii::$app->user->isGuest)
$current_level = 0;
else
$current_level = userRole;
if ($min_level > $current_level) {
$this->redirect(array("/pages/not-allowed"),true);
}
}