permissify 0.0.17 → 0.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/generators/permissify/ability/template/abilities.rb +7 -2
- data/lib/generators/permissify/controller/template/permissified_controller.rb +7 -7
- data/lib/permissify.rb +2 -1
- data/lib/permissify/ability_class.rb +20 -7
- data/lib/permissify/common.rb +33 -0
- data/lib/permissify/controller.rb +31 -14
- data/lib/permissify/roles.rb +39 -36
- data/lib/permissify/union.rb +33 -0
- data/spec/permissify/ability_class_spec.rb +28 -11
- data/spec/permissify/controller_spec.rb +64 -39
- data/spec/permissify/{aggregate_spec.rb → union_spec.rb} +1 -23
- data/spec/spec_helper.rb +14 -4
- metadata +7 -6
- data/lib/permissify/aggregate.rb +0 -102
@@ -7,14 +7,19 @@ module SystemFixtures::Abilities
|
|
7
7
|
|
8
8
|
# can organize permissions into categories that correspond to your client's/product team's view of app.
|
9
9
|
# suggest playing with your Ability class and the builder methods in console.
|
10
|
-
|
10
|
+
|
11
|
+
# NOTE : 'Role' and 'Product' references in following example are actually *class names*.
|
12
|
+
# This is a name coupling (see Permissify::Union) that can be overriden.
|
13
|
+
|
14
|
+
# applies_to_users_only = [User::PERMISSIFIED_ABILITY_APPLICABILITY]
|
15
|
+
# add_category('Tabs', 'Tabs', applies_to_users_only, %w(Admin Dealer Corporate Brand Merchant))
|
11
16
|
# { 'Roles' => 'Admin',
|
12
17
|
# 'Admin Users' => 'Admin',
|
13
18
|
# 'Dealer Users' => 'Dealer Admin',
|
14
19
|
# 'Corporate Users' => 'Corporate Admin',
|
15
20
|
# 'Brand Users' => 'Brand Admin',
|
16
21
|
# 'Merchant Users' => 'Merchant Admin',
|
17
|
-
# }.each{ |category, section| add_category(category, section) }
|
22
|
+
# }.each{ |category, section| add_category(category, section, applies_to_users_only) }
|
18
23
|
end
|
19
24
|
|
20
25
|
end
|
@@ -1,10 +1,10 @@
|
|
1
|
-
module PermissifiedController #
|
1
|
+
module PermissifiedController # Interfaces : override/rewrite as needed for your app
|
2
2
|
|
3
|
-
#
|
4
|
-
|
5
|
-
TODO_IMPLEMENT_PERMISSIFY_CONTROLLER_CURRENT_ENTITY_INTERFACE_METHOD
|
6
|
-
# return @current_entity if @current_entity
|
7
|
-
# nil
|
8
|
-
end
|
3
|
+
# controller-accessible methods that app has implemented to access models that have been permissified.
|
4
|
+
PERMISSIFY = SPECIFY_PERMISSIFIED_MODEL_LIST_IN__APP__CONTROLLERS__PERMISSIFIED_CONTROLLER
|
9
5
|
|
6
|
+
# PERMISSIFY = { User::PERMISSIFIED_ABILITY_APPLICABILITY => :current_user,
|
7
|
+
# 'Product' => :current_entity, # Merchant::PERMISSIFIED_ABILITY_APPLICABILITY
|
8
|
+
# }
|
9
|
+
|
10
10
|
end
|
data/lib/permissify.rb
CHANGED
@@ -18,22 +18,30 @@ module Permissify
|
|
18
18
|
|
19
19
|
def all_for(applicability_types)
|
20
20
|
applicability_types = [applicability_types] if applicability_types.kind_of?(String)
|
21
|
+
applicability_types = applicability_types.to_set
|
21
22
|
all.select{|a| (a[:applicability] & applicability_types) == applicability_types}
|
22
23
|
end
|
23
24
|
|
24
|
-
def
|
25
|
-
|
25
|
+
def get(action, category)
|
26
|
+
key = key_for(action, category)
|
27
|
+
all.select{ |ability| ability[:key] == key }.first
|
28
|
+
end
|
29
|
+
|
30
|
+
def add(category, section, action, applicability, requires_any_or_all, number_of_values, position, default_values, admin_expression='', category_allows = :multiple)
|
31
|
+
applicability = applicability.to_set
|
32
|
+
@@abilities << { :key => key_for(action, category), :category => category, :section => section, :action => action,
|
26
33
|
:applicability => applicability, :number_of_values => number_of_values, :position => position,
|
27
|
-
:default_values => default_values, :administration_expression => admin_expression,
|
34
|
+
:default_values => default_values, :administration_expression => admin_expression,
|
35
|
+
:category_allows => category_allows, :any_or_all => requires_any_or_all}
|
28
36
|
end
|
29
37
|
|
30
|
-
def add_category(category, section, applicability
|
38
|
+
def add_category(category, section, applicability, actions = %w(View Create Update Delete), requires_any_or_all = :all?, category_allows = :multiple)
|
31
39
|
actions = [actions] unless actions.kind_of?(Array)
|
32
40
|
actions.collect do |action|
|
33
|
-
add(
|
41
|
+
add(category, section, action, applicability, requires_any_or_all, 1, actions.index(action)+1, [false], '', category_allows)
|
34
42
|
end
|
35
43
|
end
|
36
|
-
|
44
|
+
|
37
45
|
def create_permissions_hash(view_only_categories=[], remove_categories=[], applicability_types = 'Role')
|
38
46
|
@@permissions = {}
|
39
47
|
all_for(applicability_types).each{|permission| @@permissions[permission[:key]] = {'0' => '1'}}
|
@@ -55,10 +63,15 @@ module Permissify
|
|
55
63
|
def current_permissions_hash
|
56
64
|
@@permissions
|
57
65
|
end
|
66
|
+
|
67
|
+
def key_for(action, category)
|
68
|
+
"#{key_token(category)}_#{key_token(action)}"
|
69
|
+
end
|
58
70
|
|
59
71
|
private
|
72
|
+
|
60
73
|
def key_token(token)
|
61
|
-
token.downcase.gsub('-','_').gsub(':','').gsub(' ',' ').gsub(' ','_')
|
74
|
+
token.to_s.downcase.gsub('-','_').gsub(':','').gsub(' ',' ').gsub(' ','_')
|
62
75
|
end
|
63
76
|
|
64
77
|
def view_only(category)
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Permissify
|
2
|
+
module Common
|
3
|
+
|
4
|
+
def allowed_to?(action, ability_category)
|
5
|
+
permission(action, ability_category) == true
|
6
|
+
end
|
7
|
+
|
8
|
+
def viewable?(ability_category); allowed_to?(:view, ability_category); end
|
9
|
+
def createable?(ability_category); allowed_to?(:create, ability_category); end
|
10
|
+
def updateable?(ability_category); allowed_to?(:update, ability_category); end
|
11
|
+
def deleteable?(ability_category); allowed_to?(:delete, ability_category); end
|
12
|
+
def subscribed_to?(ability_category); allowed_to?(:on, ability_category); end
|
13
|
+
|
14
|
+
def permissible?(permissions_hash, action, category)
|
15
|
+
key = Ability.key_for(action, category)
|
16
|
+
(permission = permissions_hash[key]) && permission['0'] == true
|
17
|
+
end
|
18
|
+
|
19
|
+
def log_permissions
|
20
|
+
message = "*** PermissifyController permissions: #{@permissions.inspect}"
|
21
|
+
defined?(logger) ? logger.debug(message) : puts(message)
|
22
|
+
end
|
23
|
+
|
24
|
+
# NOTE : mothballed additional permission values...
|
25
|
+
# def arbitrate(aggregation, other_descriptor, key, min_or_max) # assuming all permission 'args' are integers stored as strings
|
26
|
+
# 1.upto(other_descriptor.size-1) do |i|
|
27
|
+
# is = i.to_s
|
28
|
+
# aggregation[key][is] = [aggregation[key][is].to_i, other_descriptor[is].to_i].send(min_or_max) # .to_s ?
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -1,24 +1,41 @@
|
|
1
1
|
module Permissify
|
2
2
|
module Controller
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
include Permissify::Common
|
5
|
+
|
6
|
+
def permission(action, category) # interface used by Permissfy::Common.allowed_to?
|
7
|
+
@permissions ||= construct_permissions # TODO : get lazier?
|
8
|
+
permissible?(@permissions, action, category)
|
6
9
|
end
|
7
|
-
|
10
|
+
|
8
11
|
private
|
9
12
|
|
10
|
-
def
|
11
|
-
@
|
13
|
+
def construct_permissions
|
14
|
+
@applicable_permissions = {}
|
15
|
+
permissions = {}
|
16
|
+
Ability.all.each { |ability| permissions[ ability[:key] ] = authorized?(ability) }
|
17
|
+
# puts "*** PERMISSIONS: #{permissions.inspect} ***"
|
18
|
+
permissions
|
12
19
|
end
|
13
|
-
|
14
|
-
def
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
|
21
|
+
def authorized?(ability, key = ability[:key], applicablity = ability[:applicability], requires_any_or_all = ability[:any_or_all])
|
22
|
+
# puts "AUTHORIZED?: ability: #{key}, #{applicablity}, #{requires_any_or_all}"
|
23
|
+
authorizations = applicablity.collect{|applicable| applicable_authorization(applicable, key) }
|
24
|
+
{ '0' => authorizations.send(requires_any_or_all) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def applicable_authorization(applicable, key)
|
28
|
+
(permission = applicable_permissions(applicable)[key]) && permission['0'] == true
|
29
|
+
end
|
30
|
+
|
31
|
+
def applicable_permissions(applicablity)
|
32
|
+
@applicable_permissions[applicablity] ||= permissified_model_permissions(applicablity)
|
33
|
+
end
|
34
|
+
|
35
|
+
def permissified_model_permissions(applicablity)
|
36
|
+
permissified_model_method = self.class::PERMISSIFY[applicablity]
|
37
|
+
permissified_model = send(permissified_model_method)
|
38
|
+
permissified_model ? permissified_model.permissions : Hash.new({})
|
22
39
|
end
|
23
40
|
|
24
41
|
end
|
data/lib/permissify/roles.rb
CHANGED
@@ -1,42 +1,45 @@
|
|
1
|
-
module Permissify
|
2
|
-
|
1
|
+
module Permissify
|
2
|
+
module Roles
|
3
3
|
|
4
|
-
|
5
|
-
# 1. domain_type field
|
6
|
-
# 2. define DOMAIN_TYPES as a ranked list of strings in implementation of Role/Permissify::Model-including class
|
4
|
+
include Permissify::Union
|
7
5
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
return
|
6
|
+
# inclusion of this module necessitates that the app's roles implementation includes:
|
7
|
+
# 1. domain_type field
|
8
|
+
# 2. define DOMAIN_TYPES as a ranked list of strings in implementation of Role/Permissify::Model-including class
|
9
|
+
|
10
|
+
# NOTE: in example app, helper methods enforce only assigning domain_type-specific roles to users
|
11
|
+
# (an brand user can only be asigned roles that have a domain type of Brand).
|
12
|
+
def primary_domain_type
|
13
|
+
return nil if roles.empty?
|
14
|
+
domain_types = roles.collect(&:domain_type)
|
15
|
+
ranked_domain_types = roles.first.class::DOMAIN_TYPES
|
16
|
+
ranked_domain_types.each do |ranked_domain_type|
|
17
|
+
return ranked_domain_type if domain_types.include?(ranked_domain_type)
|
18
|
+
end
|
19
|
+
nil # unrecognized domain_type value(s)
|
16
20
|
end
|
17
|
-
nil # unrecognized domain_type value(s)
|
18
|
-
end
|
19
21
|
|
20
|
-
|
22
|
+
# uncomment/reimplement methods as needed for example app/as more light shines...
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
24
|
+
# def primary_domain_type; sorted_domain_types.first; end
|
25
|
+
# def primary_role_name; role_list(:description).sort.first; end
|
26
|
+
# def sorted_domain_types; role_list(:domain_type).sort; end
|
27
|
+
# def manages_roles; role_list(:manages_roles); end
|
28
|
+
# def manages_role_names; sorted_role_names(manages_roles); end
|
29
|
+
# def role_list(collection_method); roles.collect(&collection_method).flatten.uniq; end
|
30
|
+
#
|
31
|
+
# def can_create_and_modify(users, app_instance) # TODO : unit test for this
|
32
|
+
# role_names_that_this_user_can_manage = manages_role_names
|
33
|
+
# # 2 ways to go here : must be able to manage *all* or *any* of a user's roles.
|
34
|
+
# # Chose *all* to prevent lower ranking users from demoting higher ranking users
|
35
|
+
# users.select do |u|
|
36
|
+
# u_role_names = sorted_role_names(u.roles)
|
37
|
+
# ! u_role_names.blank? && (role_names_that_this_user_can_manage & u_role_names) == u_role_names
|
38
|
+
# end
|
39
|
+
# users.delete_if{|u| !u.instances.include?(app_instance)}
|
40
|
+
# return users
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# def sorted_role_names(roles); roles.collect(&:name).sort; end
|
44
|
+
end
|
42
45
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Permissify
|
2
|
+
module Union
|
3
|
+
|
4
|
+
include Permissify::Common
|
5
|
+
|
6
|
+
def permission(action, category) # interface used by Permissify::Common.allowed_to?
|
7
|
+
@union ||= construct_union # TODO : get lazier?
|
8
|
+
permissible?(@union, action, category)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def construct_union
|
14
|
+
union = {}
|
15
|
+
permissified_models = self.send(self.class::PERMISSIFIED_ASSOCIATION)
|
16
|
+
permissions_hashes = permissified_models.collect(&:permissions)
|
17
|
+
permissions_hashes.each do |permissions_hash|
|
18
|
+
permissions_hash.each do |key, descriptor|
|
19
|
+
union[key] ||= {'0' => false}
|
20
|
+
# NOTE : mothballed additional permission values...
|
21
|
+
# if union[key].nil?
|
22
|
+
# union[key] = descriptor # TODO : check : does this have '1' or true? is spec construction masking reality?
|
23
|
+
# else
|
24
|
+
# arbitrate(union, descriptor, key, :max)
|
25
|
+
# end
|
26
|
+
# union[key]['0'] = (union[key]['0'] == true || descriptor['0'] == '1')
|
27
|
+
union[key]['0'] = true if descriptor['0'] == '1'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
union
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -11,10 +11,20 @@ describe Permissify::AbilityClass do
|
|
11
11
|
describe 'and interface seed implemented' do
|
12
12
|
it 'should return the correct number of abilities' do
|
13
13
|
(a = Ability.all).size.should == 29
|
14
|
-
a.first.should ==
|
14
|
+
a.first.should == tab_admin_ability
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
18
|
+
|
19
|
+
describe 'get' do
|
20
|
+
it 'should return ability that has input action and category' do
|
21
|
+
Ability.get('admin', 'tabs').should == tab_admin_ability
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should be nil when no ability with input key exists' do
|
25
|
+
Ability.get('wrong', 'tabs').should be_nil
|
26
|
+
end
|
27
|
+
end
|
18
28
|
|
19
29
|
describe 'abilities builder method' do
|
20
30
|
before(:each) { Ability.reset }
|
@@ -28,24 +38,27 @@ describe Permissify::AbilityClass do
|
|
28
38
|
describe 'add_category' do
|
29
39
|
it 'should establish correct ability expression for fully specified category and action' do
|
30
40
|
add_category1_section1
|
31
|
-
Ability.current.should == [{:section=>"section1", :category=>"category1", :action=>"view", :position=>1, :key=>"category1_view", :applicability=>["Role", "Product"], :category_allows=>:one_or_none, :administration_expression=>"", :number_of_values=>1, :default_values=>[false]}]
|
41
|
+
Ability.current.should == [{:section=>"section1", :category=>"category1", :action=>"view", :position=>1, :key=>"category1_view", :applicability=>["Role", "Product"].to_set, :category_allows=>:one_or_none, :administration_expression=>"", :number_of_values=>1, :default_values=>[false], :any_or_all => :all?}]
|
32
42
|
end
|
33
43
|
|
34
44
|
it 'should establish correct ability expression for category and action with defaulted values' do
|
35
|
-
Ability.add_category('category2', 'section2')
|
36
|
-
Ability.current.should == [ {:section=>"section2", :category=>"category2", :action=>"View", :position=>1, :key=>"category2_view", :applicability=>["Role"], :category_allows=>:multiple, :administration_expression=>"", :number_of_values=>1, :default_values=>[false]},
|
37
|
-
{:section=>"section2", :category=>"category2", :action=>"Create", :position=>2, :key=>"category2_create", :applicability=>["Role"], :category_allows=>:multiple, :administration_expression=>"", :number_of_values=>1, :default_values=>[false]},
|
38
|
-
{:section=>"section2", :category=>"category2", :action=>"Update", :position=>3, :key=>"category2_update", :applicability=>["Role"], :category_allows=>:multiple, :administration_expression=>"", :number_of_values=>1, :default_values=>[false]},
|
39
|
-
{:section=>"section2", :category=>"category2", :action=>"Delete", :position=>4, :key=>"category2_delete", :applicability=>["Role"], :category_allows=>:multiple, :administration_expression=>"", :number_of_values=>1, :default_values=>[false]} ]
|
45
|
+
Ability.add_category('category2', 'section2', ['Role'])
|
46
|
+
Ability.current.should == [ {:section=>"section2", :category=>"category2", :action=>"View", :position=>1, :key=>"category2_view", :applicability=>["Role"].to_set, :category_allows=>:multiple, :administration_expression=>"", :number_of_values=>1, :default_values=>[false], :any_or_all => :all?},
|
47
|
+
{:section=>"section2", :category=>"category2", :action=>"Create", :position=>2, :key=>"category2_create", :applicability=>["Role"].to_set, :category_allows=>:multiple, :administration_expression=>"", :number_of_values=>1, :default_values=>[false], :any_or_all => :all?},
|
48
|
+
{:section=>"section2", :category=>"category2", :action=>"Update", :position=>3, :key=>"category2_update", :applicability=>["Role"].to_set, :category_allows=>:multiple, :administration_expression=>"", :number_of_values=>1, :default_values=>[false], :any_or_all => :all?},
|
49
|
+
{:section=>"section2", :category=>"category2", :action=>"Delete", :position=>4, :key=>"category2_delete", :applicability=>["Role"].to_set, :category_allows=>:multiple, :administration_expression=>"", :number_of_values=>1, :default_values=>[false], :any_or_all => :all?} ]
|
40
50
|
end
|
41
51
|
end
|
42
52
|
end
|
43
53
|
|
44
|
-
# TODO : specs for just Product, and Role and Product, applicabilities
|
45
54
|
describe 'permission builder method' do
|
46
55
|
|
47
|
-
describe 'create_permissions_hash
|
48
|
-
before(:each)
|
56
|
+
describe 'create_permissions_hash' do
|
57
|
+
before(:each) do
|
58
|
+
Ability.reset;
|
59
|
+
Ability.seed;
|
60
|
+
@ability_key_set = Ability.current.collect{|a| a[:key]}.to_set
|
61
|
+
end
|
49
62
|
|
50
63
|
it 'should express each current ability when invoked with no arguments' do
|
51
64
|
@ability_key_set.should == Ability.create_permissions_hash.keys.to_set
|
@@ -78,6 +91,10 @@ describe Permissify::AbilityClass do
|
|
78
91
|
end
|
79
92
|
|
80
93
|
def add_category1_section1(actions = 'view')
|
81
|
-
Ability.add_category('category1', 'section1', %w(Role Product), actions, :one_or_none)
|
94
|
+
Ability.add_category('category1', 'section1', %w(Role Product), actions, :all?, :one_or_none)
|
95
|
+
end
|
96
|
+
|
97
|
+
def tab_admin_ability
|
98
|
+
{:default_values=>[false], :applicability=>["Role"].to_set, :category=>"Tabs", :administration_expression=>"", :section=>"Tabs", :category_allows=>:multiple, :number_of_values=>1, :key=>"tabs_admin", :position=>1, :action=>"Admin", :any_or_all => :all?}
|
82
99
|
end
|
83
100
|
end
|
@@ -1,74 +1,99 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Permissify::Controller do
|
4
|
-
|
5
|
-
|
4
|
+
|
6
5
|
describe 'allowed_to?' do
|
7
|
-
|
8
|
-
|
6
|
+
before(:each) do
|
7
|
+
@controller = PermissifiedControllerish.new # controller specifies multiple permissified_model_method/ability applicability pairs
|
8
|
+
end
|
9
9
|
|
10
|
-
|
10
|
+
describe 'for an ability with a single applicability type, ' do
|
11
|
+
before(:each) do
|
12
|
+
@applicability = [Userish::PERMISSIFIED_ABILITY_APPLICABILITY]
|
13
|
+
add_ability :all?
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should be false when controller-specified model method for applicability type returns nil' do
|
11
17
|
@controller.should_receive(:current_user).and_return(nil)
|
12
18
|
allowed_to_should be_false
|
13
19
|
end
|
14
20
|
|
15
|
-
describe 'and
|
16
|
-
before(:each)
|
17
|
-
|
18
|
-
|
19
|
-
end
|
20
|
-
|
21
|
-
it 'should return true when current user has permission for category action' do
|
21
|
+
describe 'and the model method associated with applicability type returns a (permissified) model, ' do
|
22
|
+
before(:each) { current_user_exists }
|
23
|
+
|
24
|
+
it 'should be true when model authorizes permission for ability' do
|
22
25
|
category_action_permission_causes_allowed_to_be true, be_true
|
23
26
|
end
|
24
27
|
|
25
|
-
it 'should
|
28
|
+
it 'should be false when model does not authorize permission for ability' do
|
26
29
|
category_action_permission_causes_allowed_to_be false, be_false
|
27
30
|
end
|
28
31
|
end
|
29
32
|
end
|
30
33
|
|
31
|
-
describe '
|
32
|
-
|
33
|
-
|
34
|
-
|
34
|
+
describe 'for an ability with multiple applicability types, ' do
|
35
|
+
before(:each) do
|
36
|
+
current_user_exists
|
37
|
+
@applicability = [Entityish::PERMISSIFIED_ABILITY_APPLICABILITY, Userish::PERMISSIFIED_ABILITY_APPLICABILITY]
|
38
|
+
end
|
39
|
+
|
40
|
+
describe 'and ability requires all, ' do
|
41
|
+
before(:each) { add_ability(:all?) }
|
42
|
+
|
43
|
+
it 'should be false when any applicable model permission method returns nil' do
|
44
|
+
@controller.should_receive(:current_entity).and_return(nil)
|
45
|
+
category_action_permission_causes_allowed_to_be false, be_false
|
35
46
|
end
|
47
|
+
|
48
|
+
describe 'and all applicable model permission methods return a (permissified) model' do
|
49
|
+
before(:each) do
|
50
|
+
@entity = Entityish.new
|
51
|
+
@controller.should_receive(:current_entity).and_return(@entity)
|
52
|
+
@entity.should_receive(:permissions).and_return( {"something_manage" => {'0' => true} })
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should be false when any applicable permissified model does not have permission for the ability' do
|
56
|
+
category_action_permission_causes_allowed_to_be false, be_false
|
57
|
+
end
|
36
58
|
|
37
|
-
|
38
|
-
|
59
|
+
it 'should be true when all applicable permissified models have permission for the ability' do
|
60
|
+
category_action_permission_causes_allowed_to_be true, be_true
|
61
|
+
end
|
39
62
|
end
|
40
63
|
end
|
41
64
|
|
42
|
-
describe 'and
|
43
|
-
|
44
|
-
it 'should return true when current user has permission for category action' do
|
45
|
-
pending('products phase')
|
46
|
-
end
|
65
|
+
describe 'and ability requires any' do
|
66
|
+
before(:each) { add_ability(:any?) ; @controller.should_receive(:current_entity).and_return(nil) }
|
47
67
|
|
48
|
-
|
49
|
-
|
50
|
-
end
|
68
|
+
it 'should be true when any applicable permissified model has permission for the ability' do
|
69
|
+
category_action_permission_causes_allowed_to_be true, be_true, :log_permissions
|
51
70
|
end
|
52
|
-
|
53
|
-
describe 'and current entity does not have permission for category action' do
|
54
|
-
it 'should return false when current user has permission for category action' do
|
55
|
-
pending('products phase')
|
56
|
-
end
|
57
71
|
|
58
|
-
|
59
|
-
|
60
|
-
end
|
72
|
+
it 'should be false when no applicable permissified model has permission for the ability' do
|
73
|
+
category_action_permission_causes_allowed_to_be false, be_false, :log_permissions
|
61
74
|
end
|
62
75
|
end
|
63
76
|
end
|
64
77
|
|
65
|
-
def
|
66
|
-
|
78
|
+
def add_ability(requires_any_or_all)
|
79
|
+
Ability.reset
|
80
|
+
Ability.add_category('something', 'a section', @applicability, 'manage', requires_any_or_all)
|
81
|
+
end
|
82
|
+
|
83
|
+
def category_action_permission_causes_allowed_to_be(permission_true_or_false, be_true_or_false, log_option = :none, action = :manage, category = :something)
|
84
|
+
@user.should_receive(:permissions).and_return( {"#{category}_#{action}" => {'0' => permission_true_or_false} })
|
67
85
|
allowed_to_should be_true_or_false, action, category
|
86
|
+
@controller.log_permissions if log_option == :log_permissions
|
68
87
|
end
|
69
88
|
|
70
|
-
def allowed_to_should(be_true_or_false, action = :
|
71
|
-
@controller.allowed_to?(:
|
89
|
+
def allowed_to_should(be_true_or_false, action = :manage, category = :something)
|
90
|
+
@controller.allowed_to?(:manage, :something).should be_true_or_false
|
72
91
|
end
|
92
|
+
|
93
|
+
def current_user_exists
|
94
|
+
@user = Userish.new
|
95
|
+
@controller.should_receive(:current_user).and_return(@user)
|
96
|
+
end
|
97
|
+
|
73
98
|
end
|
74
99
|
end
|
@@ -1,23 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe Permissify::
|
3
|
+
describe Permissify::Union do
|
4
4
|
|
5
|
-
describe 'permissions_union' do
|
6
|
-
before(:each) { new_aggregate; @union = @a.permissions_union}
|
7
|
-
|
8
|
-
it 'should include keys for permissions specified (true or false) in any permissified model' do
|
9
|
-
@union.keys.to_set.should == @pms.collect(&:permissions).collect(&:keys).flatten.uniq.to_set
|
10
|
-
end
|
11
|
-
|
12
|
-
it 'should indicate permission for a key is true if permission set to true in any permissified object permission set' do
|
13
|
-
%w(p_1 p_2 p_3 p_4 p_5).each{ |permission| @union[permission]['0'].should be_true }
|
14
|
-
end
|
15
|
-
|
16
|
-
it 'should indicate permission for a key is false if permission not set to true in any permissified object permission set' do
|
17
|
-
%w(p_6 p_7 p_8).each{ |permission| @union[permission]['0'].should be_false }
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
5
|
describe 'allowed_to?' do
|
22
6
|
before(:each) { new_aggregate }
|
23
7
|
|
@@ -56,12 +40,6 @@ describe Permissify::Aggregate do
|
|
56
40
|
end
|
57
41
|
end
|
58
42
|
|
59
|
-
# TODO : work out intersection specs in corp/brand/business products phase
|
60
|
-
# describe 'permissions_intersection' do
|
61
|
-
# it 'should ...' do
|
62
|
-
# end
|
63
|
-
# end
|
64
|
-
|
65
43
|
def new_aggregate(permissified_models = default_permissified_models)
|
66
44
|
@a = Userish.new
|
67
45
|
@pms = permissified_models
|
data/spec/spec_helper.rb
CHANGED
@@ -2,7 +2,6 @@ require 'rubygems'
|
|
2
2
|
require 'bundler/setup'
|
3
3
|
|
4
4
|
require 'permissify'
|
5
|
-
require 'permissify/ability_class'
|
6
5
|
|
7
6
|
RSpec.configure do |config|
|
8
7
|
config.treat_symbols_as_metadata_keys_with_true_values = true
|
@@ -22,7 +21,7 @@ class Ability
|
|
22
21
|
'Corporate Users' => 'Corporate Admin',
|
23
22
|
'Brand Users' => 'Brand Admin',
|
24
23
|
'Merchant Users' => 'Merchant Admin',
|
25
|
-
}.each{ |category, section| add_category(category, section) }
|
24
|
+
}.each{ |category, section| add_category(category, section, ['Role']) }
|
26
25
|
end
|
27
26
|
end
|
28
27
|
end
|
@@ -47,7 +46,8 @@ class Roleish < PermissifiedModel
|
|
47
46
|
end
|
48
47
|
|
49
48
|
class Userish
|
50
|
-
include Permissify::
|
49
|
+
include Permissify::Union
|
50
|
+
PERMISSIFIED_ABILITY_APPLICABILITY = 'Role'
|
51
51
|
PERMISSIFIED_ASSOCIATION = :roles
|
52
52
|
attr_accessor :roles
|
53
53
|
end
|
@@ -58,6 +58,16 @@ class UserishWithRoles
|
|
58
58
|
attr_accessor :roles
|
59
59
|
end
|
60
60
|
|
61
|
-
class
|
61
|
+
class Entityish
|
62
|
+
include Permissify::Union
|
63
|
+
PERMISSIFIED_ABILITY_APPLICABILITY = 'Product'
|
64
|
+
PERMISSIFIED_ASSOCIATION = :products
|
65
|
+
attr_accessor :products
|
66
|
+
end
|
67
|
+
|
68
|
+
class PermissifiedControllerish
|
62
69
|
include Permissify::Controller
|
70
|
+
PERMISSIFY = { Userish::PERMISSIFIED_ABILITY_APPLICABILITY => :current_user,
|
71
|
+
Entityish::PERMISSIFIED_ABILITY_APPLICABILITY => :current_entity,
|
72
|
+
} # in lieu of 'include PermissifiedController'
|
63
73
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: permissify
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 59
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 18
|
10
|
+
version: 0.0.18
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Frederick Fix
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-06-
|
18
|
+
date: 2012-06-13 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: rspec
|
@@ -102,18 +102,19 @@ files:
|
|
102
102
|
- lib/generators/permissify/views/USAGE
|
103
103
|
- lib/generators/permissify/views/views_generator.rb
|
104
104
|
- lib/permissify/ability_class.rb
|
105
|
-
- lib/permissify/
|
105
|
+
- lib/permissify/common.rb
|
106
106
|
- lib/permissify/controller.rb
|
107
107
|
- lib/permissify/model.rb
|
108
108
|
- lib/permissify/model_class.rb
|
109
109
|
- lib/permissify/roles.rb
|
110
|
+
- lib/permissify/union.rb
|
110
111
|
- lib/permissify.rb
|
111
112
|
- spec/permissify/ability_class_spec.rb
|
112
|
-
- spec/permissify/aggregate_spec.rb
|
113
113
|
- spec/permissify/controller_spec.rb
|
114
114
|
- spec/permissify/model_class_spec.rb
|
115
115
|
- spec/permissify/model_spec.rb
|
116
116
|
- spec/permissify/roles_spec.rb
|
117
|
+
- spec/permissify/union_spec.rb
|
117
118
|
- spec/spec.opts
|
118
119
|
- spec/spec_helper.rb
|
119
120
|
- CHANGELOG.rdoc
|
data/lib/permissify/aggregate.rb
DELETED
@@ -1,102 +0,0 @@
|
|
1
|
-
module Permissify
|
2
|
-
module Aggregate
|
3
|
-
attr_accessor :abilities
|
4
|
-
|
5
|
-
def permissions_union
|
6
|
-
@union ||= construct_union
|
7
|
-
@working_permissions = @union
|
8
|
-
end
|
9
|
-
|
10
|
-
def permissions_intersection(product_permissions)
|
11
|
-
@intersection ||= construct_intersection
|
12
|
-
@working_permissions = @intersection
|
13
|
-
end
|
14
|
-
|
15
|
-
def allowed_to?(action, feature)
|
16
|
-
@working_permissions ||= permissions_union
|
17
|
-
(@working_permissions["#{feature}_#{action}"]['0'] == true rescue false)
|
18
|
-
end
|
19
|
-
|
20
|
-
def viewable?(feature); allowed_to?(:view, feature); end
|
21
|
-
def createable?(feature); allowed_to?(:create, feature); end
|
22
|
-
def updateable?(feature); allowed_to?(:update, feature); end
|
23
|
-
def deleteable?(feature); allowed_to?(:delete, feature); end
|
24
|
-
def subscribed_to?(feature); allowed_to?(:on, feature); end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
def construct_union
|
29
|
-
union = {}
|
30
|
-
permissified_models = self.send(self.class::PERMISSIFIED_ASSOCIATION)
|
31
|
-
permissions_hashes = permissified_models.collect(&:permissions)
|
32
|
-
permissions_hashes.each do |permissions_hash|
|
33
|
-
permissions_hash.each do |key, descriptor|
|
34
|
-
descriptor ||= {'0' => false}
|
35
|
-
if union[key].nil?
|
36
|
-
union[key] = descriptor
|
37
|
-
else
|
38
|
-
arbitrate(union, descriptor, key, :max)
|
39
|
-
end
|
40
|
-
union[key]['0'] = (union[key]['0'] == true || descriptor['0'] == '1')
|
41
|
-
end
|
42
|
-
end
|
43
|
-
union
|
44
|
-
end
|
45
|
-
|
46
|
-
def arbitrate(aggregation, other_descriptor, key, min_or_max) # assuming all permission 'args' are integers stored as strings
|
47
|
-
1.upto(other_descriptor.size-1) do |i|
|
48
|
-
is = i.to_s
|
49
|
-
aggregation[key][is] = [aggregation[key][is].to_i, other_descriptor[is].to_i].send(min_or_max) # .to_s ?
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
# TODO : flush out intersection stuff in corp/brand/business products phase
|
54
|
-
# def construct_intersection
|
55
|
-
# intersection = {}
|
56
|
-
# permissions_union
|
57
|
-
#
|
58
|
-
# # TODO : Product/Role references should be plugged-in...
|
59
|
-
# # - could aggregate keep track of class names that it is included in?
|
60
|
-
# # - there is a lot tangled up in here...
|
61
|
-
# Ability.all_for(%w(Product Role)).each do |ability|
|
62
|
-
# key = ability[:key]
|
63
|
-
# descriptor = @union[key]
|
64
|
-
# product_descriptor = product_permissions[key]
|
65
|
-
# if permissable?(descriptor) && permissable?(product_descriptor)
|
66
|
-
# intersection[key] = descriptor
|
67
|
-
# arbitrate(intersection, product_descriptor, key, :min)
|
68
|
-
# else
|
69
|
-
# intersection[key] = null_permission
|
70
|
-
# end
|
71
|
-
# end
|
72
|
-
#
|
73
|
-
# include_permissions_unless_common_to_role_and_product('Product', product_permissions)
|
74
|
-
# include_permissions_unless_common_to_role_and_product('Role', @union)
|
75
|
-
# intersection
|
76
|
-
# end
|
77
|
-
#
|
78
|
-
# def permissable?(descriptor)
|
79
|
-
# descriptor && (descriptor['0'] == true || descriptor['0'] == '1')
|
80
|
-
# end
|
81
|
-
#
|
82
|
-
# def include_permissions_unless_common_to_role_and_product(applicability_type, applicable_permissions)
|
83
|
-
# Ability.all_for(applicability_type).each do |ability|
|
84
|
-
# key = ability[:key]
|
85
|
-
# descriptor = applicable_permissions[key]
|
86
|
-
# unless @intersection.has_key?(key)
|
87
|
-
# if permissable?(descriptor)
|
88
|
-
# descriptor['0'] = true
|
89
|
-
# @intersection[key] = descriptor
|
90
|
-
# arbitrate(@intersection, descriptor, key, :min)
|
91
|
-
# else
|
92
|
-
# @intersection[key] = null_permission
|
93
|
-
# end
|
94
|
-
# end
|
95
|
-
# end
|
96
|
-
# end
|
97
|
-
#
|
98
|
-
# def null_permission
|
99
|
-
# {'0' => false, '1' => 0}
|
100
|
-
# end
|
101
|
-
end
|
102
|
-
end
|