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.
Files changed (84) hide show
  1. data/README.textile +35 -7
  2. data/VERSION +1 -1
  3. data/cantango.gemspec +39 -12
  4. data/lib/cantango.rb +4 -3
  5. data/lib/cantango/ability.rb +17 -21
  6. data/lib/cantango/ability/cache/key.rb +32 -4
  7. data/lib/cantango/ability/cache/rules_cache.rb +9 -2
  8. data/lib/cantango/ability/cache_helpers.rb +1 -7
  9. data/lib/cantango/ability/engine_helpers.rb +27 -0
  10. data/lib/cantango/ability_executor.rb +41 -0
  11. data/lib/cantango/api/user/ability.rb +8 -12
  12. data/lib/cantango/api/user/session.rb +2 -1
  13. data/lib/cantango/api/user_account.rb +2 -2
  14. data/lib/cantango/api/user_account/ability.rb +19 -14
  15. data/lib/cantango/api/user_account/can.rb +8 -0
  16. data/lib/cantango/api/user_account/session.rb +33 -0
  17. data/lib/cantango/cached_ability.rb +26 -0
  18. data/lib/cantango/configuration.rb +3 -3
  19. data/lib/cantango/configuration/ability.rb +1 -0
  20. data/lib/cantango/configuration/candidate_registry.rb +51 -0
  21. data/lib/cantango/configuration/categories.rb +2 -2
  22. data/lib/cantango/configuration/engines.rb +7 -3
  23. data/lib/cantango/configuration/engines/permission.rb +5 -0
  24. data/lib/cantango/configuration/engines/permit.rb +1 -0
  25. data/lib/cantango/configuration/engines/user_ac.rb +19 -0
  26. data/lib/cantango/configuration/guest.rb +1 -1
  27. data/lib/cantango/configuration/modes.rb +21 -0
  28. data/lib/cantango/configuration/permits.rb +1 -1
  29. data/lib/cantango/configuration/role_groups.rb +1 -2
  30. data/lib/cantango/configuration/user_accounts.rb +1 -1
  31. data/lib/cantango/configuration/users.rb +1 -1
  32. data/lib/cantango/engine.rb +40 -0
  33. data/lib/cantango/helpers.rb +1 -1
  34. data/lib/cantango/helpers/debug.rb +9 -0
  35. data/lib/cantango/model.rb +6 -0
  36. data/lib/cantango/model/filter.rb +102 -0
  37. data/lib/cantango/model/scope.rb +57 -0
  38. data/lib/cantango/permission_engine.rb +14 -3
  39. data/lib/cantango/permission_engine/loader/base.rb +1 -6
  40. data/lib/cantango/permission_engine/loader/permissions.rb +10 -16
  41. data/lib/cantango/permission_engine/store.rb +1 -7
  42. data/lib/cantango/permission_engine/yaml_store.rb +3 -10
  43. data/lib/cantango/permit_engine.rb +17 -4
  44. data/lib/cantango/permit_engine/builder/base.rb +3 -1
  45. data/lib/cantango/permit_engine/executor/abstract.rb +2 -0
  46. data/lib/cantango/permit_engine/executor/base.rb +1 -1
  47. data/lib/cantango/permit_engine/factory.rb +5 -3
  48. data/lib/cantango/permit_engine/finder.rb +4 -6
  49. data/lib/cantango/permit_engine/util.rb +1 -1
  50. data/lib/cantango/permits/permit.rb +25 -0
  51. data/lib/cantango/permits/role_group_permit/builder.rb +23 -7
  52. data/lib/cantango/permits/role_permit.rb +1 -2
  53. data/lib/cantango/rails/helpers/rest_helper.rb +3 -2
  54. data/lib/cantango/user_ac_engine.rb +40 -0
  55. data/lib/cantango/user_ac_engine/executor.rb +59 -0
  56. data/lib/cantango/users/macros.rb +3 -0
  57. data/lib/cantango/users/user.rb +1 -1
  58. data/lib/cantango/users/user_account.rb +1 -1
  59. data/lib/generators/cantango/permission/permission_generator.rb +43 -0
  60. data/spec/active_record/migrations/008_create_permissions.rb +10 -0
  61. data/spec/cantango/ability_executor/cached_only_spec.rb +76 -0
  62. data/spec/cantango/ability_executor_spec.rb +75 -0
  63. data/spec/cantango/api/attributes_spec.rb +2 -1
  64. data/spec/cantango/api/current_user_accounts.rb +5 -1
  65. data/spec/cantango/api/user/ability_api_spec.rb +17 -4
  66. data/spec/cantango/api/user/can_api_spec.rb +9 -5
  67. data/spec/cantango/api/user/scope_api_spec.rb +15 -7
  68. data/spec/cantango/api/user_account/ability_api_spec.rb +12 -5
  69. data/spec/cantango/api/user_account/can_api_spec.rb +8 -4
  70. data/spec/cantango/cached_ability_spec.rb +0 -0
  71. data/spec/cantango/model/filter_spec.rb +168 -0
  72. data/spec/cantango/model/scope_spec.rb +107 -0
  73. data/spec/cantango/permission_engine/loader/permissions/{cantango_permissions_loader.rb → cantango_permissions_loader_spec.rb} +0 -0
  74. data/spec/cantango/permission_engine/loader/permissions/shared.rb +2 -2
  75. data/spec/cantango/permission_engine/yaml_store_spec.rb +0 -1
  76. data/spec/cantango/permit_engine/role_group_permit_spec.rb +2 -2
  77. data/spec/cantango/permits/permit_spec.rb +2 -2
  78. data/spec/cantango/rules_spec.rb +6 -6
  79. data/spec/cantango/user_ac_engine_spec.rb +53 -0
  80. data/spec/fixtures/config/cantango_permissions.yml +49 -0
  81. data/spec/fixtures/models/permission.rb +12 -0
  82. data/spec/fixtures/models/user.rb +8 -0
  83. data/spec/generators/cantango/permission_generator_spec.rb +44 -0
  84. metadata +59 -35
