Skip to content

Commit

Permalink
new features and testability for controller integration
Browse files Browse the repository at this point in the history
  • Loading branch information
triskweline committed Aug 13, 2010
1 parent 3357aa6 commit f5d9f19
Show file tree
Hide file tree
Showing 27 changed files with 224 additions and 34 deletions.
Empty file modified lib/aegis.rb 100755 → 100644
Empty file.
5 changes: 5 additions & 0 deletions lib/aegis/access_denied.rb → lib/aegis/errors.rb
@@ -1,4 +1,9 @@
module Aegis module Aegis

class AccessDenied < StandardError class AccessDenied < StandardError
end end

class UncheckedPermissions < StandardError
end

end end
3 changes: 2 additions & 1 deletion lib/aegis/has_role.rb
Expand Up @@ -12,7 +12,7 @@ def has_role(options = {})
end end


send :define_method, :role_names= do |role_names| send :define_method, :role_names= do |role_names|
self.role_name = role_names.join(',') self.role_name = role_names.reject(&:blank?).join(',')
end end


send :define_method, :role do send :define_method, :role do
Expand All @@ -29,6 +29,7 @@ def has_role(options = {})
role_names.include?(role_name.to_s) role_names.include?(role_name.to_s)
end end


metaclass ||= singleton_class
metaclass.send :define_method, :validates_role do |*validate_options| metaclass.send :define_method, :validates_role do |*validate_options|
validate_options = validate_options[0] || {} validate_options = validate_options[0] || {}


