Skip to content
This repository has been archived by the owner on Nov 19, 2019. It is now read-only.

Dynamic authority resource #21

Merged
merged 5 commits into from
Feb 13, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions README.markdown
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -369,6 +369,19 @@ class LlamasController < ApplicationController
end end
``` ```


Finally, note that if you have a controller that dynamically determines the class it's working with, you can pass the name of a controller instance method to `authorize_actions_for` instead of a class, and the class will be looked up when a request is made.

```ruby
class LlamasController < ApplicationController

authorize_actions_for :llama_class

def llama_class
[StandardLlama, LludicriousLlama].sample
end
end
```

<a name="views"> <a name="views">
### Views ### Views


Expand Down
30 changes: 23 additions & 7 deletions lib/authority/controller.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -14,18 +14,23 @@ def self.security_violation_callback


included do included do
rescue_from(Authority::SecurityViolation, :with => Authority::Controller.security_violation_callback) rescue_from(Authority::SecurityViolation, :with => Authority::Controller.security_violation_callback)
class_attribute :authority_resource class << self
attr_accessor :authority_resource
end
end end


module ClassMethods module ClassMethods


# Sets up before_filter to ensure user is allowed to perform a given controller action # Sets up before_filter to ensure user is allowed to perform a given controller action
# #
# @param [Class] model_class - class whose authorizer should be consulted # @param [Class OR Symbol] resource_or_finder - class whose authorizer
# @param [Hash] options - can contain :actions to be merged with existing # should be consulted, or instance method on the controller which will
# determine that class when the request is made
# @param [Hash] options - can contain :actions to
# be merged with existing
# ones and any other options applicable to a before_filter # ones and any other options applicable to a before_filter
def authorize_actions_for(model_class, options = {}) def authorize_actions_for(resource_or_finder, options = {})
self.authority_resource = model_class self.authority_resource = resource_or_finder
authority_actions(options[:actions] || {}) authority_actions(options[:actions] || {})
before_filter :run_authorization_check, options before_filter :run_authorization_check, options
end end
Expand Down Expand Up @@ -86,7 +91,17 @@ def authority_forbidden(error)
# The `before_filter` that will be setup to run when the class method # The `before_filter` that will be setup to run when the class method
# `authorize_actions_for` is called # `authorize_actions_for` is called
def run_authorization_check def run_authorization_check
authorize_action_for self.class.authority_resource authorize_action_for authority_resource
end

def authority_resource
return self.class.authority_resource if self.class.authority_resource.is_a?(Class)
return send(self.class.authority_resource) if respond_to?(self.class.authority_resource)
raise MissingResource.new(
"Trying to authorize actions for '#{self.class.authority_resource}', but can't. \
Must be either a resource class OR the name of a controller instance method that \
returns one.".squeeze(' ')
)
end end


# Convenience wrapper for sending configured `user_method` to extract the # Convenience wrapper for sending configured `user_method` to extract the
Expand All @@ -97,6 +112,7 @@ def authority_user
send(Authority.configuration.user_method) send(Authority.configuration.user_method)
end end


class MissingAction < StandardError ; end class MissingAction < StandardError ; end
class MissingResource < StandardError ; end
end end
end end
52 changes: 48 additions & 4 deletions spec/authority/controller_spec.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -87,11 +87,16 @@ def self.before_filter(*args) ; end


describe "authorize_actions_for" do describe "authorize_actions_for" do


it "allows specifying the model to protect" do it "allows specifying the class of the model to protect" do
controller_class.authorize_actions_for(resource_class) controller_class.authorize_actions_for(resource_class)
expect(controller_class.authority_resource).to eq(resource_class) expect(controller_class.authority_resource).to eq(resource_class)
end end


it "allows specifying an instance method to find the class of the model to protect" do
controller_class.authorize_actions_for(:finder_method)
expect(controller_class.authority_resource).to eq(:finder_method)
end

it "sets up a before_filter, passing the options it was given" do it "sets up a before_filter, passing the options it was given" do
filter_options = {:only => [:show, :edit, :update]} filter_options = {:only => [:show, :edit, :update]}
controller_class.should_receive(:before_filter).with(:run_authorization_check, filter_options) controller_class.should_receive(:before_filter).with(:run_authorization_check, filter_options)
Expand Down Expand Up @@ -146,9 +151,48 @@ def self.before_filter(*args) ; end


describe "run_authorization_check (used as a before_filter)" do describe "run_authorization_check (used as a before_filter)" do


it "checks authorization on the model specified" do context "if a resource class was specified" do
controller_instance.should_receive(:authorize_action_for).with(resource_class)
controller_instance.send(:run_authorization_check) it "checks authorization on the model specified" do
controller_instance.should_receive(:authorize_action_for).with(resource_class)
controller_instance.send(:run_authorization_check)
end

end

context "if a method for determining the class was specified" do

let(:resource_class) { Hash }
let(:controller_class) do
Class.new(ExampleController).tap do |c|
c.send(:include, Authority::Controller)
c.authorize_actions_for(:method_to_find_class)
end
end

context "if the controller has such an instance method" do

before :each do
controller_instance.stub(:method_to_find_class).and_return(resource_class)
end

it "checks authorization on class returned by that method" do
controller_instance.should_receive(:authorize_action_for).with(resource_class)
controller_instance.send(:run_authorization_check)
end

end

context "if the controller has no such instance method" do

it "raises an exception" do
expect{controller_instance.send(:run_authorization_check)}.to raise_error(
Authority::Controller::MissingResource
)
end

end

end end


it "raises a MissingAction if there is no corresponding action for the controller" do it "raises a MissingAction if there is no corresponding action for the controller" do
Expand Down