Skip to content

Commit f38bf32

Browse files
committed
Update method of wrapping PHI access proxy
1 parent 9637b1b commit f38bf32

File tree

1 file changed

+48
-18
lines changed

1 file changed

+48
-18
lines changed

lib/phi_attrs/phi_record.rb

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@ module PhiRecord
1313
included do
1414
class_attribute :__phi_exclude_methods
1515
class_attribute :__phi_include_methods
16-
class_attribute :__phi_extended_methods
16+
class_attribute :__phi_extend_methods
1717
class_attribute :__phi_methods_wrapped
18+
class_attribute :__phi_methods_extended
1819

1920
after_initialize :wrap_phi
2021

22+
# These have to default to an empty array
2123
self.__phi_methods_wrapped = []
22-
self.__phi_extended_methods = []
24+
self.__phi_methods_extended = []
2325
end
2426

2527
class_methods do
@@ -49,15 +51,15 @@ def include_in_phi(*methods)
4951
# is allowed. The methods that are extended should return ActiveRecord
5052
# models that also extend PhiAttrs.
5153
#
52-
# If they do not, this is essentially an alias for PhiRecord#include_in_phi
53-
#
5454
# @param [Array<Symbol>] *methods Any number of methods to extend access to
5555
#
5656
# @example
57+
# has_one :foo
58+
# has_one :bar
5759
# extend_phi_access :foo, :bar
5860
#
5961
def extend_phi_access(*methods)
60-
self.__phi_extended_methods = methods.map(&:to_s)
62+
self.__phi_extend_methods = methods.map(&:to_s)
6163
end
6264

6365
# Enable PHI access for any instance of this class.
@@ -100,11 +102,18 @@ def disallow_phi!
100102
# @return [Array<String>] the method names to be wrapped with PHI access logging
101103
#
102104
def __phi_wrapped_methods
103-
extended_methods = self.class.__phi_extended_methods.to_a
104105
excluded_methods = self.class.__phi_exclude_methods.to_a
105106
included_methods = self.class.__phi_include_methods.to_a
106107

107-
extended_methods + attribute_names - excluded_methods + included_methods - [self.class.primary_key]
108+
attribute_names - excluded_methods + included_methods - [self.class.primary_key]
109+
end
110+
111+
# Get all method names to be wrapped with PHI access extension
112+
#
113+
# @return [Array<String>] the method names to be wrapped with PHI access extension
114+
#
115+
def __phi_extended_methods
116+
self.class.__phi_extend_methods.to_a
108117
end
109118

110119
# Enable PHI access for a single instance of this class.
@@ -167,7 +176,8 @@ def wrap_phi
167176
@__phi_access_logged = false
168177

169178
# Wrap attributes with PHI Logger and Access Control
170-
__phi_wrapped_methods.each { |attr| phi_wrap_method(attr) }
179+
__phi_wrapped_methods.each { |m| phi_wrap_method(m) }
180+
__phi_extended_methods.each { |m| phi_extend_access(m) }
171181
end
172182

173183
# Log Key for an instance of this class. If the instance is persisted in the
@@ -255,26 +265,46 @@ def phi_wrap_method(method_name)
255265
@__phi_access_logged = true
256266
end
257267

258-
# extend PHI access to relationships if needed
259-
if self.class.__phi_extended_methods.include? method_name
268+
send(unwrapped_method, *args, &block)
269+
end
270+
end
260271

261-
# get the unwrapped relation
262-
relation = send(unwrapped_method, *args, &block)
272+
# method_name => wrapped_method => unwrapped_method
273+
self.class.send(:alias_method, unwrapped_method, method_name)
274+
self.class.send(:alias_method, method_name, wrapped_method)
263275

264-
if relation.class.included_modules.include?(PhiRecord)
265-
relation.allow_phi!(phi_allowed_by, phi_access_reason) unless relation.phi_allowed?
266-
end
267-
end
276+
self.class.__phi_methods_wrapped << method_name
277+
end
268278

269-
send(unwrapped_method, *args, &block)
279+
# Core logic for wrapping methods in PHI access extensions. Almost
280+
# functionally equivalent to the phi_wrap_method call above,
281+
# this method doesn't add any logging or access restriction, but
282+
# simply proxies the PhiRecord#allow_phi! call.
283+
#
284+
# @private
285+
#
286+
def phi_extend_access(method_name)
287+
return if self.class.__phi_methods_extended.include? method_name
288+
289+
wrapped_method = :"__#{method_name}_phi_access_extended"
290+
unwrapped_method = :"__#{method_name}_phi_access_original"
291+
292+
self.class.send(:define_method, wrapped_method) do |*args, &block|
293+
# get the unwrapped relation
294+
relation = send(unwrapped_method, *args, &block)
295+
296+
if phi_allowed? && relation.class.included_modules.include?(PhiRecord)
297+
relation.allow_phi!(phi_allowed_by, phi_access_reason) unless relation.phi_allowed?
270298
end
299+
300+
relation
271301
end
272302

273303
# method_name => wrapped_method => unwrapped_method
274304
self.class.send(:alias_method, unwrapped_method, method_name)
275305
self.class.send(:alias_method, method_name, wrapped_method)
276306

277-
self.class.__phi_methods_wrapped << method_name
307+
self.class.__phi_methods_extended << method_name
278308
end
279309
end
280310
end

0 commit comments

Comments
 (0)