-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Determine pattern for base policy #9
Conversation
78f898e
to
bd695cb
Compare
lib/accessly/policy/base_option_1.rb
Outdated
@@ -0,0 +1,86 @@ | |||
module Accessly | |||
module Policy | |||
class BaseOption1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@armilam So in the first case, you would interact with the policy like BaseOptions1.new(actors).edit?
or BaseOptions1.new(actors).edit?(object)
and in the second case you would use it like BaseOptions2.new(actors).can?(:edit)
or BaseOptions2.new(actors).can?(:edit, object)
? Would that be correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh nevermind... the tests spell it out 😆
@armilam One of the goals that we had for the policy classes is that they would be able to customize the individual checks for permission based on business logic. In option1, if you wanted a straight check for "edit" for example, you would just get |
lib/accessly/policy/base_option_1.rb
Outdated
end | ||
|
||
def self._define_action_method(action, action_id) | ||
unless method_defined?("#{action}?") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One other potential option with this might be to overide "method_missing". It might end up being potentially easier to reason about than metaprogramming them? Something like:
def method_missing(method_name, *args)
action = method_name.to_s.sub(/\?$/,"")
if args.empty?
action_id = actions[action]
return super unless action_id
accessly_query.can?(actions[action], object_type)
else
action_id = actions_on_object[action]
return super unless action_id
object = args.first
accessly_query.can?(action_id, object.class, object.id)
end
end
That's maybe a roughly what I'm picturing... and then if you defined anything more complex than a straight check, it would not be a missing method. If you didn't, then the this would catch that and make a basic check for that action?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
on thing that would make that even simpler is if we came up with a convention of naming the checker methods to distinguish an action from an action on... something like #edit?
vs #edit_on?
then we could just know which type it was from the name?
Good point! You are right that we wanted to make customizing super easy to do. I'll put some thought into how we might do that in both scenarios. |
@rreinhardt9, again regarding #9 (comment)
I added the ability to customize the permission checks in each of option 1 and 2. I didn't like the customization for option 2, so I made an option 3 with the same API as option 2, but with a better customization pattern. @rreinhardt9 and @ehourigan I would love to get your take on those. |
test/base_option_3_policy_test.rb
Outdated
|
||
class CustomizedPolicyOption3 < UserPolicyOption3 | ||
# Customize a general action check | ||
override_action_check :destroy, ->(actor) { true if actor.name == "Aaron" } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm liking the Option3 API, but I'm not married to the override_action_check
naming.
test/base_option_3_policy_test.rb
Outdated
|
||
class CustomizedPolicyOption3 < UserPolicyOption3 | ||
# Customize a general action check | ||
override_action_check :destroy, ->(actor) { true if actor.name == "Aaron" } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For each permission check, the logic is like this:
- Is there an override for this action?
- If yes, run it
- Is the result of that overridden check
nil
?- If so, do the normal
Accessly::Query
lookup
- If so, do the normal
- If not
- Do the normal
Accessly::Query
lookup
- Do the normal
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this API - but is our main use case to override on the given 'actor'? So for any given action, if the actor is an "admin" return true
Also - How do you envision the policies handle the multiple actor scenario? Would a policy accept an actor and then know how to get the actor relationships?
Add method of creating actions in the policy. This uses define_method to create methods based on those actions. Those methods respond appropriately when given or not given an object based on whether the action was defined for OnObject contexts or otherwise.
And update Option 1 (the one we're using) to use namespace instead of object_type where possible
…erride action checks
fc85ed6
to
f040687
Compare
Also, move grant and revoke policy tests to their own test file
1398649
to
b2e886b
Compare
This auto-defines one method per object action that lists the records the actor has the given permission on. For example, if there exists an action `:view`, then the policy defines a method `#view` which returns an ActiveRecord::Relation of the records the actor has `:view` permission granted on.
If the actor is an admin, the base policy will skip the `list` query and return the whole `model_scope`.
Closing this PR so I can open one for the proposed solution. |
DO NOT MERGE
This PR currently contains two options for the base policy class.
I am looking for feedback on the
twothree options. Which should we go with? Is there another option?See the tests for examples of how they would be used in practice.