health_cards 0.0.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|