cantango 0.8.9.5 → 0.9.3.2

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.
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
+