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
@@ -8,10 +8,9 @@ module CanTango
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def execute!
|
11
|
-
if
|
12
|
-
|
13
|
-
|
14
|
-
end
|
11
|
+
return if !valid?
|
12
|
+
debug "Permit Engine executing..."
|
13
|
+
|
15
14
|
# CanTango.config.permits.clear_executed! # should there be an option clear before each execution?
|
16
15
|
permits.each do |permit|
|
17
16
|
CanTango.config.permits.was_executed(permit, ability) if CanTango.config.debug.on?
|
@@ -19,6 +18,15 @@ module CanTango
|
|
19
18
|
end
|
20
19
|
end
|
21
20
|
|
21
|
+
def engine_name
|
22
|
+
:permit
|
23
|
+
end
|
24
|
+
|
25
|
+
def valid?
|
26
|
+
return false if !valid_mode?
|
27
|
+
permits.empty? ? invalid : true
|
28
|
+
end
|
29
|
+
|
22
30
|
# by default, only execute permits for which the user
|
23
31
|
# has a role or a role group
|
24
32
|
# also execute any permit marked as special
|
@@ -32,6 +40,11 @@ module CanTango
|
|
32
40
|
|
33
41
|
protected
|
34
42
|
|
43
|
+
def invalid
|
44
|
+
debug "No permits found!"
|
45
|
+
false
|
46
|
+
end
|
47
|
+
|
35
48
|
def permit_factory
|
36
49
|
@permit_factory ||= CanTango::PermitEngine::Factory.new ability
|
37
50
|
end
|
@@ -4,6 +4,8 @@ module CanTango
|
|
4
4
|
class CreatePermitError < StandardError; end;
|
5
5
|
|
6
6
|
class Base
|
7
|
+
include CanTango::Helpers::Debug
|
8
|
+
|
7
9
|
attr_accessor :ability
|
8
10
|
|
9
11
|
# creates the factory for the ability
|
@@ -37,7 +39,7 @@ module CanTango
|
|
37
39
|
end
|
38
40
|
|
39
41
|
def permit_clazz name
|
40
|
-
|
42
|
+
debug "Permit Finder: #{finder}"
|
41
43
|
finder.new(subject, name).get_permit
|
42
44
|
end
|
43
45
|
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module CanTango
|
2
2
|
class PermitEngine < Engine
|
3
3
|
class Factory
|
4
|
+
include CanTango::Helpers::Debug
|
5
|
+
|
4
6
|
attr_accessor :ability
|
5
7
|
|
6
8
|
# creates the factory for the ability
|
@@ -11,15 +13,15 @@ module CanTango
|
|
11
13
|
end
|
12
14
|
|
13
15
|
def build!
|
14
|
-
|
16
|
+
debug "PermitEngine Factory: No permits could be built" if permits.empty?
|
15
17
|
permits
|
16
18
|
end
|
17
19
|
|
18
20
|
def permits
|
19
21
|
@permits ||= builders.inject([]) do |permits, builder|
|
20
|
-
|
22
|
+
debug "++ Permit Builder: #{builder_class builder}"
|
21
23
|
built_permits = permits_built_with(builder)
|
22
|
-
|
24
|
+
debug "== Permits built: #{built_permits.size}"
|
23
25
|
permits = permits + built_permits if built_permits
|
24
26
|
end.flatten
|
25
27
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module CanTango
|
2
2
|
class PermitEngine < Engine
|
3
3
|
class Finder
|
4
|
+
include CanTango::Helpers::Debug
|
5
|
+
|
4
6
|
# This class is used to find the right permit, possible scoped for a specific user account
|
5
7
|
attr_reader :user_account, :name
|
6
8
|
|
@@ -44,12 +46,8 @@ module CanTango
|
|
44
46
|
|
45
47
|
def permit
|
46
48
|
found = registered_permits.registered_for type, name
|
47
|
-
|
48
|
-
found
|
49
|
-
end
|
50
|
-
|
51
|
-
def debug_msg msg
|
52
|
-
puts msg if CanTango.debug?
|
49
|
+
debug permit_msg(found)
|
50
|
+
found
|
53
51
|
end
|
54
52
|
|
55
53
|
def permit_msg found
|
@@ -30,6 +30,10 @@ module CanTango
|
|
30
30
|
clazz.name.gsub(/::.*/,'').gsub(/(.*)Permits/, '\1').underscore.to_sym
|
31
31
|
end
|
32
32
|
|
33
|
+
def cached?
|
34
|
+
ability.cached?
|
35
|
+
end
|
36
|
+
|
33
37
|
def permit_type
|
34
38
|
self.class.type
|
35
39
|
end
|
@@ -119,11 +123,32 @@ module CanTango
|
|
119
123
|
# or if subclassing another Permit than Permit::Base
|
120
124
|
#
|
121
125
|
def permit?
|
126
|
+
cached? ? cached_rules : non_cached_rules
|
127
|
+
run_rule_methods
|
128
|
+
end
|
129
|
+
|
130
|
+
def run_rule_methods
|
122
131
|
static_rules
|
123
132
|
permit_rules
|
124
133
|
dynamic_rules
|
125
134
|
end
|
126
135
|
|
136
|
+
def non_cached_rules
|
137
|
+
include_non_cached if defined?(self.class::NonCached)
|
138
|
+
end
|
139
|
+
|
140
|
+
def cached_rules
|
141
|
+
include_cached if defined?(self.class::Cached)
|
142
|
+
end
|
143
|
+
|
144
|
+
def include_non_cached
|
145
|
+
self.class.send :include, self.class::NonCached
|
146
|
+
end
|
147
|
+
|
148
|
+
def include_cached
|
149
|
+
self.class.send :include, self.class::Cached
|
150
|
+
end
|
151
|
+
|
127
152
|
def licenses *names
|
128
153
|
names.to_strings.each do |name|
|
129
154
|
try_license name
|
@@ -8,17 +8,17 @@ module CanTango
|
|
8
8
|
# builds a list of Permits for each role group of the current ability user (or account)
|
9
9
|
# @return [Array<RoleGroupPermit::Base>] the role group permits built for this ability
|
10
10
|
def build
|
11
|
-
|
12
|
-
puts "Not building any RoleGroupPermit" if CanTango.debug?
|
13
|
-
return [] if role_groups.empty?
|
14
|
-
end
|
15
|
-
|
16
|
-
role_groups.inject([]) do |permits, role_group|
|
11
|
+
matching_permits = matching_role_groups(roles).inject([]) do |permits, role_group|
|
17
12
|
puts "Building RoleGroupPermit for #{role_group}" if CanTango.debug?
|
18
13
|
(permits << create_permit(role_group)) if valid?(role_group)
|
19
14
|
permits
|
20
15
|
end.compact
|
21
|
-
|
16
|
+
|
17
|
+
if matching_permits.empty?
|
18
|
+
puts "Not building any RoleGroupPermits since no role groups are roles that are members of a role group could be found for the permission candidate" if CanTango.debug?
|
19
|
+
return []
|
20
|
+
end
|
21
|
+
end
|
22
22
|
|
23
23
|
def valid? role_group
|
24
24
|
return true if !role_groups_filter?
|
@@ -31,6 +31,22 @@ module CanTango
|
|
31
31
|
|
32
32
|
private
|
33
33
|
|
34
|
+
def matching_role_groups roles
|
35
|
+
role_groups | matching_role_groups_for(roles)
|
36
|
+
end
|
37
|
+
|
38
|
+
# will also run role_groups for which any role of the candidate is a member
|
39
|
+
# so if the candidate is a user and the user has a :trustee role and this role is part of the :trust role group,
|
40
|
+
# then the :trust role group permit will be run!
|
41
|
+
# Thus if the candidate has a particular role group or just has a role belonging to that role group, the permit
|
42
|
+
# for that role group will be run
|
43
|
+
def matching_role_groups_for roles
|
44
|
+
roles.inject([]) do |groups, role|
|
45
|
+
groups << subject.role_groups_for(role) if subject.respond_to?(:role_groups_for)
|
46
|
+
groups
|
47
|
+
end.flatten.compact.uniq
|
48
|
+
end
|
49
|
+
|
34
50
|
def role_groups_filter?
|
35
51
|
CanTango.config.role_groups.filter?
|
36
52
|
end
|
@@ -28,7 +28,6 @@ module CanTango
|
|
28
28
|
super
|
29
29
|
end
|
30
30
|
|
31
|
-
|
32
31
|
# In a specific Role based Permit you can use
|
33
32
|
# def permit? user, options = {}
|
34
33
|
# ... permission logic follows
|
@@ -49,7 +48,7 @@ module CanTango
|
|
49
48
|
def valid_for? subject
|
50
49
|
in_role? subject
|
51
50
|
end
|
52
|
-
|
51
|
+
|
53
52
|
protected
|
54
53
|
|
55
54
|
include CanTango::Helpers::RoleMethods
|
@@ -8,9 +8,10 @@ module CanTango::Rails::Helpers::RestHelper
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def link_to_new obj, user_type, options = {}
|
11
|
-
|
11
|
+
clazz = obj.kind_of?(Class) ? obj : obj.class
|
12
|
+
return unless can_perform_action?(user_type, :create, clazz)
|
12
13
|
# use i18n translation on label
|
13
|
-
link_to t(".create"), send(action_method
|
14
|
+
link_to t(".create"), send(action_method clazz, :new, options)
|
14
15
|
end
|
15
16
|
|
16
17
|
def link_to_delete obj, user_type, options = {}
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module CanTango
|
2
|
+
class UserAcEngine < Engine
|
3
|
+
autoload_modules :Executor
|
4
|
+
|
5
|
+
def initialize ability
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def execute!
|
10
|
+
return if !valid?
|
11
|
+
debug "User AC Engine executing..."
|
12
|
+
|
13
|
+
permissions.each do |permission|
|
14
|
+
ability.can permission.action.to_sym, permission.thing_type.constantize do |thing|
|
15
|
+
thing.nil? || permission.thing_id.nil? || permission.thing_id == thing.id
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def valid?
|
21
|
+
return false if !valid_mode?
|
22
|
+
permissions.empty? ? invalid : true
|
23
|
+
end
|
24
|
+
|
25
|
+
def engine_name
|
26
|
+
:user_ac
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
def permissions
|
32
|
+
candidate.respond_to?(:permissions) ? candidate.permissions : []
|
33
|
+
end
|
34
|
+
|
35
|
+
def invalid
|
36
|
+
debug "No permissions for #{candidate} found!"
|
37
|
+
false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# This class is responsible for executing a set of similar Permits and collecting their rule results into one rule collection
|
2
|
+
# which can be cached under some key and later reused
|
3
|
+
#
|
4
|
+
module CanTango
|
5
|
+
class UserAcEngine < Engine
|
6
|
+
class Executor
|
7
|
+
include CanTango::Ability::CacheHelpers
|
8
|
+
|
9
|
+
attr_reader :ability, :permits
|
10
|
+
|
11
|
+
delegate :session, :user, :subject, :cached?, :to => :ability
|
12
|
+
|
13
|
+
def initialize ability, permit_type, permissions
|
14
|
+
@ability = ability
|
15
|
+
@permissions = permissions
|
16
|
+
end
|
17
|
+
|
18
|
+
def cache_key
|
19
|
+
:user_ac
|
20
|
+
end
|
21
|
+
|
22
|
+
def rules
|
23
|
+
@rules ||= []
|
24
|
+
end
|
25
|
+
|
26
|
+
def clear_rules!
|
27
|
+
@rules ||= []
|
28
|
+
end
|
29
|
+
|
30
|
+
def cache
|
31
|
+
@cache ||= CanTango::Ability::Cache.new self, :cache_key => cache_key, :key_method_names => key_method_names
|
32
|
+
end
|
33
|
+
|
34
|
+
def execute!
|
35
|
+
return if cached_rules?
|
36
|
+
|
37
|
+
clear_rules!
|
38
|
+
permit_rules
|
39
|
+
|
40
|
+
cache_rules!
|
41
|
+
end
|
42
|
+
|
43
|
+
def permit_rules
|
44
|
+
# TODO: somehow type specific caching of result of permits!
|
45
|
+
permits.each do |permit|
|
46
|
+
CanTango.config.permits.was_executed(permit, ability) if CanTango.debug?
|
47
|
+
break if permit.execute == :break
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
def key_method_names
|
54
|
+
[:permissions_hash]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
@@ -3,11 +3,14 @@ class Module
|
|
3
3
|
self.send :include, CanTango::Users::User
|
4
4
|
self.send :include, CanTango::Users::Masquerade if options[:masquerade]
|
5
5
|
end
|
6
|
+
alias_method :cantango_user, :tango_user
|
6
7
|
|
7
8
|
def tango_user_account options = {}
|
8
9
|
self.send :include, CanTango::Users::UserAccount
|
9
10
|
self.send :include, CanTango::Users::Masquerade if options[:masquerade]
|
10
11
|
end
|
12
|
+
alias_method :tango_account, :tango_user_account
|
13
|
+
alias_method :cantango_account, :tango_user_account
|
11
14
|
|
12
15
|
def masquerader
|
13
16
|
self.send :include, CanTango::Users::Masquerade
|
data/lib/cantango/users/user.rb
CHANGED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'generators/cantango/base'
|
2
|
+
|
3
|
+
module Cantango
|
4
|
+
module Generators
|
5
|
+
class PermissionGenerator < Cantango::Generators::Base
|
6
|
+
desc "Creates a Permission for a model in 'app/models'"
|
7
|
+
|
8
|
+
argument :name, :type => :string,
|
9
|
+
:desc => "Model to create Permission model for"
|
10
|
+
|
11
|
+
source_root File.dirname(__FILE__) + '/templates'
|
12
|
+
|
13
|
+
def main_flow
|
14
|
+
relational
|
15
|
+
end
|
16
|
+
|
17
|
+
def relational
|
18
|
+
Rails::Generators.invoke "model", ['Permission', "#{name}_id:integer", "thing_id:integer", "thing_type:string", "action:string"
|
19
|
+
|
20
|
+
puts "Manual Modifications to Permission model:"
|
21
|
+
puts "-----------------------------------------"
|
22
|
+
puts "belongs_to :#{name}"
|
23
|
+
puts "belongs_to :thing, :polymorphic => true"
|
24
|
+
|
25
|
+
puts "Manual Modifications to #{name.to_s.camelize} model:"
|
26
|
+
puts "has_many :permissions"
|
27
|
+
puts "-----------------------------------------"
|
28
|
+
|
29
|
+
puts "and then run:"
|
30
|
+
puts "rake db:migrate"
|
31
|
+
end
|
32
|
+
|
33
|
+
def document_store
|
34
|
+
Rails::Generators.invoke "model", ['Permission']
|
35
|
+
# use rails_artifactor to edit model?!
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|