Module: PhiAttrs::PhiRecord

Extended by:
ActiveSupport::Concern
Defined in:
lib/phi_attrs/phi_record.rb

Overview

Module for extending ActiveRecord models to handle PHI access logging and restrict access to attributes.

Author:

  • Apsis Labs

Since:

  • 0.1.0

Instance Method Summary collapse

Instance Method Details

#__phi_extended_methodsArray<String>

Get all method names to be wrapped with PHI access extension

Returns:

  • (Array<String>)

    the method names to be wrapped with PHI access extension

Since:

  • 0.1.0



291
292
293
# File 'lib/phi_attrs/phi_record.rb', line 291

def __phi_extended_methods
  self.class.__phi_extend_methods.to_a
end

#__phi_wrapped_methodsArray<String>

Get all method names to be wrapped with PHI access logging

Returns:

  • (Array<String>)

    the method names to be wrapped with PHI access logging

Since:

  • 0.1.0



280
281
282
283
284
285
# File 'lib/phi_attrs/phi_record.rb', line 280

def __phi_wrapped_methods
  excluded_methods = self.class.__phi_exclude_methods.to_a
  included_methods = self.class.__phi_include_methods.to_a

  attribute_names - excluded_methods + included_methods - [self.class.primary_key]
end

#allow_phi(user_id = nil, reason = nil) { ... } ⇒ Object

Enable PHI access for a single instance of this class inside the block. Nested calls to allow_phi will log once per nested call

Examples:

foo = Foo.find(1)
foo.allow_phi('[email protected]', 'viewing patient record') do
 # PHI Access Allowed Here
end
# PHI Access Disallowed Here

Parameters:

  • user_id (String) (defaults to: nil)

    A unique identifier for the person accessing the PHI

  • reason (String) (defaults to: nil)

    The reason for accessing PHI

Yields:

  • The block in which phi access is allowed

Raises:

  • (ArgumentError)

Since:

  • 0.1.0



336
337
338
339
340
341
342
343
344
345
346
347
# File 'lib/phi_attrs/phi_record.rb', line 336

def allow_phi(user_id = nil, reason = nil)
  raise ArgumentError, 'block required. use allow_phi! without block' unless block_given?

  extended_instances = @__phi_relations_extended.clone
  allow_phi!(user_id, reason)

  yield if block_given?

  new_extensions = @__phi_relations_extended - extended_instances
  disallow_last_phi!(preserve_extensions: true)
  revoke_extended_phi!(new_extensions) if new_extensions.any?
end

#allow_phi!(user_id = nil, reason = nil) ⇒ Object

Enable PHI access for a single instance of this class.

Examples:

foo = Foo.find(1)
foo.allow_phi!('[email protected]', 'viewing patient record')

Parameters:

  • user_id (String) (defaults to: nil)

    A unique identifier for the person accessing the PHI

  • reason (String) (defaults to: nil)

    The reason for accessing PHI

Raises:

  • (ArgumentError)

Since:

  • 0.1.0



304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/phi_attrs/phi_record.rb', line 304

def allow_phi!(user_id = nil, reason = nil)
  raise ArgumentError, 'block not allowed. use allow_phi with block' if block_given?

  user_id ||= self.class.current_user
  reason ||= self.class.i18n_reason
  raise ArgumentError, 'user_id and reason cannot be blank' if user_id.blank? || reason.blank?

  PhiAttrs::Logger.tagged(*phi_log_keys) do
    @__phi_access_stack.push({
                               phi_access_allowed: true,
                               user_id: user_id,
                               reason: reason
                             })

    PhiAttrs::Logger.info("PHI Access Enabled for '#{user_id}': #{reason}")
  end
end

#disallow_last_phi!(preserve_extensions: false) ⇒ Object

Revoke last PHI access for a single instance of this class.

Examples:

foo = Foo.find(1)
foo.disallow_last_phi!

Raises:

  • (ArgumentError)

Since:

  • 0.1.0



401
402
403
404
405
406
407
408
409
410
411
# File 'lib/phi_attrs/phi_record.rb', line 401

def disallow_last_phi!(preserve_extensions: false)
  raise ArgumentError, 'block not allowed' if block_given?

  PhiAttrs::Logger.tagged(*phi_log_keys) do
    removed_access = @__phi_access_stack.pop

    revoke_extended_phi! unless preserve_extensions
    message = removed_access.present? ? "PHI access disabled for #{removed_access[:user_id]}" : 'PHI access disabled. No instance level access was granted.'
    PhiAttrs::Logger.info(message)
  end
end

#disallow_phi { ... } ⇒ Object

Dissables PHI access for a single instance of this class inside the block. Nested calls to allow_phi will log once per nested call

Examples:

foo = Foo.find(1)
foo.allow_phi('[email protected]', 'viewing patient record') do
 # PHI Access Allowed Here
end
# PHI Access Disallowed Here

Parameters:

  • user_id (String)

    A unique identifier for the person accessing the PHI

  • reason (String)

    The reason for accessing PHI

Yields:

  • The block in which phi access is allowed

Raises:

  • (ArgumentError)

Since:

  • 0.1.0



383
384
385
386
387
388
389
390
391
392
393
# File 'lib/phi_attrs/phi_record.rb', line 383

def disallow_phi
  raise ArgumentError, 'block required. use disallow_phi! without block' unless block_given?

  add_disallow_flag!
  add_disallow_flag_to_extended_phi!

  yield if block_given?

  remove_disallow_flag_from_extended_phi!
  remove_disallow_flag!
end

#disallow_phi!Object

Revoke all PHI access for a single instance of this class.

Examples:

foo = Foo.find(1)
foo.disallow_phi!

Raises:

  • (ArgumentError)

Since:

  • 0.1.0



355
356
357
358
359
360
361
362
363
364
365
366
367
# File 'lib/phi_attrs/phi_record.rb', line 355

def disallow_phi!
  raise ArgumentError, 'block not allowed. use disallow_phi with block' if block_given?

  PhiAttrs::Logger.tagged(*phi_log_keys) do
    removed_access_for = self.class.__user_id_string(@__phi_access_stack)

    revoke_extended_phi!
    @__phi_access_stack = []

    message = removed_access_for.present? ? "PHI access disabled for #{removed_access_for}" : 'PHI access disabled. No instance level access was granted.'
    PhiAttrs::Logger.info(message)
  end
end

#phi_allowed?Boolean

Whether PHI access is allowed for a single instance of this class

Examples:

foo = Foo.find(1)
foo.phi_allowed?

Returns:

  • (Boolean)

    whether PHI access is allowed for this instance

Since:

  • 0.1.0



421
422
423
# File 'lib/phi_attrs/phi_record.rb', line 421

def phi_allowed?
  !phi_context.nil? && phi_context[:phi_access_allowed]
end