I have written policy in ALFA where:
if the amount to transfer is greater than the transfer limit, then the request should be denied.
I wanted to know how to specify a particular transfer limit. And from an administrator point of view, if in future i want to update the transfer limit, where and how should it be done?
For eg .I want to set current transfer limit to $2000. How do I do that and then how do I update the transfer limit, if I want to?
To add to what David mentioned, you could set this value in a database cell and update its value offline of ABA/XACML (using other processes in place in your org.). The value can then be queried at evaluation time by telling your PDP that withdrawalLimit is queried from a specific DB using the SQL "select limit from ...."
So, that is the advantage of XACML, As rules are defined in a policy, you can update the access control rules in dynamic manner. Transfer limit may have been configured in your XACML policy. You can update it using the by editing the XACML policy. Then this update must be eserveredffected for the PDP runtime and request would be severed according to it.
ALFA plugin would have easy way of updating the XACML policy attributes rather than just updating the raw policy. Hope you may find it. But if you update the XACML policy, it would also work.
It's good to see you are making progress. In your case, the authorization policy in ALFA would look as follows:
namespace com.axiomatics.banking{
attribute actionId{
category = actionCat
id = "actionId"
type = string
}
attribute resourceType{
category = resourceCat
id = "resourceType"
type = string
}
attribute amount{
category = resourceCat
id = "amount"
type = double
}
/**
* Policy to transfer money
*/
policy transferMoney{
target clause actionId=="transfer" and resourceType=="money"
apply firstApplicable
/**
* Deny access if amount is greater than 2000
*/
rule checkAmount{
target clause amount > 2000
deny
}
/**
* Grant access
*/
rule allow{
permit
}
}
}
Note that in my example I use a negative rule to deny access. This is great, for instance, if I want to add Advice or Obligation to that rule to indicate the reason for denial.
Now your question doesn't relate so much to the policy structure but rather to the values of the attributes. In XACML you have two options. Either:
you "hard-code" the value into the policy, or
you externalize the value and put it inside a policy information point (PIP) which could be a database, a web service, an LDAP... The benefit of externalizing the value is that you can now update the value without having to change the policy. The policy becomes more generic. Also it means you can have user-specific limits.
In the latter case, your rule becomes:
/**
* Deny access if amount is greater than the user's withdrawal limit
*/
rule checkAmount{
condition amount > withdrawalLimit
deny
}
In the Axiomatics Policy Server, you can configure a PIP / attribute connector to retrieve the value based on, say, the user id.
HTH,
David.
Related
I'm following a tutorial about firestore but I don't understand firestore rules very well. I'm trying to allow anyone to be able to create in the standard
users/uid/
path but only allow updates if the requester is trying to update
users/theirUserId/
I saw this in the documentation, but it didn't seem to work for me:
allow write: if request.auth.uid == resource.data.author_id;
Can anyone explain the functionality of the above line and/or offer suggestions as to how I can achieve this?
Additionally, is there any way to specify rules for a specific piece of data within a document?
It looks like that your document doesn't contain a author_id field.
The Firebase documentation Writing Conditions for Security Rules use this example:
service cloud.firestore {
match /databases/{database}/documents {
// Make sure the uid of the requesting user matches the 'author_id' field
// of the document
match /users/{user} {
allow read, write: if request.auth.uid == resource.data.author_id;
}
}
}
It means that a random user will be able to read and write in the users collections only if their authentication ID equals the author_id field of a specific document.
The resource variable refers to the requested document, and resource.data is a map of all of the fields and values stored in the document. For more information on the resource variable, see the reference documentation.
For your second question, I recommend you to have a look on the documentation about resource variable (link in the quote above). It is the same logic as your author_id question.
You can split allow write in to three create, update, delete for specific cases.
In your case
allow create: if request.auth.uid != null;
allow update: if request.auth.uid == resource.data.author_id;
which says any authenticated users can create and only update their on document. and created user must have a field author_id which is their user id.
We got a request to write a set of rules in a policy which should generate a detailed log message, both for Permit and Deny condition. The resulting log output is a construction of several attributes, or transferred from the XACML request input, or from the PIP Java routines, where we get additional attributes like UTC log event time and the service status of the data sources, among a set of attributes from the database. All information should be transported to the PEP by means of XACML obligations, which converts the String into a final database logging step.
Is there a way to create a reusable function in ALFA language that groups all required String manipulation statements in a callable manner, in a similar way to Java programmatic function, avoiding a writing of redundant code sections.
No there isn't a way in the current version of ALFA to define a custom function that would group all that you are interested in.
However, you could very well define a rule that contains the advice / obligation with all the logic and then reference the rule in all the places you need it. In ALFA, you can reference Rule elements (something that is not possible in XML/XACML)
Here is an example:
namespace so{
import Attributes.*
attribute messageContent{
category = environmentCat
id = "messageContent"
type = string
}
obligation message = "message"
/**
* Reusable rule with obligation pattern
*/
rule giveReason{
deny
on deny {
obligation message{
subjectId = subjectId
currentDateTime = currentDateTime
messageContent = "Hi, "+stringOneAndOnly(subjectId)+
". You cannot do action "+
stringOneAndOnly(Attributes.actionId)+
" on "+stringOneAndOnly(resourceId)+
" at "+stringFromDateTime(dateTimeOneAndOnly(currentDateTime))+"."
}
}
}
/**
* Your policies
*/
policyset somePS{
apply firstApplicable
policy example1{
apply firstApplicable
/**
* Other rules go here
*/
giveReason // link to common rule
}
policy example2{
apply firstApplicable
/**
* Other rules go here
*/
giveReason // link to common rule
}
}
}
Wonder why the WSO2 Balana framework at the Obligations statements only accepts fulfillOn argument for "Permit ", or "Deny" conditions, but ignores the "Not applicable" result, which also could be interesting to intercept and document in the logic flow to assist the policy debug process.
In source of Balana ObligationExpression.java we find:
if("Permit".equals(effect)){
fulfillOn = Result.DECISION_PERMIT;
} else if("Deny".equals(effect)){
fulfillOn = Result.DECISION_DENY;
} else {
throw new ParsingException("Invalid FulfillOn : " + effect);
}
What is your opinion about it, is this logic working correctly?
This is by spec. The XACML specification only allows obligations and advice to be returned on either of Permit or Deny. You have to keep that in mind when you build your policies. See below and here.
7.18 Obligations and advice
A rule, policy, or policy set may contain one or more obligation or advice expressions. When such a rule, policy, or policy set is evaluated, the obligation or advice expression SHALL be evaluated to an obligation or advice respectively, which SHALL be passed up to the next level of evaluation (the enclosing or referencing policy, policy set, or authorization decision) only if the result of the rule, policy, or policy set being evaluated matches the value of the FulfillOn attribute of the obligation or the AppliesTo attribute of the advice. If any of the attribute assignment expressions in an obligation or advice expression with a matching FulfillOn or AppliesTo attribute evaluates to “Indeterminate”, then the whole rule, policy, or policy set SHALL be “Indeterminate”. If the FulfillOn or AppliesTo attribute does not match the result of the combining algorithm or the rule evaluation, then any indeterminate in an obligation or advice expression has no effect.
As a consequence of this procedure, no obligations or advice SHALL be returned to the PEP if the rule, policies, or policy sets from which they are drawn are not evaluated, or if their evaluated result is "Indeterminate" or "NotApplicable", or if the decision resulting from evaluating the rule, policy, or policy set does not match the decision resulting from evaluating an enclosing policy set.
If the PDP's evaluation is viewed as a tree of rules, policy sets and policies, each of which returns "Permit" or "Deny", then the set of obligations and advice returned by the PDP to the PEP will include only the obligations and advice associated with those paths where the result at each level of evaluation is the same as the result being returned by the PDP. In situations where any lack of determinism is unacceptable, a deterministic combining algorithm, such as ordered-deny-overrides, should be used.
Also see Section 7.2.
Allowing for obligations & advice on NotApplicable would be messy. Imagine the following policy structure:
PolicySet Main Policy
Policy Allow managers to view documents + obligation A on NotApplicable
Policy Allow employees to view documents in their own department + obligation B on NotApplicable
Policy Allow publishers to view and publish documents + obligation C on Permit
Imagine the request coming in is:
Request: can Alice the publisher publish doc #123?
In your world, the response would (could) be: Permit + obligations A,B,C
It doesn't really make any sense.
I want to write a layered policy whereby the first layer will always check the environment such as:
checking the user has a valid IP-address,
checking the time is within a valid time frame and day of the week
the user's device is a valid device
The lower layers will take care of the actual requested actions e.g. add, view, update, delete on a certain set of resources such as medical records, insurance data, bank accounts...
In plain English, the rules look like the following
Policy that permits if all environment rules return permit
Rule1: Only allow users with an IP-address that is in the range of [bag with ip-address ranges]
Rule2: Only allow requests for actions from monday to friday
Rule3: Only allow requests for actions from 7:00 AM to 22:00 PM
Rule4: Only allow requests for actions from a Desktop or a laptop
How can this be done using ALFA, the Axiomatics Language for Authorization?
This is a good question and there are several good ways of doing it. One example would be to rewrite the logic and express the following:
PolicySet with a combining algorithm of first-applicable
deny if time is wrong
deny if device is invalid
deny if IP is not in valid range
set of possible actions and resources-based policies yielding a Permit.
This is what it looks like in ALFA:
namespace com.axiomatics.example{
policyset global{
apply firstApplicable
policy securityChecks{
apply firstApplicable
rule denyOutsideOfficeHours{
deny
}
rule denyInvalidDevice{
deny
}
rule denyInvalidIP{
deny
}
}
policyset myBusinessPolicies{
apply firstApplicable
/**
* Add your business policies here
*/
}
}
}
This is merely the scaffolding. Let's now look at the attributes we need:
the current time
the user's current IP
the user's device type
We will not worry about how we obtained these values. It is up to the Policy Enforcement Point or Policy Information Point to worry about that.
The first rule will use the currentTime attribute. It is a default attribute in ALFA and is defined as follows:
attribute currentTime {
id = "urn:oasis:names:tc:xacml:1.0:environment:current-time"
type = time
category = environmentCat
}
The updated rule now looks like the following:
rule denyOutsideOfficeHours{
target clause currentTime<"09:00:00":time or currentTime>"17:00:00":time
deny
}
In this example we use static lower and upper limits (9am and 5pm respectively) but these could also be attributes in which case we'd have to use a condition rather than a target. Note the ALFA notation used to convert the string value into the relevant datatype: "17:00:00":time.
The second rule looks as follows:
rule denyInvalidDevice{
condition not(stringIsIn(stringOneAndOnly(deviceType), stringBag("laptop","desktop")))
deny
}
In this rule, we have to use a condition because it is impossible to express a negative constraint in a target. The condition checks that there is an attribute called deviceType and that it contains a single value, no more and no less. That value must not be equal to either laptop or desktop in order for the deny to kick in. By the way, string comparison in XACML is case-sensitive by default.
The last rule is similar and again we have to use a condition to negate the test. Here we use the ipAddressRegexpMAtch XACML function to check whether the user's IP (subjectLocalityIpAddress) matches a given IP address pattern.
rule denyInvalidIP{
condition not(
ipAddressRegexpMatch(
"^(10)\\.(10)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9])\\:([80|443])$",
ipAddressOneAndOnly(subjectLocalityIpAddress)
)
)
deny
}
Note that the the backslash had to be escaped with another backslash. This is due to the ALFA syntax. The XACML policy itself, once converted to XML, will not contain 2 backslash characters.
The resulting policy all combined together is the following:
namespace com.axiomatics.example{
import Attributes.*
attribute deviceType{
category = subjectCat
id = "deviceType"
type = string
}
attribute userIP{
category = subjectCat
id = "deviceType"
type = string
}
policyset global{
apply firstApplicable
policy securityChecks{
apply firstApplicable
rule denyOutsideOfficeHours{
target clause currentTime<"09:00:00":time or currentTime>"17:00:00":time
deny
}
rule denyInvalidDevice{
condition not(stringIsIn(stringOneAndOnly(deviceType), stringBag("laptop","desktop")))
deny
}
rule denyInvalidIP{
condition not(
ipAddressRegexpMatch(
"^(10)\\.(10)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9])\\:([80|443])$",
ipAddressOneAndOnly(subjectLocalityIpAddress)
)
)
deny
}
}
policyset myBusinessPolicies{
apply firstApplicable
/**
* Add your business policies here
*/
}
}
}
I hope this helps. Send us your questions via Stackoverflow or via our Developer's Blog.
According to a requirement, i have to change the owner of an account if the user does not have read access to a third object.
I need a functionality similar to the isAccessible() method of Describe Field Result, but it is only available for the current logged in user.
Is there any other way to check the user's CRUD permissions for an object in Apex code?
I wrote an article about this on my blog. There is a feature that was just released in version 24.0 of the API (Spring Release) that will let you do just this on a record by record basis for the current user.
Here is the link to that blog entry that goes into details: How to tell if a user has access to a record
Don't confuse record level access with CRUD - the latter is the ability for a user to Create, Read, Update or Delete an object in general, regardless of sharing rules etc. that might affect the user's access to a particular record.
To check whether a user can create (e.g. Contacts) in general, just use
Schema.sObjectType.Contact.isCreateable()
(returns true or false)
From the documentation. it sounds like you want to use execute anonymously.
Apex generally runs in system context; that is, the current user's permissions, field-level security, and sharing rules aren’t taken into account during code execution. The only exceptions to this rule are Apex code that is executed with the executeAnonymous call. executeAnonymous always executes using the full permissions of the current user. For more information on executeAnonymous, see Anonymous Blocks.
Although Apex doesn't enforce object-level and field-level permissions by default, you can enforce these permissions in your code by explicitly calling the sObject describe result methods (of Schema.DescribeSObjectResult) and the field describe result methods (of Schema.DescribeFieldResult) that check the current user's access permission levels. In this way, you can verify if the current user has the necessary permissions, and only if he or she has sufficient permissions, you can then perform a specific DML operation or a query.
For example, you can call the isAccessible, isCreateable, or isUpdateable methods of Schema.DescribeSObjectResult to verify whether the current user has read, create, or update access to an sObject, respectively. Similarly, Schema.DescribeFieldResult exposes these access control methods that you can call to check the current user's read, create, or update access for a field. In addition, you can call the isDeletable method provided by Schema.DescribeSObjectResult to check if the current user has permission to delete a specific sObject.
http://www.salesforce.com/us/developer/docs/apexcode/index_Left.htm#StartTopic=Content/apex_classes_perms_enforcing.htm#kanchor431
Have you tried the runAs() method?
Something like (not verified):
User u = [SELECT Id FROM User WHERE Name='John Doe'];
System.runAs(u) {
if (Schema.sObjectType.Contact.fields.Email.isAccessible()) {
// do something
}
}
The DescribeSObjectResult class has methods for checking CRUD.
E.g. this allows you to test whether or not the current user can update the account object in general.
Schema.DescribeSObjectResult drSObj = Schema.sObjectType.Account;
Boolean thisUserMayUpdate = drSObj.isUpdateable();
#John De Santiago: your article covers record level access rather than object CRUD (= object level access)
Very old post. Since then SF add option to query object permission:
Select SobjectType ,ParentId, PermissionsEdit, PermissionsRead
From ObjectPermissions
Order by ParentID, SobjectType ASC
Basically you will need to get the profile and permissionset of the user that you want to check and the relevant object. So it will be something like:
Select SobjectType ,ParentId, PermissionsEdit, PermissionsRead
From ObjectPermissions
where parentId IN :UserProfileIdAndPermission
AND sObjectType=:objectType
Order by ParentID, SobjectType ASC