@@ -8,10 +8,9 @@ module CanTango
8
8
  end
9
9
 
10
10
  def execute!
11
- if CanTango.config.debug.on?
12
- puts "Permit Engine executing..."
13
- puts "No permits found!" if permits.empty?
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
- puts "Permit Finder: #{finder}" if CanTango.debug?
42
+ debug "Permit Finder: #{finder}"
41
43
  finder.new(subject, name).get_permit
42
44
  end
43
45
 
@@ -2,6 +2,8 @@ module CanTango
2
2
  class PermitEngine < Engine
3
3
  module Executor
4
4
  class Abstract
5
+ include CanTango::Helpers::Debug
6
+
5
7
  attr_accessor :permit
6
8
 
7
9
  def initialize permit
@@ -10,7 +10,7 @@ module CanTango
10
10
  protected
11
11
 
12
12
  def not_candidate_permit
13
- puts "Permit #{permit} is not valid for #{subject}" if CanTango.debug?
13
+ debug "Permit #{permit} is not valid for #{subject}"
14
14
  end
15
15
  end
16
16
  end
@@ -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
- puts "PermitEngine Factory: No permits could be built" if permits.empty? && CanTango.debug?
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
- puts "++ Permit Builder: #{builder_class builder}" if CanTango.debug?
22
+ debug "++ Permit Builder: #{builder_class builder}"
21
23
  built_permits = permits_built_with(builder)
22
- puts "== Permits built: #{built_permits.size}" if CanTango.debug?
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
- debug_msg permit_msg(found)
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
@@ -6,7 +6,7 @@ module CanTango
6
6
  def permit_name clazz
7
7
  @name ||= clazz.to_s.demodulize.gsub(/Role/,'').gsub(/Permit$/, '').gsub(/Group/,'').underscore.to_sym
8
8
  end
9
-
9
+
10
10
  # TODO:
11
11
  def role
12
12
  @role ||= permit_name
@@ -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
- if roles.empty?
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
- end
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
- return unless can_perform_action?(user_type, :create, obj)
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 obj, :new, options)
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
@@ -23,7 +23,7 @@ module CanTango
23
23
  end
24
24
 
25
25
  def self.included(base)
26
- CanTango.config.users.register base.name.underscore
26
+ CanTango.config.users.register base.name.underscore, base
27
27
  end
28
28
  end
29
29
  end
@@ -16,7 +16,7 @@ module CanTango
16
16
  end
17
17
 
18
18
  def self.included(base)
19
- CanTango.config.user_accounts.register base.name.underscore.gsub(/_account$/, '')
19
+ CanTango.config.user_accounts.register base.name.underscore.gsub(/_account$/, ''), base
20
20
  end
21
21
  end
22
22
  end
@@ -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
+