From 2363f33aff9025d38ef7d18e5ea2ffb463b1af6f Mon Sep 17 00:00:00 2001 From: Jan Schulz-Hofen Date: Tue, 17 Jan 2017 16:53:26 +0100 Subject: [PATCH 3/4] Add optional `scope` parameter to Role#allowed_to? MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scope can be an array of permissions and will be used as a filter using the logical AND operation between the role’s permissions and the permissions given as scope. --- app/models/role.rb | 26 ++++++++++++++++++-------- test/unit/role_test.rb | 26 ++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/app/models/role.rb b/app/models/role.rb index ee1a664e6..d4e7bdc6a 100644 --- a/app/models/role.rb +++ b/app/models/role.rb @@ -193,11 +193,14 @@ class Role < ActiveRecord::Base # action can be: # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit') # * a permission Symbol (eg. :edit_project) - def allowed_to?(action) + # scope can be: + # * an array of permissions which will be used as filter (logical AND) + + def allowed_to?(action, scope=nil) if action.is_a? Hash - allowed_actions.include? "#{action[:controller]}/#{action[:action]}" + allowed_actions(scope).include? "#{action[:controller]}/#{action[:action]}" else - allowed_permissions.include? action + allowed_permissions(scope).include? action end end @@ -289,13 +292,20 @@ class Role < ActiveRecord::Base private - def allowed_permissions - @allowed_permissions ||= permissions + Redmine::AccessControl.public_permissions.collect {|p| p.name} + def allowed_permissions(scope = nil) + scope = scope.sort if scope.present? # to maintain stable cache keys + @allowed_permissions ||= {} + @allowed_permissions[scope] ||= begin + unscoped = permissions + Redmine::AccessControl.public_permissions.collect {|p| p.name} + scope.present? ? unscoped & scope : unscoped + end end - def allowed_actions - @actions_allowed ||= - allowed_permissions.inject([]) do |actions, permission| + def allowed_actions(scope = nil) + scope = scope.sort if scope.present? # to maintain stable cache keys + @actions_allowed ||= {} + @actions_allowed[scope] ||= + allowed_permissions(scope).inject([]) do |actions, permission| actions += Redmine::AccessControl.allowed_actions(permission) end.flatten end diff --git a/test/unit/role_test.rb b/test/unit/role_test.rb index c5ee93a3a..5c89908d5 100644 --- a/test/unit/role_test.rb +++ b/test/unit/role_test.rb @@ -101,6 +101,32 @@ class RoleTest < ActiveSupport::TestCase assert_equal false, role.has_permission?(:delete_issues) end + def test_allowed_to_with_symbol + role = Role.create!(:name => 'Test', :permissions => [:view_issues]) + assert_equal true, role.allowed_to?(:view_issues) + assert_equal false, role.allowed_to?(:add_issues) + end + + def test_allowed_to_with_symbol_and_scope + role = Role.create!(:name => 'Test', :permissions => [:view_issues, :delete_issues]) + assert_equal true, role.allowed_to?(:view_issues, [:view_issues, :add_issues]) + assert_equal false, role.allowed_to?(:add_issues, [:view_issues, :add_issues]) + assert_equal false, role.allowed_to?(:delete_issues, [:view_issues, :add_issues]) + end + + def test_allowed_to_with_hash + role = Role.create!(:name => 'Test', :permissions => [:view_issues]) + assert_equal true, role.allowed_to?(:controller => 'issues', :action => 'show') + assert_equal false, role.allowed_to?(:controller => 'issues', :action => 'create') + end + + def test_allowed_to_with_hash_and_scope + role = Role.create!(:name => 'Test', :permissions => [:view_issues, :delete_issues]) + assert_equal true, role.allowed_to?({:controller => 'issues', :action => 'show'}, [:view_issues, :add_issues]) + assert_equal false, role.allowed_to?({:controller => 'issues', :action => 'create'}, [:view_issues, :add_issues]) + assert_equal false, role.allowed_to?({:controller => 'issues', :action => 'destroy'}, [:view_issues, :add_issues]) + end + def test_has_permission_without_permissions role = Role.create!(:name => 'Test') assert_equal false, role.has_permission?(:delete_issues) -- 2.20.1