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
}
}
}
Related
I'm trying to achieve the following points with Ory Keto:
UserA has ownership of ProductA -> Ownership gives one CRUD rights.
UserB has the role Admin -> Admin gives one CRUD rights on everything.
UserA has a role KYCVerified or is part of a group named KYCVerified -> This gives the user additional permissions.
Point 1 describes the concept of ownership, which is described as one of the shortcomings of RBAC (source) with the current state of Ory Keto:
There is no concept of ownership: Dan is the author of article "Hello
World" and is thus allowed to update it.
Point 2 describes a role that basically passes the ownership check, since this role can do everything. This should be possible in the current state, but not in combination with point 1.
Point 3 describes basically the same thing as point 2, but this is more specific to my use case.
I've read this article on the Ory website and this article. However, I'm still unable to wrap my head around this concept. I've made the following example of how I see the concept of ownership with Ory Keto:
# Tenant TenantA needs to be a owner of product ProductA in order to view it
products:ProductA#view#(tenants:TenantA#owner)
# Tenant A is a owner of ProductA
tenants:ProductA#owner#TenantA
But this will result in a lot of rules and I'm not even sure if this is the way to go.
As of this moment you are right. You have to create a bunch of tuples manually. The full set of tuples should be something like:
products:ProductA#owner#UserA
products:ProductA#crud#(products:ProductA#owner)
roles:admin#member#UserB
products:ProductA#curd#(roles:admin#member)
products:ProductA#additional_permissions#(roles:KYCVerified#member)
roles:KYCVerified#member#UserA
With https://github.com/ory/keto/pull/877 you will be able to define global rewrites. It would looks similar to:
import { Context, Namespace } from #ory/keto-config
/**
* "User" is a namespace with no additional rewrite rules defined.
*/
class User implements Namespace {}
/**
* "Role"s only have members.
*/
class Role implements Namespace {
related: {
members: User[]
}
}
/**
* "Product" is a namespace representing a product. It has some rewrites.
*/
class Product implements Namespace {
// Relations are defined and type-annotated.
related: {
/**
* "owners" are the users that are the owners of the product.
*/
owners: User[]
/**
* "admins" are the roles that are administrators of this product (potentially only one).
*/
admins: Role[]
/**
* "special_roles" are the roles a user has to be member of to gain "additional_permissions"
*/
special_roles: Role[]
}
permits = {
// this is probably three/four rewrites (create, read, update, delete) with similar rules
crud: (ctx: Context): boolean =>
this.related.owners.includes(ctx.subject) ||
this.related.admins.some((admin) => admin.related.members.includes(ctx.subject)),
// for the additional_permissions one has to have curd and be member of a special role
additional_permissions: (ctx: Context): boolean =>
this.permits.crud(ctx) &&
this.related.special_roles.some((role) => role.related.members.includes(ctx.subject))
}
}
With that you have to create these tuples:
products:ProductA#owners#UserA
roles:admin#members#UserB
roles:KYCVerified#members#UserA
products:ProductA#admins#(roles:admin)
products:ProductA#additional_permissions#(roles:KYCVerified)
Please note that it is not possible (and not planned right now) to define a single admin group that would have access to everything. You always have to have some kind of relation between the object and subject to query/rewrite it. That is the reason for having the admins and special_roles relations.
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.
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.
If I have a document Shop that has many Activities defined as ReferenceMany, is there a way I can directly query for the list of Activities for a Shop without hydrating a Shop instance?
For example:
{
"_id": fd390j09afj09dfj,
"activities": [
...
]
}
All I want is to be able to say "get me the array activities where _id is fd390j09afj09dfj, and hydrate them as Activity instances.
Here's the first solution I came up with:
/**
* Gets all activities configured for a shop.
*
* #param string $shopId
* #return \BikeShed\Domain\Activity[]|\Doctrine\Common\Collections\ArrayCollection
*/
public function findByShopId($shopId) {
/** #var \BikeShed\Domain\Repository\Shop $shopRepository */
$shopRepository = $this->dm->getRepository('BikeShed\Domain\Shop');
$shop = $shopRepository->findOneById($shopId);
return $shop->getActivities();
}
It's simply fetching the Shop and then getting all the Activities via the defined relation.
Here's a working example of how you would implement jmikola's last suggestion:
/**
* #param string $shopId
* #return ActivityModel[]
*/
public function findByShopId($shopId) {
$partialShopData = $this->dm->getRepository('BikeShed\Domain\Shop')->createQueryBuilder()
->hydrate(false)
->field('activities')
->getQuery()
->getSingleResult()
;
$activityIds = [];
if(!empty($partialShopData['activities']))
foreach($partialShopData['activities'] as $activity)
if(!empty($activity['$id']))
$activityIds[] = $activity['$id'];
return $this->createQueryBuilder()
->field('id')
->in($activityIds)
->getQuery()
->toArray()
;
}
You cannot directly query the Shop collection or (or ODM repository) and receive Activity instances; however, you can use the Query Builder API to specify a projection with select('activities'). The executed query will still return Shop instances, but the activities field should be the only thing hydrated (as a PersistentCollection of Activity instances). In this case, you shouldn't modify any of the non-hydrated Shop fields, as ODM will detect any non-null value as a change.
It should be trivial to add a convenience method on ShopRepository that issues the above query with its select() and returns the collection (or an array) of Activity documents instead of the Shop. Keeping the Shop inaccessible should also protect you from inadvertently modifying other non-hydrated fields within it.
The down-side with this method is that the Activities will be proxy objects and lazily loaded. You can mitigate this with reference priming. With priming, you'll end up doing two queries (one for the Shop and one for all referenced Activity documents).
Regarding your follow-up question about putting this method on the Activity repository, you do have another option. Firstly, I agree that ActivityRepository::findByShopId() is preferable to calling a method on ShopRepository that returns Activity objects.
Each repository has a reference to the document manager, which you can use to access other repositories via the getRepository() method. An ActivityRepository::findByShopId() could do the following:
Access the Shop repository through the document manager
Query for the Shop by its ID, projecting only the activities field and disabling hydration completely
Collect the identifiers from the activities array. Depending on whether the Activity references are simple or not, the elements in that array may be the raw _id values or DBRef objects.
Execute a query for all Activity objects (easy, since we're already in that repository) where the ID is $in the array of identifiers
I am new with doctrine 2.
Why Doctrine 2 not have basic validate method that validate if all values fit entities attributes?
My question target to understand more how doctrine 2 works and why without say that something wrong in doctine 2. (Mostly because i am new i miss some understanding about doctrine 2 way of design)
Example:
<?php
// entities/User.php
/**
* #Entity #Table(name="users")
**/
class User
{
/**
* #Id #GeneratedValue #Column(type="integer")
* #var int
**/
protected $id;
/**
* #Column(type="string")
* #var string
**/
protected $name;
}
code example of use of build in validate(not need connect to db, only validate #Column(type="integer") ) basic function that not exist in doctrine 2:
$user=new User();
$user->setId('trtr');
$user->setName("goodname");
if($user->validate()){
echo 'ok';
}
else{
echo $user->validateError();
}
//output: id of User should be integer and not string
Thanks
Doctrine ORM assumes that entities you're persisting are in a valid state. That's the only job of the persistence layer, and adding validation to it would just be wrong. If you have entities with invalid data in them, you already have an invalid object graph that should not be saved.
So please keep in mind that if you ever had some API like
$someEntity->isValid();
Then something is probably wrong, since the entity should always be valid, and any dependencies of it should be set at construction time and handled in setters/getters so that the object never reaches an inconsistent state.
The main reason is separation of concerns. Since entities are fairly dumb objects that don't know much about the rest of the world, their ability to do validations is limited to begin with.
For instance, there's no way that your typical entity could validate that a particular property is unique.
That said, if you just want to do basic validations, just do them in the setters.
<?php
class MyEntity {
// ...
/**
* #ORM\Column(length="32")
*/
protected $myProperty;
public function setMyProperty($prop){
if (! is_string($prop))
throw new \InvalidArgumentException('MyEntity::setMyProperty() expects a string!';
if (strlen($prop) > 32)
throw new \LengthException('Argument passed to MyEntity::setMyProperty() is too long!');
$this->myProperty = $prop;
}
}
This approach can be used to enforce data types, lengths, etc. Anything beyond that is better handled somewhere other than inside your entity.
It's not good idea to mix entity and validation, but it make sense to have this rules in entity as annotation and validation logic in separated aspect validator class.
Check how it's done in Spring framework -
http://www.abtosoftware.com/blog/form-validation-using-aspect-oriented-programming-aop-in-spring-framework
and how to implement it with doctrine2 and go -
http://go.aopphp.com/blog/2013/07/21/implementing-logging-aspect-with-doctrine-annotations/