Skip to content
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

Ehouriga/ch9191/implement grant #3

Merged
merged 7 commits into from
Mar 16, 2018
Merged

Conversation

ehourigan
Copy link
Contributor

@ehourigan ehourigan commented Mar 13, 2018

Resolves [ch9191]

add the ability to GRANT permissions on a namespace or specific object

Please SQUASH my commits

@ehourigan ehourigan added the wip label Mar 13, 2018
@ehourigan ehourigan changed the base branch from armilam/ch9270/implement-caching-on-can to master March 14, 2018 14:40
@@ -2,6 +2,10 @@
require "accesscontrol/query"

module AccessControl

unless defined?(CouldNotGrantError) == "constant" && CouldNotGrantError.class == Class
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

borrowed this structure from acts_as_archival

test/schema.rb Outdated
t.column :action, :integer
t.column :actor_id, :integer
t.column :actor_type, :string
t.column :object_type, :string
t.column :object_id, :integer
end

add_index(:access_control_permitted_actions, [:segment_id, :action, :actor_id, :actor_type, :object_type], unique: true, name: "acpm_acpa_uniq_table_idx")
add_index(:access_control_permitted_action_on_objects, [:segment_id, :action, :actor_id, :actor_type, :object_type, :object_id], unique: true, name: "acpm_acpaoo_uniq_table_idx")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adding unique index on all columns to force it at the database level (prevents identical GRANTs)

test/schema.rb Outdated
@@ -6,20 +6,23 @@
end

create_table :access_control_permitted_actions, force: true, id: :uuid do |t|
t.column :segment_id, :integer
t.column :segment_id, :integer, default: -1
Copy link
Contributor Author

@ehourigan ehourigan Mar 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The table indexes need a sensible default on segment_id for the unique constraint functionality

@ehourigan ehourigan removed the wip label Mar 14, 2018
@armilam
Copy link
Contributor

armilam commented Mar 14, 2018

I just added the Clubhouse wekbhook, so:

[ch9191]

assert_nil(AccessControl::Query.new(actor).grant(1, Post, post.id))
end

it 'retuns raises an error the actor has access' do
Copy link
Contributor Author

@ehourigan ehourigan Mar 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fix all these poorly worded 'it' blocks in #4

Copy link
Contributor

@armilam armilam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is good! I have a few questions here asking whether we should tweak a couple things. But overall, I like this.

One caveat: I haven't tested it fully yet, but I wanted to get my comments to you early in case there's any discussion. I will be testing shortly.


unless defined?(CouldNotGrantError) == "constant" && CouldNotGrantError.class == Class
CouldNotGrantError = Class.new(StandardError)
end
# AccessControl's tables are prefixed with access_control to
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tiny nitpick, but normally I would expect to see an empty line between the end of one thing and the comment for the next thing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

# A grant is universally unique and is enforced at the database level.
#
# @param action_id [Integer] The action to grant for the object
# @param object_type [ActiveRecord::Base] The ActiveRecord model that receives a permission grant.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically, we pass in a Class for object_type, right? I know I did in can? exactly what you're doing here by specifying ActiveRecord::Base, but I'm wondering if this should actually be Class. I'm still getting used to yardoc, so I could be wrong. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya I think you're right - I was talking with @rreinhardt9 about it yesterday and were talking about this scenario:

Consider we have a LessonPolicy and a RestrictedLessonPolicy - Both of these policies define actions with different meaning BUT refer to the Lesson ActiveRecord model/object. This means that the object_type we compare against should be different between the two policies? (in this case modifying what you have suggested to Class)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on previous conversations, I expect LessonPolicy and RestrictedLessonPolicy to use the same Lesson permissions via Accessly, but to filter them with business logic. Especially since we use object_type to chain AR calls on in list.

Copy link
Contributor Author

@ehourigan ehourigan Mar 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup! business logic

The only argument I can think of to keep [ActiveRecord::Base] as the object_type is in support of the list operation

# @param action_id [Integer] The action to grant for the object
# @param object_type [ActiveRecord::Base] The ActiveRecord model that receives a permission grant.
# @param object_id [Integer] The id of the ActiveRecord object which receives a permission grant
# @return [nil] Returns nil if successful, otherwise will raise an Error (AccessControl::CouldNotGrantError).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use yardoc's @raise syntax to describe raised exceptions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

# # Allow the user access to Post 7
# AccessControl::Query.new(user).grant(3, Post, 7)
def grant(action_id, object_type, object_id)
a = PermittedActionOnObject.create(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a need for the local variable a? If so, can we name it something more clear? If not, remove it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

damn - that shouldn't be there

#
# @param action_id [Integer] The action to grant for the object
# @param object_type [String] The namespace of the given action_id.
# @return [nil] Returns nil if successful, otherwise will raise an Error (AccessControl::CouldNotGrantError).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, you can use yardoc's @raise syntax to describe raised exceptions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

)
nil
rescue
raise AccessControl::CouldNotGrantError.new("Could not grant action #{action_id} on object #{object_type} with id #{object_id} for actor #{@actor}")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we should silently rescue from PG::UniqueViolation (or the equivalent on other DBMSs if we end up supporting them)? Technically, if that error is raised, then the permission has already been granted and nothing really wrong has happened, right?

Of course, if other errors happened, then the caller definitely needs to know about it. In those cases, should we bubble up or include the error message so it's clear what the problem is?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we just return nil on the PG::UniqueViolation?

Also, if we start using Postgres specific errors we will need to update our test suite to run against a Postgres database, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we just return nil on the PG::UniqueViolation?

Makes sense to me!

Also, if we start using Postgres specific errors we will need to update our test suite to run against a Postgres database, right?

Bah, you're right! Is there a more generic way we can do it?

Copy link
Contributor Author

@ehourigan ehourigan Mar 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know there's an easier way - but since we're leveraging where_tuples we may just want to support Postgres to start with?

rescue ActiveRecord::RecordNotUnique

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh sweet!

)
nil
rescue => e
raise AccessControl::CouldNotGrantError.new("Could not grant action #{action_id} on object #{object_type} for actor #{@actor}")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like I asked above, should we silently rescue from PG::UniqueViolation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see question above

# # Allow the user access to Post 7
# AccessControl::Query.new(user).grant(3, Post, 7)
def grant(action_id, object_type, object_id)
a = PermittedActionOnObject.create(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be create! with the ! so that errors get raised (which we rescue below) instead of it just returning false and failing relatively silently. Thoughts?

Copy link
Contributor Author

@ehourigan ehourigan Mar 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍- I had it there initially and then changed it

it 'returns nil after a successful grant' do
actor = User.create!

assert_nil(AccessControl::Query.new(actor).grant(1, Post))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed this before, but we're using must_equal in other tests and assert_stuff here. It feels like we should standardize one way or the other.

Preferences? I think it's fine either way. I'm just looking for consistency.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had must_equal nil -- but there were a bunch of deprecation warnings about that and the suggested remedy was to use assert

Copy link
Contributor

@armilam armilam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks fantastic! I like that we're swallowing duplicate records and bubbling up anything else. And the tests look like they cover the cases I thought of.

Onward!

test/schema.rb Outdated
t.column :action, :integer, null: false
t.column :actor_id, :integer, null: false
t.column :actor_type, :string, null: false
t.column :object_type, :string, null: false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call!

@armilam armilam merged commit dd61d65 into master Mar 16, 2018
@rreinhardt9 rreinhardt9 deleted the ehouriga/ch9191/implement-grant branch February 19, 2021 19:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants