careacademy-acl9 3.3.0
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.
- checksums.yaml +7 -0
- data/.gitignore +24 -0
- data/.ruby-version +1 -0
- data/.travis.yml +26 -0
- data/Appraisals +23 -0
- data/CHANGELOG.md +122 -0
- data/CONTRIBUTING.md +62 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +167 -0
- data/LICENSE +9 -0
- data/MIT-LICENSE +20 -0
- data/README.md +326 -0
- data/Rakefile +20 -0
- data/TODO +42 -0
- data/acl9.gemspec +27 -0
- data/bin/appraisal +16 -0
- data/bin/bundler +16 -0
- data/bin/cc-tddium-post-worker +16 -0
- data/bin/erubis +16 -0
- data/bin/rackup +16 -0
- data/bin/rails +16 -0
- data/bin/rake +16 -0
- data/bin/sprockets +16 -0
- data/bin/tapout +16 -0
- data/bin/thor +16 -0
- data/bin/tilt +16 -0
- data/bin/yard +16 -0
- data/bin/yardoc +16 -0
- data/bin/yri +16 -0
- data/gemfiles/.bundle/config +2 -0
- data/gemfiles/rails_5.0.gemfile +10 -0
- data/gemfiles/rails_5.1.gemfile +10 -0
- data/gemfiles/rails_5.2.gemfile +9 -0
- data/gemfiles/rails_6.0.gemfile +9 -0
- data/gemfiles/rails_6.1.gemfile +9 -0
- data/gemfiles/rails_7.0.gemfile +9 -0
- data/lib/acl9/controller_extensions/dsl_base.rb +212 -0
- data/lib/acl9/controller_extensions/generators.rb +166 -0
- data/lib/acl9/controller_extensions.rb +85 -0
- data/lib/acl9/helpers.rb +49 -0
- data/lib/acl9/model_extensions/for_object.rb +74 -0
- data/lib/acl9/model_extensions/for_subject.rb +232 -0
- data/lib/acl9/model_extensions.rb +136 -0
- data/lib/acl9/prepositions.rb +18 -0
- data/lib/acl9/version.rb +3 -0
- data/lib/acl9.rb +78 -0
- data/lib/generators/acl9/setup/USAGE +35 -0
- data/lib/generators/acl9/setup/setup_generator.rb +122 -0
- data/lib/generators/acl9/setup/templates/create_role_tables.rb +31 -0
- data/lib/generators/acl9/setup/templates/role.rb +3 -0
- data/test/config_test.rb +55 -0
- data/test/controller_extensions/actions_test.rb +199 -0
- data/test/controller_extensions/anon_test.rb +39 -0
- data/test/controller_extensions/base.rb +96 -0
- data/test/controller_extensions/basics_test.rb +44 -0
- data/test/controller_extensions/conditions_test.rb +48 -0
- data/test/controller_extensions/method_test.rb +70 -0
- data/test/controller_extensions/multi_match_test.rb +142 -0
- data/test/controller_extensions/multiple_role_arguments_test.rb +136 -0
- data/test/controller_extensions/prepositions_test.rb +108 -0
- data/test/controller_extensions/pseudo_role_test.rb +26 -0
- data/test/controller_extensions/role_test.rb +75 -0
- data/test/controllers/acl_action_override_test.rb +24 -0
- data/test/controllers/acl_arguments_test.rb +5 -0
- data/test/controllers/acl_block_test.rb +5 -0
- data/test/controllers/acl_boolean_method_test.rb +5 -0
- data/test/controllers/acl_helper_method_test.rb +29 -0
- data/test/controllers/acl_ivars_test.rb +15 -0
- data/test/controllers/acl_method2_test.rb +6 -0
- data/test/controllers/acl_method_test.rb +6 -0
- data/test/controllers/acl_object_hash_test.rb +18 -0
- data/test/controllers/acl_query_method_named_test.rb +9 -0
- data/test/controllers/acl_query_method_test.rb +9 -0
- data/test/controllers/acl_query_method_with_lambda_test.rb +9 -0
- data/test/controllers/acl_query_mixin.rb +54 -0
- data/test/controllers/acl_subject_method_test.rb +15 -0
- data/test/controllers/arguments_checking_test.rb +43 -0
- data/test/dummy/app/assets/config/manifest.js +0 -0
- data/test/dummy/app/controllers/acl_action_override.rb +15 -0
- data/test/dummy/app/controllers/acl_arguments.rb +10 -0
- data/test/dummy/app/controllers/acl_block.rb +6 -0
- data/test/dummy/app/controllers/acl_boolean_method.rb +23 -0
- data/test/dummy/app/controllers/acl_helper_method.rb +11 -0
- data/test/dummy/app/controllers/acl_ivars.rb +17 -0
- data/test/dummy/app/controllers/acl_method.rb +6 -0
- data/test/dummy/app/controllers/acl_method2.rb +6 -0
- data/test/dummy/app/controllers/acl_objects_hash.rb +10 -0
- data/test/dummy/app/controllers/acl_query_method.rb +9 -0
- data/test/dummy/app/controllers/acl_query_method_named.rb +15 -0
- data/test/dummy/app/controllers/acl_query_method_with_lambda.rb +9 -0
- data/test/dummy/app/controllers/acl_subject_method.rb +16 -0
- data/test/dummy/app/controllers/application_controller.rb +13 -0
- data/test/dummy/app/controllers/empty_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/helpers/some_helper.rb +8 -0
- data/test/dummy/app/models/.keep +0 -0
- data/test/dummy/app/models/access.rb +3 -0
- data/test/dummy/app/models/account.rb +3 -0
- data/test/dummy/app/models/bar.rb +3 -0
- data/test/dummy/app/models/concerns/.keep +0 -0
- data/test/dummy/app/models/foo.rb +3 -0
- data/test/dummy/app/models/foo_bar.rb +3 -0
- data/test/dummy/app/models/other/foo.rb +5 -0
- data/test/dummy/app/models/other/role.rb +5 -0
- data/test/dummy/app/models/other/user.rb +5 -0
- data/test/dummy/app/models/role.rb +3 -0
- data/test/dummy/app/models/string_object_role.rb +3 -0
- data/test/dummy/app/models/string_user.rb +3 -0
- data/test/dummy/app/models/user.rb +3 -0
- data/test/dummy/app/models/uuid.rb +4 -0
- data/test/dummy/config/application.rb +23 -0
- data/test/dummy/config/boot.rb +4 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/test.rb +40 -0
- data/test/dummy/config/initializers/assets.rb +8 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/secrets.rb +1 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +14 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/db/migrate/20141117132218_create_tables.rb +149 -0
- data/test/helpers/helper_test.rb +89 -0
- data/test/models/roles_test.rb +369 -0
- data/test/models/roles_with_custom_association_names_test.rb +28 -0
- data/test/models/roles_with_custom_class_names_test.rb +28 -0
- data/test/models/system_roles_test.rb +22 -0
- data/test/models/users_roles_and_subjects_with_namespaced_class_names_test.rb +30 -0
- data/test/test_helper.rb +94 -0
- data/test/version_test.rb +7 -0
- metadata +321 -0
@@ -0,0 +1,232 @@
|
|
1
|
+
require_relative "../prepositions"
|
2
|
+
|
3
|
+
module Acl9
|
4
|
+
module ModelExtensions
|
5
|
+
module ForSubject
|
6
|
+
include Prepositions
|
7
|
+
|
8
|
+
DEFAULT = Class.new do
|
9
|
+
def default?
|
10
|
+
true
|
11
|
+
end
|
12
|
+
end.new.freeze
|
13
|
+
|
14
|
+
##
|
15
|
+
# Role check.
|
16
|
+
#
|
17
|
+
# There is a global option, +Acl9.config[:protect_global_roles]+, which governs
|
18
|
+
# this method behavior.
|
19
|
+
#
|
20
|
+
# If protect_global_roles is +false+, an object role is automatically counted
|
21
|
+
# as global role. E.g.
|
22
|
+
#
|
23
|
+
# Acl9.config[:protect_global_roles] = false
|
24
|
+
# user.has_role!(:manager, @foo)
|
25
|
+
# user.has_role?(:manager, @foo) # => true
|
26
|
+
# user.has_role?(:manager) # => true
|
27
|
+
#
|
28
|
+
# In this case manager is anyone who "manages" at least one object.
|
29
|
+
#
|
30
|
+
# However, if protect_global_roles option set to +true+, you'll need to
|
31
|
+
# explicitly grant global role with same name.
|
32
|
+
#
|
33
|
+
# Acl9.config[:protect_global_roles] = true
|
34
|
+
# user.has_role!(:manager, @foo)
|
35
|
+
# user.has_role?(:manager) # => false
|
36
|
+
# user.has_role!(:manager)
|
37
|
+
# user.has_role?(:manager) # => true
|
38
|
+
#
|
39
|
+
# protect_global_roles option is +false+ by default as for now, but this
|
40
|
+
# may change in future!
|
41
|
+
#
|
42
|
+
# @return [Boolean] Whether +self+ has a role +role_name+ on +object+.
|
43
|
+
# @param [Symbol,String] role_name Role name
|
44
|
+
# @param [Object] object Object to query a role on
|
45
|
+
#
|
46
|
+
# @see Acl9::ModelExtensions::Object#accepts_role?
|
47
|
+
def has_role?(role_name, object = default)
|
48
|
+
check! object
|
49
|
+
role_name = normalize role_name
|
50
|
+
object = _by_preposition object
|
51
|
+
|
52
|
+
!! if object == default && !::Acl9.config[:protect_global_roles]
|
53
|
+
_role_objects.find_by_name(role_name.to_s) ||
|
54
|
+
_role_objects.member?(get_role(role_name, object))
|
55
|
+
else
|
56
|
+
role = get_role(role_name, object)
|
57
|
+
role && _role_objects.exists?(role.id)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# Add specified role on +object+ to +self+.
|
63
|
+
#
|
64
|
+
# @param [Symbol,String] role_name Role name
|
65
|
+
# @param [Object] object Object to add a role for
|
66
|
+
# @see Acl9::ModelExtensions::Object#accepts_role!
|
67
|
+
def has_role!(role_name, object = default)
|
68
|
+
check! object
|
69
|
+
role_name = normalize role_name
|
70
|
+
object = _by_preposition object
|
71
|
+
|
72
|
+
role = get_role(role_name, object)
|
73
|
+
|
74
|
+
if role.nil?
|
75
|
+
role_attrs = case object
|
76
|
+
when Class then { :authorizable_type => object.to_s }
|
77
|
+
when default then {}
|
78
|
+
else { :authorizable => object }
|
79
|
+
end.merge({ :name => role_name.to_s })
|
80
|
+
|
81
|
+
role = _auth_role_class.create(role_attrs)
|
82
|
+
end
|
83
|
+
|
84
|
+
_role_objects << role if role && !_role_objects.exists?(role.id)
|
85
|
+
end
|
86
|
+
|
87
|
+
##
|
88
|
+
# Free +self+ from a specified role on +object+.
|
89
|
+
#
|
90
|
+
# @param [Symbol,String] role_name Role name
|
91
|
+
# @param [Object] object Object to remove a role on
|
92
|
+
# @see Acl9::ModelExtensions::Object#accepts_no_role!
|
93
|
+
def has_no_role!(role_name, object = default)
|
94
|
+
check! object
|
95
|
+
role_name = normalize role_name
|
96
|
+
object = _by_preposition object
|
97
|
+
delete_role(get_role(role_name, object))
|
98
|
+
end
|
99
|
+
|
100
|
+
##
|
101
|
+
# Are there any roles for +self+ on +object+?
|
102
|
+
#
|
103
|
+
# @param [Object] object Object to query roles
|
104
|
+
# @return [Boolean] Returns true if +self+ has any roles on +object+.
|
105
|
+
# @see Acl9::ModelExtensions::Object#accepts_roles_by?
|
106
|
+
def has_roles_for?(object)
|
107
|
+
check! object
|
108
|
+
!!_role_objects.detect(&role_selecting_lambda(object))
|
109
|
+
end
|
110
|
+
|
111
|
+
alias :has_role_for? :has_roles_for?
|
112
|
+
|
113
|
+
##
|
114
|
+
# Which roles does +self+ have on +object+?
|
115
|
+
#
|
116
|
+
# @return [Array<Role>] Role instances, associated both with +self+ and +object+
|
117
|
+
# @param [Object] object Object to query roles
|
118
|
+
# @see Acl9::ModelExtensions::Object#accepted_roles_by
|
119
|
+
# @example
|
120
|
+
# user = User.find(...)
|
121
|
+
# product = Product.find(...)
|
122
|
+
#
|
123
|
+
# user.roles_for(product).map(&:name).sort #=> role names in alphabetical order
|
124
|
+
def roles_for(object)
|
125
|
+
check! object
|
126
|
+
_role_objects.select(&role_selecting_lambda(object))
|
127
|
+
end
|
128
|
+
|
129
|
+
##
|
130
|
+
# Unassign any roles on +object+ from +self+.
|
131
|
+
#
|
132
|
+
# @param [Object,default] object Object to unassign roles for. Empty args means unassign global roles.
|
133
|
+
def has_no_roles_for!(object = default)
|
134
|
+
check! object
|
135
|
+
roles_for(object).each { |role| delete_role(role) }
|
136
|
+
end
|
137
|
+
|
138
|
+
##
|
139
|
+
# Unassign all roles from +self+.
|
140
|
+
def has_no_roles!
|
141
|
+
# for some reason simple
|
142
|
+
#
|
143
|
+
# roles.each { |role| delete_role(role) }
|
144
|
+
#
|
145
|
+
# doesn't work. seems like a bug in ActiveRecord
|
146
|
+
_role_objects.map(&:id).each do |role_id|
|
147
|
+
delete_role _auth_role_class.find(role_id)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
def role_selecting_lambda(object)
|
154
|
+
case object
|
155
|
+
when Class
|
156
|
+
lambda { |role| role.authorizable_type == object.to_s }
|
157
|
+
when default
|
158
|
+
lambda { |role| role.authorizable.nil? }
|
159
|
+
else
|
160
|
+
lambda do |role|
|
161
|
+
auth_id = role.authorizable_id.kind_of?(String) ? object.id.to_s : object.id
|
162
|
+
role.authorizable_type == object.class.base_class.to_s && role.authorizable_id == auth_id
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def get_role(role_name, object = default)
|
168
|
+
check! object
|
169
|
+
role_name = normalize role_name
|
170
|
+
|
171
|
+
cond = case object
|
172
|
+
when Class
|
173
|
+
[ 'name = ? and authorizable_type = ? and authorizable_id IS NULL', role_name, object.to_s ]
|
174
|
+
when default
|
175
|
+
[ 'name = ? and authorizable_type IS NULL and authorizable_id IS NULL', role_name ]
|
176
|
+
else
|
177
|
+
[
|
178
|
+
'name = ? and authorizable_type = ? and authorizable_id = ?',
|
179
|
+
role_name, object.class.base_class.to_s, object.id
|
180
|
+
]
|
181
|
+
end
|
182
|
+
|
183
|
+
if _auth_role_class.respond_to?(:where)
|
184
|
+
_auth_role_class.where(cond).first
|
185
|
+
else
|
186
|
+
_auth_role_class.find(:first, :conditions => cond)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def delete_role(role)
|
191
|
+
if role
|
192
|
+
if ret = _role_objects.delete(role)
|
193
|
+
if role.send(_auth_subject_class_name.demodulize.tableize).empty?
|
194
|
+
ret &&= role.destroy unless role.respond_to?(:system?) && role.system?
|
195
|
+
end
|
196
|
+
end
|
197
|
+
ret
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def normalize role_name
|
202
|
+
Acl9.config[:normalize_role_names] ? role_name.to_s.underscore.singularize : role_name.to_s
|
203
|
+
end
|
204
|
+
|
205
|
+
private
|
206
|
+
|
207
|
+
def check! object
|
208
|
+
raise NilObjectError if object.nil?
|
209
|
+
end
|
210
|
+
|
211
|
+
def _by_preposition object
|
212
|
+
object.is_a?(Hash) ? super : object
|
213
|
+
end
|
214
|
+
|
215
|
+
def _auth_role_class
|
216
|
+
self.class._auth_role_class_name.constantize
|
217
|
+
end
|
218
|
+
|
219
|
+
def _auth_role_assoc
|
220
|
+
self.class._auth_role_assoc_name
|
221
|
+
end
|
222
|
+
|
223
|
+
def _role_objects
|
224
|
+
send(_auth_role_assoc)
|
225
|
+
end
|
226
|
+
|
227
|
+
def default
|
228
|
+
DEFAULT
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require_relative "model_extensions/for_subject"
|
2
|
+
require_relative "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.role_objects #=> 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] || self.table_name_prefix + [undecorated_table_name(self.to_s), undecorated_table_name(role)].sort.join("_") + self.table_name_suffix
|
39
|
+
|
40
|
+
has_and_belongs_to_many assoc.to_sym, :class_name => role, :join_table => join_table
|
41
|
+
|
42
|
+
before_destroy :has_no_roles!
|
43
|
+
|
44
|
+
cattr_accessor :_auth_role_class_name, :_auth_subject_class_name,
|
45
|
+
:_auth_role_assoc_name
|
46
|
+
|
47
|
+
self._auth_role_class_name = role
|
48
|
+
self._auth_subject_class_name = self.to_s
|
49
|
+
self._auth_role_assoc_name = assoc
|
50
|
+
|
51
|
+
include Acl9::ModelExtensions::ForSubject
|
52
|
+
end
|
53
|
+
|
54
|
+
# Add role query and set methods to the class (making it an auth object class).
|
55
|
+
#
|
56
|
+
# @param [Hash] options the options for tuning
|
57
|
+
# @option options [String] :subject_class_name (Acl9::config[:default_subject_class_name])
|
58
|
+
# Subject class name (e.g. 'User', or 'Account)
|
59
|
+
# @option options [String] :role_class_name (Acl9::config[:default_role_class_name])
|
60
|
+
# Role class name (e.g. 'AccountRole')
|
61
|
+
# @example
|
62
|
+
# class Product < ActiveRecord::Base
|
63
|
+
# acts_as_authorization_object
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# product = Product.new
|
67
|
+
# product.accepted_roles #=> returns Role objects, associated with the product
|
68
|
+
# product.users #=> returns User objects, associated with the product
|
69
|
+
# product.accepts_role!(...)
|
70
|
+
# product.accepts_no_role!(...)
|
71
|
+
# # other functions from Acl9::ModelExtensions::Object are made available
|
72
|
+
#
|
73
|
+
# @see Acl9::ModelExtensions::Object
|
74
|
+
#
|
75
|
+
def acts_as_authorization_object(options = {})
|
76
|
+
subject = options[:subject_class_name] || Acl9::config[:default_subject_class_name]
|
77
|
+
subj_table = subject.constantize.table_name
|
78
|
+
|
79
|
+
role = options[:role_class_name] || Acl9::config[:default_role_class_name]
|
80
|
+
|
81
|
+
has_many :accepted_roles, :as => :authorizable, :class_name => role, :dependent => :destroy
|
82
|
+
|
83
|
+
subj_assoc = "assoc_#{subj_table}".to_sym
|
84
|
+
has_many subj_assoc, -> { distinct.readonly }, source: subj_table.to_sym, through: :accepted_roles
|
85
|
+
|
86
|
+
define_method subj_table.to_sym do |role_name=nil|
|
87
|
+
rel = send subj_assoc
|
88
|
+
|
89
|
+
if role_name
|
90
|
+
rel = rel.where role.constantize.table_name.to_sym => { name: role_name }
|
91
|
+
end
|
92
|
+
rel
|
93
|
+
end
|
94
|
+
|
95
|
+
include Acl9::ModelExtensions::ForObject
|
96
|
+
end
|
97
|
+
|
98
|
+
# Make a class an auth role class.
|
99
|
+
#
|
100
|
+
# You'll probably never create or use objects of this class directly.
|
101
|
+
# Various auth. subject and object methods will do that for you
|
102
|
+
# internally.
|
103
|
+
#
|
104
|
+
# @param [Hash] options the options for tuning
|
105
|
+
# @option options [String] :subject_class_name (Acl9::config[:default_subject_class_name])
|
106
|
+
# Subject class name (e.g. 'User', or 'Account)
|
107
|
+
# @option options [String] :join_table_name (Acl9::config[:default_join_table_name])
|
108
|
+
# Join table name (e.g. 'accounts_account_roles')
|
109
|
+
#
|
110
|
+
# @example
|
111
|
+
# class Role < ActiveRecord::Base
|
112
|
+
# acts_as_authorization_role
|
113
|
+
# end
|
114
|
+
#
|
115
|
+
# @see Acl9::ModelExtensions::Subject#has_role!
|
116
|
+
# @see Acl9::ModelExtensions::Subject#has_role?
|
117
|
+
# @see Acl9::ModelExtensions::Subject#has_no_role!
|
118
|
+
# @see Acl9::ModelExtensions::Object#accepts_role!
|
119
|
+
# @see Acl9::ModelExtensions::Object#accepts_role?
|
120
|
+
# @see Acl9::ModelExtensions::Object#accepts_no_role!
|
121
|
+
def acts_as_authorization_role(options = {})
|
122
|
+
subject = options[:subject_class_name] || Acl9::config[:default_subject_class_name]
|
123
|
+
join_table = options[:join_table_name] || Acl9::config[:default_join_table_name] ||
|
124
|
+
self.table_name_prefix + [undecorated_table_name(self.to_s), undecorated_table_name(subject)].sort.join("_") + self.table_name_suffix
|
125
|
+
# comment out use deprecated API
|
126
|
+
#join_table_name(undecorated_table_name(self.to_s), undecorated_table_name(subject))
|
127
|
+
|
128
|
+
has_and_belongs_to_many subject.demodulize.tableize.to_sym,
|
129
|
+
:class_name => subject,
|
130
|
+
:join_table => join_table
|
131
|
+
|
132
|
+
belongs_to :authorizable, polymorphic: true, optional: true
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Acl9
|
2
|
+
module Prepositions
|
3
|
+
VALID_PREPOSITIONS = %i(of for in on at by).freeze unless defined? VALID_PREPOSITIONS
|
4
|
+
|
5
|
+
def _by_preposition options
|
6
|
+
object = nil
|
7
|
+
|
8
|
+
VALID_PREPOSITIONS.each do |prep|
|
9
|
+
if options[prep]
|
10
|
+
raise ArgumentError, "You may only use one preposition to specify object" if object
|
11
|
+
|
12
|
+
object = options[prep]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
object
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/acl9/version.rb
ADDED
data/lib/acl9.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'acl9/version'
|
2
|
+
require 'acl9/model_extensions'
|
3
|
+
require 'acl9/controller_extensions'
|
4
|
+
require 'acl9/helpers'
|
5
|
+
|
6
|
+
module Acl9
|
7
|
+
CONFIG = {
|
8
|
+
:default_role_class_name => 'Role',
|
9
|
+
:default_subject_class_name => 'User',
|
10
|
+
:default_subject_method => :current_user,
|
11
|
+
:default_association_name => :role_objects,
|
12
|
+
:default_join_table_name => nil,
|
13
|
+
:protect_global_roles => true,
|
14
|
+
:normalize_role_names => true,
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
class Config < Struct.new(*CONFIG.keys )
|
18
|
+
def [] k; send k.to_sym; end
|
19
|
+
def []= k, v; send "#{k}=", v; end
|
20
|
+
def reset!
|
21
|
+
Acl9::CONFIG.each do |k,v|
|
22
|
+
send "#{k}=", v
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def merge! h
|
27
|
+
h.each { |k,v| self[k.to_sym] = v }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
@@config = Config.new( *CONFIG.values_at(*Config.members))
|
32
|
+
|
33
|
+
mattr_reader :config
|
34
|
+
|
35
|
+
def self.configure
|
36
|
+
yield config
|
37
|
+
end
|
38
|
+
|
39
|
+
class ArgumentError < ArgumentError; end
|
40
|
+
class RuntimeError < RuntimeError; end
|
41
|
+
class NilObjectError < RuntimeError; end
|
42
|
+
|
43
|
+
##
|
44
|
+
# This exception is raised whenever ACL block finds that the current user
|
45
|
+
# is not authorized for the controller action he wants to execute.
|
46
|
+
# @example How to catch this exception in ApplicationController
|
47
|
+
# class ApplicationController < ActionController::Base
|
48
|
+
# rescue_from 'Acl9::AccessDenied', :with => :access_denied
|
49
|
+
#
|
50
|
+
# # ...other stuff...
|
51
|
+
# private
|
52
|
+
#
|
53
|
+
# def access_denied
|
54
|
+
# if current_user
|
55
|
+
# # It's presumed you have a template with words of pity and regret
|
56
|
+
# # for unhappy user who is not authorized to do what he wanted
|
57
|
+
# render :template => 'home/access_denied'
|
58
|
+
# else
|
59
|
+
# # In this case user has not even logged in. Might be OK after login.
|
60
|
+
# flash[:notice] = 'Access denied. Try to log in first.'
|
61
|
+
# redirect_to login_path
|
62
|
+
# end
|
63
|
+
# end
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
class AccessDenied < RuntimeError; end
|
67
|
+
|
68
|
+
##
|
69
|
+
# This exception is raised when acl9 has generated invalid code for the
|
70
|
+
# filtering method or block. Should never happen, and it's a bug when it
|
71
|
+
# happens.
|
72
|
+
class FilterSyntaxError < ArgumentError; end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
ActiveRecord::Base.send(:include, Acl9::ModelExtensions)
|
77
|
+
AbstractController::Base.send :include, Acl9::ControllerExtensions
|
78
|
+
Acl9Helpers = Acl9::Helpers unless defined?(Acl9Helpers)
|
@@ -0,0 +1,35 @@
|
|
1
|
+
Description:
|
2
|
+
Installs the basic framework for Acl9. Creates the necessary migration for
|
3
|
+
your new roles table and the join table for associating roles with users.
|
4
|
+
|
5
|
+
The optional arguments are as follows:
|
6
|
+
|
7
|
+
subject: if you want something other than 'User'
|
8
|
+
role: if you want something other than 'Role'
|
9
|
+
objects: space separated list of class names of objects that you can
|
10
|
+
attach roles to (see the docs)
|
11
|
+
|
12
|
+
Examples:
|
13
|
+
`rails g acl9:setup`
|
14
|
+
|
15
|
+
This will create:
|
16
|
+
Migration: db/migrate/XXX_create_role_tables.rb
|
17
|
+
Role Model: app/models/role.rb
|
18
|
+
Config: config/initializers/acl9.rb
|
19
|
+
|
20
|
+
And it will update (or create a skeleton):
|
21
|
+
Subject Model: app/models/user.rb
|
22
|
+
|
23
|
+
`rails g acl9:setup account permission school classroom department`
|
24
|
+
|
25
|
+
This will create:
|
26
|
+
Migration: db/migrate/XXX_create_permission_tables.rb
|
27
|
+
Role Model: app/models/permission.rb
|
28
|
+
Config: config/initializers/acl9.rb
|
29
|
+
|
30
|
+
And it will update (or create a skeleton):
|
31
|
+
Subject Model: app/models/account.rb
|
32
|
+
Object Models: app/models/school.rb
|
33
|
+
app/models/classroom.rb
|
34
|
+
app/models/department.rb
|
35
|
+
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require "rails/generators/active_record"
|
2
|
+
|
3
|
+
module Acl9
|
4
|
+
class SetupGenerator < Rails::Generators::Base
|
5
|
+
include ActiveRecord::Generators::Migration
|
6
|
+
|
7
|
+
source_root File.expand_path('../templates', __FILE__)
|
8
|
+
|
9
|
+
argument :arg_subject, type: :string, default: 'user', banner: "subject"
|
10
|
+
argument :arg_role, type: :string, default: 'role', banner: "role"
|
11
|
+
argument :arg_objects, type: :array, default: [], banner: "objects..."
|
12
|
+
|
13
|
+
def create_migration_file
|
14
|
+
migration_template "create_role_tables.rb", File.join(db_migrate_path, "create_#{role_name}_tables.rb")
|
15
|
+
end
|
16
|
+
|
17
|
+
def create_models
|
18
|
+
template "role.rb", "app/models/#{role_name}.rb"
|
19
|
+
|
20
|
+
objects.each do |object|
|
21
|
+
my_inject "app/models/#{object}.rb", object.classify, " #{object_helper}\n"
|
22
|
+
end
|
23
|
+
|
24
|
+
my_inject "app/models/#{subject_name}.rb", subject_class_name, " #{subject_helper}\n"
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_initializer
|
28
|
+
initializer "acl9.rb" do
|
29
|
+
<<-RUBY.strip_heredoc
|
30
|
+
# See https://github.com/be9/acl9#configuration for details
|
31
|
+
#
|
32
|
+
# Acl9.configure do |c|
|
33
|
+
# c.default_role_class_name = 'Role'
|
34
|
+
# c.default_subject_class_name = 'User'
|
35
|
+
# c.default_subject_method = :current_user
|
36
|
+
# c.default_association_name = :role_objects
|
37
|
+
# c.default_join_table_name = nil
|
38
|
+
# c.protect_global_roles = true
|
39
|
+
# c.normalize_role_names = true
|
40
|
+
# end
|
41
|
+
RUBY
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
def role_name
|
47
|
+
arg_role.underscore.singularize
|
48
|
+
end
|
49
|
+
|
50
|
+
def role_table_name
|
51
|
+
role_name.tableize
|
52
|
+
end
|
53
|
+
|
54
|
+
def role_class_name
|
55
|
+
role_name.classify
|
56
|
+
end
|
57
|
+
|
58
|
+
def model_base_name
|
59
|
+
r5? ? ApplicationRecord : ActiveRecord::Base
|
60
|
+
end
|
61
|
+
|
62
|
+
def r5?
|
63
|
+
Rails.gem_version >= Gem::Version.new(5)
|
64
|
+
end
|
65
|
+
|
66
|
+
def habtm_table
|
67
|
+
Acl9.config.default_join_table_name || [ subject_name, role_name ].map(&:pluralize).sort.join('_')
|
68
|
+
end
|
69
|
+
|
70
|
+
def subject_helper
|
71
|
+
"acts_as_authorization_subject" + ( subject_options ? " #{subject_options}" : '' )
|
72
|
+
end
|
73
|
+
|
74
|
+
def object_helper
|
75
|
+
"acts_as_authorization_object" + ( object_options ? " #{object_options}" : '' )
|
76
|
+
end
|
77
|
+
|
78
|
+
def role_helper
|
79
|
+
"acts_as_authorization_role" + ( role_options ? " #{role_options}" : '' )
|
80
|
+
end
|
81
|
+
|
82
|
+
def my_inject file_name, class_name, string
|
83
|
+
inject_into_class file_name, class_name, string
|
84
|
+
rescue Errno::ENOENT
|
85
|
+
create_file file_name do
|
86
|
+
<<-RUBY.strip_heredoc
|
87
|
+
class #{class_name} < ActiveRecord::Base
|
88
|
+
#{string}
|
89
|
+
end
|
90
|
+
RUBY
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def role_options
|
95
|
+
if defined?(Acl9::config) && Acl9::config[:default_subject_class_name].to_s.classify != subject_class_name
|
96
|
+
"subject_class_name: #{subject_class_name}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def subject_options
|
101
|
+
if defined?(Acl9::config) && Acl9::config[:default_role_class_name].to_s.classify != role_class_name
|
102
|
+
"role_class_name: #{role_class_name}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def object_options
|
107
|
+
[ role_options, subject_options ].compact.join ', '
|
108
|
+
end
|
109
|
+
|
110
|
+
def subject_name
|
111
|
+
@subject_name ||= arg_subject.underscore.singularize
|
112
|
+
end
|
113
|
+
|
114
|
+
def objects
|
115
|
+
@objects ||= arg_objects.map{|o|o.underscore.singularize}
|
116
|
+
end
|
117
|
+
|
118
|
+
def subject_class_name
|
119
|
+
subject_name.classify
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Create<%= role_class_name %>Tables < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
2
|
+
def change
|
3
|
+
create_table :<%= role_table_name %> do |t|
|
4
|
+
t.string :name, null: false
|
5
|
+
<% if r5? %>
|
6
|
+
t.references :authorizable, polymorphic: true
|
7
|
+
<% else %>
|
8
|
+
t.string :authorizable_type, null: true
|
9
|
+
t.integer :authorizable_id, null: true
|
10
|
+
<% end %>
|
11
|
+
t.boolean :system, default: false, null: false
|
12
|
+
t.timestamps null: false
|
13
|
+
end
|
14
|
+
|
15
|
+
add_index :<%= role_table_name %>, :name
|
16
|
+
|
17
|
+
<% unless r5? %>
|
18
|
+
add_index :<%= role_table_name %>, [:authorizable_type, :authorizable_id]
|
19
|
+
<% end -%>
|
20
|
+
|
21
|
+
create_table :<%= habtm_table %>, id: false do |t|
|
22
|
+
t.references :<%= subject_name %>, null: false
|
23
|
+
t.references :<%= role_name %>, null: false
|
24
|
+
end
|
25
|
+
|
26
|
+
<% unless r5? %>
|
27
|
+
add_index :<%= habtm_table %>, :<%= subject_name %>_id
|
28
|
+
add_index :<%= habtm_table %>, :<%= role_name %>_id
|
29
|
+
<% end %>
|
30
|
+
end
|
31
|
+
end
|