Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

new features and testability for controller integration

  • Loading branch information...
commit f5d9f190a8bcfb60ece7d5c86382ed2414251bd0 1 parent 3357aa6
@henning-koch henning-koch authored
Showing with 224 additions and 34 deletions.
  1. 0  lib/aegis.rb
  2. +5 −0 lib/aegis/{access_denied.rb → errors.rb}
  3. +2 −1  lib/aegis/has_role.rb
  4. +1 −1  lib/aegis/loader.rb
  5. +2 −1  lib/aegis/permissions.rb
  6. +86 −0 lib/aegis/spec.rb
  7. +61 −26 lib/rails/action_controller.rb
  8. 0  lib/rails/active_record.rb
  9. +31 −0 spec/action_controller_spec.rb
  10. +2 −0  spec/app_root/app/controllers/application_controller.rb
  11. +1 −1  spec/app_root/app/controllers/reviews_controller.rb
  12. +8 −0 spec/app_root/app/controllers/songs_controller.rb
  13. +4 −0 spec/app_root/app/models/permissions.rb
  14. 0  spec/app_root/config/boot.rb
  15. 0  spec/app_root/config/database.yml
  16. 0  spec/app_root/config/environment.rb
  17. 0  spec/app_root/config/environments/in_memory.rb
  18. 0  spec/app_root/config/environments/mysql.rb
  19. 0  spec/app_root/config/environments/postgresql.rb
  20. 0  spec/app_root/config/environments/sqlite.rb
  21. 0  spec/app_root/config/environments/sqlite3.rb
  22. 0  spec/app_root/config/routes.rb
  23. 0  spec/app_root/lib/console_with_fixtures.rb
  24. +6 −0 spec/has_role_spec.rb
  25. +3 −3 spec/loader_spec.rb
  26. +12 −1 spec/permissions_spec.rb
  27. 0  spec/spec_helper.rb
View
0  lib/aegis.rb 100755 → 100644
File mode changed
View
5 lib/aegis/access_denied.rb → lib/aegis/errors.rb
@@ -1,4 +1,9 @@
module Aegis
+
class AccessDenied < StandardError
end
+
+ class UncheckedPermissions < StandardError
+ end
+
end
View
3  lib/aegis/has_role.rb
@@ -12,7 +12,7 @@ def has_role(options = {})
end
send :define_method, :role_names= do |role_names|
- self.role_name = role_names.join(',')
+ self.role_name = role_names.reject(&:blank?).join(',')
end
send :define_method, :role do
@@ -29,6 +29,7 @@ def has_role(options = {})
role_names.include?(role_name.to_s)
end
+ metaclass ||= singleton_class
metaclass.send :define_method, :validates_role do |*validate_options|
validate_options = validate_options[0] || {}
View
2  lib/aegis/loader.rb
@@ -5,7 +5,7 @@ class << self
def paths
[ 'ostruct',
- 'aegis/access_denied',
+ 'aegis/errors',
'aegis/action',
'aegis/compiler',
'aegis/has_role',
View
3  lib/aegis/permissions.rb
@@ -130,7 +130,8 @@ def guess_action_paths(resource_name, action_name, map)
[ mapped.to_s ]
else
[ "#{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
View
86 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
View
87 lib/rails/action_controller.rb
@@ -1,38 +1,73 @@
module Aegis
module ActionController
- def permissions(resource, options = {})
-
- before_filter :check_permissions, options.slice(:except, :only)
-
- 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)
+ module ClassMethods
+
+ private
+
+ def require_permissions(options = {})
+ before_filter :unchecked_permissions, options
+ end
+
+ def skip_permissions(options = {})
+ skip_before_filter :unchecked_permissions, options
+ end
+
+ def permissions(resource, options = {})
+
+ filter_options = options.slice(:except, :only)
+
+ skip_before_filter :unchecked_permissions, filter_options
+
+ # Store arguments for testing
+ @aegis_permissions_resource = resource
+ @aegis_permissions_options = options
+
+ before_filter :check_permissions, filter_options
+
+ 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
+ module InstanceMethods
+
+ private
+
+ def unchecked_permissions
+ raise Aegis::UncheckedPermissions, "This controller does not check permissions"
+ end
+
+ end
+
end
end
-ActionController::Base.extend(Aegis::ActionController)
+ActionController::Base.extend Aegis::ActionController::ClassMethods
+ActionController::Base.send :include, Aegis::ActionController::InstanceMethods
+
+
View
0  lib/rails/active_record.rb 100755 → 100644
File mode changed
View
31 spec/action_controller_spec.rb
@@ -28,6 +28,37 @@
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
it "should fetch the context through #object, #parent_object and #current_user by default" do
View
2  spec/app_root/app/controllers/application_controller.rb 100755 → 100644
@@ -1,5 +1,7 @@
class ApplicationController < ActionController::Base
+ require_permissions
+
def current_user
User.new(:role_name => 'user')
end
View
2  spec/app_root/app/controllers/reviews_controller.rb
@@ -33,4 +33,4 @@ def parent_object
@parent_object ||= Property.find(params[:id])
end
-end
+end
View
8 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
View
4 spec/app_root/app/models/permissions.rb
@@ -10,5 +10,9 @@ class Permissions < Aegis::Permissions
end
end
+ resources :maps do
+ action :with_permission, :collection => true
+ end
+
end
View
0  spec/app_root/config/boot.rb 100755 → 100644
File mode changed
View
0  spec/app_root/config/database.yml 100755 → 100644
File mode changed
View
0  spec/app_root/config/environment.rb 100755 → 100644
File mode changed
View
0  spec/app_root/config/environments/in_memory.rb 100755 → 100644
File mode changed
View
0  spec/app_root/config/environments/mysql.rb 100755 → 100644
File mode changed
View
0  spec/app_root/config/environments/postgresql.rb 100755 → 100644
File mode changed
View
0  spec/app_root/config/environments/sqlite.rb 100755 → 100644
File mode changed
View
0  spec/app_root/config/environments/sqlite3.rb 100755 → 100644
File mode changed
View
0  spec/app_root/config/routes.rb 100755 → 100644
File mode changed
View
0  spec/app_root/lib/console_with_fixtures.rb 100755 → 100644
File mode changed
View
6 spec/has_role_spec.rb 100755 → 100644
@@ -104,6 +104,12 @@
user.role_names = ['first', 'second']
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
describe 'has_role?' do
View
6 spec/loader_spec.rb
@@ -4,12 +4,12 @@
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/"
- Dir["#{root}*/*.rb"].collect do |file|
+ Dir["#{root}*/*.rb"].each do |file|
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
View
13 spec/permissions_spec.rb
@@ -399,7 +399,7 @@
end
@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
@@ -591,6 +591,17 @@
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
describe 'find_action_by_path' do
View
0  spec/spec_helper.rb 100755 → 100644
File mode changed
Please sign in to comment.
Something went wrong with that request. Please try again.