Class: ActiveRecord::Base
- Inherits:
-
Object
show all
- Extended by:
- ClassMethods
- Includes:
- InstanceMethods
- Defined in:
- lib/active_record_base.rb,
lib/active_record_base.rb,
lib/reactive_record/permissions.rb,
lib/reactive_record/active_record/base.rb,
lib/reactive_record/active_record/aggregations.rb,
lib/reactive_record/active_record/associations.rb,
lib/reactive_record/active_record/public_columns_hash.rb
Overview
adds method to get the HyperMesh public column types this works because the public folder is currently required to be eager loaded.
Constant Summary
ClassMethods::SERVER_METHODS
Class Attribute Summary collapse
Instance Attribute Summary collapse
#backing_record
Class Method Summary
collapse
-
.__secure_remote_access_to_all(_self, _acting_user) ⇒ Object
Here we set up the base ‘all` and `unscoped` methods.
-
.__secure_remote_access_to_find(_self, _acting_user, *args) ⇒ Object
add secure access for find, find_by, and belongs_to and has_one relations.
-
.__secure_remote_access_to_find_by(_self, _acting_user, *args) ⇒ Object
-
.__secure_remote_access_to_unscoped(_self, _acting_user, *args) ⇒ Object
-
.__set_synchromesh_permission_granted(old_rel, new_rel, obj, acting_user, args = [], &block) ⇒ Object
helper method to set the value of __synchromesh_permission_granted on the relationship Set acting_user on the object, then or in the result of running the block in context of the obj with the current value of __synchromesh_permission_granted.
-
.__synchromesh_parse_regulator_params(name, block) ⇒ Object
We allow three forms: regulate_xxx name &block : the block is the regulation regulate_xxx name: const : const can be denied!, deny, denied, or any other truthy or falsy value regulate_xxx name: proc : the proc is the regulation.
-
.__synchromesh_regulate_from_macro(opts, name, already_defined) ⇒ Object
helper method for providing a regulation in line with a scope or relationship this is done using the ‘regulate` key on the opts.
-
._synchromesh_scope_args_check(args) ⇒ Object
-
.belongs_to(attr_name, *args) ⇒ Object
-
.belongs_to_without_reactive_record_add_is_method ⇒ Object
-
.default_scope(*args, &block) ⇒ Object
-
.denied! ⇒ Object
The wrapper method may simply return the normal result or may act to secure the data.
-
.do_not_synchronize ⇒ Object
call do_not_synchronize to block synchronization of a model.
-
.do_not_synchronize? ⇒ Boolean
used by the broadcast mechanism to determine if this model is to be synchronized.
-
.finder_method(name, &block) ⇒ Object
For finder_method we have to preapply ‘all` so that we always have a relationship.
-
.has_many(name, *args, &block) ⇒ Object
-
.pre_synchromesh_default_scope ⇒ Object
-
.pre_synchromesh_scope ⇒ Object
monkey patch scope and default_scope macros to process hyperloop special opts, and add regulations if present.
-
.pre_syncromesh_has_many ⇒ Object
-
.public_columns_hash ⇒ Object
-
.public_columns_hash_as_json ⇒ Object
-
.reflect_on_aggregation(attribute) ⇒ Object
-
.reflect_on_all_aggregations ⇒ Object
-
.reflect_on_all_associations ⇒ Object
-
.reflect_on_association(attr) ⇒ Object
-
.reflect_on_association_by_foreign_key(key) ⇒ Object
-
.reflection_finder(&block) ⇒ Object
-
.regulate_default_scope(*args, &block) ⇒ Object
-
.regulate_relationship(name, &block) ⇒ Object
add regulate_relationship method and monkey patch has_many macro to add regulations if present.
-
.regulate_scope(name, &block) ⇒ Object
regulate scope has to deal with the special case that the scope returns an an array instead of a relationship.
-
.scope(name, *args, &block) ⇒ Object
-
.server_method(name, _opts = {}, &block) ⇒ Object
Instance Method Summary
collapse
_all_filter, _attribute_aliases, _dealias_attribute, _new_without_sti_type_cast, _react_param_conversion, abstract_class=, abstract_class?, alias_attribute, all, base_class, column_names, columns_hash, composed_of, create, default_scope, define_attribute_methods, enum, find, find_by, finder_method, inheritance_column, inheritance_column=, method_missing, model_name, new, primary_key, primary_key=, scope, serialize, server_method, server_methods, unscoped
#<=>, #==, #[], #[]=, #attributes, #becomes, #becomes!, #cast_to_current_sti_type, #changed?, #changed_attributes, #changes, #destroy, #destroyed?, #dup, #errors, #id, #id=, #id?, #initialize, #inspect, #itself, #load, #model_name, #new?, #primary_key, #revert, #save, #saving?, #to_key, #update, #update_attribute, #valid?, #validate
Class Attribute Details
.reactive_record_association_keys ⇒ Object
Returns the value of attribute reactive_record_association_keys.
74
75
76
|
# File 'lib/reactive_record/permissions.rb', line 74
def reactive_record_association_keys
@reactive_record_association_keys
end
|
Instance Attribute Details
#acting_user ⇒ Object
Returns the value of attribute acting_user.
26
27
28
|
# File 'lib/reactive_record/permissions.rb', line 26
def acting_user
@acting_user
end
|
Class Method Details
.__secure_remote_access_to_all(_self, _acting_user) ⇒ Object
Here we set up the base ‘all` and `unscoped` methods. See below for more on how access protection works on relationships.
66
67
68
|
# File 'lib/active_record_base.rb', line 66
def __secure_remote_access_to_all(_self, _acting_user)
all
end
|
.__secure_remote_access_to_find(_self, _acting_user, *args) ⇒ Object
add secure access for find, find_by, and belongs_to and has_one relations. No explicit security checks are needed here, as the data returned by these objects will be further processedand checked before returning. I.e. it is not possible to simply return ‘find(1)` but if you try returning `find(1).name` the permission system will check to see if the name attribute can be legally sent to the current acting user.
259
260
261
|
# File 'lib/active_record_base.rb', line 259
def __secure_remote_access_to_find(_self, _acting_user, *args)
find(*args)
end
|
.__secure_remote_access_to_find_by(_self, _acting_user, *args) ⇒ Object
263
264
265
|
# File 'lib/active_record_base.rb', line 263
def __secure_remote_access_to_find_by(_self, _acting_user, *args)
find_by(*args)
end
|
.__secure_remote_access_to_unscoped(_self, _acting_user, *args) ⇒ Object
70
71
72
|
# File 'lib/active_record_base.rb', line 70
def __secure_remote_access_to_unscoped(_self, _acting_user, *args)
unscoped(*args)
end
|
.__set_synchromesh_permission_granted(old_rel, new_rel, obj, acting_user, args = [], &block) ⇒ Object
helper method to set the value of __synchromesh_permission_granted on the relationship Set acting_user on the object, then or in the result of running the block in context of the obj with the current value of __synchromesh_permission_granted
173
174
175
176
177
178
179
180
181
|
# File 'lib/active_record_base.rb', line 173
def __set_synchromesh_permission_granted(old_rel, new_rel, obj, acting_user, args = [], &block)
saved_acting_user = obj.acting_user
obj.acting_user = acting_user
new_rel.__synchromesh_permission_granted =
obj.instance_exec(*args, &block) || (old_rel && old_rel.try(:__synchromesh_permission_granted))
new_rel
ensure
obj.acting_user = saved_acting_user
end
|
.__synchromesh_parse_regulator_params(name, block) ⇒ Object
We allow three forms: regulate_xxx name &block : the block is the regulation regulate_xxx name: const : const can be denied!, deny, denied, or any other truthy or
falsy value
regulate_xxx name: proc : the proc is the regulation
141
142
143
144
145
146
147
148
149
150
151
152
|
# File 'lib/active_record_base.rb', line 141
def __synchromesh_parse_regulator_params(name, block)
if name.is_a? Hash
name, block = name.first
if %i[denied! deny denied].include? block
block = ->(*_args) { denied! }
elsif !block.is_a? Proc
value = block
block = ->(*_args) { value }
end
end
[name, block || ->(*_args) { true }]
end
|
.__synchromesh_regulate_from_macro(opts, name, already_defined) ⇒ Object
helper method for providing a regulation in line with a scope or relationship this is done using the ‘regulate` key on the opts. if no regulate key is provided and there is no regulation already defined for this name, then we create one that returns nil (don’t care) once we have things figured out, we yield to the provided proc which is either regulate_scope or regulate_relationship
161
162
163
164
165
166
167
|
# File 'lib/active_record_base.rb', line 161
def __synchromesh_regulate_from_macro(opts, name, already_defined)
if opts.key?(:regulate)
yield name => opts[:regulate]
elsif !already_defined
yield name => ->(*_args) {}
end
end
|
._synchromesh_scope_args_check(args) ⇒ Object
8
9
10
11
12
13
14
15
16
17
18
19
|
# File 'lib/active_record_base.rb', line 8
def self._synchromesh_scope_args_check(args)
opts = if args.count == 2 && args[1].is_a?(Hash)
args[1].merge(server: args[0])
elsif args[0].is_a? Hash
args[0]
else
{ server: args[0] }
end
return opts if opts && opts[:server].respond_to?(:call)
raise 'must provide either a proc as the first arg or by the '\
'`:server` option to scope and default_scope methods'
end
|
.belongs_to(attr_name, *args) ⇒ Object
90
91
92
93
94
95
96
|
# File 'lib/reactive_record/permissions.rb', line 90
def belongs_to(attr_name, *args)
belongs_to_without_reactive_record_add_is_method(attr_name, *args).tap do
define_method "#{attr_name}_is?".to_sym do |model|
self.class.reflections[attr_name].foreign_key == model.id
end
end
end
|
.belongs_to_without_reactive_record_add_is_method ⇒ Object
88
|
# File 'lib/reactive_record/permissions.rb', line 88
alias belongs_to_without_reactive_record_add_is_method belongs_to
|
.default_scope(*args, &block) ⇒ Object
219
220
221
222
223
224
225
226
227
|
# File 'lib/active_record_base.rb', line 219
def default_scope(*args, &block)
__synchromesh_regulate_from_macro(
(opts = _synchromesh_scope_args_check([*block, *args])),
:all,
respond_to?(:__secure_remote_access_to_all),
&method(:regulate_scope)
)
pre_synchromesh_default_scope(opts[:server], &block)
end
|
The wrapper method may simply return the normal result or may act to secure the data. The simpliest case is for the method to call ‘denied!` which will raise a Hyperloop access protection fault.
59
60
61
|
# File 'lib/active_record_base.rb', line 59
def denied!
Hyperloop::InternalPolicy.raise_operation_access_violation(:scoped_denied, "#{self} regulation denies scope access. Called from #{caller_locations(1)}")
end
|
.do_not_synchronize ⇒ Object
call do_not_synchronize to block synchronization of a model
284
285
286
|
# File 'lib/active_record_base.rb', line 284
def self.do_not_synchronize
@do_not_synchronize = true
end
|
.do_not_synchronize? ⇒ Boolean
used by the broadcast mechanism to determine if this model is to be synchronized
290
291
292
|
# File 'lib/active_record_base.rb', line 290
def self.do_not_synchronize?
@do_not_synchronize
end
|
.finder_method(name, &block) ⇒ Object
For finder_method we have to preapply ‘all` so that we always have a relationship
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
# File 'lib/active_record_base.rb', line 80
def finder_method(name, &block)
singleton_class.send(:define_method, :"__secure_remote_access_to__#{name}") do |this, acting_user, *args|
this = respond_to?(:acting_user) ? this : all
begin
old = this.acting_user
this.acting_user = acting_user
ReactiveRecordPsuedoRelationArray.new([this.instance_exec(*args, &block)])
ensure
this.acting_user = old
end
end
singleton_class.send(:define_method, name) do |*args|
all.instance_exec(*args, &block)
end
end
|
.has_many(name, *args, &block) ⇒ Object
243
244
245
246
247
248
249
250
251
|
# File 'lib/active_record_base.rb', line 243
def has_many(name, *args, &block)
__synchromesh_regulate_from_macro(
opts = args.,
name,
method_defined?(:"__secure_remote_access_to_#{name}"),
&method(:regulate_relationship)
)
pre_syncromesh_has_many name, *args, opts.except(:regulate), &block
end
|
.pre_synchromesh_default_scope ⇒ Object
217
|
# File 'lib/active_record_base.rb', line 217
alias pre_synchromesh_default_scope default_scope
|
.pre_synchromesh_scope ⇒ Object
monkey patch scope and default_scope macros to process hyperloop special opts, and add regulations if present
205
|
# File 'lib/active_record_base.rb', line 205
alias pre_synchromesh_scope scope
|
.pre_syncromesh_has_many ⇒ Object
241
|
# File 'lib/active_record_base.rb', line 241
alias pre_syncromesh_has_many has_many
|
.public_columns_hash ⇒ Object
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
# File 'lib/reactive_record/active_record/public_columns_hash.rb', line 9
def self.public_columns_hash
return @public_columns_hash if @public_columns_hash && Rails.env.production?
files = []
Hyperloop.public_model_directories.each do |dir|
dir_length = Rails.root.join(dir).to_s.length + 1
Dir.glob(Rails.root.join(dir, '**', '*.rb')).each do |file|
require_dependency(file) files << file[dir_length..-4]
end
end
@public_columns_hash = {}
descendants.each do |model|
if files.include?(model.name.underscore) && model.name.underscore != 'application_record'
@public_columns_hash[model.name] = model.columns_hash rescue nil end
end
@public_columns_hash
end
|
.public_columns_hash_as_json ⇒ Object
36
37
38
39
40
41
42
|
# File 'lib/reactive_record/active_record/public_columns_hash.rb', line 36
def self.public_columns_hash_as_json
return @public_columns_hash_json if @public_columns_hash_json && Rails.env.production?
pch = public_columns_hash
return @public_columns_hash_json if @prev_public_columns_hash == pch
@prev_public_columns_hash = pch
@public_columns_hash_json = pch.to_json
end
|
.reflect_on_aggregation(attribute) ⇒ Object
9
10
11
|
# File 'lib/reactive_record/active_record/aggregations.rb', line 9
def self.reflect_on_aggregation(attribute)
reflect_on_all_aggregations.detect { |aggregation| aggregation.attribute == attribute }
end
|
.reflect_on_all_aggregations ⇒ Object
5
6
7
|
# File 'lib/reactive_record/active_record/aggregations.rb', line 5
def self.reflect_on_all_aggregations
base_class.instance_eval { @aggregations ||= [] }
end
|
.reflect_on_all_associations ⇒ Object
5
6
7
|
# File 'lib/reactive_record/active_record/associations.rb', line 5
def self.reflect_on_all_associations
base_class.instance_eval { @associations ||= superclass.instance_eval { (@associations && @associations.dup) || [] } }
end
|
.reflect_on_association(attr) ⇒ Object
9
10
11
|
# File 'lib/reactive_record/active_record/associations.rb', line 9
def self.reflect_on_association(attr)
reflection_finder { |assoc| assoc.attribute == attr }
end
|
.reflect_on_association_by_foreign_key(key) ⇒ Object
13
14
15
|
# File 'lib/reactive_record/active_record/associations.rb', line 13
def self.reflect_on_association_by_foreign_key(key)
reflection_finder { |assoc| assoc.association_foreign_key == key }
end
|
.reflection_finder(&block) ⇒ Object
17
18
19
20
21
22
23
24
25
26
27
28
|
# File 'lib/reactive_record/active_record/associations.rb', line 17
def self.reflection_finder(&block)
found = reflect_on_all_associations.detect do |assoc|
assoc.owner_class == self && yield(assoc)
end
if found
found
elsif superclass == Base
nil
else
superclass.reflection_finder(&block)
end
end
|
.regulate_default_scope(*args, &block) ⇒ Object
197
198
199
200
|
# File 'lib/active_record_base.rb', line 197
def regulate_default_scope(*args, &block)
block = __synchromesh_parse_regulator_params({ all: args[0] }, block).last unless args.empty?
regulate_scope(:all, &block)
end
|
.regulate_relationship(name, &block) ⇒ Object
add regulate_relationship method and monkey patch has_many macro to add regulations if present
232
233
234
235
236
237
238
239
|
# File 'lib/active_record_base.rb', line 232
def regulate_relationship(name, &block)
name, block = __synchromesh_parse_regulator_params(name, block)
define_method(:"__secure_remote_access_to_#{name}") do |this, acting_user, *args|
this.class.__set_synchromesh_permission_granted(
nil, this.send(name, *args), this, acting_user, &block
)
end
end
|
.regulate_scope(name, &block) ⇒ Object
regulate scope has to deal with the special case that the scope returns an an array instead of a relationship. In this case we wrap the array and go on
186
187
188
189
190
191
192
193
|
# File 'lib/active_record_base.rb', line 186
def regulate_scope(name, &block)
name, block = __synchromesh_parse_regulator_params(name, block)
singleton_class.send(:define_method, :"__secure_remote_access_to_#{name}") do |this, acting_user, *args|
r = this.send(name, *args)
r = ReactiveRecordPsuedoRelationArray.new(r) if r.is_a? Array
__set_synchromesh_permission_granted(this, r, r, acting_user, args, &block)
end
end
|
.scope(name, *args, &block) ⇒ Object
207
208
209
210
211
212
213
214
215
|
# File 'lib/active_record_base.rb', line 207
def scope(name, *args, &block)
__synchromesh_regulate_from_macro(
(opts = _synchromesh_scope_args_check(args)),
name,
respond_to?(:"__secure_remote_access_to_#{name}"),
&method(:regulate_scope)
)
pre_synchromesh_scope(name, opts[:server], &block)
end
|
.server_method(name, _opts = {}, &block) ⇒ Object
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
# File 'lib/active_record_base.rb', line 98
def server_method(name, _opts = {}, &block)
define_method(name, &block)
define_method("__secure_remote_access_to_#{name}") do |_self, acting_user, *args|
begin
old = self.acting_user
self.acting_user = acting_user
send(name, *args)
ensure
self.acting_user = old
end
end
end
|
Instance Method Details
#__hyperloop_secure_attributes(acting_user) ⇒ Object
317
318
319
320
321
|
# File 'lib/active_record_base.rb', line 317
def __hyperloop_secure_attributes(acting_user)
accessible_attributes =
Hyperloop::InternalPolicy.accessible_attributes_for(self, acting_user)
attributes.select { |attr| accessible_attributes.include? attr.to_sym }
end
|
#all_changed?(*attributes) ⇒ Boolean
65
66
67
68
69
70
|
# File 'lib/reactive_record/permissions.rb', line 65
def all_changed?(*attributes)
attributes.each do |key|
return false unless self.send("#{key}_changed?")
end
true
end
|
#any_changed?(*attributes) ⇒ Boolean
58
59
60
61
62
63
|
# File 'lib/reactive_record/permissions.rb', line 58
def any_changed?(*attributes)
attributes.each do |key|
return true if self.send("#{key}_changed?")
end
false
end
|
#check_permission_with_acting_user(user, permission, *args) ⇒ Object
99
100
101
102
103
104
105
106
107
108
|
# File 'lib/reactive_record/permissions.rb', line 99
def check_permission_with_acting_user(user, permission, *args)
old = acting_user
self.acting_user = user
if self.send(permission, *args)
self.acting_user = old
self
else
Hyperloop::InternalPolicy.raise_operation_access_violation(:crud_access_violation, "for #{self} - #{permission}(#{args}) acting_user: #{user}")
end
end
|
#create_permitted? ⇒ Boolean
32
33
34
|
# File 'lib/reactive_record/permissions.rb', line 32
def create_permitted?
false
end
|
278
279
280
|
# File 'lib/active_record_base.rb', line 278
def denied!
Hyperloop::InternalPolicy.raise_operation_access_violation(:scoped_denied, "#{self.class} regulation denies scope access. Called from #{caller_locations(1)}")
end
|
#destroy_permitted? ⇒ Boolean
40
41
42
|
# File 'lib/reactive_record/permissions.rb', line 40
def destroy_permitted?
false
end
|
#do_not_synchronize? ⇒ Boolean
294
295
296
|
# File 'lib/active_record_base.rb', line 294
def do_not_synchronize?
self.class.do_not_synchronize?
end
|
#none_changed?(*attributes) ⇒ Boolean
51
52
53
54
55
56
|
# File 'lib/reactive_record/permissions.rb', line 51
def none_changed?(*attributes)
attributes.each do |key|
return false if self.send("#{key}_changed?")
end
true
end
|
#only_changed?(*attributes) ⇒ Boolean
44
45
46
47
48
49
|
# File 'lib/reactive_record/permissions.rb', line 44
def only_changed?(*attributes)
(self.attributes.keys + self.class.reactive_record_association_keys).each do |key|
return false if self.send("#{key}_changed?") and !attributes.include? key
end
true
end
|
#synchromesh_after_change ⇒ Object
307
308
309
310
|
# File 'lib/active_record_base.rb', line 307
def synchromesh_after_change
return if do_not_synchronize? || previous_changes.empty?
ReactiveRecord::Broadcast.after_commit :change, self
end
|
#synchromesh_after_create ⇒ Object
302
303
304
305
|
# File 'lib/active_record_base.rb', line 302
def synchromesh_after_create
return if do_not_synchronize?
ReactiveRecord::Broadcast.after_commit :create, self
end
|
#synchromesh_after_destroy ⇒ Object
312
313
314
315
|
# File 'lib/active_record_base.rb', line 312
def synchromesh_after_destroy
return if do_not_synchronize?
ReactiveRecord::Broadcast.after_commit :destroy, self
end
|
#update_permitted? ⇒ Boolean
36
37
38
|
# File 'lib/reactive_record/permissions.rb', line 36
def update_permitted?
false
end
|
#view_permitted?(attribute) ⇒ Boolean