cantango 0.8.9.5 → 0.9.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +35 -7
- data/VERSION +1 -1
- data/cantango.gemspec +39 -12
- data/lib/cantango.rb +4 -3
- data/lib/cantango/ability.rb +17 -21
- data/lib/cantango/ability/cache/key.rb +32 -4
- data/lib/cantango/ability/cache/rules_cache.rb +9 -2
- data/lib/cantango/ability/cache_helpers.rb +1 -7
- data/lib/cantango/ability/engine_helpers.rb +27 -0
- data/lib/cantango/ability_executor.rb +41 -0
- data/lib/cantango/api/user/ability.rb +8 -12
- data/lib/cantango/api/user/session.rb +2 -1
- data/lib/cantango/api/user_account.rb +2 -2
- data/lib/cantango/api/user_account/ability.rb +19 -14
- data/lib/cantango/api/user_account/can.rb +8 -0
- data/lib/cantango/api/user_account/session.rb +33 -0
- data/lib/cantango/cached_ability.rb +26 -0
- data/lib/cantango/configuration.rb +3 -3
- data/lib/cantango/configuration/ability.rb +1 -0
- data/lib/cantango/configuration/candidate_registry.rb +51 -0
- data/lib/cantango/configuration/categories.rb +2 -2
- data/lib/cantango/configuration/engines.rb +7 -3
- data/lib/cantango/configuration/engines/permission.rb +5 -0
- data/lib/cantango/configuration/engines/permit.rb +1 -0
- data/lib/cantango/configuration/engines/user_ac.rb +19 -0
- data/lib/cantango/configuration/guest.rb +1 -1
- data/lib/cantango/configuration/modes.rb +21 -0
- data/lib/cantango/configuration/permits.rb +1 -1
- data/lib/cantango/configuration/role_groups.rb +1 -2
- data/lib/cantango/configuration/user_accounts.rb +1 -1
- data/lib/cantango/configuration/users.rb +1 -1
- data/lib/cantango/engine.rb +40 -0
- data/lib/cantango/helpers.rb +1 -1
- data/lib/cantango/helpers/debug.rb +9 -0
- data/lib/cantango/model.rb +6 -0
- data/lib/cantango/model/filter.rb +102 -0
- data/lib/cantango/model/scope.rb +57 -0
- data/lib/cantango/permission_engine.rb +14 -3
- data/lib/cantango/permission_engine/loader/base.rb +1 -6
- data/lib/cantango/permission_engine/loader/permissions.rb +10 -16
- data/lib/cantango/permission_engine/store.rb +1 -7
- data/lib/cantango/permission_engine/yaml_store.rb +3 -10
- data/lib/cantango/permit_engine.rb +17 -4
- data/lib/cantango/permit_engine/builder/base.rb +3 -1
- data/lib/cantango/permit_engine/executor/abstract.rb +2 -0
- data/lib/cantango/permit_engine/executor/base.rb +1 -1
- data/lib/cantango/permit_engine/factory.rb +5 -3
- data/lib/cantango/permit_engine/finder.rb +4 -6
- data/lib/cantango/permit_engine/util.rb +1 -1
- data/lib/cantango/permits/permit.rb +25 -0
- data/lib/cantango/permits/role_group_permit/builder.rb +23 -7
- data/lib/cantango/permits/role_permit.rb +1 -2
- data/lib/cantango/rails/helpers/rest_helper.rb +3 -2
- data/lib/cantango/user_ac_engine.rb +40 -0
- data/lib/cantango/user_ac_engine/executor.rb +59 -0
- data/lib/cantango/users/macros.rb +3 -0
- data/lib/cantango/users/user.rb +1 -1
- data/lib/cantango/users/user_account.rb +1 -1
- data/lib/generators/cantango/permission/permission_generator.rb +43 -0
- data/spec/active_record/migrations/008_create_permissions.rb +10 -0
- data/spec/cantango/ability_executor/cached_only_spec.rb +76 -0
- data/spec/cantango/ability_executor_spec.rb +75 -0
- data/spec/cantango/api/attributes_spec.rb +2 -1
- data/spec/cantango/api/current_user_accounts.rb +5 -1
- data/spec/cantango/api/user/ability_api_spec.rb +17 -4
- data/spec/cantango/api/user/can_api_spec.rb +9 -5
- data/spec/cantango/api/user/scope_api_spec.rb +15 -7
- data/spec/cantango/api/user_account/ability_api_spec.rb +12 -5
- data/spec/cantango/api/user_account/can_api_spec.rb +8 -4
- data/spec/cantango/cached_ability_spec.rb +0 -0
- data/spec/cantango/model/filter_spec.rb +168 -0
- data/spec/cantango/model/scope_spec.rb +107 -0
- data/spec/cantango/permission_engine/loader/permissions/{cantango_permissions_loader.rb → cantango_permissions_loader_spec.rb} +0 -0
- data/spec/cantango/permission_engine/loader/permissions/shared.rb +2 -2
- data/spec/cantango/permission_engine/yaml_store_spec.rb +0 -1
- data/spec/cantango/permit_engine/role_group_permit_spec.rb +2 -2
- data/spec/cantango/permits/permit_spec.rb +2 -2
- data/spec/cantango/rules_spec.rb +6 -6
- data/spec/cantango/user_ac_engine_spec.rb +53 -0
- data/spec/fixtures/config/cantango_permissions.yml +49 -0
- data/spec/fixtures/models/permission.rb +12 -0
- data/spec/fixtures/models/user.rb +8 -0
- data/spec/generators/cantango/permission_generator_spec.rb +44 -0
- metadata +59 -35
data/lib/cantango/engine.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module CanTango
|
2
2
|
class Engine
|
3
|
+
include CanTango::Helpers::Debug
|
4
|
+
|
3
5
|
attr_reader :ability
|
4
6
|
|
5
7
|
def initialize ability
|
@@ -9,5 +11,43 @@ module CanTango
|
|
9
11
|
def execute!
|
10
12
|
# raise NotImplementedError
|
11
13
|
end
|
14
|
+
|
15
|
+
def engine_name
|
16
|
+
raise NotImplementedError
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def valid_mode?
|
22
|
+
valid_cache_mode? || valid_no_cache_mode?
|
23
|
+
end
|
24
|
+
|
25
|
+
def valid_cache_mode?
|
26
|
+
modes.include?(:cache) && cache_mode?
|
27
|
+
end
|
28
|
+
|
29
|
+
def valid_no_cache_mode?
|
30
|
+
modes.include?(:no_cache) && !cache_mode?
|
31
|
+
end
|
32
|
+
|
33
|
+
def modes
|
34
|
+
CanTango.config.engine(engine_name.to_sym).modes
|
35
|
+
end
|
36
|
+
|
37
|
+
def cache_mode?
|
38
|
+
ability.cached?
|
39
|
+
end
|
40
|
+
|
41
|
+
def user
|
42
|
+
ability.user
|
43
|
+
end
|
44
|
+
|
45
|
+
def subject
|
46
|
+
ability.subject
|
47
|
+
end
|
48
|
+
|
49
|
+
def candidate
|
50
|
+
ability.candidate
|
51
|
+
end
|
12
52
|
end
|
13
53
|
end
|
data/lib/cantango/helpers.rb
CHANGED
@@ -0,0 +1,102 @@
|
|
1
|
+
module CanTango::Model
|
2
|
+
module Filter
|
3
|
+
def self.included(base)
|
4
|
+
base.send :include, CanTango::Api::User::Ability
|
5
|
+
base.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.clean_meth_name meth_name
|
9
|
+
postfix = (meth_name =~ /\!$/) ? '!' : ''
|
10
|
+
postfix = (meth_name =~ /\?$/) ? '?' : postfix
|
11
|
+
postfix = "_by#{postfix}"
|
12
|
+
meth_name.to_s.sub(/[\!|\?]$/, '') + postfix
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.normalize_args args
|
16
|
+
args = args.kind_of?(Symbol) ? [args] : args
|
17
|
+
args.map(&:to_s).map{|a| a == 'ARGS' ? '*args' : a}
|
18
|
+
end
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
def tango_filter *method_names
|
22
|
+
method_names.flatten.each do |name|
|
23
|
+
case name
|
24
|
+
when String, Symbol
|
25
|
+
case name.to_sym
|
26
|
+
when :REST, :MANAGE
|
27
|
+
tango_filter :create => :OPTS, :create! => :OPTS, :update_attributes => :OPTS, :update_attributes! => :OPTS
|
28
|
+
tango_filter :destroy, :destroy!
|
29
|
+
when :CREATE, :NEW
|
30
|
+
tango_filter :create => :OPTS, :create! => :OPTS
|
31
|
+
when :UPDATE, :EDIT
|
32
|
+
tango_filter :update_attributes => :OPTS, :update_attributes! => :OPTS
|
33
|
+
when :DELETE, :DESTROY
|
34
|
+
tango_filter :destroy, :destroy!
|
35
|
+
else
|
36
|
+
meth_name = name.to_s
|
37
|
+
method_name = CanTango::Model::Filter.clean_meth_name meth_name
|
38
|
+
define_method :"#{method_name}" do |user|
|
39
|
+
send(name) if user_ability(user).can? meth_name.to_sym, self
|
40
|
+
end
|
41
|
+
end
|
42
|
+
when Hash
|
43
|
+
base = self
|
44
|
+
name.each_pair do |meth_name, args|
|
45
|
+
norm_args = CanTango::Model::Filter.normalize_args args
|
46
|
+
args = norm_args.map{|a| a == 'OPTS' ? 'options = {}' : a}.join(',')
|
47
|
+
args_call = norm_args.map{|a| a == 'OPTS' ? 'options' : a}.join(',')
|
48
|
+
|
49
|
+
method_name = CanTango::Model::Filter.clean_meth_name meth_name
|
50
|
+
|
51
|
+
base.class_eval %{
|
52
|
+
def #{method_name} the_user, #{args}
|
53
|
+
send(:#{meth_name}, #{args_call}) if user_ability(the_user).can? :#{meth_name}, self
|
54
|
+
end
|
55
|
+
}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end # def
|
60
|
+
|
61
|
+
def tango_account_filter *method_names
|
62
|
+
method_names.flatten.each do |name|
|
63
|
+
case name
|
64
|
+
when String, Symbol
|
65
|
+
case name.to_sym
|
66
|
+
when :REST, :MANAGE
|
67
|
+
tango_account_filter :create => :OPTS, :create! => :OPTS, :update_attributes => :OPTS, :update_attributes! => :OPTS
|
68
|
+
tango_account_filter :destroy, :destroy!
|
69
|
+
when :CREATE, :NEW
|
70
|
+
tango_account_filter :create => :OPTS, :create! => :OPTS
|
71
|
+
when :UPDATE, :EDIT
|
72
|
+
tango_account_filter :update_attributes => :OPTS, :update_attributes! => :OPTS
|
73
|
+
when :DELETE, :DESTROY
|
74
|
+
tango_account_filter :destroy, :destroy!
|
75
|
+
else
|
76
|
+
meth_name = name.to_s
|
77
|
+
method_name = CanTango::Model::Filter.clean_meth_name meth_name
|
78
|
+
define_method :"#{method_name}" do |user|
|
79
|
+
send(name) if account_ability(user).can? meth_name.to_sym, self
|
80
|
+
end
|
81
|
+
end
|
82
|
+
when Hash
|
83
|
+
base = self
|
84
|
+
name.each_pair do |meth_name, args|
|
85
|
+
norm_args = CanTango::Model::Filter.normalize_args args
|
86
|
+
args = norm_args.map{|a| a == 'OPTS' ? 'options = {}' : a}.join(',')
|
87
|
+
args_call = norm_args.map{|a| a == 'OPTS' ? 'options' : a}.join(',')
|
88
|
+
|
89
|
+
method_name = CanTango::Model::Filter.clean_meth_name meth_name
|
90
|
+
|
91
|
+
base.class_eval %{
|
92
|
+
def #{method_name} the_user, #{args}
|
93
|
+
send(:#{meth_name}, #{args_call}) if account_ability(the_user).can? :#{meth_name}, self
|
94
|
+
end
|
95
|
+
}
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module CanTango::Model
|
2
|
+
module Scope
|
3
|
+
def self.included(base)
|
4
|
+
base.send :include, CanTango::Api::User::Ability
|
5
|
+
base.extend CanTango::Api::User::Ability
|
6
|
+
base.extend ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.rest_actions
|
10
|
+
[:read, :access, :write, :manage, :edit, :create, :delete]
|
11
|
+
end
|
12
|
+
|
13
|
+
class AllowedActions
|
14
|
+
include CanTango::Api::User::Ability
|
15
|
+
|
16
|
+
|
17
|
+
attr_reader :actions, :clazz
|
18
|
+
|
19
|
+
def initialize clazz, *actions
|
20
|
+
@clazz = clazz
|
21
|
+
@actions = actions.flatten
|
22
|
+
end
|
23
|
+
|
24
|
+
def by_user user
|
25
|
+
check user_ability(user)
|
26
|
+
end
|
27
|
+
alias_method :by, :by_user
|
28
|
+
|
29
|
+
def by_account account
|
30
|
+
check account_ability(account)
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
def check ability
|
36
|
+
clazz.all.select do |obj|
|
37
|
+
actions.all? do |action|
|
38
|
+
ability.can? action.to_sym, obj
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
module ClassMethods
|
45
|
+
def allowed_to *actions
|
46
|
+
CanTango::Model::Scope::AllowedActions.new self, *actions
|
47
|
+
end
|
48
|
+
|
49
|
+
CanTango::Model::Scope.rest_actions.each do |action|
|
50
|
+
meth_name = action.to_s.sub(/e$/, '') << "able"
|
51
|
+
define_method :"#{meth_name}_by" do |user|
|
52
|
+
all.select {|obj| obj.user_ability(user).can? action.to_sym, obj }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -9,20 +9,31 @@ module CanTango
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def execute!
|
12
|
-
|
12
|
+
return if !valid?
|
13
|
+
debug "Permission Engine executing..."
|
13
14
|
permissions.each do |permission|
|
14
15
|
permission.evaluate! user
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
19
|
+
def engine_name
|
20
|
+
:permission
|
21
|
+
end
|
22
|
+
|
23
|
+
def valid?
|
24
|
+
return false if !valid_mode?
|
25
|
+
permissions.empty? ? invalid : true
|
26
|
+
end
|
27
|
+
|
18
28
|
def permissions
|
19
29
|
permission_factory.build!
|
20
30
|
end
|
21
31
|
|
22
32
|
protected
|
23
33
|
|
24
|
-
def
|
25
|
-
|
34
|
+
def invalid
|
35
|
+
debug "No permissions found!"
|
36
|
+
false
|
26
37
|
end
|
27
38
|
|
28
39
|
def permission_factory
|
@@ -4,7 +4,7 @@ module CanTango
|
|
4
4
|
class Base
|
5
5
|
attr_accessor :file_name
|
6
6
|
|
7
|
-
def self.inherited
|
7
|
+
def self.inherited subclass
|
8
8
|
subclass.extend ClassMethods
|
9
9
|
end
|
10
10
|
|
@@ -12,11 +12,6 @@ module CanTango
|
|
12
12
|
raise NotImplementedError
|
13
13
|
end
|
14
14
|
|
15
|
-
def file_name= file
|
16
|
-
raise "Couldn't find permissions file: #{file}. Either disable Permission engine or add this file." if file.nil? || !File.file?(file)
|
17
|
-
@file_name = file
|
18
|
-
end
|
19
|
-
|
20
15
|
def yml_content
|
21
16
|
YAML.load_file(file_name)
|
22
17
|
rescue
|
@@ -5,36 +5,28 @@ module CanTango
|
|
5
5
|
attr_accessor :permissions
|
6
6
|
|
7
7
|
def initialize file_name
|
8
|
-
|
8
|
+
@file_name = file_name
|
9
|
+
|
9
10
|
load!
|
10
11
|
end
|
11
12
|
|
12
13
|
def load_from_hash hash
|
13
14
|
return if hash.empty?
|
14
15
|
hash.each do |type, groups|
|
16
|
+
permissions[type] ||= {}
|
17
|
+
|
18
|
+
next if groups.nil?
|
19
|
+
|
15
20
|
groups.each do |group, rules|
|
16
|
-
permissions[type] ||= {}
|
17
21
|
parser.parse(group, rules) do |permission|
|
18
22
|
permissions[type][permission.name] = permission
|
19
23
|
end
|
20
24
|
end
|
21
25
|
end
|
22
|
-
rescue => e
|
23
|
-
raise "PermissionsLoader Error: The permissions for the file #{file_name} could not be loaded - cause was #{e}"
|
24
26
|
end
|
25
27
|
|
26
28
|
def load!
|
27
|
-
|
28
|
-
yml_content.each do |type, groups|
|
29
|
-
(permissions[type] = {} # This is for having fx empty users: section
|
30
|
-
next) if groups.nil? #
|
31
|
-
groups.each do |group, rules|
|
32
|
-
permissions[type] ||= {}
|
33
|
-
parser.parse(group, rules) do |permission|
|
34
|
-
permissions[type][permission.name] = permission
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
29
|
+
load_from_hash yml_content
|
38
30
|
rescue => e
|
39
31
|
raise "PermissionsLoader Error: The permissions for the file #{file_name} could not be loaded - cause was #{e}"
|
40
32
|
end
|
@@ -54,15 +46,17 @@ module CanTango
|
|
54
46
|
|
55
47
|
define_method(:"#{type}_compiled_permissions") do
|
56
48
|
type_permissions = send(:"#{type}_permissions")
|
49
|
+
|
57
50
|
return Hashie::Mash.new if !type_permissions || type_permissions.empty?
|
51
|
+
|
58
52
|
compiled_sum = send(:"#{type}_permissions").inject({}) do |compiled_sum, (actor, permission)|
|
59
53
|
compiled_sum.merge(permission.to_compiled_hash)
|
60
54
|
end
|
55
|
+
|
61
56
|
Hashie::Mash.new(compiled_sum)
|
62
57
|
end
|
63
58
|
end
|
64
59
|
|
65
|
-
include ClassMethods
|
66
60
|
end
|
67
61
|
end
|
68
62
|
end
|
@@ -14,18 +14,12 @@ module CanTango
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
def self.create name, options = {}
|
18
|
-
self.new name, options
|
19
|
-
end
|
20
|
-
|
21
17
|
def load!
|
22
18
|
raise NotImplementedError
|
23
19
|
end
|
24
20
|
|
25
21
|
def save! permissions
|
26
|
-
|
27
|
-
store permission
|
28
|
-
end
|
22
|
+
raise NotImplementedError
|
29
23
|
end
|
30
24
|
|
31
25
|
end
|
@@ -12,10 +12,6 @@ module CanTango
|
|
12
12
|
super
|
13
13
|
end
|
14
14
|
|
15
|
-
def self.create name, options = {}
|
16
|
-
super
|
17
|
-
end
|
18
|
-
|
19
15
|
def load!
|
20
16
|
loader.load!
|
21
17
|
end
|
@@ -32,7 +28,7 @@ module CanTango
|
|
32
28
|
|
33
29
|
CanTango.config.permission_engine.types.each do |type|
|
34
30
|
define_method(:"#{type}_permissions") do
|
35
|
-
loader.send(:"#{type}_permissions")
|
31
|
+
loader.send(:"#{type}_permissions") || {}
|
36
32
|
end
|
37
33
|
|
38
34
|
define_method(:"#{type}_permissions_rules") do
|
@@ -51,10 +47,7 @@ module CanTango
|
|
51
47
|
|
52
48
|
# @stanislaw: this needs revision!
|
53
49
|
|
54
|
-
|
55
|
-
#cache(":#{type}") ||
|
56
|
-
send(:"#{type}_compiled_permissions")
|
57
|
-
end
|
50
|
+
alias_method :"#{type}_rules", :"#{type}_compiled_permissions"
|
58
51
|
end
|
59
52
|
|
60
53
|
def save! perms = nil
|
@@ -65,7 +58,7 @@ module CanTango
|
|
65
58
|
end
|
66
59
|
end
|
67
60
|
|
68
|
-
def save_permissions
|
61
|
+
def save_permissions perms
|
69
62
|
load_from_hash perms
|
70
63
|
end
|
71
64
|
|