phi_attrs 0.1.2 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/workflows/build.yml +25 -0
- data/.github/workflows/publish.yml +28 -0
- data/.gitignore +5 -0
- data/.rubocop.yml +91 -0
- data/Appraisals +15 -3
- data/CHANGELOG.md +17 -0
- data/DISCLAIMER.txt +15 -0
- data/Dockerfile +1 -1
- data/Gemfile +2 -0
- data/LICENSE.txt +1 -1
- data/README.md +409 -11
- data/Rakefile +13 -0
- data/bin/console +6 -0
- data/bin/helpers/docker +12 -0
- data/bin/rubo_fix +6 -0
- data/bin/run_tests +19 -0
- data/bin/setup +2 -0
- data/bin/ssh_to_container +0 -0
- data/config.ru +2 -2
- data/docker-compose.yml +2 -0
- data/docker/start.sh +0 -0
- data/gemfiles/rails_5.1.gemfile +8 -0
- data/gemfiles/{rails_5.0.gemfile → rails_5.2.gemfile} +2 -2
- data/gemfiles/rails_6.0.gemfile +8 -0
- data/lib/phi_attrs.rb +26 -15
- data/lib/phi_attrs/configure.rb +29 -11
- data/lib/phi_attrs/exceptions.rb +5 -1
- data/lib/phi_attrs/formatter.rb +5 -3
- data/lib/phi_attrs/logger.rb +13 -1
- data/lib/phi_attrs/phi_record.rb +635 -48
- data/lib/phi_attrs/railtie.rb +5 -2
- data/lib/phi_attrs/rspec.rb +43 -0
- data/lib/phi_attrs/version.rb +3 -1
- data/phi_attrs.gemspec +17 -5
- metadata +94 -26
- data/.travis.yml +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 0c95e0078a0816fa79591bab794e8c423d70d8792354cbb09cb69cda60b93d4b
|
4
|
+
data.tar.gz: 7af031bbc1cc1aa2ba308dc778611b590faf33b4e572e9e264477dde4fb24ace
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a13b688097f56ad46044a7da0f91402565e582383e80f522f6ef3829fc75e677a5518a3190e261114713177ab6ab746783eb10bec4cc83215cf8089ca17cbc35
|
7
|
+
data.tar.gz: 323f3337e8d687e5e8e781324fd284008344a9ee8c15ff737840b736908face774331b762783076ffd5bb9e37a150452e4d712129a4a30fc169a33edde3abec9
|
@@ -0,0 +1,25 @@
|
|
1
|
+
name: Spec CI
|
2
|
+
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
name: Ruby ${{ matrix.ruby }}
|
9
|
+
strategy:
|
10
|
+
matrix:
|
11
|
+
ruby: [2.5, 2.6, 2.7]
|
12
|
+
|
13
|
+
steps:
|
14
|
+
- uses: actions/checkout@v2
|
15
|
+
- name: Set up Ruby ${{ matrix.ruby }}
|
16
|
+
uses: actions/setup-ruby@v1
|
17
|
+
with:
|
18
|
+
ruby-version: ${{ matrix.ruby }}
|
19
|
+
- name: Install dependencies
|
20
|
+
run: |
|
21
|
+
gem install bundler
|
22
|
+
bundle install
|
23
|
+
bundle exec appraisal install
|
24
|
+
- name: Run rspec
|
25
|
+
run: bundler exec appraisal rspec
|
@@ -0,0 +1,28 @@
|
|
1
|
+
name: Publish Gem
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches:
|
6
|
+
- "main"
|
7
|
+
tags:
|
8
|
+
- v*
|
9
|
+
jobs:
|
10
|
+
build:
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
|
13
|
+
steps:
|
14
|
+
- uses: actions/checkout@v2
|
15
|
+
- uses: actions/setup-ruby@v1
|
16
|
+
with:
|
17
|
+
ruby-version: '2.6'
|
18
|
+
- name: Install dependencies
|
19
|
+
run: |
|
20
|
+
gem install bundler:2.1.4
|
21
|
+
bundle install
|
22
|
+
- name: Release Gem
|
23
|
+
if: contains(github.ref, 'refs/tags/v')
|
24
|
+
uses: cadwallion/publish-rubygems-action@master
|
25
|
+
env:
|
26
|
+
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
27
|
+
RUBYGEMS_API_KEY: ${{secrets.RUBYGEMS_API_KEY}}
|
28
|
+
RELEASE_COMMAND: bundle exec rake release
|
data/.gitignore
CHANGED
data/.rubocop.yml
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
Rails:
|
2
|
+
Enabled: true
|
3
|
+
|
4
|
+
AllCops:
|
5
|
+
Exclude:
|
6
|
+
- 'bin/**/*'
|
7
|
+
- 'gemfiles/**/*'
|
8
|
+
- 'spec/internal/db/schema.rb'
|
9
|
+
TargetRubyVersion: 2.5
|
10
|
+
|
11
|
+
Documentation:
|
12
|
+
Enabled: false
|
13
|
+
|
14
|
+
Layout/IndentationWidth:
|
15
|
+
Enabled: true
|
16
|
+
|
17
|
+
Lint/UnreachableCode:
|
18
|
+
Exclude:
|
19
|
+
- 'lib/phi_attrs/phi_record.rb' # TODO: RUBOCOP Cleanup exclusion
|
20
|
+
|
21
|
+
Lint/UnusedMethodArgument:
|
22
|
+
Exclude:
|
23
|
+
- 'lib/phi_attrs.rb' # TODO: RUBOCOP Cleanup exclusion
|
24
|
+
|
25
|
+
Metrics/AbcSize:
|
26
|
+
Max: 30
|
27
|
+
Exclude:
|
28
|
+
- 'spec/internal/db/**/*'
|
29
|
+
- 'lib/phi_attrs/phi_record.rb' # TODO: RUBOCOP Cleanup exclusion
|
30
|
+
|
31
|
+
Metrics/BlockLength:
|
32
|
+
Enabled: false
|
33
|
+
|
34
|
+
Metrics/ClassLength:
|
35
|
+
Max: 1500
|
36
|
+
|
37
|
+
Metrics/CyclomaticComplexity:
|
38
|
+
Exclude:
|
39
|
+
- 'lib/phi_attrs/phi_record.rb' # TODO: RUBOCOP Cleanup exclusion
|
40
|
+
|
41
|
+
Metrics/LineLength:
|
42
|
+
Exclude:
|
43
|
+
- 'spec/**/*'
|
44
|
+
- 'lib/phi_attrs/phi_record.rb' # TODO: RUBOCOP Cleanup exclusion
|
45
|
+
Max: 140
|
46
|
+
|
47
|
+
Metrics/MethodLength:
|
48
|
+
Exclude:
|
49
|
+
- 'spec/**/*'
|
50
|
+
- 'lib/phi_attrs/phi_record.rb' # TODO: RUBOCOP Cleanup exclusion
|
51
|
+
Max: 20
|
52
|
+
|
53
|
+
Metrics/ModuleLength:
|
54
|
+
Exclude:
|
55
|
+
- 'lib/phi_attrs/phi_record.rb' # TODO: RUBOCOP Cleanup exclusion
|
56
|
+
|
57
|
+
Metrics/PerceivedComplexity:
|
58
|
+
Exclude:
|
59
|
+
- 'lib/phi_attrs/phi_record.rb' # TODO: RUBOCOP Cleanup exclusion
|
60
|
+
|
61
|
+
Naming/PredicateName:
|
62
|
+
Enabled: false
|
63
|
+
|
64
|
+
Rails/DynamicFindBy:
|
65
|
+
Exclude:
|
66
|
+
- 'spec/spec_helper.rb' # TODO: RUBOCOP Cleanup exclusion
|
67
|
+
|
68
|
+
Style/BracesAroundHashParameters:
|
69
|
+
Enabled: false
|
70
|
+
|
71
|
+
Style/ClassVars:
|
72
|
+
Enabled: false
|
73
|
+
|
74
|
+
Style/CommentedKeyword:
|
75
|
+
Exclude:
|
76
|
+
- 'spec/**/*'
|
77
|
+
|
78
|
+
Style/ConditionalAssignment:
|
79
|
+
Enabled: false
|
80
|
+
|
81
|
+
Style/EmptyMethod:
|
82
|
+
EnforcedStyle: expanded
|
83
|
+
|
84
|
+
Style/SymbolArray:
|
85
|
+
EnforcedStyle: brackets
|
86
|
+
|
87
|
+
Style/RedundantReturn:
|
88
|
+
Enabled: false
|
89
|
+
|
90
|
+
Style/WordArray:
|
91
|
+
MinSize: 4
|
data/Appraisals
CHANGED
@@ -1,4 +1,16 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
appraise 'rails-5.1' do
|
4
|
+
gem 'rails', '5.1.3'
|
5
|
+
gem 'sqlite3', '~> 1.3', '< 1.4'
|
4
6
|
end
|
7
|
+
|
8
|
+
appraise 'rails-5.2' do
|
9
|
+
gem 'rails', '5.2.4'
|
10
|
+
gem 'sqlite3', '~> 1.4'
|
11
|
+
end
|
12
|
+
|
13
|
+
appraise 'rails-6.0' do
|
14
|
+
gem 'rails', '6.0.3'
|
15
|
+
gem 'sqlite3', '~> 1.4'
|
16
|
+
end
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# Changelog
|
2
|
+
All notable changes to this project will be documented in this file.
|
3
|
+
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
5
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
|
+
|
7
|
+
## [Unreleased]
|
8
|
+
### Added
|
9
|
+
- Added documentation for CHANGELOG to README
|
10
|
+
|
11
|
+
## [v0.1.4] - 2018-07-05
|
12
|
+
## [v0.1.3] - 2018-07-05
|
13
|
+
## [v0.1.2] - 2018-07-05
|
14
|
+
## [v0.1.1] - 2018-07-05
|
15
|
+
|
16
|
+
## [v0.1.0] - 2018-07-04
|
17
|
+
Initial release
|
data/DISCLAIMER.txt
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
DISCLAIMER
|
2
|
+
==========
|
3
|
+
|
4
|
+
While access logging is part of maintaining a HIPAA compliant application, there are many requirements for building a HIPAA secure application which are not addressed by `phi_attrs`, and as such use of `phi_attrs` on its own does not ensure HIPAA Compliance. As such, this software is provided on an "as-is" basis, and Apsis Labs, LLP makes no warranties or assurances with regards to the HIPAA compliance of any application which makes use of `phi_attrs`.
|
5
|
+
|
6
|
+
For further reading on how to ensure your application meets the HIPAA security standards, review the HHS Security Series Technical Safeguards and the Summary of the HIPAA Security Rule, in addition to consulting your compliance and legal counsel.
|
7
|
+
|
8
|
+
Apsis Labs, LLP is not a law firm and does not provide legal advice. The information in this software does not constitute legal advice, nor does usage of this software create an attorney-client relationship.
|
9
|
+
|
10
|
+
Apsis Labs, LLP is not a HIPAA covered entity, and usage of this software does not create a business associate relationship, nor does it enact a business associate agreement.
|
11
|
+
|
12
|
+
---
|
13
|
+
|
14
|
+
1. HHS Security Series Technical Safeguards: https://www.hhs.gov/sites/default/files/ocr/privacy/hipaa/administrative/securityrule/techsafeguards.pdf
|
15
|
+
2. Summary of the HIPAA Security Rule: https://www.hhs.gov/hipaa/for-professionals/security/laws-regulations/index.html
|
data/Dockerfile
CHANGED
data/Gemfile
CHANGED
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,25 @@
|
|
1
|
-
#
|
1
|
+
# phi_attrs [![Gem Version](https://badge.fury.io/rb/phi_attrs.svg)](https://badge.fury.io/rb/phi_attrs) [![Spec CI](https://github.com/apsislabs/phi_attrs/workflows/Spec%20CI/badge.svg)](https://github.com/apsislabs/phi_attrs/actions)
|
2
|
+
|
3
|
+
|
4
|
+
HIPAA compliant PHI access logging for Ruby on Rails.
|
5
|
+
|
6
|
+
According to [HIPAA Security Rule](https://www.hhs.gov/hipaa/for-professionals/security/index.html) `§ 164.312(b)`, HIPAA covered entities are required to:
|
7
|
+
|
8
|
+
> Implement hardware, software, and/or procedural mechanisms that record and examine activity in information systems that contain or use electronic protected health information.
|
9
|
+
|
10
|
+
The `phi_attrs` gem is intended to assist with implementing logging to comply with the access log requirements of `§ 164.308(a)(1)(ii)(D)`:
|
11
|
+
|
12
|
+
> Information system activity review (Required). Implement procedures to regularly review records of information system activity, such as audit logs, access reports, and security incident tracking reports.
|
13
|
+
|
14
|
+
To do so, `phi_attrs` extends `ActiveRecord` models by adding automated logging and explicit access control methods. The access control mechanism creates a separate `phi_access_log`.
|
15
|
+
|
16
|
+
**Please Note:** while `phi_attrs` helps facilitate access logging, it still requires due diligence by developers, both in ensuring that models and attributes which store PHI are flagged with `phi_model` and that calls to `allow_phi!` properly attribute both a _unique_ identifier and an explicit reason for PHI access.
|
17
|
+
|
18
|
+
**Please Note:** there are other aspects of building a HIPAA secure application which are not addressed by `phi_attrs`, and as such _use of `phi_attrs` on its own does not ensure HIPAA Compliance_. For further reading on how to ensure your application meets the HIPAA security standards, review the [HHS Security Series Technical Safeguards](https://www.hhs.gov/sites/default/files/ocr/privacy/hipaa/administrative/securityrule/techsafeguards.pdf) and [Summary of the HIPAA Security Rule](https://www.hhs.gov/hipaa/for-professionals/security/laws-regulations/index.html), in addition to consulting your compliance and legal counsel.
|
19
|
+
|
20
|
+
## Stability
|
21
|
+
|
22
|
+
All versions of this project below `1.0.0` should be considered unstable beta software. Even minor-version updates may introduce breaking changes to the public API at this stage. We strongly suggest that you lock the installed version in your Gemfile to avoid unintended breaking updates.
|
2
23
|
|
3
24
|
## Installation
|
4
25
|
|
@@ -16,6 +37,20 @@ Or install it yourself as:
|
|
16
37
|
|
17
38
|
$ gem install phi_attrs
|
18
39
|
|
40
|
+
## Initialize
|
41
|
+
|
42
|
+
Create an initializer to configure the PHI log file location.
|
43
|
+
|
44
|
+
Example:
|
45
|
+
|
46
|
+
`config/initializers/phi_attrs.rb`
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
PhiAttrs.configure do |conf|
|
50
|
+
conf.log_path = Rails.root.join("log", "phi_access_#{Rails.env}.log")
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
19
54
|
## Usage
|
20
55
|
|
21
56
|
```ruby
|
@@ -31,36 +66,399 @@ class PatientInfo < ActiveRecord::Base
|
|
31
66
|
end
|
32
67
|
```
|
33
68
|
|
34
|
-
Access is granted on a
|
69
|
+
Access is granted on a instance level:
|
70
|
+
|
35
71
|
```ruby
|
36
|
-
info = new
|
72
|
+
info = PatientInfo.new
|
37
73
|
info.allow_phi!("allowed_user@example.com", "Customer Service")
|
38
74
|
```
|
39
75
|
|
76
|
+
*When using on an instance if you find it in a second place you will need to call allow_phi! again.*
|
77
|
+
|
40
78
|
or a class:
|
79
|
+
|
41
80
|
```ruby
|
42
81
|
PatientInfo.allow_phi!("allowed_user@example.com", "Customer Service")
|
43
82
|
```
|
44
83
|
|
84
|
+
As of version `0.1.5`, a block syntax is available. As above, this is available on both class and instance levels.
|
85
|
+
|
86
|
+
Note the lack of a `!` at the end. These methods should not be used alongside the mutating (bang) methods! We recommend using the block syntax for tighter control.
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
patient = PatientInfo.find(params[:id])
|
90
|
+
patient.allow_phi('allowed_user@example.com', 'Display Customer Data') do
|
91
|
+
@data = patient.to_json
|
92
|
+
end # Access no longer allowed beyond this point
|
93
|
+
```
|
94
|
+
|
95
|
+
or a block on a class:
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
PatientInfo.allow_phi('allowed_user@example.com', 'Display Customer Data') do
|
99
|
+
@data = PatientInfo.find(params[:id]).to_json
|
100
|
+
end # Access no longer allowed beyond this point
|
101
|
+
```
|
102
|
+
|
103
|
+
### Controlling What Is PHI
|
104
|
+
|
105
|
+
When you include `phi_model` on your active record all fields except the id will be considered PHI.
|
106
|
+
|
107
|
+
To remove fields from PHI tracking use `exclude_from_phi`:
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
# created_at and updated_at will be accessible as normal
|
111
|
+
class PatientInfo < ActiveRecord::Base
|
112
|
+
phi_model
|
113
|
+
|
114
|
+
exclude_from_phi :created_at, :updated_at
|
115
|
+
end
|
116
|
+
```
|
117
|
+
|
118
|
+
To add a method as PHI use `include_in_phi`. Include takes precedence over exclude so a method that appears in both will be considered PHI.
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
# birthday and node will throw PHIExceptions if accessed without permission
|
122
|
+
class PatientInfo < ActiveRecord::Base
|
123
|
+
phi_model
|
124
|
+
|
125
|
+
include_in_phi :birthday, :note
|
126
|
+
|
127
|
+
def birthday
|
128
|
+
Time.current
|
129
|
+
end
|
130
|
+
|
131
|
+
attr_accessor :note
|
132
|
+
end
|
133
|
+
```
|
134
|
+
|
135
|
+
#### Example Usage
|
136
|
+
|
137
|
+
Example of `exclude_from_phi` and `include_in_phi` with inheritance.
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
class PatientInfo < ActiveRecord::Base
|
141
|
+
phi_model
|
142
|
+
end
|
143
|
+
|
144
|
+
pi = PatientInfo.new(first_name: "Ash", last_name: "Ketchum")
|
145
|
+
pi.created_at
|
146
|
+
# PHIAccessException!
|
147
|
+
pi.last_name
|
148
|
+
# PHIAccessException!
|
149
|
+
pi.allow_phi "Ash", "Testing PHI Attrs" { pi.last_name }
|
150
|
+
# "Ketchum"
|
151
|
+
```
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
class PatientInfoTwo < PatientInfo
|
155
|
+
exclude_from_phi :created_at
|
156
|
+
end
|
157
|
+
|
158
|
+
pi = PatientInfoTwo.new(first_name: "Ash", last_name: "Ketchum")
|
159
|
+
pi.created_at
|
160
|
+
# current time
|
161
|
+
pi.last_name
|
162
|
+
# PHIAccessException!
|
163
|
+
pi.allow_phi "Ash", "Testing PHI Attrs" { pi.last_name }
|
164
|
+
# "Ketchum"
|
165
|
+
```
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
class PatientInfoThree < PatientInfoTwo
|
169
|
+
include_in_phi :created_at # Changed our mind
|
170
|
+
end
|
171
|
+
|
172
|
+
pi = PatientInfoThree.new(first_name: "Ash", last_name: "Ketchum")
|
173
|
+
pi.created_at
|
174
|
+
# PHIAccessException!
|
175
|
+
pi.last_name
|
176
|
+
# PHIAccessException!
|
177
|
+
pi.allow_phi "Ash", "Testing PHI Attrs" { pi.last_name }
|
178
|
+
# "Ketchum"
|
179
|
+
```
|
180
|
+
|
181
|
+
### Extending PHI Access
|
182
|
+
|
183
|
+
Sometimes you'll have a single mental model that is composed of several `ActiveRecord` models joined by association. In this case, instead of calling `allow_phi!` on all joined models, we expose a shorthand of extending PHI access to related models.
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
class PatientInfo < ActiveRecord::Base
|
187
|
+
phi_model
|
188
|
+
end
|
189
|
+
|
190
|
+
class Patient < ActiveRecord::Base
|
191
|
+
has_one :patient_info
|
192
|
+
|
193
|
+
phi_model
|
194
|
+
|
195
|
+
extend_phi_access :patient_info
|
196
|
+
end
|
197
|
+
|
198
|
+
patient = Patient.new
|
199
|
+
patient.allow_phi!('user@example.com', 'reason')
|
200
|
+
patient.patient_info.first_name
|
201
|
+
```
|
202
|
+
|
203
|
+
**NOTE:** This is not intended to be used on all relationships! Only those where you intend to grant implicit access based on access to another model. In this use case, we assume that allowed access to `Patient` implies allowed access to `PatientInfo`, and therefore does not require an additional `allow_phi!` check. There are no guaranteed safeguards against circular `extend_phi_access` calls!
|
204
|
+
|
205
|
+
### Check If PHI Access Is Allowed
|
206
|
+
|
207
|
+
To check if PHI is allowed for a particular instance of a class call `phi_allowed?`.
|
208
|
+
|
209
|
+
```ruby
|
210
|
+
patient = Patient.new
|
211
|
+
patient.phi_allowed? # => false
|
212
|
+
|
213
|
+
patient.allow_phi('user@example.com', 'reason') do
|
214
|
+
patient.phi_allowed? # => true
|
215
|
+
end
|
216
|
+
|
217
|
+
patient.phi_allowed? # => false
|
218
|
+
|
219
|
+
patient.allow_phi!('user@example.com', 'reason')
|
220
|
+
patient.phi_allowed? # => true
|
221
|
+
```
|
222
|
+
|
223
|
+
This also works if access was granted at the class level:
|
224
|
+
|
225
|
+
```ruby
|
226
|
+
patient = Patient.new
|
227
|
+
patient.phi_allowed? # => false
|
228
|
+
Patient.allow_phi!('user@example.com', 'reason')
|
229
|
+
patient.phi_allowed? # => true
|
230
|
+
```
|
231
|
+
|
232
|
+
There is also a `phi_allowed?` check available to see at the class level.
|
233
|
+
|
234
|
+
```ruby
|
235
|
+
Patient.phi_allowed? # => false
|
236
|
+
Patient.allow_phi!('user@example.com', 'reason')
|
237
|
+
Patient.phi_allowed? # => true
|
238
|
+
```
|
239
|
+
|
240
|
+
**Note that any instance level access grants will not change class level access:**
|
241
|
+
|
242
|
+
```ruby
|
243
|
+
patient = Patient.new
|
244
|
+
|
245
|
+
patient.phi_allowed? # => false
|
246
|
+
Patient.phi_allowed? # => false
|
247
|
+
|
248
|
+
patient.allow_phi!('user@example.com', 'reason')
|
249
|
+
|
250
|
+
patient.phi_allowed? # => true
|
251
|
+
Patient.phi_allowed? # => false
|
252
|
+
```
|
253
|
+
|
254
|
+
|
255
|
+
### Revoking PHI Access
|
256
|
+
|
257
|
+
You can remove access to PHI with `disallow_phi!`. Each `disallow_phi!` call removes all access granted by `allow_phi!` at that level (class or instance).
|
258
|
+
|
259
|
+
At a class level:
|
260
|
+
|
261
|
+
```ruby
|
262
|
+
Patient.disallow_phi!
|
263
|
+
```
|
264
|
+
|
265
|
+
Or at a instance level:
|
266
|
+
|
267
|
+
```ruby
|
268
|
+
patient.disallow_phi!
|
269
|
+
```
|
270
|
+
|
271
|
+
* *If access is granted at both class and instance level you will need to call `disallow_phi!` twice, once for the instance and once for the class.*
|
272
|
+
|
273
|
+
There is also a block syntax of `disallow_phi` for temporary suppression phi access to the class or instance level
|
274
|
+
|
275
|
+
```ruby
|
276
|
+
patient = PatientInfo.find(params[:id])
|
277
|
+
patient.allow_phi!('allowed_user@example.com', 'Display Patient Data')
|
278
|
+
patient.diallow_phi do
|
279
|
+
@data = patient.to_json # PHIAccessException
|
280
|
+
end # Access is allowed again beyond this point
|
281
|
+
```
|
282
|
+
|
283
|
+
or a block level on a class:
|
284
|
+
|
285
|
+
```ruby
|
286
|
+
PatientInfo.allow_phi!('allowed_user@example.com', 'Display Patient Data')
|
287
|
+
PatientInfo.diallow_phi do
|
288
|
+
@data = PatientInfo.find(params[:id]).to_json # PHIAccessException
|
289
|
+
end # Access is allowed again beyond this point
|
290
|
+
```
|
291
|
+
|
292
|
+
* *Reminder instance level `phi_allow` will take precedent over a class level `disallow_phi`*
|
293
|
+
|
294
|
+
### Manual PHI Access Logging
|
295
|
+
|
296
|
+
If you aren't using `phi_record` you can still use `phi_attrs` to manually log phi access in your application. Where ever you are granting PHI access call:
|
297
|
+
|
298
|
+
```ruby
|
299
|
+
user = 'user@example.com'
|
300
|
+
message = 'accessed list of all patients'
|
301
|
+
PhiAttrs.log_phi_access(user, message)
|
302
|
+
```
|
303
|
+
|
304
|
+
### Reason Translations
|
305
|
+
|
306
|
+
It can get cumbersome to pass around PHI Access reasons. PHI Attrs allows you to
|
307
|
+
use your translations file to keep your code dry. If your translation file
|
308
|
+
contains a reason for the combination of controller, action, and model you can
|
309
|
+
skip passing `reason`:
|
310
|
+
|
311
|
+
```ruby
|
312
|
+
module Admin
|
313
|
+
class PatientDashboardController < ApplicationController
|
314
|
+
def expelliarmus
|
315
|
+
patient_info.allow_phi(current_user) do
|
316
|
+
# reason tries to use `phi.admin.patient_dashbaord.expelliarmus.patient_info`
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
def leviosa
|
321
|
+
patient_info.allow_phi(current_user) do
|
322
|
+
# reason tries to use `phi.admin.patient_dashbaord.expelliarmus.patient_info`
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
```
|
328
|
+
|
329
|
+
The following `en.yml` file would work:
|
330
|
+
|
331
|
+
```yml
|
332
|
+
en:
|
333
|
+
phi:
|
334
|
+
admin:
|
335
|
+
patient_dashboard:
|
336
|
+
expelliarmus:
|
337
|
+
patient_info: "Patient Disarmed"
|
338
|
+
leviosa:
|
339
|
+
patient_info: "Patient Levitated"
|
340
|
+
```
|
341
|
+
|
342
|
+
If you have a typo in your en.yml file or you choose not to provide a translation
|
343
|
+
for your phi reasons your code will fail with an ArgumentError. To assist you in
|
344
|
+
debugging PHI Attrs will print a `:warn` message with the expected location for
|
345
|
+
the missing translation.
|
346
|
+
|
347
|
+
If you would like to change from `phi` to a custom location you can set the path in your initializer.
|
348
|
+
|
349
|
+
```ruby
|
350
|
+
PhiAttrs.configure do |conf|
|
351
|
+
conf.translation_prefix = 'custom_prefix'
|
352
|
+
end
|
353
|
+
```
|
354
|
+
|
355
|
+
### Default User
|
356
|
+
|
357
|
+
Passing around the current user can clutter your code. PHI Attrs allows you to
|
358
|
+
configure a controller method that will be called to get the currently logged in
|
359
|
+
user:
|
360
|
+
|
361
|
+
#### `config/initializers/phi_attrs.rb`
|
362
|
+
|
363
|
+
```ruby
|
364
|
+
PhiAttrs.configure do |conf|
|
365
|
+
conf.current_user_method = :user_email
|
366
|
+
end
|
367
|
+
```
|
368
|
+
|
369
|
+
#### `app/controllers/home_controller.rb`
|
370
|
+
|
371
|
+
```ruby
|
372
|
+
class ApplicationController < ActionController::Base
|
373
|
+
private
|
374
|
+
|
375
|
+
def user_email
|
376
|
+
current_user&.email
|
377
|
+
end
|
378
|
+
end
|
379
|
+
```
|
380
|
+
|
381
|
+
With the above code, any call to `allow_phi` (that starts in a controller
|
382
|
+
derived from ApplicationController) will use the result of `user_email` as the
|
383
|
+
user argument of `allow_phi`.
|
384
|
+
|
385
|
+
Note that if you have a default user, but choose not to use translations for
|
386
|
+
reasons you'll have to pass `nil` as the user:
|
387
|
+
|
388
|
+
```ruby
|
389
|
+
person_phi.allow_phi(nil, "Because I felt like looking at PHI") do
|
390
|
+
# Allows PHI
|
391
|
+
end
|
392
|
+
```
|
393
|
+
|
394
|
+
## Best Practices
|
395
|
+
|
396
|
+
* Mix and matching `instance`, `class` and `block` syntaxes for allowing/denying PHI is not recommended.
|
397
|
+
* Sticking with one style in your application will make it easier to understand what access is granted and where.
|
398
|
+
|
45
399
|
## Development
|
46
400
|
|
47
|
-
|
401
|
+
It is recommended to use the provided `docker-compose` environment for development to help ensure dependency consistency and code isolation from other projects you may be working on.
|
402
|
+
|
403
|
+
### Begin
|
404
|
+
|
405
|
+
$ docker-compose up
|
406
|
+
$ bin/ssh_to_container
|
407
|
+
|
408
|
+
### Tests
|
409
|
+
|
410
|
+
Tests are written using [RSpec](http://rspec.info/) and are setup to use [Appraisal](https://github.com/thoughtbot/appraisal) to run tests over multiple rails versions.
|
411
|
+
|
412
|
+
$ bin/run_tests
|
413
|
+
or for individual tests:
|
414
|
+
$ bin/ssh_to_container
|
415
|
+
$ bundle exec appraisal rspec spec/path/to/spec.rb
|
416
|
+
|
417
|
+
To run just a particular rails version:
|
418
|
+
$ bundle exec appraisal rails-5.1 rspec
|
419
|
+
$ bundle exec appraisal rails-5.2 rspec
|
420
|
+
$ bundle exec appraisal rails-6.0 rspec
|
421
|
+
|
422
|
+
### Console
|
48
423
|
|
49
|
-
|
424
|
+
An interactive prompt that will allow you to experiment with the gem.
|
50
425
|
|
51
|
-
|
426
|
+
$ bin/ssh_to_container
|
427
|
+
$ bin/console
|
52
428
|
|
53
|
-
|
54
|
-
* `bin/ssh_to_container`
|
429
|
+
### Local Install
|
55
430
|
|
56
|
-
|
431
|
+
Run `bin/setup` to install dependencies. Then, run `bundle exec appraisal rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
432
|
+
|
433
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
434
|
+
|
435
|
+
### Versioning
|
436
|
+
|
437
|
+
To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
57
438
|
|
58
|
-
$ bundle exec appraisal rspec spec/phi_attrs_spec.rb
|
59
439
|
|
60
440
|
## Contributing
|
61
441
|
|
62
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
442
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/apsislabs/phi_attrs.
|
443
|
+
|
444
|
+
Any PRs should be accompanied with documentation in `README.md`, and changes documented in [`CHANGELOG.md`](https://keepachangelog.com/).
|
63
445
|
|
64
446
|
## License
|
65
447
|
|
66
448
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
449
|
+
|
450
|
+
## Legal Disclaimer
|
451
|
+
|
452
|
+
Apsis Labs, LLP is not a law firm and does not provide legal advice. The information in this repo and software does not constitute legal advice, nor does usage of this software create an attorney-client relationship.
|
453
|
+
|
454
|
+
Apsis Labs, LLP is not a HIPAA covered entity, and usage of this software does not create a business associate relationship, nor does it enact a business associate agreement.
|
455
|
+
|
456
|
+
[Full Disclaimer](./DISCLAIMER.txt)
|
457
|
+
|
458
|
+
---
|
459
|
+
|
460
|
+
# Built by Apsis
|
461
|
+
|
462
|
+
[![apsis](https://s3-us-west-2.amazonaws.com/apsiscdn/apsis.png)](https://www.apsis.io)
|
463
|
+
|
464
|
+
`phi_attrs` was built by Apsis Labs. We love sharing what we build! Check out our [other libraries on Github](https://github.com/apsislabs), and if you like our work you can [hire us](https://www.apsis.io/work-with-us/) to build your vision.
|