health_cards 0.0.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/health_cards/attribute_filters.rb +8 -8
- data/lib/health_cards/chunk.rb +16 -76
- data/lib/health_cards/errors.rb +1 -1
- data/lib/health_cards/exporter.rb +1 -1
- data/lib/health_cards/health_card.rb +45 -179
- data/lib/health_cards/importer.rb +1 -1
- data/lib/health_cards/issuer.rb +10 -5
- data/lib/health_cards/jws.rb +6 -3
- data/lib/health_cards/key_set.rb +1 -1
- data/lib/health_cards/payload.rb +216 -0
- data/lib/health_cards/payload_types/covid_immunization_payload.rb +11 -0
- data/lib/health_cards/payload_types/covid_lab_result_payload.rb +11 -0
- data/lib/health_cards/payload_types/covid_payload.rb +13 -0
- data/lib/health_cards/{card_types.rb → payload_types.rb} +9 -12
- data/lib/health_cards/verification.rb +3 -3
- data/lib/health_cards/verifier.rb +2 -2
- data/lib/health_cards/version.rb +1 -1
- data/lib/health_cards.rb +5 -2
- metadata +29 -12
- data/lib/health_cards/covid_health_card.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 68a66d710cdd4d93e04b20f5dfdd9391523c3499b1d1ce2b006ed0bf5bb9ab65
|
4
|
+
data.tar.gz: 1b6d3a3c8819a1815aa4b9950610693b5f941c17ac568f5213c2735c776ba89f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d4da867fb565f3cea48355d94edf36f30e6cccf59b15c85219b1aab54bf7b7172fe8482e7571a57a3322abe9cbaf11c69fe10350e3a94f60d87516b5ba9fb2b
|
7
|
+
data.tar.gz: e1ab50714275959ebcdef0065a2048d7fa0ec5147b2cb4aca0461999c4c3775eb118cc1c84b7a2167058e42288708495975a2df7ebf8df24a90d77b44b482736
|
@@ -9,9 +9,9 @@ module HealthCards
|
|
9
9
|
base.extend ClassMethods
|
10
10
|
end
|
11
11
|
|
12
|
-
# Class level methods for
|
12
|
+
# Class level methods for Payload class specific settings
|
13
13
|
module ClassMethods
|
14
|
-
# Define allowed attributes for this
|
14
|
+
# Define allowed attributes for this Payload class
|
15
15
|
# @param type [Class] Scopes the attributes to a spefic class. Must be a subclass of FHIR::Model
|
16
16
|
# @param attributes [Array] An array of string with the attribute names that will be passed through
|
17
17
|
# when data is minimized
|
@@ -19,7 +19,7 @@ module HealthCards
|
|
19
19
|
allowable[type] = attributes
|
20
20
|
end
|
21
21
|
|
22
|
-
# Define disallowed attributes for this
|
22
|
+
# Define disallowed attributes for this Payload class
|
23
23
|
# @param type [Class] Scopes the attributes to a spefic class. If not used will default to all FHIR resources.
|
24
24
|
# To apply a rule to all FHIR types (resources and types), use FHIR::Model as the type
|
25
25
|
# @param attributes [Array] An array of string with the attribute names that will be passed through
|
@@ -29,7 +29,7 @@ module HealthCards
|
|
29
29
|
disallowable[type].concat(attributes)
|
30
30
|
end
|
31
31
|
|
32
|
-
# Define disallowed attributes for this
|
32
|
+
# Define disallowed attributes for this Payload class
|
33
33
|
# @return [Hash] A hash of FHIR::Model subclasses and attributes that will pass through minimization
|
34
34
|
def disallowable
|
35
35
|
return @disallowable if @disallowable
|
@@ -37,7 +37,7 @@ module HealthCards
|
|
37
37
|
@disallowable = parent_disallowables
|
38
38
|
end
|
39
39
|
|
40
|
-
# Define allowed attributes for this
|
40
|
+
# Define allowed attributes for this Payload class
|
41
41
|
# @return [Hash] A hash of FHIR::Model subclasses and attributes that will pass through minimization
|
42
42
|
def allowable
|
43
43
|
return @allowable if @allowable
|
@@ -48,11 +48,11 @@ module HealthCards
|
|
48
48
|
protected
|
49
49
|
|
50
50
|
def parent_allowables(base = {})
|
51
|
-
self < HealthCards::
|
51
|
+
self < HealthCards::Payload ? base.merge(superclass.allowable) : base
|
52
52
|
end
|
53
53
|
|
54
54
|
def parent_disallowables(base = {})
|
55
|
-
self < HealthCards::
|
55
|
+
self < HealthCards::Payload ? base.merge(superclass.disallowable) : base
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
@@ -61,7 +61,7 @@ module HealthCards
|
|
61
61
|
|
62
62
|
return unless class_allowables
|
63
63
|
|
64
|
-
allowed = resource.to_hash.
|
64
|
+
allowed = resource.to_hash.keep_if { |att| class_allowables.include?(att) }
|
65
65
|
|
66
66
|
resource.from_hash(allowed)
|
67
67
|
end
|
data/lib/health_cards/chunk.rb
CHANGED
@@ -1,92 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# rubocop:disable Lint/MissingSuper
|
4
3
|
module HealthCards
|
5
4
|
# Represents a single QRCode in a sequence. This class is a shim to the RQRCode library
|
6
5
|
# to enable multimode encoding
|
7
|
-
class Chunk
|
8
|
-
attr_reader :ordinal
|
6
|
+
class Chunk
|
7
|
+
attr_reader :ordinal, :data, :qrcode
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
end
|
9
|
+
SINGLE_REGEX = %r{shc:/}.freeze
|
10
|
+
MULTI_REGEX = %r{shc:/[0-9]*/[0-9]*/}.freeze
|
13
11
|
|
14
|
-
def initialize(ordinal: 1
|
12
|
+
def initialize(input:, ordinal: 1)
|
15
13
|
@ordinal = ordinal
|
16
|
-
@qrcode = ChunkCore.new(input)
|
17
|
-
end
|
18
|
-
|
19
|
-
def image
|
20
|
-
as_png(
|
21
|
-
border_modules: 1,
|
22
|
-
module_px_size: 2
|
23
|
-
)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
# RQRCodeCore shim for to enable multimode encoding
|
28
|
-
class ChunkCore < RQRCodeCore::QRCode
|
29
|
-
attr_accessor :data
|
30
|
-
|
31
|
-
def initialize(input)
|
32
14
|
@data = input
|
33
|
-
|
34
|
-
@version = 22
|
35
|
-
@module_count = @version * 4 + RQRCodeCore::QRPOSITIONPATTERNLENGTH
|
36
|
-
@modules = Array.new(@module_count)
|
37
|
-
@data_list = SHCQRCode.new(@data)
|
38
|
-
@data_cache = nil
|
39
|
-
make
|
40
|
-
end
|
41
|
-
|
42
|
-
# RQRCodeCore data shim for multimode encoding
|
43
|
-
class SHCQRCode
|
44
|
-
SINGLE_REGEX = %r{shc:/}.freeze
|
45
|
-
MULTI_REGEX = %r{shc:/[0-9]*/[0-9]*/}.freeze
|
46
|
-
|
47
|
-
def initialize(data)
|
48
|
-
@data = data
|
49
|
-
end
|
15
|
+
multi = MULTI_REGEX.match(input)
|
50
16
|
|
51
|
-
|
52
|
-
|
53
|
-
prefix = multi ? multi.to_s : SINGLE_REGEX.match(@data).to_s
|
17
|
+
prefix = multi ? multi.to_s : SINGLE_REGEX.match(input).to_s
|
18
|
+
content = input.delete_prefix(prefix)
|
54
19
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
buffer.put(b, 8)
|
59
|
-
end
|
60
|
-
|
61
|
-
num_content = @data.delete_prefix(prefix)
|
62
|
-
|
63
|
-
buffer.numeric_encoding_start(num_content.length)
|
64
|
-
|
65
|
-
num_content.size.times do |i|
|
66
|
-
next unless (i % 3).zero?
|
67
|
-
|
68
|
-
chars = num_content[i, 3]
|
69
|
-
bit_length = get_bit_length(chars.length)
|
70
|
-
buffer.put(get_code(chars), bit_length)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
private
|
75
|
-
|
76
|
-
NUMBER_LENGTH = {
|
77
|
-
3 => 10,
|
78
|
-
2 => 7,
|
79
|
-
1 => 4
|
80
|
-
}.freeze
|
20
|
+
@qrcode = RQRCode::QRCode.new([{ mode: :byte_8bit, data: prefix }, { mode: :number, data: content }],
|
21
|
+
max_size: 22, level: :l)
|
22
|
+
end
|
81
23
|
|
82
|
-
|
83
|
-
|
84
|
-
|
24
|
+
def qr_code
|
25
|
+
@qrcode.qrcode
|
26
|
+
end
|
85
27
|
|
86
|
-
|
87
|
-
|
88
|
-
end
|
28
|
+
def image
|
29
|
+
@qrcode.as_png(module_px_size: 2)
|
89
30
|
end
|
90
31
|
end
|
91
32
|
end
|
92
|
-
# rubocop:enable Lint/MissingSuper
|
data/lib/health_cards/errors.rb
CHANGED
@@ -25,7 +25,7 @@ module HealthCards
|
|
25
25
|
|
26
26
|
class UnresolvableKeySetError < JWSError; end
|
27
27
|
|
28
|
-
# Errors related to
|
28
|
+
# Errors related to Payload / Bundle
|
29
29
|
|
30
30
|
# Exception thrown when an invalid payload is provided
|
31
31
|
class InvalidPayloadError < HealthCardError
|
@@ -7,7 +7,7 @@ module HealthCards
|
|
7
7
|
# Export JWS for file download
|
8
8
|
# @param [Array<JWS, String>] An array of JWS objects to be exported
|
9
9
|
# @return [String] JSON string containing file download contents
|
10
|
-
def
|
10
|
+
def file_download(jws)
|
11
11
|
{ verifiableCredential: jws.map(&:to_s) }.to_json
|
12
12
|
end
|
13
13
|
|
@@ -1,203 +1,69 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'zlib'
|
4
|
-
require 'uri'
|
5
|
-
|
6
|
-
require 'health_cards/attribute_filters'
|
7
|
-
require 'health_cards/card_types'
|
8
|
-
|
9
3
|
module HealthCards
|
10
|
-
#
|
4
|
+
# Represents a signed SMART Health Card
|
11
5
|
class HealthCard
|
12
|
-
|
13
|
-
extend HealthCards::CardTypes
|
14
|
-
|
15
|
-
FHIR_REF_REGEX = %r{((http|https)://([A-Za-z0-9\-\\.:%$]*/)+)?(
|
16
|
-
Account|ActivityDefinition|AdverseEvent|AllergyIntolerance|Appointment|AppointmentResponse|AuditEvent|Basic|
|
17
|
-
Binary|BiologicallyDerivedProduct|BodyStructure|Bundle|CapabilityStatement|CarePlan|CareTeam|CatalogEntry|
|
18
|
-
ChargeItem|ChargeItemDefinition|Claim|ClaimResponse|ClinicalImpression|CodeSystem|Communication|
|
19
|
-
CommunicationRequest|CompartmentDefinition|Composition|ConceptMap|Condition|Consent|Contract|Coverage|
|
20
|
-
CoverageEligibilityRequest|CoverageEligibilityResponse|DetectedIssue|Device|DeviceDefinition|DeviceMetric
|
21
|
-
|DeviceRequest|DeviceUseStatement|DiagnosticReport|DocumentManifest|DocumentReference|EffectEvidenceSynthesis|
|
22
|
-
Encounter|Endpoint|EnrollmentRequest|EnrollmentResponse|EpisodeOfCare|EventDefinition|Evidence|EvidenceVariable|
|
23
|
-
ExampleScenario|ExplanationOfBenefit|FamilyMemberHistory|Flag|Goal|GraphDefinition|Group|GuidanceResponse|
|
24
|
-
HealthcareService|ImagingStudy|Immunization|ImmunizationEvaluation|ImmunizationRecommendation|
|
25
|
-
ImplementationGuide|InsurancePlan|Invoice|Library|Linkage|List|Location|Measure|MeasureReport|Media|Medication|
|
26
|
-
MedicationAdministration|MedicationDispense|MedicationKnowledge|MedicationRequest|MedicationStatement|
|
27
|
-
MedicinalProduct|MedicinalProductAuthorization|MedicinalProductContraindication|MedicinalProductIndication|
|
28
|
-
MedicinalProductIngredient|MedicinalProductInteraction|MedicinalProductManufactured|MedicinalProductPackaged|
|
29
|
-
MedicinalProductPharmaceutical|MedicinalProductUndesirableEffect|MessageDefinition|MessageHeader|
|
30
|
-
MolecularSequence|NamingSystem|NutritionOrder|Observation|ObservationDefinition|OperationDefinition|
|
31
|
-
OperationOutcome|Organization|OrganizationAffiliation|Patient|PaymentNotice|PaymentReconciliation|Person|
|
32
|
-
PlanDefinition|Practitioner|PractitionerRole|Procedure|Provenance|Questionnaire|QuestionnaireResponse|
|
33
|
-
RelatedPerson|RequestGroup|ResearchDefinition|ResearchElementDefinition|ResearchStudy|ResearchSubject|
|
34
|
-
RiskAssessment|RiskEvidenceSynthesis|Schedule|SearchParameter|ServiceRequest|Slot|Specimen|SpecimenDefinition|
|
35
|
-
StructureDefinition|StructureMap|Subscription|Substance|SubstanceNucleicAcid|SubstancePolymer|SubstanceProtein|
|
36
|
-
SubstanceReferenceInformation|SubstanceSourceMaterial|SubstanceSpecification|SupplyDelivery|SupplyRequest|Task|
|
37
|
-
TerminologyCapabilities|TestReport|TestScript|ValueSet|VerificationResult|VisionPrescription)/
|
38
|
-
[A-Za-z0-9\-.]{1,64}(/_history/[A-Za-z0-9\-.]{1,64})?}x.freeze
|
39
|
-
|
40
|
-
attr_reader :issuer, :nbf, :bundle
|
41
|
-
|
42
|
-
class << self
|
43
|
-
# Creates a HealthCard from a JWS
|
44
|
-
# @param jws [String] the JWS string
|
45
|
-
# @param public_key [HealthCards::PublicKey] the public key associated with the JWS
|
46
|
-
# @param key [HealthCards::PrivateKey] the private key associated with the JWS
|
47
|
-
# @return [HealthCards::HealthCard]
|
48
|
-
def from_jws(jws, public_key: nil, key: nil)
|
49
|
-
jws = JWS.from_jws(jws, public_key: public_key, key: key)
|
50
|
-
from_payload(jws.payload)
|
51
|
-
end
|
52
|
-
|
53
|
-
# Create a HealthCard from a compressed payload
|
54
|
-
# @param payload [String]
|
55
|
-
# @return [HealthCards::HealthCard]
|
56
|
-
def from_payload(payload)
|
57
|
-
json = decompress_payload(payload)
|
58
|
-
bundle_hash = json.dig('vc', 'credentialSubject', 'fhirBundle')
|
59
|
-
|
60
|
-
raise HealthCards::InvalidCredentialError unless bundle_hash
|
61
|
-
|
62
|
-
bundle = FHIR::Bundle.new(bundle_hash)
|
63
|
-
new(issuer: json['iss'], bundle: bundle)
|
64
|
-
end
|
65
|
-
|
66
|
-
# Decompress an arbitrary payload, useful for debugging
|
67
|
-
# @param payload [String] compressed payload
|
68
|
-
# @return [Hash] Hash built from JSON contents of payload
|
69
|
-
def decompress_payload(payload)
|
70
|
-
inf = Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate(payload)
|
71
|
-
JSON.parse(inf)
|
72
|
-
end
|
73
|
-
|
74
|
-
# Compress an arbitrary payload, useful for debugging
|
75
|
-
# @param payload [Object] Any object that responds to to_s
|
76
|
-
# @return A compressed version of that payload parameter
|
77
|
-
def compress_payload(payload)
|
78
|
-
Zlib::Deflate.new(nil, -Zlib::MAX_WBITS).deflate(payload.to_s, Zlib::FINISH)
|
79
|
-
end
|
80
|
-
|
81
|
-
# Sets/Gets the fhir version that will be passed through to the credential created by an instnace of
|
82
|
-
# this HealthCard (sub)class
|
83
|
-
# @param ver [String] FHIR Version supported by this HealthCard (sub)class. Leaving this param out
|
84
|
-
# will only return the current value
|
85
|
-
# value (used as a getter)
|
86
|
-
# @return [String] Current FHIR version supported
|
87
|
-
def fhir_version(ver = nil)
|
88
|
-
@fhir_version ||= ver unless ver.nil?
|
89
|
-
@fhir_version
|
90
|
-
end
|
91
|
-
end
|
6
|
+
extend Forwardable
|
92
7
|
|
93
|
-
|
8
|
+
attr_reader :jws
|
94
9
|
|
95
|
-
|
96
|
-
|
97
|
-
disallow type: FHIR::Coding, attributes: %w[display]
|
10
|
+
def_delegator :@qr_codes, :code_by_ordinal
|
11
|
+
def_delegators :@payload, :bundle, :issuer
|
98
12
|
|
99
|
-
# Create a HealthCard
|
100
|
-
#
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
@issuer = issuer
|
107
|
-
@bundle = bundle
|
13
|
+
# Create a HealthCard from a JWS
|
14
|
+
# @param jws [JWS, String] A JWS object or JWS string
|
15
|
+
def initialize(jws)
|
16
|
+
@jws = JWS.from_jws(jws)
|
17
|
+
@payload = Payload.from_payload(@jws.payload)
|
18
|
+
@qr_codes = QRCodes.from_jws(@jws)
|
108
19
|
end
|
109
20
|
|
110
|
-
#
|
111
|
-
# @return [
|
112
|
-
def
|
113
|
-
|
114
|
-
iss: issuer,
|
115
|
-
nbf: Time.now.to_i,
|
116
|
-
vc: {
|
117
|
-
type: self.class.types,
|
118
|
-
credentialSubject: {
|
119
|
-
fhirVersion: self.class.fhir_version,
|
120
|
-
fhirBundle: strip_fhir_bundle.to_hash
|
121
|
-
}
|
122
|
-
}
|
123
|
-
}
|
21
|
+
# Export HealthCard as JSON, formatted for file downloads
|
22
|
+
# @return [String] JSON string containing file download contents
|
23
|
+
def to_json(*_args)
|
24
|
+
Exporter.file_download([@jws])
|
124
25
|
end
|
125
26
|
|
126
|
-
#
|
127
|
-
#
|
128
|
-
|
129
|
-
|
130
|
-
HealthCard.compress_payload(to_json)
|
27
|
+
# QR Codes representing this HealthCard
|
28
|
+
# @return [Array<Chunk>] an array of QR Code chunks
|
29
|
+
def qr_codes
|
30
|
+
@qr_codes.chunks
|
131
31
|
end
|
132
32
|
|
133
|
-
#
|
134
|
-
#
|
135
|
-
|
136
|
-
|
33
|
+
# Extracts a resource from the bundle contained in the HealthCard. A filter
|
34
|
+
# can be applied by using a block. The method will yield each resource to the block.
|
35
|
+
# The block should return a boolean
|
36
|
+
# @param type [Class] :type should be a class representing a FHIR resource
|
37
|
+
# @return The first bundle resource that matches the type and/or block evaluation
|
38
|
+
def resource(type: nil, &block)
|
39
|
+
resources(type: type, &block).first
|
137
40
|
end
|
138
41
|
|
139
|
-
#
|
140
|
-
# a
|
141
|
-
#
|
142
|
-
#
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
entry.each_element do |value, metadata, _|
|
151
|
-
case metadata['type']
|
152
|
-
when 'Reference'
|
153
|
-
value.reference = process_reference(url_map, entry, value)
|
154
|
-
when 'Resource'
|
155
|
-
value.meta = nil unless value.meta&.security
|
156
|
-
end
|
157
|
-
|
158
|
-
handle_allowable(value)
|
159
|
-
handle_disallowable(value)
|
160
|
-
end
|
42
|
+
# Extracts all resources from the bundle contained in the HealthCard. A filter
|
43
|
+
# can be applied by using a block. The method will yield each resource to the block.
|
44
|
+
# The block should return a boolean
|
45
|
+
# @param type [Class] :type should be a class representing a FHIR resource
|
46
|
+
# @return The first bundle resource that matches the type and/or block evaluation
|
47
|
+
def resources(type: nil, &block)
|
48
|
+
all_resources = bundle.entry.map(&:resource)
|
49
|
+
return all_resources unless type || block
|
50
|
+
|
51
|
+
all_resources.filter do |r|
|
52
|
+
resource_matches_criteria(r, type, &block)
|
161
53
|
end
|
162
|
-
|
163
|
-
new_bundle
|
164
54
|
end
|
165
55
|
|
166
56
|
private
|
167
57
|
|
168
|
-
def
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
old_url = entry.fullUrl
|
177
|
-
new_url = "resource:#{resource_count}"
|
178
|
-
url_map[old_url] = new_url
|
179
|
-
entry.fullUrl = new_url
|
180
|
-
resource_count += 1
|
58
|
+
def resource_matches_criteria(resource, type, &block)
|
59
|
+
of_type = type && resource.is_a?(type)
|
60
|
+
if block && type
|
61
|
+
of_type && yield(resource)
|
62
|
+
elsif !type && block
|
63
|
+
yield(resource)
|
64
|
+
else
|
65
|
+
of_type
|
181
66
|
end
|
182
|
-
url_map
|
183
|
-
end
|
184
|
-
|
185
|
-
def process_reference(url_map, entry, ref)
|
186
|
-
entry_url = URI(url_map.key(entry.fullUrl))
|
187
|
-
ref_url = ref.reference
|
188
|
-
|
189
|
-
return unless ref_url
|
190
|
-
|
191
|
-
return url_map[ref_url] if url_map[ref_url]
|
192
|
-
|
193
|
-
fhir_base_url = FHIR_REF_REGEX.match(entry_url.to_s)[1]
|
194
|
-
full_url = URI.join(fhir_base_url, ref_url).to_s
|
195
|
-
|
196
|
-
new_url = url_map[full_url]
|
197
|
-
|
198
|
-
raise InvalidBundleReferenceError, full_url unless new_url
|
199
|
-
|
200
|
-
new_url
|
201
67
|
end
|
202
68
|
end
|
203
69
|
end
|
@@ -27,7 +27,7 @@ module HealthCards
|
|
27
27
|
# @return [Hash] Hash containing the JWS payload and verification contents
|
28
28
|
private_class_method def self.verify_jws(jws_string)
|
29
29
|
jws = JWS.from_jws jws_string
|
30
|
-
result = { payload:
|
30
|
+
result = { payload: Payload.decompress_payload(jws.payload) }
|
31
31
|
begin
|
32
32
|
result[:verified] = Verifier.verify jws
|
33
33
|
result[:error_message] = 'Signature Invalid' if result[:verified] == false
|
data/lib/health_cards/issuer.rb
CHANGED
@@ -16,11 +16,11 @@ module HealthCards
|
|
16
16
|
self.key = key
|
17
17
|
end
|
18
18
|
|
19
|
-
# Create a
|
19
|
+
# Create a Payload from the supplied FHIR bundle
|
20
20
|
#
|
21
21
|
# @param bundle [FHIR::Bundle, String] the FHIR bundle used as the Health Card payload
|
22
|
-
# @return [
|
23
|
-
def
|
22
|
+
# @return [Payload::]
|
23
|
+
def create_payload(bundle, type: Payload)
|
24
24
|
type.new(issuer: url, bundle: bundle)
|
25
25
|
end
|
26
26
|
|
@@ -30,11 +30,16 @@ module HealthCards
|
|
30
30
|
# @param type [Class] A subclass of HealthCards::Card that processes the bundle according to a specific IG.
|
31
31
|
# Leave blank for default SMART Health Card behavior
|
32
32
|
# @return [HealthCards::JWS] An instance of JWS using the payload and signed by the issuer's private key
|
33
|
-
def issue_jws(bundle, type:
|
34
|
-
card =
|
33
|
+
def issue_jws(bundle, type: Payload)
|
34
|
+
card = create_payload(bundle, type: type)
|
35
35
|
JWS.new(header: jws_header, payload: card.to_s, key: key)
|
36
36
|
end
|
37
37
|
|
38
|
+
def issue_health_card(bundle, type: Payload)
|
39
|
+
jws = issue_jws(bundle, type: type)
|
40
|
+
HealthCards::HealthCard.new(jws)
|
41
|
+
end
|
42
|
+
|
38
43
|
# Set the private key used for signing issued health cards
|
39
44
|
#
|
40
45
|
# @param key [HealthCards::PrivateKey, nil] the private key used for signing issued health cards
|
data/lib/health_cards/jws.rb
CHANGED
@@ -6,12 +6,15 @@ module HealthCards
|
|
6
6
|
class << self
|
7
7
|
include Encoding
|
8
8
|
|
9
|
-
# Creates a
|
10
|
-
#
|
9
|
+
# Creates a JWS from a String representation, or returns the HealthCards::JWS object
|
10
|
+
# that was passed in
|
11
|
+
# @param jws [String, HealthCards::JWS] the JWS string, or a JWS object
|
11
12
|
# @param public_key [HealthCards::PublicKey] the public key associated with the JWS
|
12
13
|
# @param key [HealthCards::PrivateKey] the private key associated with the JWS
|
13
|
-
# @return [HealthCards::
|
14
|
+
# @return [HealthCards::JWS] A new JWS object, or the JWS object that was passed in
|
14
15
|
def from_jws(jws, public_key: nil, key: nil)
|
16
|
+
return jws if jws.is_a?(HealthCards::JWS) && public_key.nil? && key.nil?
|
17
|
+
|
15
18
|
unless jws.is_a?(HealthCards::JWS) || jws.is_a?(String)
|
16
19
|
raise ArgumentError,
|
17
20
|
'Expected either a HealthCards::JWS or String'
|
data/lib/health_cards/key_set.rb
CHANGED
@@ -44,7 +44,7 @@ module HealthCards
|
|
44
44
|
# Retrieves a key from the keyset with a kid
|
45
45
|
# that matches the parameter
|
46
46
|
# @param kid [String] a Base64 encoded kid from a JWS or Key
|
47
|
-
# @return [
|
47
|
+
# @return [Payload::Key] a key with a matching kid or nil if not found
|
48
48
|
def find_key(kid)
|
49
49
|
@key_map[kid]
|
50
50
|
end
|
@@ -0,0 +1,216 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'zlib'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
require 'health_cards/attribute_filters'
|
7
|
+
require 'health_cards/payload_types'
|
8
|
+
|
9
|
+
module HealthCards
|
10
|
+
# A Payload which implements the credential claims specified by https://smarthealth.cards/
|
11
|
+
class Payload
|
12
|
+
include HealthCards::AttributeFilters
|
13
|
+
extend HealthCards::PayloadTypes
|
14
|
+
|
15
|
+
FHIR_REF_REGEX = %r{((http|https)://([A-Za-z0-9\-\\.:%$]*/)+)?(
|
16
|
+
Account|ActivityDefinition|AdverseEvent|AllergyIntolerance|Appointment|AppointmentResponse|AuditEvent|Basic|
|
17
|
+
Binary|BiologicallyDerivedProduct|BodyStructure|Bundle|CapabilityStatement|CarePlan|CareTeam|CatalogEntry|
|
18
|
+
ChargeItem|ChargeItemDefinition|Claim|ClaimResponse|ClinicalImpression|CodeSystem|Communication|
|
19
|
+
CommunicationRequest|CompartmentDefinition|Composition|ConceptMap|Condition|Consent|Contract|Coverage|
|
20
|
+
CoverageEligibilityRequest|CoverageEligibilityResponse|DetectedIssue|Device|DeviceDefinition|DeviceMetric
|
21
|
+
|DeviceRequest|DeviceUseStatement|DiagnosticReport|DocumentManifest|DocumentReference|EffectEvidenceSynthesis|
|
22
|
+
Encounter|Endpoint|EnrollmentRequest|EnrollmentResponse|EpisodeOfCare|EventDefinition|Evidence|EvidenceVariable|
|
23
|
+
ExampleScenario|ExplanationOfBenefit|FamilyMemberHistory|Flag|Goal|GraphDefinition|Group|GuidanceResponse|
|
24
|
+
HealthcareService|ImagingStudy|Immunization|ImmunizationEvaluation|ImmunizationRecommendation|
|
25
|
+
ImplementationGuide|InsurancePlan|Invoice|Library|Linkage|List|Location|Measure|MeasureReport|Media|Medication|
|
26
|
+
MedicationAdministration|MedicationDispense|MedicationKnowledge|MedicationRequest|MedicationStatement|
|
27
|
+
MedicinalProduct|MedicinalProductAuthorization|MedicinalProductContraindication|MedicinalProductIndication|
|
28
|
+
MedicinalProductIngredient|MedicinalProductInteraction|MedicinalProductManufactured|MedicinalProductPackaged|
|
29
|
+
MedicinalProductPharmaceutical|MedicinalProductUndesirableEffect|MessageDefinition|MessageHeader|
|
30
|
+
MolecularSequence|NamingSystem|NutritionOrder|Observation|ObservationDefinition|OperationDefinition|
|
31
|
+
OperationOutcome|Organization|OrganizationAffiliation|Patient|PaymentNotice|PaymentReconciliation|Person|
|
32
|
+
PlanDefinition|Practitioner|PractitionerRole|Procedure|Provenance|Questionnaire|QuestionnaireResponse|
|
33
|
+
RelatedPerson|RequestGroup|ResearchDefinition|ResearchElementDefinition|ResearchStudy|ResearchSubject|
|
34
|
+
RiskAssessment|RiskEvidenceSynthesis|Schedule|SearchParameter|ServiceRequest|Slot|Specimen|SpecimenDefinition|
|
35
|
+
StructureDefinition|StructureMap|Subscription|Substance|SubstanceNucleicAcid|SubstancePolymer|SubstanceProtein|
|
36
|
+
SubstanceReferenceInformation|SubstanceSourceMaterial|SubstanceSpecification|SupplyDelivery|SupplyRequest|Task|
|
37
|
+
TerminologyCapabilities|TestReport|TestScript|ValueSet|VerificationResult|VisionPrescription)/
|
38
|
+
[A-Za-z0-9\-.]{1,64}(/_history/[A-Za-z0-9\-.]{1,64})?}x.freeze
|
39
|
+
|
40
|
+
attr_reader :issuer, :nbf, :bundle
|
41
|
+
|
42
|
+
class << self
|
43
|
+
# Create a Payload from a compressed payload
|
44
|
+
# @param payload [String]
|
45
|
+
# @return [HealthCards::Payload]
|
46
|
+
def from_payload(payload)
|
47
|
+
json = decompress_payload(payload)
|
48
|
+
bundle_hash = json.dig('vc', 'credentialSubject', 'fhirBundle')
|
49
|
+
|
50
|
+
raise HealthCards::InvalidCredentialError unless bundle_hash
|
51
|
+
|
52
|
+
bundle = extract_bundle(payload)
|
53
|
+
new(issuer: json['iss'], bundle: bundle)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Extract a bundle from a compressed payload
|
57
|
+
# @param payload [String]
|
58
|
+
# @return [FHIR::Bundle]
|
59
|
+
def extract_bundle(payload)
|
60
|
+
json = decompress_payload(payload)
|
61
|
+
bundle_hash = json.dig('vc', 'credentialSubject', 'fhirBundle')
|
62
|
+
|
63
|
+
raise HealthCards::InvalidCredentialError unless bundle_hash
|
64
|
+
|
65
|
+
FHIR::Bundle.new(bundle_hash)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Decompress an arbitrary payload, useful for debugging
|
69
|
+
# @param payload [String] compressed payload
|
70
|
+
# @return [Hash] Hash built from JSON contents of payload
|
71
|
+
def decompress_payload(payload)
|
72
|
+
inf = Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate(payload)
|
73
|
+
JSON.parse(inf)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Compress an arbitrary payload, useful for debugging
|
77
|
+
# @param payload [Object] Any object that responds to to_s
|
78
|
+
# @return A compressed version of that payload parameter
|
79
|
+
def compress_payload(payload)
|
80
|
+
Zlib::Deflate.new(nil, -Zlib::MAX_WBITS).deflate(payload.to_s, Zlib::FINISH)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Sets/Gets the fhir version that will be passed through to the credential created by an instnace of
|
84
|
+
# this Payload (sub)class
|
85
|
+
# @param ver [String] FHIR Version supported by this Payload (sub)class. Leaving this param out
|
86
|
+
# will only return the current value
|
87
|
+
# value (used as a getter)
|
88
|
+
# @return [String] Current FHIR version supported
|
89
|
+
def fhir_version(ver = nil)
|
90
|
+
if @fhir_version.nil? && ver.nil?
|
91
|
+
@fhir_version = superclass.fhir_version unless self == HealthCards::Payload
|
92
|
+
elsif ver
|
93
|
+
@fhir_version = ver
|
94
|
+
end
|
95
|
+
@fhir_version
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
fhir_version '4.0.1'
|
100
|
+
|
101
|
+
additional_types 'https://smarthealth.cards#health-card'
|
102
|
+
|
103
|
+
allow type: FHIR::Meta, attributes: %w[security]
|
104
|
+
|
105
|
+
disallow attributes: %w[id text]
|
106
|
+
disallow type: FHIR::CodeableConcept, attributes: %w[text]
|
107
|
+
disallow type: FHIR::Coding, attributes: %w[display]
|
108
|
+
|
109
|
+
# Create a Payload
|
110
|
+
#
|
111
|
+
# @param bundle [FHIR::Bundle] VerifiableCredential containing a fhir bundle
|
112
|
+
# @param issuer [String] The url from the Issuer of the Payload
|
113
|
+
def initialize(bundle:, issuer: nil)
|
114
|
+
raise InvalidPayloadError unless bundle.is_a?(FHIR::Bundle) # && bundle.valid?
|
115
|
+
|
116
|
+
@issuer = issuer
|
117
|
+
@bundle = bundle
|
118
|
+
end
|
119
|
+
|
120
|
+
# A Hash matching the VC structure specified by https://smarthealth.cards/#health-cards-are-encoded-as-compact-serialization-json-web-signatures-jws
|
121
|
+
# @param filter [Boolean] specifies whether the bundle should apply allow/disallow rules
|
122
|
+
# and meta filtering features. Defaults to true.
|
123
|
+
# @return [Hash]
|
124
|
+
def to_hash(filter: true)
|
125
|
+
fhir_bundle = filter ? strip_fhir_bundle : bundle
|
126
|
+
{
|
127
|
+
iss: issuer,
|
128
|
+
nbf: Time.now.to_i,
|
129
|
+
vc: {
|
130
|
+
type: self.class.types,
|
131
|
+
credentialSubject: {
|
132
|
+
fhirVersion: self.class.fhir_version,
|
133
|
+
fhirBundle: fhir_bundle.to_hash
|
134
|
+
}
|
135
|
+
}
|
136
|
+
}
|
137
|
+
end
|
138
|
+
|
139
|
+
# A compressed version of the FHIR::Bundle based on the SMART Health Cards frame work and any other constraints
|
140
|
+
# defined by a subclass
|
141
|
+
# @return String compressed payload
|
142
|
+
def to_s
|
143
|
+
Payload.compress_payload(to_json)
|
144
|
+
end
|
145
|
+
|
146
|
+
# A minified JSON string matching the VC structure specified by https://smarthealth.cards/#health-cards-are-encoded-as-compact-serialization-json-web-signatures-jws
|
147
|
+
# @return [String] JSON string
|
148
|
+
def to_json(*args)
|
149
|
+
to_hash.to_json(*args)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Processes the bundle according to https://smarthealth.cards/#health-cards-are-small and returns
|
153
|
+
# a Hash with equivalent values
|
154
|
+
# @return [Hash] A hash with the same content as the FHIR::Bundle, processed accoding
|
155
|
+
# to SMART Health Cards framework and any constraints created by subclasses
|
156
|
+
def strip_fhir_bundle
|
157
|
+
return [] unless bundle.entry
|
158
|
+
|
159
|
+
new_bundle = duplicate_bundle
|
160
|
+
url_map = redefine_uris(new_bundle)
|
161
|
+
|
162
|
+
new_bundle.entry.each do |entry|
|
163
|
+
entry.each_element do |value, metadata, _|
|
164
|
+
case metadata['type']
|
165
|
+
when 'Reference'
|
166
|
+
value.reference = process_reference(url_map, entry, value)
|
167
|
+
when 'Resource'
|
168
|
+
value.meta = nil unless value.meta&.security
|
169
|
+
end
|
170
|
+
|
171
|
+
handle_allowable(value)
|
172
|
+
handle_disallowable(value)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
new_bundle
|
177
|
+
end
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
def duplicate_bundle
|
182
|
+
FHIR::Bundle.new(bundle.to_hash)
|
183
|
+
end
|
184
|
+
|
185
|
+
def redefine_uris(bundle)
|
186
|
+
url_map = {}
|
187
|
+
resource_count = 0
|
188
|
+
bundle.entry.each do |entry|
|
189
|
+
old_url = entry.fullUrl
|
190
|
+
new_url = "resource:#{resource_count}"
|
191
|
+
url_map[old_url] = new_url
|
192
|
+
entry.fullUrl = new_url
|
193
|
+
resource_count += 1
|
194
|
+
end
|
195
|
+
url_map
|
196
|
+
end
|
197
|
+
|
198
|
+
def process_reference(url_map, entry, ref)
|
199
|
+
entry_url = URI(url_map.key(entry.fullUrl))
|
200
|
+
ref_url = ref.reference
|
201
|
+
|
202
|
+
return unless ref_url
|
203
|
+
|
204
|
+
return url_map[ref_url] if url_map[ref_url]
|
205
|
+
|
206
|
+
fhir_base_url = FHIR_REF_REGEX.match(entry_url.to_s)[1]
|
207
|
+
full_url = URI.join(fhir_base_url, ref_url).to_s
|
208
|
+
|
209
|
+
new_url = url_map[full_url]
|
210
|
+
|
211
|
+
raise InvalidBundleReferenceError, full_url unless new_url
|
212
|
+
|
213
|
+
new_url
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HealthCards
|
4
|
+
class COVIDImmunizationPayload < COVIDPayload
|
5
|
+
additional_types 'https://smarthealth.cards#immunization'
|
6
|
+
|
7
|
+
allow type: FHIR::Immunization,
|
8
|
+
attributes: %w[meta status vaccineCode patient occurrenceDateTime manufacturer lotNumber performer
|
9
|
+
isSubpotent]
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HealthCards
|
4
|
+
class COVIDLabResultPayload < COVIDPayload
|
5
|
+
additional_types 'https://smarthealth.cards#laboratory'
|
6
|
+
|
7
|
+
allow type: FHIR::Observation,
|
8
|
+
attributes: %w[meta status code subject effectiveDateTime effectivePeriod performer
|
9
|
+
valueCodeableConcept valueQuantity valueString referenceRange]
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'health_cards/attribute_filters'
|
4
|
+
require 'health_cards/payload_types'
|
5
|
+
|
6
|
+
module HealthCards
|
7
|
+
# Implements Payload for use with COVID Vaccination IG
|
8
|
+
class COVIDPayload < HealthCards::Payload
|
9
|
+
additional_types 'https://smarthealth.cards#covid19'
|
10
|
+
|
11
|
+
allow type: FHIR::Patient, attributes: %w[identifier name birthDate]
|
12
|
+
end
|
13
|
+
end
|
@@ -1,30 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module HealthCards
|
4
|
-
# Handles behavior related to support types by
|
5
|
-
module
|
6
|
-
|
7
|
-
'https://smarthealth.cards#health-card'
|
8
|
-
].freeze
|
9
|
-
|
10
|
-
# Additional type claims this HealthCard class supports
|
4
|
+
# Handles behavior related to support types by Payload subclasses
|
5
|
+
module PayloadTypes
|
6
|
+
# Additional type claims this Payload class supports
|
11
7
|
# @param types [String, Array] A string or array of string representing the additional type claims or nil
|
12
8
|
# if used as a getter
|
13
9
|
# @return [Array] the additional types added by this classes
|
14
10
|
def additional_types(*add_types)
|
15
|
-
|
16
|
-
|
11
|
+
@additional_types ||= []
|
12
|
+
@additional_types.concat(add_types) unless add_types.nil?
|
13
|
+
@additional_types
|
17
14
|
end
|
18
15
|
|
19
|
-
# Type claims supported by this
|
16
|
+
# Type claims supported by this Payload subclass
|
20
17
|
# @return [Array] an array of Strings with all the supported type claims
|
21
18
|
def types
|
22
|
-
@types ||=
|
19
|
+
@types ||= self == HealthCards::Payload ? additional_types : superclass.types + additional_types
|
23
20
|
end
|
24
21
|
|
25
22
|
# Check if this class supports the given type claim(s)
|
26
23
|
# @param type [Array, String] A type as defined by the SMART Health Cards framework
|
27
|
-
# @return [Boolean] Whether or not the type param is included in the types supported by the
|
24
|
+
# @return [Boolean] Whether or not the type param is included in the types supported by the Payload (sub)class
|
28
25
|
def supports_type?(*type)
|
29
26
|
!types.intersection(type.flatten).empty?
|
30
27
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module HealthCards
|
4
|
-
# Logic for verifying a
|
4
|
+
# Logic for verifying a Payload JWS
|
5
5
|
module Verification
|
6
6
|
# Verify Health Card with given KeySet
|
7
7
|
#
|
@@ -10,7 +10,7 @@ module HealthCards
|
|
10
10
|
# @param resolve_keys [Boolean] if keys should be resolved
|
11
11
|
# @return [Boolean]
|
12
12
|
def verify_using_key_set(verifiable, key_set = nil, resolve_keys: true)
|
13
|
-
jws = JWS.from_jws(verifiable)
|
13
|
+
jws = verifiable.is_a?(HealthCards::HealthCard) ? verifiable.jws : JWS.from_jws(verifiable)
|
14
14
|
key_set ||= HealthCards::KeySet.new
|
15
15
|
key_set.add_keys(resolve_key(jws)) if resolve_keys && key_set.find_key(jws.kid).nil?
|
16
16
|
|
@@ -28,7 +28,7 @@ module HealthCards
|
|
28
28
|
# @param jws [HealthCards::JWS, String] The JWS for which to resolve keys
|
29
29
|
# @return [HealthCards::KeySet]
|
30
30
|
def resolve_key(jws)
|
31
|
-
jwks_uri = URI("#{HealthCard.
|
31
|
+
jwks_uri = URI("#{HealthCard.new(jws.to_s).issuer}/.well-known/jwks.json")
|
32
32
|
res = Net::HTTP.get(jwks_uri)
|
33
33
|
HealthCards::KeySet.from_jwks(res)
|
34
34
|
# Handle response if key is malformed or unreachable
|
@@ -12,7 +12,7 @@ module HealthCards
|
|
12
12
|
include HealthCards::Verification
|
13
13
|
extend HealthCards::Verification
|
14
14
|
|
15
|
-
# Verify a
|
15
|
+
# Verify a Payload
|
16
16
|
#
|
17
17
|
# This method _always_ uses key resolution and does not depend on any cached keys
|
18
18
|
#
|
@@ -53,7 +53,7 @@ module HealthCards
|
|
53
53
|
@keys.remove_keys(key)
|
54
54
|
end
|
55
55
|
|
56
|
-
# Verify a
|
56
|
+
# Verify a Payload
|
57
57
|
#
|
58
58
|
# @param verifiable [HealthCards::JWS, String] the health card to verify
|
59
59
|
# @return [Boolean]
|
data/lib/health_cards/version.rb
CHANGED
data/lib/health_cards.rb
CHANGED
@@ -9,15 +9,18 @@ require 'health_cards/key_set'
|
|
9
9
|
require 'health_cards/private_key'
|
10
10
|
require 'health_cards/public_key'
|
11
11
|
require 'health_cards/health_card'
|
12
|
+
require 'health_cards/payload'
|
12
13
|
require 'health_cards/attribute_filters'
|
13
|
-
require 'health_cards/
|
14
|
+
require 'health_cards/payload_types'
|
14
15
|
require 'health_cards/chunking_utils'
|
15
16
|
require 'health_cards/errors'
|
16
17
|
require 'health_cards/qr_codes'
|
17
18
|
require 'health_cards/chunk'
|
18
19
|
require 'health_cards/verifier'
|
19
20
|
require 'health_cards/importer'
|
20
|
-
require 'health_cards/
|
21
|
+
require 'health_cards/payload_types/covid_payload'
|
22
|
+
require 'health_cards/payload_types/covid_immunization_payload'
|
23
|
+
require 'health_cards/payload_types/covid_lab_result_payload'
|
21
24
|
require 'health_cards/exporter'
|
22
25
|
|
23
26
|
require 'base64'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: health_cards
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Reece Adamson
|
@@ -15,7 +15,7 @@ authors:
|
|
15
15
|
autorequire:
|
16
16
|
bindir: exe
|
17
17
|
cert_chain: []
|
18
|
-
date: 2021-
|
18
|
+
date: 2021-11-30 00:00:00.000000000 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: fhir_models
|
@@ -45,14 +45,28 @@ dependencies:
|
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '0'
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: rqrcode_core
|
50
|
+
requirement: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.2.0
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.2.0
|
48
62
|
description: "Health Cards implements SMART Health Cards, a secure and decentralized
|
49
63
|
framework that allows \npeople to prove their vaccination status or medical test
|
50
|
-
results. It is
|
51
|
-
|
52
|
-
|
53
|
-
|
64
|
+
results. It is based off of W3C \nVerifiable Credentials and FHIR R4 health data
|
65
|
+
exchange standards. It allows conversion of \nclinical data into JWS which may then
|
66
|
+
be embedded into QR codes, exported to smart-health-card \nfiles, or returned by
|
67
|
+
a $health-card-issue FHIR operation.\n"
|
54
68
|
email:
|
55
|
-
-
|
69
|
+
- vci-developers@mitre.org
|
56
70
|
executables: []
|
57
71
|
extensions: []
|
58
72
|
extra_rdoc_files: []
|
@@ -60,10 +74,8 @@ files:
|
|
60
74
|
- LICENSE.txt
|
61
75
|
- lib/health_cards.rb
|
62
76
|
- lib/health_cards/attribute_filters.rb
|
63
|
-
- lib/health_cards/card_types.rb
|
64
77
|
- lib/health_cards/chunk.rb
|
65
78
|
- lib/health_cards/chunking_utils.rb
|
66
|
-
- lib/health_cards/covid_health_card.rb
|
67
79
|
- lib/health_cards/encoding.rb
|
68
80
|
- lib/health_cards/errors.rb
|
69
81
|
- lib/health_cards/exporter.rb
|
@@ -73,19 +85,24 @@ files:
|
|
73
85
|
- lib/health_cards/jws.rb
|
74
86
|
- lib/health_cards/key.rb
|
75
87
|
- lib/health_cards/key_set.rb
|
88
|
+
- lib/health_cards/payload.rb
|
89
|
+
- lib/health_cards/payload_types.rb
|
90
|
+
- lib/health_cards/payload_types/covid_immunization_payload.rb
|
91
|
+
- lib/health_cards/payload_types/covid_lab_result_payload.rb
|
92
|
+
- lib/health_cards/payload_types/covid_payload.rb
|
76
93
|
- lib/health_cards/private_key.rb
|
77
94
|
- lib/health_cards/public_key.rb
|
78
95
|
- lib/health_cards/qr_codes.rb
|
79
96
|
- lib/health_cards/verification.rb
|
80
97
|
- lib/health_cards/verifier.rb
|
81
98
|
- lib/health_cards/version.rb
|
82
|
-
homepage: https://github.com/dvci/
|
99
|
+
homepage: https://github.com/dvci/health_cards
|
83
100
|
licenses:
|
84
101
|
- Apache-2.0
|
85
102
|
metadata:
|
86
|
-
homepage_uri: https://github.com/dvci/
|
103
|
+
homepage_uri: https://github.com/dvci/health_cards
|
87
104
|
source_code_uri: https://github.com/dvci/health_cards
|
88
|
-
changelog_uri: https://github.com/dvci/health_cards/CHANGELOG.md
|
105
|
+
changelog_uri: https://github.com/dvci/health_cards/blob/main/CHANGELOG.md
|
89
106
|
post_install_message:
|
90
107
|
rdoc_options: []
|
91
108
|
require_paths:
|
@@ -1,17 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'health_cards/attribute_filters'
|
4
|
-
require 'health_cards/card_types'
|
5
|
-
|
6
|
-
module HealthCards
|
7
|
-
# Implements HealthCard for use with COVID Vaccination IG
|
8
|
-
class COVIDHealthCard < HealthCards::HealthCard
|
9
|
-
fhir_version '4.0.1'
|
10
|
-
|
11
|
-
additional_types 'https://smarthealth.cards#covid19'
|
12
|
-
additional_types 'https://smarthealth.cards#immunization'
|
13
|
-
|
14
|
-
allow type: FHIR::Patient, attributes: %w[name birthDate]
|
15
|
-
allow type: FHIR::Immunization, attributes: %w[status vaccineCode patient occurrenceDateTime]
|
16
|
-
end
|
17
|
-
end
|