strongbolt 0.3.12 → 0.3.13
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +56 -0
- data/.rubocop_todo.yml +91 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +18 -2
- data/Rakefile +1 -1
- data/app/controllers/strongbolt/capabilities_controller.rb +36 -45
- data/app/controllers/strongbolt/roles_controller.rb +39 -47
- data/app/controllers/strongbolt/security_controller.rb +2 -3
- data/app/controllers/strongbolt/user_groups_controller.rb +48 -54
- data/app/controllers/strongbolt/user_groups_users_controller.rb +2 -4
- data/app/controllers/strongbolt_controller.rb +1 -1
- data/circle.yml +13 -0
- data/lib/generators/strongbolt/fix_generator.rb +5 -6
- data/lib/generators/strongbolt/fix_unique_group_members_generator.rb +2 -3
- data/lib/generators/strongbolt/indexes_generator.rb +3 -4
- data/lib/generators/strongbolt/install_generator.rb +8 -9
- data/lib/generators/strongbolt/templates/fix_unique_group_members.rb +1 -1
- data/lib/generators/strongbolt/templates/indexes.rb +1 -1
- data/lib/generators/strongbolt/templates/migration.rb +11 -12
- data/lib/generators/strongbolt/templates/strongbolt.rb +1 -1
- data/lib/generators/strongbolt/views_generator.rb +4 -4
- data/lib/strongbolt.rb +51 -54
- data/lib/strongbolt/base.rb +1 -1
- data/lib/strongbolt/bolted.rb +12 -13
- data/lib/strongbolt/bolted_controller.rb +46 -57
- data/lib/strongbolt/capabilities_role.rb +5 -5
- data/lib/strongbolt/capability.rb +32 -31
- data/lib/strongbolt/configuration.rb +18 -19
- data/lib/strongbolt/controllers/url_helpers.rb +5 -5
- data/lib/strongbolt/engine.rb +9 -9
- data/lib/strongbolt/errors.rb +4 -4
- data/lib/strongbolt/generators/migration.rb +4 -6
- data/lib/strongbolt/helpers.rb +5 -7
- data/lib/strongbolt/rails/routes.rb +4 -4
- data/lib/strongbolt/role.rb +11 -12
- data/lib/strongbolt/roles_user_group.rb +5 -5
- data/lib/strongbolt/rspec.rb +2 -2
- data/lib/strongbolt/rspec/user.rb +13 -15
- data/lib/strongbolt/tenantable.rb +78 -80
- data/lib/strongbolt/user_abilities.rb +44 -54
- data/lib/strongbolt/user_group.rb +8 -10
- data/lib/strongbolt/user_groups_user.rb +6 -6
- data/lib/strongbolt/version.rb +1 -1
- data/lib/tasks/strongbolt_tasks.rake +4 -4
- data/spec/controllers/strongbolt/capabilities_controller_spec.rb +28 -45
- data/spec/controllers/strongbolt/roles_controller_spec.rb +39 -72
- data/spec/controllers/strongbolt/user_groups_controller_spec.rb +34 -65
- data/spec/controllers/strongbolt/user_groups_users_controller_spec.rb +11 -19
- data/spec/controllers/without_authorization_controller_spec.rb +5 -5
- data/spec/dummy/app/controllers/posts_controller.rb +2 -2
- data/spec/dummy/app/controllers/test_controller.rb +1 -1
- data/spec/dummy/app/controllers/without_authorization_controller.rb +1 -1
- data/spec/dummy/bin/rails +1 -1
- data/spec/dummy/config.ru +1 -1
- data/spec/dummy/config/application.rb +4 -5
- data/spec/dummy/config/initializers/cookies_serializer.rb +1 -1
- data/spec/dummy/config/initializers/strongbolt.rb +2 -2
- data/spec/dummy/config/routes.rb +1 -3
- data/spec/dummy/db/migrate/20150630212236_create_strongbolt_tables.rb +9 -10
- data/spec/dummy/db/migrate/20150630212251_create_strongbolt_tables_indexes.rb +2 -2
- data/spec/dummy/db/migrate/20160531110509_fix_unique_group_members.rb +1 -1
- data/spec/fabricators/capability_fabricator.rb +4 -4
- data/spec/fabricators/role_fabricator.rb +3 -3
- data/spec/fabricators/user_fabricator.rb +2 -2
- data/spec/fabricators/user_group_fabricator.rb +3 -3
- data/spec/fixtures/application.rb +6 -3
- data/spec/fixtures/controllers.rb +1 -1
- data/spec/spec_helper.rb +7 -8
- data/spec/strongbolt/bolted_controller_spec.rb +110 -208
- data/spec/strongbolt/bolted_spec.rb +26 -40
- data/spec/strongbolt/capability_spec.rb +72 -86
- data/spec/strongbolt/configuration_spec.rb +33 -46
- data/spec/strongbolt/controllers/url_helpers_spec.rb +7 -9
- data/spec/strongbolt/helpers_spec.rb +14 -16
- data/spec/strongbolt/role_spec.rb +32 -35
- data/spec/strongbolt/tenantable_spec.rb +88 -86
- data/spec/strongbolt/user_abilities_multiple_tenants_spec.rb +29 -34
- data/spec/strongbolt/user_abilities_spec.rb +142 -188
- data/spec/strongbolt/user_group_spec.rb +14 -14
- data/spec/strongbolt/users_tenant_spec.rb +10 -12
- data/spec/strongbolt_spec.rb +53 -73
- data/spec/support/controller_macros.rb +1 -3
- data/spec/support/db_setup.rb +31 -25
- data/spec/support/helpers.rb +12 -12
- data/spec/support/transactional_specs.rb +1 -3
- data/strongbolt.gemspec +14 -12
- metadata +20 -3
@@ -1,8 +1,9 @@
|
|
1
1
|
module Strongbolt
|
2
2
|
module Tenantable
|
3
3
|
module ClassMethods
|
4
|
-
|
5
|
-
|
4
|
+
def tenant?
|
5
|
+
(@tenant.present? && @tenant) || Strongbolt.tenants.include?(name)
|
6
|
+
end
|
6
7
|
|
7
8
|
private
|
8
9
|
|
@@ -12,6 +13,7 @@ module Strongbolt
|
|
12
13
|
def singular_association_name
|
13
14
|
@singular_association_name ||= self.name.demodulize.underscore.to_sym
|
14
15
|
end
|
16
|
+
|
15
17
|
def plural_association_name
|
16
18
|
@plural_association_name ||= self.name.demodulize.underscore.pluralize.to_sym
|
17
19
|
end
|
@@ -21,21 +23,21 @@ module Strongbolt
|
|
21
23
|
# It will traverse all the has_many relationships
|
22
24
|
# and add a has_one :tenant if not specified
|
23
25
|
#
|
24
|
-
def tenant
|
26
|
+
def tenant(_opts = {})
|
25
27
|
# Stops if already configured
|
26
28
|
return if tenant?
|
27
29
|
|
28
|
-
Strongbolt.logger.debug "-------------------------------------------------------------------\n"
|
29
|
-
|
30
|
-
|
30
|
+
Strongbolt.logger.debug "-------------------------------------------------------------------\n" \
|
31
|
+
"Configuring tenant #{self.name}\n" \
|
32
|
+
"-------------------------------------------------------------------\n\n"
|
31
33
|
#
|
32
34
|
# We're traversing using BFS the relationships
|
33
35
|
#
|
34
36
|
# Keep track of traversed models and their relationship to the tenant
|
35
|
-
@models_traversed = {self.name => self}
|
37
|
+
@models_traversed = { self.name => self }
|
36
38
|
# File of models/associations to traverse
|
37
39
|
models_to_traverse = reflect_on_all_associations
|
38
|
-
|
40
|
+
until models_to_traverse.empty?
|
39
41
|
# BFS search, shiftin first elt of array (older)
|
40
42
|
current_association = models_to_traverse.shift
|
41
43
|
# We don't check has_many :through association,
|
@@ -46,15 +48,14 @@ module Strongbolt
|
|
46
48
|
# So unless we've already traversed this model, or that's a through relationship
|
47
49
|
# or a polymorphic association
|
48
50
|
# Also we don't go following belongs_to relationship, it becomes crazy
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
51
|
+
next unless should_visit? current_association
|
52
|
+
# We setup the model using the association given
|
53
|
+
method = setup_model(current_association)
|
54
|
+
# We flag the model, storing the name of the method used to link to tenant
|
55
|
+
@models_traversed[current_association.klass.name] = method
|
56
|
+
# And add its relationships into the array, at the end, if method not nil
|
57
|
+
if method.present?
|
58
|
+
models_to_traverse.concat current_association.klass.reflect_on_all_associations
|
58
59
|
end
|
59
60
|
end
|
60
61
|
|
@@ -71,7 +72,7 @@ module Strongbolt
|
|
71
72
|
# Setup a model and returns the method name in symbol of the
|
72
73
|
# implemented link to the tenant
|
73
74
|
#
|
74
|
-
def setup_model
|
75
|
+
def setup_model(association)
|
75
76
|
# Source class
|
76
77
|
original_class = association.active_record
|
77
78
|
# Current class
|
@@ -104,11 +105,10 @@ module Strongbolt
|
|
104
105
|
# If no inverse, we cannot go further
|
105
106
|
if inverse.nil?
|
106
107
|
raise InverseAssociationNotConfigured, "Assocation #{association.name} on #{association.klass.name} could not be configured correctly as no inverse has been found"
|
107
|
-
elsif inverse.options.
|
108
|
+
elsif inverse.options.key? :polymorphic
|
108
109
|
return nil
|
109
110
|
end
|
110
111
|
|
111
|
-
|
112
112
|
# Common options
|
113
113
|
options = {
|
114
114
|
through: inverse.name,
|
@@ -145,22 +145,21 @@ module Strongbolt
|
|
145
145
|
scope "with_#{plur}", -> { includes assoc }
|
146
146
|
|
147
147
|
scope "where_#{plur}_among", ->(values) do
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
148
|
+
if values.is_a? Array
|
149
|
+
# If objects
|
150
|
+
values = values.map(&:id) if values.first.respond_to? :id
|
151
|
+
elsif values.respond_to?(:id)
|
152
|
+
# If object
|
153
|
+
values = values.id
|
154
|
+
end
|
155
|
+
|
156
|
+
includes(assoc).where(table_name => { id: values })
|
157
|
+
end
|
158
158
|
end
|
159
159
|
|
160
160
|
# And return name of association
|
161
|
-
|
162
|
-
end
|
163
|
-
|
161
|
+
assoc
|
162
|
+
end # /setup_model
|
164
163
|
|
165
164
|
#
|
166
165
|
# The initial idea of using a polymorphic association on UsersTenant
|
@@ -196,50 +195,48 @@ module Strongbolt
|
|
196
195
|
RUBY
|
197
196
|
Strongbolt.const_set users_tenants_subclass_name, users_tenant_subclass
|
198
197
|
end
|
199
|
-
end
|
198
|
+
end # /create_users_tenant_subclass
|
200
199
|
|
201
200
|
#
|
202
201
|
# Setups the has_many thru association on the User class
|
203
202
|
#
|
204
203
|
def setup_association_on_user
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
end
|
204
|
+
user_class = Configuration.user_class.constantize
|
205
|
+
|
206
|
+
# Setup the association
|
207
|
+
# The first one should never be there before
|
208
|
+
user_class.has_many :"users_#{plural_association_name}",
|
209
|
+
class_name: "Strongbolt::#{users_tenants_subclass_name}",
|
210
|
+
inverse_of: :user,
|
211
|
+
dependent: :delete_all,
|
212
|
+
foreign_key: :user_id
|
213
|
+
|
214
|
+
# This one may have been overriden by the developer
|
215
|
+
unless user_class.respond_to? plural_association_name
|
216
|
+
user_class.has_many plural_association_name,
|
217
|
+
source: :"#{singular_association_name}",
|
218
|
+
class_name: self.name,
|
219
|
+
through: :"users_#{plural_association_name}"
|
220
|
+
end
|
223
221
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
end
|
222
|
+
# Setup a quick method to get accessible clients directly
|
223
|
+
unless user_class.respond_to? "accessible_#{plural_association_name}"
|
224
|
+
user_class.class_exec(self, plural_association_name) do |klass, plur|
|
225
|
+
define_method "accessible_#{plur}" do
|
226
|
+
# If can find ALL the tenants
|
227
|
+
if can? :find, klass, :any, true
|
228
|
+
# Then it can access all of them
|
229
|
+
klass.all
|
230
|
+
else
|
231
|
+
# Otherwise, only the ones he manages
|
232
|
+
send plur
|
236
233
|
end
|
237
234
|
end
|
238
235
|
end
|
239
|
-
rescue NameError => e
|
240
|
-
Strongbolt.logger.error "User #{Configuration.user_class} could not have his association to tenant #{name} created"
|
241
236
|
end
|
242
|
-
|
237
|
+
rescue NameError
|
238
|
+
Strongbolt.logger.error "User #{Configuration.user_class} could not have his association to tenant #{name} created"
|
239
|
+
end # /setup_association_on_user
|
243
240
|
|
244
241
|
#
|
245
242
|
# returns the name of the subclass of Strongbolt::UsersTenant, containing
|
@@ -251,13 +248,13 @@ module Strongbolt
|
|
251
248
|
# The module name is left in the name, so that we won't have any collisions
|
252
249
|
# with the same class names in different modules.
|
253
250
|
"Users#{self.name.gsub('::', '')}"
|
254
|
-
end
|
251
|
+
end # /users_tenants_subclass_name
|
255
252
|
|
256
253
|
#
|
257
254
|
# Returns the inverse of specified association, using what's given
|
258
255
|
# as inverse_of or trying to guess it
|
259
256
|
#
|
260
|
-
def inverse_of
|
257
|
+
def inverse_of(association)
|
261
258
|
# If specified in association configuration
|
262
259
|
return association.inverse_of if association.has_inverse?
|
263
260
|
|
@@ -266,15 +263,17 @@ module Strongbolt
|
|
266
263
|
# Else we need to find it, using the class as reference
|
267
264
|
association.klass.reflect_on_all_associations.each do |assoc|
|
268
265
|
# If the association is polymorphic
|
269
|
-
if assoc.options.
|
266
|
+
if assoc.options.key? :polymorphic
|
267
|
+
if association.options && association.options[:as] == assoc.name
|
268
|
+
return assoc
|
269
|
+
end
|
270
270
|
polymorphic_associations << assoc
|
271
|
-
|
272
271
|
# If same class than the original source of the association
|
273
272
|
elsif assoc.klass == association.active_record
|
274
273
|
|
275
|
-
Strongbolt.logger.debug "Selected inverse of #{association.name} between #{association.active_record} "
|
276
|
-
|
277
|
-
|
274
|
+
Strongbolt.logger.debug "Selected inverse of #{association.name} between #{association.active_record} " \
|
275
|
+
"and #{association.klass} is #{assoc.name}.\n " \
|
276
|
+
"If not, please configure manually the inverse of #{association.name}\n"
|
278
277
|
|
279
278
|
return assoc
|
280
279
|
end
|
@@ -284,7 +283,7 @@ module Strongbolt
|
|
284
283
|
return polymorphic_associations.first
|
285
284
|
end
|
286
285
|
|
287
|
-
|
286
|
+
nil
|
288
287
|
end
|
289
288
|
|
290
289
|
#
|
@@ -296,13 +295,12 @@ module Strongbolt
|
|
296
295
|
# and not HasManyThrough, unless it's AR v >= 4.1.0 && < 4.2.0 where
|
297
296
|
# they define a HasManyAndBelongsTo as a HasManyThrough in the reflections
|
298
297
|
#
|
299
|
-
def should_visit?
|
300
|
-
!
|
298
|
+
def should_visit?(association)
|
299
|
+
!(association.is_a?(ActiveRecord::Reflection::ThroughReflection) ||
|
301
300
|
association.macro == :belongs_to ||
|
302
|
-
(@models_traversed.
|
303
|
-
@models_traversed[association.klass.name].present?)
|
301
|
+
(@models_traversed.key?(association.klass.name) &&
|
302
|
+
@models_traversed[association.klass.name].present?))
|
304
303
|
end
|
305
|
-
|
306
304
|
end
|
307
305
|
|
308
306
|
module InstanceMethods
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module Strongbolt
|
2
2
|
module UserAbilities
|
3
3
|
module ClassMethods
|
4
|
-
|
5
4
|
end
|
6
5
|
|
7
6
|
module InstanceMethods
|
@@ -12,16 +11,16 @@ module Strongbolt
|
|
12
11
|
#----------------------------------------------------------#
|
13
12
|
def capabilities
|
14
13
|
@capabilities_cache ||= Strongbolt::Capability.unscoped.joins(:roles)
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
.joins('INNER JOIN strongbolt_roles as children_roles ON strongbolt_roles.lft <= children_roles.lft AND children_roles.rgt <= strongbolt_roles.rgt')
|
15
|
+
.joins('INNER JOIN strongbolt_roles_user_groups rug ON rug.role_id = children_roles.id')
|
16
|
+
.joins('INNER JOIN strongbolt_user_groups_users ugu ON ugu.user_group_id = rug.user_group_id')
|
17
|
+
.where('ugu.user_id = ?', self.id).distinct.to_a.concat(Strongbolt.default_capabilities)
|
19
18
|
end
|
20
19
|
|
21
20
|
#
|
22
21
|
# Adds a managed tenant to the user
|
23
22
|
#
|
24
|
-
def add_tenant
|
23
|
+
def add_tenant(tenant)
|
25
24
|
sing_tenant_name = tenant.class.name.demodulize.underscore
|
26
25
|
send("users_#{sing_tenant_name.pluralize}").create! sing_tenant_name => tenant
|
27
26
|
# users_tenants.create! tenant: tenant
|
@@ -31,17 +30,16 @@ module Strongbolt
|
|
31
30
|
# Main method for user, used to check whether the user
|
32
31
|
# is authorized to perform a certain action on an instance/class
|
33
32
|
#
|
34
|
-
def can?
|
33
|
+
def can?(action, instance, attrs = :any, all_instance = false)
|
35
34
|
without_grant do
|
36
|
-
|
37
35
|
# Get the actual instance if we were given AR
|
38
36
|
instance = instance.try(:first) if instance.is_a?(ActiveRecord::Relation)
|
39
37
|
return false if instance.nil?
|
40
38
|
|
41
39
|
# We require this to be an *existing* user, that the action and attribute be symbols
|
42
40
|
# and that the instance is a class or a String
|
43
|
-
raise ArgumentError,
|
44
|
-
|
41
|
+
raise ArgumentError, 'Action must be a symbol and instance must be Class, String, Symbol or AR' unless self.id.present? && action.is_a?(Symbol) &&
|
42
|
+
(instance.is_a?(ActiveRecord::Base) || instance.is_a?(Class) || instance.is_a?(String)) && attrs.is_a?(Symbol)
|
45
43
|
|
46
44
|
# Pre-populate all the capabilities into a results cache for quick lookup. Permissions for all "non-owned" objects are
|
47
45
|
# immediately available; additional lookups are required for owned objects (e.g. User, CheckoutBag, etc.).
|
@@ -58,35 +56,33 @@ module Strongbolt
|
|
58
56
|
model_name = model.send(:name_for_authorization)
|
59
57
|
else
|
60
58
|
model = nil # We could do model_name.constantize, but there's a big cost to doing this
|
61
|
-
|
59
|
+
# if we don't need it, so just defer until we determine there's an actual need
|
62
60
|
model_name = instance
|
63
61
|
end
|
64
62
|
|
65
63
|
# Look up the various possible valid entries in the cache that would allow us to see this
|
66
64
|
return capability_in_cache?(action, instance, model_name, attrs, all_instance)
|
67
|
-
|
68
|
-
end #end w/o grant
|
65
|
+
end # end w/o grant
|
69
66
|
end
|
70
67
|
|
71
68
|
#
|
72
69
|
# Convenient method
|
73
70
|
#
|
74
|
-
def cannot?
|
75
|
-
!can?
|
71
|
+
def cannot?(*args)
|
72
|
+
!can?(*args)
|
76
73
|
end
|
77
74
|
|
78
75
|
#
|
79
76
|
# Checks if the user owns the instance given
|
80
77
|
#
|
81
|
-
def owns?
|
78
|
+
def owns?(instance)
|
82
79
|
raise ArgumentError unless instance.is_a?(Object) && !instance.is_a?(Class)
|
83
80
|
# If the user id is set, does this (a) user id match the user_id field of the instance
|
84
81
|
# or (b) if this is a User instance, does the user id match the instance id?
|
85
82
|
key = instance.is_a?(User) ? :id : :user_id
|
86
|
-
|
83
|
+
!id.nil? && instance.try(key) == id
|
87
84
|
end
|
88
85
|
|
89
|
-
|
90
86
|
private
|
91
87
|
|
92
88
|
#
|
@@ -99,14 +95,13 @@ module Strongbolt
|
|
99
95
|
@model_ancestor_cache ||= {}
|
100
96
|
|
101
97
|
# User can find itself by default
|
102
|
-
@results_cache[
|
98
|
+
@results_cache['findUserany-any'] = true
|
103
99
|
@results_cache["findUserany-#{id}"] = true
|
104
100
|
|
105
101
|
#
|
106
102
|
# Store every capability fetched
|
107
103
|
#
|
108
104
|
capabilities.each do |capability|
|
109
|
-
|
110
105
|
k = "#{capability.action}#{capability.model}"
|
111
106
|
attr_k = capability.attr || 'all'
|
112
107
|
|
@@ -137,14 +132,11 @@ module Strongbolt
|
|
137
132
|
end
|
138
133
|
end # End each capability
|
139
134
|
|
140
|
-
Strongbolt.logger.info "Populated capabilities in #{(Time.now - beginning)*1000}ms"
|
135
|
+
Strongbolt.logger.info "Populated capabilities in #{(Time.now - beginning) * 1000}ms"
|
141
136
|
|
142
137
|
@results_cache
|
143
138
|
end # End Populate capabilities Cache
|
144
139
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
140
|
#----------------------------------------------------------#
|
149
141
|
# #
|
150
142
|
# Checks if the user can perform 'action' on 'instance' #
|
@@ -154,20 +146,20 @@ module Strongbolt
|
|
154
146
|
def capability_in_cache?(action, instance, model_name, attrs = :any, all_instance = false)
|
155
147
|
action_model = "#{action}#{model_name}"
|
156
148
|
|
157
|
-
Strongbolt.logger.warn
|
149
|
+
Strongbolt.logger.warn 'User has no results cache' if @results_cache.empty?
|
158
150
|
Strongbolt.logger.debug { "Authorizing user to perform #{action} on #{instance.inspect}" }
|
159
151
|
|
160
152
|
# we don't know or care about tenants or if this is a new record
|
161
153
|
if instance.is_a?(ActiveRecord::Base) && !instance.new_record?
|
162
154
|
# First, check if we have a hash/cache hit for User being able to do this action to every instance of the model/class
|
163
|
-
return true if @results_cache["#{action_model}all-all"]
|
164
|
-
return true if @results_cache["#{action_model}#{attrs}-all"]
|
155
|
+
return true if @results_cache["#{action_model}all-all"] # Access to all attributes on ENTIRE class?
|
156
|
+
return true if @results_cache["#{action_model}#{attrs}-all"] # Access to this specific attribute on ENTIRE class?
|
165
157
|
|
166
158
|
# If we're checking on a specific instance of the class, not the general model,
|
167
159
|
# append the id to the key
|
168
160
|
id = instance.try(:id)
|
169
161
|
return true if @results_cache["#{action_model}all-#{id}"] # Access to all this instance's attributes?
|
170
|
-
return true if @results_cache["#{action_model}#{attrs}-#{id}"] #Access to this instance's attribute?
|
162
|
+
return true if @results_cache["#{action_model}#{attrs}-#{id}"] # Access to this instance's attribute?
|
171
163
|
|
172
164
|
# Checking ownership and tenant access
|
173
165
|
# Block access for non tenanted instance
|
@@ -187,30 +179,30 @@ module Strongbolt
|
|
187
179
|
end
|
188
180
|
|
189
181
|
# Finally we check for tenanted instances
|
190
|
-
@results_cache["#{action_model}all-#{id}"] = @results_cache["#{action_model}all-tenanted"] && valid_tenants
|
191
|
-
@results_cache["#{action_model}#{attrs}-#{id}"] =
|
182
|
+
@results_cache["#{action_model}all-#{id}"] = @results_cache["#{action_model}all-tenanted"] && valid_tenants # Access to all attributes on tenanted class?
|
183
|
+
@results_cache["#{action_model}#{attrs}-#{id}"] = @results_cache["#{action_model}#{attrs}-tenanted"] && valid_tenants # Access to this specific attribute on tenanted class?
|
192
184
|
return true if @results_cache["#{action_model}all-#{id}"] || @results_cache["#{action_model}#{attrs}-#{id}"]
|
193
185
|
elsif instance.is_a?(ActiveRecord::Base) && instance.new_record?
|
194
|
-
return true if @results_cache["#{action_model}all-all"]
|
195
|
-
return true if @results_cache["#{action_model}#{attrs}-all"]
|
186
|
+
return true if @results_cache["#{action_model}all-all"] # Access to all attributes on ENTIRE class?
|
187
|
+
return true if @results_cache["#{action_model}#{attrs}-all"] # Access to this specific attribute on ENTIRE class?
|
196
188
|
# Checking if the instance is from valid tenants (if necessary)
|
197
189
|
valid_tenants = has_access_to_tenants?(instance)
|
198
|
-
return true if @results_cache["#{action_model}all-tenanted"] && valid_tenants
|
199
|
-
return true if @results_cache["#{action_model}#{attrs}-tenanted"] && valid_tenants #Access to this specific attribute on tenanted class?
|
190
|
+
return true if @results_cache["#{action_model}all-tenanted"] && valid_tenants # Access to all attributes on tenanted class?
|
191
|
+
return true if @results_cache["#{action_model}#{attrs}-tenanted"] && valid_tenants # Access to this specific attribute on tenanted class?
|
200
192
|
|
201
193
|
# Finally, in the case where it's a non tenanted model (it still need to have valid_tenants == true)
|
202
194
|
return true if @results_cache["#{action_model}all-any"] && valid_tenants
|
203
195
|
return true if @results_cache["#{action_model}#{attrs}-any"] && valid_tenants
|
204
196
|
else
|
205
197
|
# First, check if we have a hash/cache hit for User being able to do this action to every instance of the model/class
|
206
|
-
return true if @results_cache["#{action_model}all-all"]
|
207
|
-
return true if @results_cache["#{action_model}#{attrs}-all"]
|
208
|
-
return true if @results_cache["#{action_model}all-any"] && !
|
209
|
-
return true if @results_cache["#{action_model}#{attrs}-any"] && !
|
198
|
+
return true if @results_cache["#{action_model}all-all"] # Access to all attributes on ENTIRE class?
|
199
|
+
return true if @results_cache["#{action_model}#{attrs}-all"] # Access to this specific attribute on ENTIRE class?
|
200
|
+
return true if @results_cache["#{action_model}all-any"] && !all_instance # Access to all attributes on at least once instance?
|
201
|
+
return true if @results_cache["#{action_model}#{attrs}-any"] && !all_instance # Access to this specific attribute on at least once instance?
|
210
202
|
end
|
211
|
-
#logger.info "Cache miss for checking access to #{key}"
|
203
|
+
# logger.info "Cache miss for checking access to #{key}"
|
212
204
|
|
213
|
-
|
205
|
+
false
|
214
206
|
end
|
215
207
|
|
216
208
|
#
|
@@ -218,7 +210,7 @@ module Strongbolt
|
|
218
210
|
#
|
219
211
|
# returns true even if instance has no relationship to any tenant
|
220
212
|
#
|
221
|
-
def has_access_to_tenants?
|
213
|
+
def has_access_to_tenants?(instance, tenants = nil)
|
222
214
|
# If no tenants list given, we take all
|
223
215
|
tenants ||= Strongbolt.tenants
|
224
216
|
# Populate the cache if needed
|
@@ -232,11 +224,11 @@ module Strongbolt
|
|
232
224
|
if instance.class == tenant
|
233
225
|
tenant_ids = [instance.id]
|
234
226
|
elsif instance.respond_to?(tenant.send(:singular_association_name))
|
235
|
-
if instance.send(tenant.send(:singular_association_name)).present?
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
227
|
+
tenant_ids = if instance.send(tenant.send(:singular_association_name)).present?
|
228
|
+
[instance.send(tenant.send(:singular_association_name)).id]
|
229
|
+
else
|
230
|
+
[]
|
231
|
+
end
|
240
232
|
elsif instance.respond_to?(tenant.send(:plural_association_name))
|
241
233
|
tenant_ids = instance.send("#{tenant.send(:singular_association_name)}_ids")
|
242
234
|
else
|
@@ -249,7 +241,7 @@ module Strongbolt
|
|
249
241
|
tenant_ids = []
|
250
242
|
end
|
251
243
|
found_any_tenant_relationship = true unless tenant_ids.empty?
|
252
|
-
has_access_to_current_tenant = (tenant_ids.
|
244
|
+
has_access_to_current_tenant = (!tenant_ids.empty? && (@tenants_cache[tenant.name] & tenant_ids).present?)
|
253
245
|
result || has_access_to_current_tenant
|
254
246
|
end
|
255
247
|
has_access_to_any_tenant || !found_any_tenant_relationship
|
@@ -270,8 +262,6 @@ module Strongbolt
|
|
270
262
|
Strongbolt.logger.debug "#{@tenants_cache[tenant.name].size} #{tenant.name}"
|
271
263
|
end
|
272
264
|
end
|
273
|
-
|
274
|
-
|
275
265
|
end # End InstanceMethods
|
276
266
|
|
277
267
|
def self.included(receiver)
|
@@ -280,11 +270,11 @@ module Strongbolt
|
|
280
270
|
|
281
271
|
receiver.class_eval do
|
282
272
|
has_many :user_groups_users,
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
has_many :user_groups, :
|
273
|
+
class_name: 'Strongbolt::UserGroupsUser',
|
274
|
+
dependent: :delete_all,
|
275
|
+
inverse_of: :user,
|
276
|
+
foreign_key: :user_id
|
277
|
+
has_many :user_groups, through: :user_groups_users
|
288
278
|
|
289
279
|
has_many :roles, through: :user_groups
|
290
280
|
end
|