-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Add authorization features and a Pundit mixin #971
Conversation
def destroy? | ||
false | ||
end | ||
|
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.
Trailing whitespace detected.
def update? | ||
record.address_state == "AZ" | ||
end | ||
|
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.
Trailing whitespace detected.
def create? | ||
false | ||
end | ||
|
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.
Trailing whitespace detected.
scope.where(customer: user) | ||
end | ||
end | ||
|
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.
Trailing whitespace detected.
def resolve | ||
scope.all | ||
end | ||
|
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.
Trailing whitespace detected.
end | ||
it 'does not show edit actions for records elsewhere' do | ||
o = create :order, customer: user, address_state: 'GA' | ||
expect(controller.show_action? :edit, o).to be false |
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.
Add parentheses to nested method call controller.show_action? :edit, o.
expect(controller.show_action? :edit, o).to be true | ||
end | ||
it 'does not show edit actions for records elsewhere' do | ||
o = create :order, customer: user, address_state: 'GA' |
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.
Prefer double-quoted strings unless you need single quotes to avoid extra backslashes for escaping.
o = create :order, customer: user, address_state: 'AZ' | ||
expect(controller.show_action? :edit, o).to be true | ||
end | ||
it 'does not show edit actions for records elsewhere' do |
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.
Prefer double-quoted strings unless you need single quotes to avoid extra backslashes for escaping.
describe '#show_action?' do | ||
it 'shows edit actions for records in AZ' do | ||
o = create :order, customer: user, address_state: 'AZ' | ||
expect(controller.show_action? :edit, o).to be true |
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.
Add parentheses to nested method call controller.show_action? :edit, o.
end | ||
describe '#show_action?' do | ||
it 'shows edit actions for records in AZ' do | ||
o = create :order, customer: user, address_state: 'AZ' |
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.
Prefer double-quoted strings unless you need single quotes to avoid extra backslashes for escaping.
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.
This looks pretty good to me. I like the feature and the agnostic approach.
I would say:
- Please address Hound's warnings. I'm surprised there aren't more, I wonder if it failed?
- Remove the comments, as I don't think they are necessary and there aren't similar ones (I think!) in the codebase.
@@ -104,6 +107,8 @@ def dashboard | |||
|
|||
def requested_resource | |||
@_requested_resource ||= find_resource(params[:id]) | |||
authorize_resource(@_requested_resource) | |||
@_requested_resource | |||
end |
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.
This breaks the existing memoization. Here's an alternative, saving us from (potentially) checking the authorization multiple times:
def requested_resource
@_requested_resource ||= find_resource(params[:id]).tap do |r|
authorize_resource(r)
end
end
But then I think... we could go further. If the spec for authorize_resource
required it to return the resource on success, we could do this:
def requested_resource
@_requested_resource ||= authorize_resource(find_resource(params[:id]))
end
In fact that's what Pundit's own authorize
does, which in turn means that the implementation provided by Punditize
does this, although not the default, blank implementation.
But I'm probably complicating this too much for too little gain, so I'll be happy with something along the lines of my first alternative.
locals = capture_view_locals { get :index } | ||
expect(locals[:resources].count).to eq(9) # only my orders | ||
end | ||
end |
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.
Please separate these with blank lines. I'm surprised that Hound is not complaining about this and the single quotes?
Thanks @pedantic-git! I know @pablobm left some comments, but I'm thinking it'd be good to get this into |
Sure! I'll try to get onto these tomorrow. Just working on another Administrate PR right now (trying to get editing working for Field::Polymorphic... nearly there too!) |
9cf018a
to
6661003
Compare
def destroy? | ||
false | ||
end | ||
|
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.
Extra empty line detected at class body end.
@@ -0,0 +1,25 @@ | |||
class OrderPolicy < ApplicationPolicy | |||
|
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.
Extra empty line detected at class body beginning.
expect(controller.show_action?(:destroy, o)).to be false | ||
end | ||
end | ||
|
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.
Extra empty line detected at block body end.
describe "DELETE destroy" do | ||
it "never allows me to delete a record" do | ||
o = create :order, customer: user, address_state: "AZ" | ||
expect{delete :destroy, id: o.id}.to raise_error(Pundit::NotAuthorizedError) |
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.
Space missing to the left of {.
Space missing inside {.
Space missing inside }.
Line is too long. [84/80]
|
||
it "does not allow me to edit other records" do | ||
ga = create :order, customer: user, address_state: "GA" | ||
expect{get :edit, id: ga.id}.to raise_error(Pundit::NotAuthorizedError) |
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.
Space missing to the left of {.
Space missing inside {.
Space missing inside }.
end | ||
|
||
end | ||
|
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.
Trailing whitespace detected.
def show_action?(action, resource) | ||
Pundit.policy!(pundit_user, resource).send("#{action}?".to_sym) | ||
end | ||
|
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.
Trailing whitespace detected.
def authorize_resource(resource) | ||
authorize resource | ||
end | ||
|
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.
Trailing whitespace detected.
def scoped_resource | ||
policy_scope_admin super | ||
end | ||
|
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.
Trailing whitespace detected.
module Punditize | ||
extend ActiveSupport::Concern | ||
include Pundit | ||
|
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.
Trailing whitespace detected.
def destroy? | ||
false | ||
end | ||
|
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.
Extra empty line detected at class body end.
@@ -0,0 +1,25 @@ | |||
class OrderPolicy < ApplicationPolicy | |||
|
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.
Extra empty line detected at class body beginning.
expect(controller.show_action?(:destroy, o)).to be false | ||
end | ||
end | ||
|
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.
Extra empty line detected at block body end.
describe "DELETE destroy" do | ||
it "never allows me to delete a record" do | ||
o = create :order, customer: user, address_state: "AZ" | ||
expect{delete :destroy, id: o.id}.to raise_error(Pundit::NotAuthorizedError) |
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.
Space missing to the left of {.
Space missing inside {.
Space missing inside }.
Line is too long. [84/80]
|
||
it "does not allow me to edit other records" do | ||
ga = create :order, customer: user, address_state: "GA" | ||
expect{get :edit, id: ga.id}.to raise_error(Pundit::NotAuthorizedError) |
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.
Space missing to the left of {.
Space missing inside {.
Space missing inside }.
end | ||
|
||
end | ||
|
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.
Trailing whitespace detected.
def show_action?(action, resource) | ||
Pundit.policy!(pundit_user, resource).send("#{action}?".to_sym) | ||
end | ||
|
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.
Trailing whitespace detected.
def authorize_resource(resource) | ||
authorize resource | ||
end | ||
|
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.
Trailing whitespace detected.
def scoped_resource | ||
policy_scope_admin super | ||
end | ||
|
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.
Trailing whitespace detected.
module Punditize | ||
extend ActiveSupport::Concern | ||
include Pundit | ||
|
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.
Trailing whitespace detected.
6661003
to
1daee39
Compare
def show_action?(action, resource) | ||
Pundit.policy!(pundit_user, resource).send("#{action}?".to_sym) | ||
end | ||
|
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.
Extra empty line detected at block body end.
def authorize_resource(resource) | ||
resource | ||
end | ||
|
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.
Extra empty line detected at class body end.
@@ -145,5 +148,20 @@ def show_search_bar? | |||
dashboard.collection_attributes | |||
).any? { |_name, attribute| attribute.searchable? } | |||
end | |||
|
|||
def show_action?(action, resource) |
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.
Unused method argument - action. If it's necessary, use _ or _action as an argument name to indicate that it won't be used. You can also write as show_action?() if you want the method to accept any arguments but don't care about them.
Unused method argument - resource. If it's necessary, use _ or _resource as an argument name to indicate that it won't be used. You can also write as show_action?() if you want the method to accept any arguments but don't care about them.
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.
Not addressing this - it's a method signature that is meant to be overridden in subclasses so the arguments and their names are important.
@@ -71,7 +74,7 @@ def destroy | |||
flash[:notice] = translate_with_resource("destroy.success") | |||
redirect_to action: :index | |||
end | |||
|
|||
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.
Trailing whitespace detected.
86e29a6
to
5b12481
Compare
OK! I've addressed all but two of the Hound violations (see above for my reasoning), I've changed the way [Removing comments is still something I struggle with - I can understand other elements of house style that I disagree with but this one always seems very counterintuitive to me.] Let me know if I can do anything else! |
Just realized that Pundit 1.1.0 (the latest gem) doesn't return the object from |
…with Pundit 1.1.0
@@ -27,7 +27,7 @@ It displays a header, and renders the `_form` partial to do the heavy lifting. | |||
"#{t("administrate.actions.show")} #{page.page_title}", | |||
[namespace, page.resource], | |||
class: "button", | |||
) if valid_action? :show %> | |||
) if valid_action?(:show) && show_action?(:show, page.resource) %> |
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.
Is it possible to use only show_action?
in all cases?
Like
def show_action?(action, resource)
valid_action?(action, resource) && authorized?(action, resource)
end
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.
Hi @pustomytnyk - #show_action?
is a method that users of administrate can (and should) override in their controllers. If we implement it in terms of #valid_action?
in the base controller and again in the Pundit mixin, there's still opportunity for administrate users to accidentally break the expected behaviour by overriding it to something that's not.
Thanks, @pedantic-git! I'm (finally!) going to merge this now. |
Yay! Thanks @nickcharlton !! |
This PR adds basic authorization features to Administrate, allowing different admins access to different data/actions.
Users are free to roll their own authorization, or use any authz gems they like, but since I personally think Pundit is the best of the bunch, I've implemented a controller concern that allows users to get Pundit support in one line.
Pundit isn't added as a dependency, except for the tests.
I've added an authorization.md file to the
docs/
with full instructions... let me know what you think and if you'd like any changes!