ktopping_acl9 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,59 @@
1
+ module Acl9
2
+ module ModelExtensions
3
+ module ForObject
4
+ ##
5
+ # Role check.
6
+ #
7
+ # @return [Boolean] Returns true if +subject+ has a role +role_name+ on this object.
8
+ #
9
+ # @param [Symbol,String] role_name Role name
10
+ # @param [Subject] subject Subject to add role for
11
+ # @see Acl9::ModelExtensions::Subject#has_role?
12
+ def accepts_role?(role_name, subject)
13
+ subject.has_role? role_name, self
14
+ end
15
+
16
+ ##
17
+ # Add role on the object to specified subject.
18
+ #
19
+ # @param [Symbol,String] role_name Role name
20
+ # @param [Subject] subject Subject to add role for
21
+ # @see Acl9::ModelExtensions::Subject#has_role!
22
+ def accepts_role!(role_name, subject)
23
+ subject.has_role! role_name, self
24
+ end
25
+
26
+ ##
27
+ # Free specified subject of a role on this object.
28
+ #
29
+ # @param [Symbol,String] role_name Role name
30
+ # @param [Subject] subject Subject to remove role from
31
+ # @see Acl9::ModelExtensions::Subject#has_no_role!
32
+ def accepts_no_role!(role_name, subject)
33
+ subject.has_no_role! role_name, self
34
+ end
35
+
36
+ ##
37
+ # Are there any roles for the specified +subject+ on this object?
38
+ #
39
+ # @param [Subject] subject Subject to query roles
40
+ # @return [Boolean] Returns true if +subject+ has any roles on this object.
41
+ # @see Acl9::ModelExtensions::Subject#has_roles_for?
42
+ def accepts_roles_by?(subject)
43
+ subject.has_roles_for? self
44
+ end
45
+
46
+ alias :accepts_role_by? :accepts_roles_by?
47
+
48
+ ##
49
+ # Which roles does +subject+ have on this object?
50
+ #
51
+ # @return [Array<Role>] Role instances, associated both with +subject+ and +object+
52
+ # @param [Subject] subject Subject to query roles
53
+ # @see Acl9::ModelExtensions::Subject#roles_for
54
+ def accepted_roles_by(subject)
55
+ subject.roles_for self
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,184 @@
1
+ module Acl9
2
+ module ModelExtensions
3
+ module ForSubject
4
+ ##
5
+ # Role check.
6
+ #
7
+ # There is a global option, +Acl9.config[:protect_global_roles]+, which governs
8
+ # this method behavior.
9
+ #
10
+ # If protect_global_roles is +false+, an object role is automatically counted
11
+ # as global role. E.g.
12
+ #
13
+ # Acl9.config[:protect_global_roles] = false
14
+ # user.has_role!(:manager, @foo)
15
+ # user.has_role?(:manager, @foo) # => true
16
+ # user.has_role?(:manager) # => true
17
+ #
18
+ # In this case manager is anyone who "manages" at least one object.
19
+ #
20
+ # However, if protect_global_roles option set to +true+, you'll need to
21
+ # explicitly grant global role with same name.
22
+ #
23
+ # Acl9.config[:protect_global_roles] = true
24
+ # user.has_role!(:manager, @foo)
25
+ # user.has_role?(:manager) # => false
26
+ # user.has_role!(:manager)
27
+ # user.has_role?(:manager) # => true
28
+ #
29
+ # protect_global_roles option is +false+ by default as for now, but this
30
+ # may change in future!
31
+ #
32
+ # @return [Boolean] Whether +self+ has a role +role_name+ on +object+.
33
+ # @param [Symbol,String] role_name Role name
34
+ # @param [Object] object Object to query a role on
35
+ #
36
+ # @see Acl9::ModelExtensions::Object#accepts_role?
37
+ def has_role?(role_name, object = nil)
38
+ !! if object.nil? && !::Acl9.config[:protect_global_roles]
39
+ self.role_objects.find_by_name(role_name.to_s) ||
40
+ self.role_objects.member?(get_role(role_name, nil))
41
+ else
42
+ role = get_role(role_name, object)
43
+ role && self.role_objects.exists?(role.id)
44
+ end
45
+ end
46
+
47
+ ##
48
+ # Add specified role on +object+ to +self+.
49
+ #
50
+ # @param [Symbol,String] role_name Role name
51
+ # @param [Object] object Object to add a role for
52
+ # @see Acl9::ModelExtensions::Object#accepts_role!
53
+ def has_role!(role_name, object = nil)
54
+ role = get_role(role_name, object)
55
+
56
+ if role.nil?
57
+ role_attrs = case object
58
+ when Class then { :authorizable_type => object.to_s }
59
+ when nil then {}
60
+ else { :authorizable => object }
61
+ end.merge( { :name => role_name.to_s })
62
+
63
+ role = self._auth_role_class.create(role_attrs)
64
+ end
65
+
66
+ self.role_objects << role if role && !self.role_objects.exists?(role.id)
67
+ end
68
+
69
+ ##
70
+ # Free +self+ from a specified role on +object+.
71
+ #
72
+ # @param [Symbol,String] role_name Role name
73
+ # @param [Object] object Object to remove a role on
74
+ # @see Acl9::ModelExtensions::Object#accepts_no_role!
75
+ def has_no_role!(role_name, object = nil)
76
+ delete_role(get_role(role_name, object))
77
+ end
78
+
79
+ ##
80
+ # Are there any roles for +self+ on +object+?
81
+ #
82
+ # @param [Object] object Object to query roles
83
+ # @return [Boolean] Returns true if +self+ has any roles on +object+.
84
+ # @see Acl9::ModelExtensions::Object#accepts_roles_by?
85
+ def has_roles_for?(object)
86
+ !!self.role_objects.detect(&role_selecting_lambda(object))
87
+ end
88
+
89
+ alias :has_role_for? :has_roles_for?
90
+
91
+ ##
92
+ # Which roles does +self+ have on +object+?
93
+ #
94
+ # @return [Array<Role>] Role instances, associated both with +self+ and +object+
95
+ # @param [Object] object Object to query roles
96
+ # @see Acl9::ModelExtensions::Object#accepted_roles_by
97
+ # @example
98
+ # user = User.find(...)
99
+ # product = Product.find(...)
100
+ #
101
+ # user.roles_for(product).map(&:name).sort #=> role names in alphabetical order
102
+ def roles_for(object)
103
+ self.role_objects.select(&role_selecting_lambda(object))
104
+ end
105
+
106
+ ##
107
+ # Unassign any roles on +object+ from +self+.
108
+ #
109
+ # @param [Object,nil] object Object to unassign roles for. +nil+ means unassign global roles.
110
+ def has_no_roles_for!(object = nil)
111
+ roles_for(object).each { |role| delete_role(role) }
112
+ end
113
+
114
+ ##
115
+ # Unassign all roles from +self+.
116
+ def has_no_roles!
117
+ # for some reason simple
118
+ #
119
+ # self.roles.each { |role| delete_role(role) }
120
+ #
121
+ # doesn't work. seems like a bug in ActiveRecord
122
+ self.role_objects.map(&:id).each do |role_id|
123
+ delete_role self._auth_role_class.find(role_id)
124
+ end
125
+ end
126
+
127
+ private
128
+
129
+ def role_selecting_lambda(object)
130
+ case object
131
+ when Class
132
+ lambda { |role| role.authorizable_type == object.to_s }
133
+ when nil
134
+ lambda { |role| role.authorizable.nil? }
135
+ else
136
+ lambda do |role|
137
+ role.authorizable_type == object.class.base_class.to_s && role.authorizable == object
138
+ end
139
+ end
140
+ end
141
+
142
+ def get_role(role_name, object)
143
+ role_name = role_name.to_s
144
+
145
+ cond = case object
146
+ when Class
147
+ [ 'name = ? and authorizable_type = ? and authorizable_id IS NULL', role_name, object.to_s ]
148
+ when nil
149
+ [ 'name = ? and authorizable_type IS NULL and authorizable_id IS NULL', role_name ]
150
+ else
151
+ [
152
+ 'name = ? and authorizable_type = ? and authorizable_id = ?',
153
+ role_name, object.class.base_class.to_s, object.id
154
+ ]
155
+ end
156
+
157
+ self._auth_role_class.first :conditions => cond
158
+ end
159
+
160
+ def delete_role(role)
161
+ if role
162
+ self.role_objects.delete role
163
+
164
+ role.destroy if role.send(self._auth_subject_class_name.demodulize.tableize).empty?
165
+ end
166
+ end
167
+
168
+ protected
169
+
170
+ def _auth_role_class
171
+ self.class._auth_role_class_name.constantize
172
+ end
173
+
174
+ def _auth_role_assoc
175
+ self.class._auth_role_assoc_name
176
+ end
177
+
178
+ def role_objects
179
+ send(self._auth_role_assoc)
180
+ end
181
+
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,139 @@
1
+ require File.join(File.dirname(__FILE__), 'model_extensions', 'for_subject')
2
+ require File.join(File.dirname(__FILE__), 'model_extensions', 'for_object')
3
+
4
+ module Acl9
5
+ module ModelExtensions #:nodoc:
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+ # Add #has_role? and other role methods to the class.
12
+ # Makes a class a auth. subject class.
13
+ #
14
+ # @param [Hash] options the options for tuning
15
+ # @option options [String] :role_class_name (Acl9::config[:default_role_class_name])
16
+ # Class name of the role class (e.g. 'AccountRole')
17
+ # @option options [String] :join_table_name (Acl9::config[:default_join_table_name])
18
+ # Join table name (e.g. 'accounts_account_roles')
19
+ # @option options [String] :association_name (Acl9::config[:default_association_name])
20
+ # Association name (e.g. ':roles')
21
+ # @example
22
+ # class User < ActiveRecord::Base
23
+ # acts_as_authorization_subject
24
+ # end
25
+ #
26
+ # user = User.new
27
+ # user.roles #=> returns Role objects, associated with the user
28
+ # user.has_role!(...)
29
+ # user.has_no_role!(...)
30
+ #
31
+ # # other functions from Acl9::ModelExtensions::Subject are made available
32
+ #
33
+ # @see Acl9::ModelExtensions::Subject
34
+ #
35
+ def acts_as_authorization_subject(options = {})
36
+ assoc = options[:association_name] || Acl9::config[:default_association_name]
37
+ role = options[:role_class_name] || Acl9::config[:default_role_class_name]
38
+ join_table = options[:join_table_name] || Acl9::config[:default_join_table_name] ||
39
+ join_table_name(undecorated_table_name(self.to_s), undecorated_table_name(role))
40
+
41
+ has_and_belongs_to_many assoc, :class_name => role, :join_table => join_table
42
+
43
+ cattr_accessor :_auth_role_class_name, :_auth_subject_class_name,
44
+ :_auth_role_assoc_name
45
+
46
+ self._auth_role_class_name = role
47
+ self._auth_subject_class_name = self.to_s
48
+ self._auth_role_assoc_name = assoc
49
+
50
+ include Acl9::ModelExtensions::ForSubject
51
+ end
52
+
53
+ # Add role query and set methods to the class (making it an auth object class).
54
+ #
55
+ # @param [Hash] options the options for tuning
56
+ # @option options [String] :subject_class_name (Acl9::config[:default_subject_class_name])
57
+ # Subject class name (e.g. 'User', or 'Account)
58
+ # @option options [String] :role_class_name (Acl9::config[:default_role_class_name])
59
+ # Role class name (e.g. 'AccountRole')
60
+ # @example
61
+ # class Product < ActiveRecord::Base
62
+ # acts_as_authorization_object
63
+ # end
64
+ #
65
+ # product = Product.new
66
+ # product.accepted_roles #=> returns Role objects, associated with the product
67
+ # product.users #=> returns User objects, associated with the product
68
+ # product.accepts_role!(...)
69
+ # product.accepts_no_role!(...)
70
+ # # other functions from Acl9::ModelExtensions::Object are made available
71
+ #
72
+ # @see Acl9::ModelExtensions::Object
73
+ #
74
+ def acts_as_authorization_object(options = {})
75
+ subject = options[:subject_class_name] || Acl9::config[:default_subject_class_name]
76
+ subj_table = subject.constantize.table_name
77
+ subj_col = subject.underscore
78
+
79
+ role = options[:role_class_name] || Acl9::config[:default_role_class_name]
80
+ role_table = role.constantize.table_name
81
+
82
+ sql_tables = <<-EOS
83
+ FROM #{subj_table}
84
+ INNER JOIN #{role_table}_#{subj_table} ON #{subj_col}_id = #{subj_table}.id
85
+ INNER JOIN #{role_table} ON #{role_table}.id = #{role.underscore}_id
86
+ EOS
87
+
88
+ sql_where = <<-'EOS'
89
+ WHERE authorizable_type = '#{self.class.base_class.to_s}'
90
+ AND authorizable_id = #{column_for_attribute(self.class.primary_key).text? ? "'#{id}'": id}
91
+ EOS
92
+
93
+ has_many :accepted_roles, :as => :authorizable, :class_name => role, :dependent => :destroy
94
+
95
+ has_many :"#{subj_table}",
96
+ :finder_sql => ("SELECT DISTINCT #{subj_table}.*" + sql_tables + sql_where),
97
+ :counter_sql => ("SELECT COUNT(DISTINCT #{subj_table}.id)" + sql_tables + sql_where),
98
+ :readonly => true
99
+
100
+ include Acl9::ModelExtensions::ForObject
101
+ end
102
+
103
+ # Make a class an auth role class.
104
+ #
105
+ # You'll probably never create or use objects of this class directly.
106
+ # Various auth. subject and object methods will do that for you
107
+ # internally.
108
+ #
109
+ # @param [Hash] options the options for tuning
110
+ # @option options [String] :subject_class_name (Acl9::config[:default_subject_class_name])
111
+ # Subject class name (e.g. 'User', or 'Account)
112
+ # @option options [String] :join_table_name (Acl9::config[:default_join_table_name])
113
+ # Join table name (e.g. 'accounts_account_roles')
114
+ #
115
+ # @example
116
+ # class Role < ActiveRecord::Base
117
+ # acts_as_authorization_role
118
+ # end
119
+ #
120
+ # @see Acl9::ModelExtensions::Subject#has_role!
121
+ # @see Acl9::ModelExtensions::Subject#has_role?
122
+ # @see Acl9::ModelExtensions::Subject#has_no_role!
123
+ # @see Acl9::ModelExtensions::Object#accepts_role!
124
+ # @see Acl9::ModelExtensions::Object#accepts_role?
125
+ # @see Acl9::ModelExtensions::Object#accepts_no_role!
126
+ def acts_as_authorization_role(options = {})
127
+ subject = options[:subject_class_name] || Acl9::config[:default_subject_class_name]
128
+ join_table = options[:join_table_name] || Acl9::config[:default_join_table_name] ||
129
+ join_table_name(undecorated_table_name(self.to_s), undecorated_table_name(subject))
130
+
131
+ has_and_belongs_to_many subject.demodulize.tableize.to_sym,
132
+ :class_name => subject,
133
+ :join_table => join_table
134
+
135
+ belongs_to :authorizable, :polymorphic => true
136
+ end
137
+ end
138
+ end
139
+ end
data/lib/acl9.rb ADDED
@@ -0,0 +1,16 @@
1
+ require File.join(File.dirname(__FILE__), 'acl9', 'config')
2
+
3
+ if defined? ActiveRecord::Base
4
+ require File.join(File.dirname(__FILE__), 'acl9', 'model_extensions')
5
+
6
+ ActiveRecord::Base.send(:include, Acl9::ModelExtensions)
7
+ end
8
+
9
+
10
+ if defined? ActionController::Base
11
+ require File.join(File.dirname(__FILE__), 'acl9', 'controller_extensions')
12
+ require File.join(File.dirname(__FILE__), 'acl9', 'helpers')
13
+
14
+ ActionController::Base.send(:include, Acl9::ControllerExtensions)
15
+ Acl9Helpers = Acl9::Helpers unless defined?(Acl9Helpers)
16
+ end