isomorfeus-policy
Policy for Isomorfeus
Community and Support
At the Isomorfeus Framework Project
Usage
Policy is enforced on the server, however, the same policy rules are also available on the client to allow for making consistent decisions everywhere.
Everything that is not explicitly allowed somewhere is denied.
Place the policy files in your projects app/policies
directory.
Any class that would like to authorize for accessing a resource needs to include the LucidAuthorization::Mixin
or inherit from LucidAuthorization::Base
. Example:
class MyUser
include LucdiAuthorization::Mixin
end
Any instance of that class then has the following methods available:
authorized?(target_class, method_name, props)
- returning true or falseauthorized!(target_class, method_name, props)
- raising a exception if access is denied, otherwise returning true
These methods, when called, look for a Policy class. The Policy class must be named after the user class plus the word 'Policy'. Example:
For a class MyUser
the policy class must be MyUserPolicy
.
Simple Rules
Example Policy:
class MyUserPolicy < LucidPolicy::Base
# allow access to all methods of a class
allow BlaBlaGraph
# class names can be given as strings which benefits autoload performance as the class can be loaded later on
allow 'BlaBlaGraph'
# allow access to all methods of multiple classes
allow BlaBlaGraph, BlaComposition
# allow access to a single method of a class
allow BlaBlaGraph, :load
# or allow multiple methods of a class
allow BlaBlaGraph, :load, :save
# deny access to all methods of multiple classes
deny BlaGraph, SuperOperation
deny others # or: allow others
# in a otherwise empty policy the following can be used too:
# allow all
# deny all
# further conditions can be used
allow BlaBlaGraph, :member_feature, only_if: :registered?
# this will call the method registered? on the user instance. The method must return a boolean.
# permission is granted if the method returned true
# method name must be given as symbol
# other versions:
allow BlaBlaGraph, :member_feature, if: :registered?
allow BlaBlaGraph, :public_feature, only_if_not: :registered?
# this will call the method registered? on the user instance. The method must return a boolean.
# permission is granted if the method returned false
# method name must be given as symbol
# other versions:
allow BlaBlaGraph, :member_feature, only_unless: :registered?
allow BlaBlaGraph, :member_feature, unless: :registered?
allow BlaBlaGraph, :member_feature, if_not: :registered?
# a proc can be passed directly to a rules condition
allow BlaBlaGraph, :member_feature, if: proc { |user, props| user.registered? }
# permission is granted if the block returns true
# similar for deny, though:
deny BlaBlaGraph, :member_feature, if: proc { |user, props| !user.registered? }
# permission is denied if the block returns true
end
and then any of:
user.(target_class)
user.(target_class, target_method)
user.(target_class, target_method, *props)
or:
user.(target_class)
user.(target_class, target_method)
user.(target_class, target_method, *props)
which will raise a LucidPolicy::Exception unless authorized
Custom Rules
Besides calling methods or executing blocks from allow or deny, custom rules can be defined. Example:
class MyUserPolicy < LucidPolicy::Base
rule BlaGraph, :load, :count do |user, target_class_name, target_method, props|
allow if user.verified?
allow if user.admin?
deny
end
end
Within the rule block the allow
and deny
methods must be used to allow or deny access.
They accept no arguments. Within the block the default is to deny, so any matching allow wins.
Combining Policies
Policies can be combined. Across policies, the first Policy rule that permits access wins.
The outer policy is considered first, then the other policies.
If the outer policy allows, then the other policies are not considered.
If the outer policy has a allow all
or a allow others
rule, then there is a good chance the other policies are never considered.
The recommended default rule for combined policies is deny others
.
Given a:
class AdminRolePolicy < LucidPolicy::Base
rule BlaGraph, :load, :count do |user, target_class_name, target_method, props|
allow if user.verified?
deny
end
end
another policy can include the rules. Example:
class MyUserPolicy < LucidPolicy::Base
combine_with AdminRolePolicy
# conditions as for allow and deny can be used too
combine_with AdminRolePolicy, if: :admin?
# this will call the method admin? on the user instance. The method must return a boolean.
# this will execute the AdminRolePolicy rules only if the method returned true
# method name must be given as symbol
combine_with AdminRolePolicy, if_not: :normal_guy?
# this will call the method normal_guy?? on the user instance. The method must return a boolean.
# this will execute the AdminRolePolicy rules only if the method returned false
# method name must be given as symbol
# other versions:
combine_with AdminRolePolicy, unless: :normal_guy?
# a block can be passed directly to a rules condition
combine_with AdminRolePolicy, if: proc { |user| user.admin? }
# this will execute the AdminRolePolicy rules only if the condition block returns true
deny others
end
Debugging Policies or finding out the winning rule
Its possible to start recording the reason for a authorization result:
my_user.
When then authorizing:
my_user.(BlaGraph, :load)
the reason for access or denied access, the winning rule, can be inspected:
my_user.authozation_reason
It will be a Hash and show the responsible policy class, condition, condition result, etc. Example:
{ combined:
{ class_name: "Resource",
others: :deny,
policy_class: "CombinedPolicy" },
policy_class: "UserPolicy" }
Recording can be stopped again, for better performance:
my_user.