Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Dynamic authority resource #21

Merged
merged 5 commits into from

2 participants

@nathanl
Owner

Allow dynamic lookup of authority resource class if symbol method name is provided.

This would be useful for Dossier's report_class, for instance.

@nathanl nathanl merged commit cb7695b into master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 12, 2013
  1. Update README

    Nathan Long authored
  2. Do syntax highlighting here

    Nathan Long authored
Commits on Feb 13, 2013
  1. @adamhunter

    fix double L typo

    adamhunter authored
  2. My LludicrousLlama is perfectly llegitimate

    Nathan Long authored
    [ci skip]
This page is out of date. Refresh to see the latest.
View
13 README.markdown
@@ -369,6 +369,19 @@ class LlamasController < ApplicationController
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">
### Views
View
30 lib/authority/controller.rb
@@ -14,18 +14,23 @@ def self.security_violation_callback
included do
rescue_from(Authority::SecurityViolation, :with => Authority::Controller.security_violation_callback)
- class_attribute :authority_resource
+ class << self
+ attr_accessor :authority_resource
+ end
end
module ClassMethods
# 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 [Hash] options - can contain :actions to be merged with existing
+ # @param [Class OR Symbol] resource_or_finder - class whose authorizer
+ # 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
- def authorize_actions_for(model_class, options = {})
- self.authority_resource = model_class
+ def authorize_actions_for(resource_or_finder, options = {})
+ self.authority_resource = resource_or_finder
authority_actions(options[:actions] || {})
before_filter :run_authorization_check, options
end
@@ -86,7 +91,17 @@ def authority_forbidden(error)
# The `before_filter` that will be setup to run when the class method
# `authorize_actions_for` is called
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
# Convenience wrapper for sending configured `user_method` to extract the
@@ -97,6 +112,7 @@ def authority_user
send(Authority.configuration.user_method)
end
- class MissingAction < StandardError ; end
+ class MissingAction < StandardError ; end
+ class MissingResource < StandardError ; end
end
end
View
52 spec/authority/controller_spec.rb
@@ -87,11 +87,16 @@ def self.before_filter(*args) ; end
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)
expect(controller_class.authority_resource).to eq(resource_class)
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
filter_options = {:only => [:show, :edit, :update]}
controller_class.should_receive(:before_filter).with(:run_authorization_check, filter_options)
@@ -146,9 +151,48 @@ def self.before_filter(*args) ; end
describe "run_authorization_check (used as a before_filter)" do
- it "checks authorization on the model specified" do
- controller_instance.should_receive(:authorize_action_for).with(resource_class)
- controller_instance.send(:run_authorization_check)
+ context "if a resource class was specified" do
+
+ 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
it "raises a MissingAction if there is no corresponding action for the controller" do
Something went wrong with that request. Please try again.