Expand Down
2 changes: 1 addition & 1 deletion lib/aegis/loader.rb
Expand Up @@ -5,7 +5,7 @@ class << self
def paths def paths
[ 'ostruct', [ 'ostruct',


'aegis/access_denied', 'aegis/errors',
'aegis/action', 'aegis/action',
'aegis/compiler', 'aegis/compiler',
'aegis/has_role', 'aegis/has_role',
Expand Down
3 changes: 2 additions & 1 deletion lib/aegis/permissions.rb
Expand Up @@ -130,7 +130,8 @@ def guess_action_paths(resource_name, action_name, map)
[ mapped.to_s ] [ mapped.to_s ]
else else
[ "#{action_name}_#{resource_name.to_s.singularize}", [ "#{action_name}_#{resource_name.to_s.singularize}",
"#{action_name}_#{resource_name.to_s.pluralize}" ] "#{action_name}_#{resource_name.to_s.pluralize}",
resource_name ]
end end
end end


Expand Down
86 changes: 86 additions & 0 deletions lib/aegis/spec.rb
@@ -0,0 +1,86 @@
module Aegis
module Matchers

class CheckPermissions

def initialize(expected_resource, expected_options = {})
@expected_resource = expected_resource
@expected_options = expected_options
end

def matches?(controller)
@controller_class = controller.class
@actual_resource = @controller_class.instance_variable_get('@aegis_permissions_resource')
@actual_options = @controller_class.instance_variable_get('@aegis_permissions_options')
@actual_resource == @expected_resource && @actual_options == @expected_options
end

def failure_message
if @actual_resource != @expected_resource
"expected #{@controller_class} to check permissions against resource #{@expected_resource.inspect}, but it checked against #{@actual_resource.inspect}"
else
"expected #{@controller_class} to check permissions with options #{@expected_options.inspect}, but options were #{@actual_options.inspect}"
end
end

def negative_failure_message
if @actual_resource == @expected_resource
"expected #{@controller_class} to not check permissions against resource #{@expected_resource.inspect}"
else
"expected #{@controller_class} to not check permissions with options #{@expected_options.inspect}"
end
end

def description
description = "check permissions against resource #{@expected_resource.inspect}"
description << " with options #{@expected_options.inspect}" if @expected_options.any?
description
end

end

def check_permissions(*args)
CheckPermissions.new(*args)
end

end
end


ActiveSupport::TestCase.send :include, Aegis::Matchers

#Spec::Rails::Example::ControllerExampleGroup.extend Aegis::ControllerSpecMacros


# def it_should_allow_access_for(*allowed_roles, &block)
#
# denied_roles = Permissions.roles.collect(&:name) - allowed_roles
#
# describe 'permissions' do
#
# before :each do
# sign_out
# end
#
# it "should deny access when no user is signed in" do
# expect { instance_eval(&block) }.to raise_error(Aegis::AccessDenied)
# end
#
# allowed_roles.each do |role|
# it "should allow access for an authenticated #{role}" do
# sign_in User.new(:role_name => role)
# expect { instance_eval(&block) }.to_not raise_error
# response.code.should == '200'
# end
# end
#
# denied_roles.each do |role|
# it "should deny access for an authenticated #{role}" do
# sign_in User.new(:role_name => role)
# expect { instance_eval(&block) }.to raise_error(Aegis::AccessDenied)
# end
# end
#
# end
#
# end
87 changes: 61 additions & 26 deletions lib/rails/action_controller.rb
@@ -1,38 +1,73 @@
module Aegis module Aegis
module ActionController module ActionController


def permissions(resource, options = {}) module ClassMethods


before_filter :check_permissions, options.slice(:except, :only) private


instance_eval do def require_permissions(options = {})

before_filter :unchecked_permissions, options
private end


actions_map = (options[:map] || {}).stringify_keys def skip_permissions(options = {})
object_method = options[:object] || :object skip_before_filter :unchecked_permissions, options
parent_object_method = options[:parent_object] || :parent_object end
user_method = options[:user] || :current_user
permissions = lambda { Aegis::Permissions.app_permissions(options[:permissions]) } def permissions(resource, options = {})


define_method :check_permissions do filter_options = options.slice(:except, :only)
action = permissions.call.guess_action(
resource, skip_before_filter :unchecked_permissions, filter_options
action_name.to_s,
actions_map # Store arguments for testing
) @aegis_permissions_resource = resource
args = [] @aegis_permissions_options = options
args << send(user_method)
args << send(parent_object_method) if action.takes_parent_object before_filter :check_permissions, filter_options
args << send(object_method) if action.takes_object
action.may!(*args) instance_eval do

private

actions_map = (options[:map] || {}).stringify_keys
object_method = options[:object] || :object
parent_object_method = options[:parent_object] || :parent_object
user_method = options[:user] || :current_user
permissions = lambda { Aegis::Permissions.app_permissions(options[:permissions]) }

define_method :check_permissions do
action = permissions.call.guess_action(
resource,
action_name.to_s,
actions_map
)
args = []
args << send(user_method)
args << send(parent_object_method) if action.takes_parent_object
args << send(object_method) if action.takes_object
action.may!(*args)
end

end end


end end


end end


module InstanceMethods

private

def unchecked_permissions
raise Aegis::UncheckedPermissions, "This controller does not check permissions"
end

end

end end
end end


ActionController::Base.extend(Aegis::ActionController) ActionController::Base.extend Aegis::ActionController::ClassMethods
ActionController::Base.send :include, Aegis::ActionController::InstanceMethods


Empty file modified lib/rails/active_record.rb 100755 → 100644
Empty file.
31 changes: 31 additions & 0 deletions spec/action_controller_spec.rb
Expand Up @@ -28,6 +28,37 @@


end end


describe 'require_permissions' do

it "should set a before_filter :unchecked_permissions" do
@controller_class.should_receive(:before_filter).with(:unchecked_permissions, :only => :show)
@controller_class.class_eval do
require_permissions :only => :show
end
end

end

describe 'skip_permissions' do

it "should skip a before_filter :unchecked_permissions" do
@controller_class.should_receive(:skip_before_filter).with(:unchecked_permissions, :only => :show)
@controller_class.class_eval do
skip_permissions :only => :show
end
end

end

describe 'unchecked_permissions' do

it "should raise Aegis::UncheckedPermissions" do
controller = @controller_class.new
expect { controller.send(:unchecked_permissions) }.to raise_error(Aegis::UncheckedPermissions)
end

end

describe 'permissions' do describe 'permissions' do


it "should fetch the context through #object, #parent_object and #current_user by default" do it "should fetch the context through #object, #parent_object and #current_user by default" do
Expand Down
2 changes: 2 additions & 0 deletions spec/app_root/app/controllers/application_controller.rb 100755 → 100644
@@ -1,5 +1,7 @@
class ApplicationController < ActionController::Base class ApplicationController < ActionController::Base


require_permissions

def current_user def current_user
User.new(:role_name => 'user') User.new(:role_name => 'user')
end end
Expand Down
2 changes: 1 addition & 1 deletion spec/app_root/app/controllers/reviews_controller.rb
Expand Up @@ -33,4 +33,4 @@ def parent_object
@parent_object ||= Property.find(params[:id]) @parent_object ||= Property.find(params[:id])
end end


end end
8 changes: 8 additions & 0 deletions spec/app_root/app/controllers/songs_controller.rb
@@ -0,0 +1,8 @@
class SongsController

require_permissions

skip_permissions :only => :index
permissions :songs, :only => :new

end
4 changes: 4 additions & 0 deletions spec/app_root/app/models/permissions.rb
Expand Up @@ -10,5 +10,9 @@ class Permissions < Aegis::Permissions
end end
end end


resources :maps do
action :with_permission, :collection => true
end

end end


Empty file modified spec/app_root/config/boot.rb 100755 → 100644
Empty file.
Empty file modified spec/app_root/config/database.yml 100755 → 100644
Empty file.
Empty file modified spec/app_root/config/environment.rb 100755 → 100644
Empty file.
Empty file modified spec/app_root/config/environments/in_memory.rb 100755 → 100644
Empty file.
Empty file modified spec/app_root/config/environments/mysql.rb 100755 → 100644
Empty file.
Empty file modified spec/app_root/config/environments/postgresql.rb 100755 → 100644
Empty file.
Empty file modified spec/app_root/config/environments/sqlite.rb 100755 → 100644
Empty file.
Empty file modified spec/app_root/config/environments/sqlite3.rb 100755 → 100644
Empty file.
Empty file modified spec/app_root/config/routes.rb 100755 → 100644
Empty file.
Empty file modified spec/app_root/lib/console_with_fixtures.rb 100755 → 100644
Empty file.
6 changes: 6 additions & 0 deletions spec/has_role_spec.rb 100755 → 100644
Expand Up @@ -104,6 +104,12 @@
user.role_names = ['first', 'second'] user.role_names = ['first', 'second']
end end


it "should ignore blank role names" do
user = @user_class.new
user.should_receive(:role_name=).with("first,second")
user.role_names = ['', nil, 'first', '', nil, 'second', '', nil]
end

end end


describe 'has_role?' do describe 'has_role?' do
Expand Down
6 changes: 3 additions & 3 deletions spec/loader_spec.rb
Expand Up @@ -4,12 +4,12 @@


describe 'paths' do describe 'paths' do


it "should return all paths in the lib folder" do it "should return all paths in the lib folder, except files that are optionally loaded" do


root = "#{File.dirname(__FILE__)}/../lib/" root = "#{File.dirname(__FILE__)}/../lib/"
Dir["#{root}*/*.rb"].collect do |file| Dir["#{root}*/*.rb"].each do |file|
path = file.sub(root, "").sub(/\.rb$/, "") path = file.sub(root, "").sub(/\.rb$/, "")
Aegis::Loader.paths.should include(path) unless path == 'aegis/loader' Aegis::Loader.paths.should include(path) unless path == 'aegis/loader' || path == 'aegis/spec'
end end


end end
Expand Down
13 changes: 12 additions & 1 deletion spec/permissions_spec.rb
Expand Up @@ -399,7 +399,7 @@
end end


@permissions.may?(@user, "zoom_into_property", "the property").should be_true @permissions.may?(@user, "zoom_into_property", "the property").should be_true
@permissions.may?(@user, "view_all_properties", "the property").should be_true @permissions.may?(@user, "view_all_properties").should be_true


end end


Expand Down Expand Up @@ -591,6 +591,17 @@


end end


it "should find a root action that has the same name as the given resource" do

@permissions.class_eval do
action :author_section
end

@permissions.guess_action(:author_section, 'irrelevant_action_name').should_not be_abstract
@permissions.guess_action(:undefined_action, 'irrelevant_action_name').should be_abstract

end

end end


describe 'find_action_by_path' do describe 'find_action_by_path' do
Expand Down
Empty file modified spec/spec_helper.rb 100755 → 100644
Empty file.

0 comments on commit f5d9f19

Please sign in to comment.