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
@@ -17,7 +17,7 @@ module CanTango
17
17
  def disable_types *types
18
18
  @enabled_types = available_types - types.flatten
19
19
  end
20
- alias_method :disable, :disable_types
20
+ alias_method :disable, :disable_types
21
21
 
22
22
  def enable_all_types!
23
23
  @enabled_types = available_types
@@ -1,9 +1,8 @@
1
1
  module CanTango
2
2
  class Configuration
3
3
  class RoleGroups < RoleRegistry
4
-
5
4
  include Singleton
6
-
5
+
7
6
  def default_has_method
8
7
  :in_role_group?
9
8
  end
@@ -1,6 +1,6 @@
1
1
  module CanTango
2
2
  class Configuration
3
- class UserAccounts < Registry
3
+ class UserAccounts < CandidateRegistry
4
4
  include Singleton
5
5
  end
6
6
  end
@@ -1,6 +1,6 @@
1
1
  module CanTango
2
2
  class Configuration
3
- class Users < Registry
3
+ class Users < CandidateRegistry
4
4
  include Singleton
5
5
  end
6
6
  end
@@ -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
@@ -1,5 +1,5 @@
1
1
  module CanTango
2
2
  module Helpers
3
- autoload_modules :RoleMethods
3
+ autoload_modules :RoleMethods, :Debug
4
4
  end
5
5
  end
@@ -0,0 +1,9 @@
1
+ module CanTango
2
+ module Helpers
3
+ module Debug
4
+ def debug msg
5
+ puts msg if CanTango.debug?
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ module CanTango
2
+ module Model
3
+ autoload_modules :Filter, :Scope
4
+ end
5
+ end
6
+
@@ -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
- puts "Permission Engine executing..." if CanTango.config.debug.on?
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 user
25
- ability.user
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(subclass)
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
- self.file_name = file_name
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
- return if yml_content.empty?
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
- permissions.each do |permission|
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
- define_method(:"#{type}_rules") do
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(perms)
61
+ def save_permissions perms
69
62
  load_from_hash perms
70
63
  end
71
64