smart_health_cards_test_kit 0.9.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 +7 -0
- data/LICENSE +201 -0
- data/lib/smart_health_cards_test_kit/fhir_operation_group.rb +105 -0
- data/lib/smart_health_cards_test_kit/file_download_group.rb +108 -0
- data/lib/smart_health_cards_test_kit/health_card.rb +33 -0
- data/lib/smart_health_cards_test_kit/javascript/jsQR.js +10100 -0
- data/lib/smart_health_cards_test_kit/javascript/qr-scanner-worker.min.js +98 -0
- data/lib/smart_health_cards_test_kit/javascript/qr-scanner.min.js +31 -0
- data/lib/smart_health_cards_test_kit/qr_code_group.rb +107 -0
- data/lib/smart_health_cards_test_kit/shc_fhir_validation.rb +39 -0
- data/lib/smart_health_cards_test_kit/shc_header_verification.rb +36 -0
- data/lib/smart_health_cards_test_kit/shc_payload_verification.rb +123 -0
- data/lib/smart_health_cards_test_kit/shc_signature_verification.rb +95 -0
- data/lib/smart_health_cards_test_kit/utils/chunking_utils.rb +71 -0
- data/lib/smart_health_cards_test_kit/utils/encoding.rb +26 -0
- data/lib/smart_health_cards_test_kit/utils/jws.rb +132 -0
- data/lib/smart_health_cards_test_kit/utils/key.rb +82 -0
- data/lib/smart_health_cards_test_kit/utils/key_set.rb +88 -0
- data/lib/smart_health_cards_test_kit/utils/private_key.rb +53 -0
- data/lib/smart_health_cards_test_kit/utils/public_key.rb +33 -0
- data/lib/smart_health_cards_test_kit/utils/verification.rb +41 -0
- data/lib/smart_health_cards_test_kit/utils/verifier.rb +71 -0
- data/lib/smart_health_cards_test_kit/version.rb +3 -0
- data/lib/smart_health_cards_test_kit/views/scan_qr_code.html +207 -0
- data/lib/smart_health_cards_test_kit/views/upload_qr_code.html +130 -0
- data/lib/smart_health_cards_test_kit.rb +67 -0
- metadata +168 -0
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require_relative 'key_set'
|
5
|
+
require_relative 'verification'
|
6
|
+
|
7
|
+
module SmartHealthCardsTestKit
|
8
|
+
module Utils
|
9
|
+
# Verifiers can validate HealthCards using public keys
|
10
|
+
class Verifier
|
11
|
+
attr_reader :keys
|
12
|
+
attr_accessor :resolve_keys
|
13
|
+
|
14
|
+
include SmartHealthCardsTestKit::Utils::Verification
|
15
|
+
extend SmartHealthCardsTestKit::Utils::Verification
|
16
|
+
|
17
|
+
# Verify a Payload
|
18
|
+
#
|
19
|
+
# This method _always_ uses key resolution and does not depend on any cached keys
|
20
|
+
#
|
21
|
+
# @param verifiable [SmartHealthCardsTestKit::Utils::JWS, String] the health card to verify
|
22
|
+
# @return [Boolean]
|
23
|
+
def self.verify(verifiable)
|
24
|
+
verify_using_key_set(verifiable)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Create a new Verifier
|
28
|
+
#
|
29
|
+
# @param keys [SmartHealthCardsTestKit::Utils::KeySet, SmartHealthCardsTestKit::Utils::Key, nil] keys to use when verifying Health Cards
|
30
|
+
# @param resolve_keys [Boolean] Enables or disables key resolution
|
31
|
+
def initialize(keys: nil, resolve_keys: true)
|
32
|
+
@keys = case keys
|
33
|
+
when KeySet
|
34
|
+
keys
|
35
|
+
when Key
|
36
|
+
KeySet.new(keys)
|
37
|
+
else
|
38
|
+
KeySet.new
|
39
|
+
end
|
40
|
+
|
41
|
+
self.resolve_keys = resolve_keys
|
42
|
+
end
|
43
|
+
|
44
|
+
# # Add a key to use when verifying
|
45
|
+
# #
|
46
|
+
# # @param key [SmartHealthCardsTestKit::Utils::Key, SmartHealthCardsTestKit::Utils::KeySet] the key to add
|
47
|
+
# def add_keys(key)
|
48
|
+
# @keys.add_keys(key)
|
49
|
+
# end
|
50
|
+
|
51
|
+
# # Remove a key to use when verifying
|
52
|
+
# #
|
53
|
+
# # @param key [SmartHealthCardsTestKit::Utils::Key] the key to remove
|
54
|
+
# def remove_keys(key)
|
55
|
+
# @keys.remove_keys(key)
|
56
|
+
# end
|
57
|
+
|
58
|
+
# Verify a Payload
|
59
|
+
#
|
60
|
+
# @param verifiable [SmartHealthCardsTestKit::Utils::JWS, String] the health card to verify
|
61
|
+
# @return [Boolean]
|
62
|
+
def verify(verifiable)
|
63
|
+
verify_using_key_set(verifiable, keys, resolve_keys: resolve_keys?)
|
64
|
+
end
|
65
|
+
|
66
|
+
def resolve_keys?
|
67
|
+
resolve_keys
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<meta charset="utf-8">
|
4
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
5
|
+
<title>Scan a QR Code</title>
|
6
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
|
7
|
+
</head>
|
8
|
+
<body class="m-3" onload="onLoad()">
|
9
|
+
<section class="hero is-small is-success">
|
10
|
+
<h3>Scan a QR Code</h3>
|
11
|
+
<p class="subtitle">Use device's camera to scan a QR code representing a Smart Health Card</p>
|
12
|
+
</section>
|
13
|
+
|
14
|
+
<div>
|
15
|
+
<div class="field is-grouped">
|
16
|
+
<button id="start" class="btn btn-primary" onclick="disableStartButton()">Start</button>
|
17
|
+
<button id="stop" class="btn btn-primary" onclick="enableStartButton()" disabled>Stop</button>
|
18
|
+
</div>
|
19
|
+
<div class="level-right" id="multi-status-container">
|
20
|
+
</div>
|
21
|
+
|
22
|
+
<video id="preview"></video>
|
23
|
+
|
24
|
+
<div class="notification is-success is-light" hidden id="success-notification">
|
25
|
+
<div class="level">
|
26
|
+
<div class="level-left">
|
27
|
+
<div class="level-item">
|
28
|
+
<span class="is-size-4">QR code scanned successfully.</span>
|
29
|
+
</div>
|
30
|
+
</div>
|
31
|
+
</div>
|
32
|
+
</div>
|
33
|
+
|
34
|
+
<div class="notification is-success is-light">
|
35
|
+
<form id="upload-qr-code" action="post_qr_code" accept-charset="UTF-8" method="post"></form>
|
36
|
+
<div class="level">
|
37
|
+
<div class="level-left">
|
38
|
+
<div class="level-item">
|
39
|
+
<span class="is-size-4" id="qr-contents"></span>
|
40
|
+
</div>
|
41
|
+
</div>
|
42
|
+
</div>
|
43
|
+
</form>
|
44
|
+
</div>
|
45
|
+
|
46
|
+
<div class="notification is-danger is-light" hidden id="error-notification">
|
47
|
+
<div class="level">
|
48
|
+
<div class="level-left">
|
49
|
+
<div class="level-item">
|
50
|
+
<span class="is-size-4">QR code does not contain a SMART Health Card.</span>
|
51
|
+
</div>
|
52
|
+
</div>
|
53
|
+
</div>
|
54
|
+
</div>
|
55
|
+
</div>
|
56
|
+
|
57
|
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
|
58
|
+
|
59
|
+
<script type="module">
|
60
|
+
import QrScanner from './qr-scanner.min.js';
|
61
|
+
|
62
|
+
const healthCardPattern = /^shc:\/(?<multipleChunks>(?<chunkIndex>[0-9]+)\/(?<chunkCount>[0-9]+)\/)?[0-9]+$/;
|
63
|
+
|
64
|
+
let qrScanner;
|
65
|
+
let startButton;
|
66
|
+
let stopButton;
|
67
|
+
let successNotification;
|
68
|
+
let errorNotification;
|
69
|
+
let inputField;
|
70
|
+
let multiStatusContainer;
|
71
|
+
let form
|
72
|
+
|
73
|
+
let scannedCodes = [];
|
74
|
+
|
75
|
+
window.onLoad = function() {
|
76
|
+
const videoElement = document.getElementById('preview');
|
77
|
+
|
78
|
+
qrScanner = new QrScanner(videoElement, handleScan);
|
79
|
+
|
80
|
+
startButton = document.getElementById('start');
|
81
|
+
stopButton = document.getElementById('stop');
|
82
|
+
successNotification = document.getElementById('success-notification');
|
83
|
+
errorNotification = document.getElementById('error-notification');
|
84
|
+
inputField = document.getElementById('qr-contents');
|
85
|
+
multiStatusContainer = document.getElementById('multi-status-container');
|
86
|
+
form = document.getElementById('upload-qr-code');
|
87
|
+
|
88
|
+
startButton.addEventListener('click', startScanning);
|
89
|
+
stopButton.addEventListener('click', stopScanning);
|
90
|
+
}
|
91
|
+
|
92
|
+
window.startScanning = function() {
|
93
|
+
disableStartButton();
|
94
|
+
hideSuccessNotification();
|
95
|
+
hideErrorNotification();
|
96
|
+
qrScanner.start();
|
97
|
+
};
|
98
|
+
|
99
|
+
window.stopScanning = function() {
|
100
|
+
enableStartButton();
|
101
|
+
qrScanner.stop();
|
102
|
+
};
|
103
|
+
|
104
|
+
window.disableStartButton = function() {
|
105
|
+
startButton.setAttribute('disabled', true);
|
106
|
+
stopButton.removeAttribute('disabled');
|
107
|
+
};
|
108
|
+
|
109
|
+
window.enableStartButton = function() {
|
110
|
+
stopButton.setAttribute('disabled', true);
|
111
|
+
startButton.removeAttribute('disabled');
|
112
|
+
};
|
113
|
+
|
114
|
+
window.hideSuccessNotification = function() {
|
115
|
+
successNotification.setAttribute('hidden', true);
|
116
|
+
};
|
117
|
+
|
118
|
+
window.showSuccessNotification = function() {
|
119
|
+
hideErrorNotification();
|
120
|
+
successNotification.removeAttribute('hidden');
|
121
|
+
};
|
122
|
+
|
123
|
+
window.hideErrorNotification = function() {
|
124
|
+
errorNotification.setAttribute('hidden', true);
|
125
|
+
};
|
126
|
+
|
127
|
+
window.showErrorNotification = function() {
|
128
|
+
hideSuccessNotification();
|
129
|
+
errorNotification.removeAttribute('hidden');
|
130
|
+
};
|
131
|
+
|
132
|
+
window.handleScan = function(result) {
|
133
|
+
console.log(result);
|
134
|
+
|
135
|
+
if (healthCardPattern.test(result)) {
|
136
|
+
const match = result.match(healthCardPattern);
|
137
|
+
if (match.groups.multipleChunks) {
|
138
|
+
hideErrorNotification();
|
139
|
+
const chunkCount = +match.groups.chunkCount;
|
140
|
+
const currentChunkIndex = +match.groups.chunkIndex;
|
141
|
+
if (scannedCodes.length !== chunkCount) {
|
142
|
+
scannedCodes = new Array(chunkCount);
|
143
|
+
scannedCodes.fill(null, 0, chunkCount);
|
144
|
+
}
|
145
|
+
scannedCodes[currentChunkIndex - 1] = result;
|
146
|
+
|
147
|
+
multiStatusContainer.innerHTML = scannedCodes
|
148
|
+
.map((code, index) => {
|
149
|
+
return code
|
150
|
+
? multiPresentElement(index + 1, chunkCount)
|
151
|
+
: multiMissingElement(index + 1, chunkCount);
|
152
|
+
})
|
153
|
+
.join('\n');
|
154
|
+
|
155
|
+
if (scannedCodes.every(code => code)) {
|
156
|
+
stopScanning();
|
157
|
+
|
158
|
+
inputField.textContent = JSON.stringify(scannedCodes);
|
159
|
+
showSuccessNotification();
|
160
|
+
|
161
|
+
//TODO: do something here. Post result to endpoint? Need to understand the logic in this nested if statement
|
162
|
+
|
163
|
+
}
|
164
|
+
} else {
|
165
|
+
stopScanning();
|
166
|
+
|
167
|
+
multiStatusContainer.innerHTML = '';
|
168
|
+
inputField.textContent = JSON.stringify([result]);
|
169
|
+
showSuccessNotification();
|
170
|
+
|
171
|
+
//QR code scanned successfully. Post to endpoint (code borrowed from upload_qr_code.html)
|
172
|
+
const urlParams = new URLSearchParams(window.location.search);
|
173
|
+
const requestId = urlParams.get('id');
|
174
|
+
if (requestId) {
|
175
|
+
form.action += '?id=' + encodeURIComponent(requestId);
|
176
|
+
}
|
177
|
+
|
178
|
+
const postUrl = `${form.action}`;
|
179
|
+
fetch(postUrl, {
|
180
|
+
method: 'POST',
|
181
|
+
headers: {
|
182
|
+
'Content-Type': 'application/json',
|
183
|
+
},
|
184
|
+
body: JSON.stringify({ qr_code_content: result }),
|
185
|
+
})
|
186
|
+
.then(response => {
|
187
|
+
if (response.ok) {
|
188
|
+
inputField.textContent += " - QR Code successfully posted to server.";
|
189
|
+
window.history.back(); // Go back to the previous page
|
190
|
+
} else {
|
191
|
+
inputField.textContent += " - Error posting QR code to server.";
|
192
|
+
}
|
193
|
+
})
|
194
|
+
.catch(error => {
|
195
|
+
inputField.textContent += ` - Network error: ${error}`;
|
196
|
+
});
|
197
|
+
|
198
|
+
}
|
199
|
+
} else {
|
200
|
+
stopScanning();
|
201
|
+
|
202
|
+
showErrorNotification();
|
203
|
+
}
|
204
|
+
};
|
205
|
+
</script>
|
206
|
+
</body>
|
207
|
+
</html>
|
@@ -0,0 +1,130 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<meta charset="utf-8">
|
4
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
5
|
+
<title>Upload a QR image file</title>
|
6
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
|
7
|
+
</head>
|
8
|
+
<body class="m-3">
|
9
|
+
<div>
|
10
|
+
<h3>Upload a QR image file</h3>
|
11
|
+
<p class="subtitle">Manually upload a pre saved QR image file. The file shall have .png, .jpg, or .jpeg file extension.</p>
|
12
|
+
</div>
|
13
|
+
<div>
|
14
|
+
<form id="upload-qr-code" action="post_qr_code" accept-charset="UTF-8" method="post">
|
15
|
+
<div class="field is-grouped">
|
16
|
+
<div class="file has-name">
|
17
|
+
<label class="file-label">
|
18
|
+
<input type="file" name="qr_file" id="qr_file" accept=".png, .jpg, .jpeg" class="file-input" style="display: none;">
|
19
|
+
<label for="qr_file" class="btn btn-primary">Choose File</label>
|
20
|
+
<span class="file-name">No file uploaded</span>
|
21
|
+
</label>
|
22
|
+
</div>
|
23
|
+
<p></p>
|
24
|
+
<p id="qr-content" style="margin-top: 10px;"></p>
|
25
|
+
<p class="control">
|
26
|
+
<input type="submit" class="btn btn-secondary" id="upload-button" disabled>
|
27
|
+
</p>
|
28
|
+
</div>
|
29
|
+
</form>
|
30
|
+
<canvas id="qr-canvas" style="display: none;"></canvas>
|
31
|
+
|
32
|
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
|
33
|
+
<script src="jsqr.js"></script>
|
34
|
+
<script>
|
35
|
+
document.addEventListener('DOMContentLoaded', function () {
|
36
|
+
const fileInput = document.getElementById('qr_file');
|
37
|
+
const fileName = document.querySelector('.file-name');
|
38
|
+
const uploadButton = document.getElementById('upload-button');
|
39
|
+
const qrCanvas = document.getElementById('qr-canvas');
|
40
|
+
const qrContent = document.getElementById('qr-content');
|
41
|
+
const form = document.getElementById('upload-qr-code');
|
42
|
+
|
43
|
+
let qrCodeContent = null;
|
44
|
+
|
45
|
+
// Update form action if `id` query param is present
|
46
|
+
const urlParams = new URLSearchParams(window.location.search);
|
47
|
+
const requestId = urlParams.get('id');
|
48
|
+
if (requestId) {
|
49
|
+
form.action += '?id=' + encodeURIComponent(requestId);
|
50
|
+
}
|
51
|
+
|
52
|
+
fileInput.addEventListener('change', function () {
|
53
|
+
const file = fileInput.files[0];
|
54
|
+
if (file) {
|
55
|
+
fileName.textContent = file.name;
|
56
|
+
|
57
|
+
// Read the image file and decode QR code
|
58
|
+
const reader = new FileReader();
|
59
|
+
reader.onload = function (e) {
|
60
|
+
const img = new Image();
|
61
|
+
img.onload = function () {
|
62
|
+
// Draw the image on the canvas
|
63
|
+
const ctx = qrCanvas.getContext('2d');
|
64
|
+
qrCanvas.width = img.width;
|
65
|
+
qrCanvas.height = img.height;
|
66
|
+
ctx.drawImage(img, 0, 0, img.width, img.height);
|
67
|
+
|
68
|
+
// Get image data from the canvas
|
69
|
+
const imageData = ctx.getImageData(0, 0, qrCanvas.width, qrCanvas.height);
|
70
|
+
|
71
|
+
// Decode QR code using jsQR
|
72
|
+
const qrCode = jsQR(imageData.data, imageData.width, imageData.height);
|
73
|
+
if (qrCode) {
|
74
|
+
qrContent.textContent = `QR Code Content: ${qrCode.data}`;
|
75
|
+
qrCodeContent = qrCode.data; // Store the decoded content
|
76
|
+
uploadButton.disabled = false; // Enable the submit button
|
77
|
+
// Change button style to "btn-primary"
|
78
|
+
uploadButton.classList.remove('btn-secondary');
|
79
|
+
uploadButton.classList.add('btn-primary');
|
80
|
+
} else {
|
81
|
+
qrContent.textContent = 'No QR code detected in the image.';
|
82
|
+
uploadButton.disabled = true;
|
83
|
+
// Reset button style to "btn-secondary"
|
84
|
+
uploadButton.classList.remove('btn-primary');
|
85
|
+
uploadButton.classList.add('btn-secondary');
|
86
|
+
}
|
87
|
+
};
|
88
|
+
img.src = e.target.result;
|
89
|
+
};
|
90
|
+
reader.readAsDataURL(file);
|
91
|
+
} else {
|
92
|
+
fileName.textContent = 'No file uploaded';
|
93
|
+
uploadButton.disabled = true;
|
94
|
+
qrContent.textContent = '';
|
95
|
+
// Reset button style to "btn-secondary"
|
96
|
+
uploadButton.classList.remove('btn-primary');
|
97
|
+
uploadButton.classList.add('btn-secondary');
|
98
|
+
}
|
99
|
+
});
|
100
|
+
|
101
|
+
form.addEventListener('submit', function (event) {
|
102
|
+
event.preventDefault(); // Prevent default form submission
|
103
|
+
|
104
|
+
if (qrCodeContent) {
|
105
|
+
const postUrl = `${form.action}`;
|
106
|
+
fetch(postUrl, {
|
107
|
+
method: 'POST',
|
108
|
+
headers: {
|
109
|
+
'Content-Type': 'application/json',
|
110
|
+
},
|
111
|
+
body: JSON.stringify({ qr_code_content: qrCodeContent }),
|
112
|
+
})
|
113
|
+
.then(response => {
|
114
|
+
if (response.ok) {
|
115
|
+
qrContent.textContent += " - QR Code successfully posted to server.";
|
116
|
+
window.history.back(); // Go back to the previous page
|
117
|
+
} else {
|
118
|
+
qrContent.textContent += " - Error posting QR code to server.";
|
119
|
+
}
|
120
|
+
})
|
121
|
+
.catch(error => {
|
122
|
+
qrContent.textContent += ` - Network error: ${error}`;
|
123
|
+
});
|
124
|
+
}
|
125
|
+
});
|
126
|
+
});
|
127
|
+
</script>
|
128
|
+
</div>
|
129
|
+
</body>
|
130
|
+
</html>
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
require_relative 'smart_health_cards_test_kit/version'
|
4
|
+
|
5
|
+
require_relative 'smart_health_cards_test_kit/utils/jws'
|
6
|
+
require_relative 'smart_health_cards_test_kit/health_card'
|
7
|
+
|
8
|
+
require_relative 'smart_health_cards_test_kit/shc_fhir_validation'
|
9
|
+
require_relative 'smart_health_cards_test_kit/shc_header_verification'
|
10
|
+
require_relative 'smart_health_cards_test_kit/shc_payload_verification'
|
11
|
+
require_relative 'smart_health_cards_test_kit/shc_signature_verification'
|
12
|
+
|
13
|
+
require_relative 'smart_health_cards_test_kit/file_download_group'
|
14
|
+
require_relative 'smart_health_cards_test_kit/qr_code_group'
|
15
|
+
require_relative 'smart_health_cards_test_kit/fhir_operation_group'
|
16
|
+
|
17
|
+
module SmartHealthCardsTestKit
|
18
|
+
class SmartHealthCardsTestSuite < Inferno::TestSuite
|
19
|
+
id :smart_health_cards
|
20
|
+
title 'SMART Health Cards'
|
21
|
+
description %(
|
22
|
+
The US Core Test Kit tests systems for their conformance to the
|
23
|
+
[SMART Health Cards Framework v1.4.0](https://spec.smarthealth.cards/)
|
24
|
+
)
|
25
|
+
version VERSION
|
26
|
+
|
27
|
+
# All FHIR validation requsets will use this FHIR validator
|
28
|
+
fhir_resource_validator do
|
29
|
+
# igs 'identifier#version' # Use this method for published IGs/versions
|
30
|
+
# igs 'igs/filename.tgz' # Use this otherwise
|
31
|
+
|
32
|
+
exclude_message do |message|
|
33
|
+
message.message.match?(/\A\S+: \S+: URL value '.*' does not resolve/)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# HTTP Routes
|
38
|
+
resume_test_route :post, '/post_qr_code' do |request|
|
39
|
+
request.query_parameters['id']
|
40
|
+
end
|
41
|
+
|
42
|
+
scan_qr_code_html = File.read(File.join(__dir__, './smart_health_cards_test_kit/views/scan_qr_code.html'))
|
43
|
+
scan_qr_code_html_route_handler = proc { [200, { 'Content-Type' => 'text/html' }, [scan_qr_code_html]] }
|
44
|
+
route(:get, '/scan_qr_code', scan_qr_code_html_route_handler)
|
45
|
+
|
46
|
+
qr_scanner = File.read(File.join(__dir__, './smart_health_cards_test_kit/javascript/qr-scanner.min.js'))
|
47
|
+
qr_scanner_route_handler = proc { [200, { 'Content-Type' => 'text/javascript' }, [qr_scanner]] }
|
48
|
+
route(:get, '/qr-scanner.min.js', qr_scanner_route_handler)
|
49
|
+
|
50
|
+
qr_scanner_worker = File.read(File.join(__dir__, './smart_health_cards_test_kit/javascript/qr-scanner-worker.min.js'))
|
51
|
+
qr_scanner_worker_route_handler = proc { [200, { 'Content-Type' => 'text/javascript' }, [qr_scanner_worker]] }
|
52
|
+
route(:get, '/qr-scanner-worker.min.js', qr_scanner_worker_route_handler)
|
53
|
+
|
54
|
+
js_qr = File.read(File.join(__dir__, './smart_health_cards_test_kit/javascript/jsQR.js'))
|
55
|
+
js_qr_route_handler = proc { [200, { 'Content-Type' => 'text/javascript' }, [js_qr]] }
|
56
|
+
route(:get, '/jsqr.js', js_qr_route_handler)
|
57
|
+
|
58
|
+
upload_html = File.read(File.join(__dir__, './smart_health_cards_test_kit/views/upload_qr_code.html'))
|
59
|
+
upload_html_route_handler = proc { [200, { 'Content-Type' => 'text/html' }, [upload_html]] }
|
60
|
+
route(:get, '/upload_qr_code', upload_html_route_handler)
|
61
|
+
|
62
|
+
# Tests and TestGroups
|
63
|
+
group from: :shc_file_download_group
|
64
|
+
group from: :shc_fhir_operation_group
|
65
|
+
group from: :shc_qr_code_group
|
66
|
+
end
|
67
|
+
end
|
metadata
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: smart_health_cards_test_kit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Yunwei Wang
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2025-01-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: inferno_core
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.5.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.5.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rqrcode
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rqrcode_core
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.2.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.2.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: database_cleaner-sequel
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.8'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.8'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: factory_bot
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '6.1'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '6.1'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.10'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.10'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: webmock
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '3.11'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '3.11'
|
111
|
+
description: Smart Health Cards test kit
|
112
|
+
email:
|
113
|
+
- inferno@groups.mitre.org
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- LICENSE
|
119
|
+
- lib/smart_health_cards_test_kit.rb
|
120
|
+
- lib/smart_health_cards_test_kit/fhir_operation_group.rb
|
121
|
+
- lib/smart_health_cards_test_kit/file_download_group.rb
|
122
|
+
- lib/smart_health_cards_test_kit/health_card.rb
|
123
|
+
- lib/smart_health_cards_test_kit/javascript/jsQR.js
|
124
|
+
- lib/smart_health_cards_test_kit/javascript/qr-scanner-worker.min.js
|
125
|
+
- lib/smart_health_cards_test_kit/javascript/qr-scanner.min.js
|
126
|
+
- lib/smart_health_cards_test_kit/qr_code_group.rb
|
127
|
+
- lib/smart_health_cards_test_kit/shc_fhir_validation.rb
|
128
|
+
- lib/smart_health_cards_test_kit/shc_header_verification.rb
|
129
|
+
- lib/smart_health_cards_test_kit/shc_payload_verification.rb
|
130
|
+
- lib/smart_health_cards_test_kit/shc_signature_verification.rb
|
131
|
+
- lib/smart_health_cards_test_kit/utils/chunking_utils.rb
|
132
|
+
- lib/smart_health_cards_test_kit/utils/encoding.rb
|
133
|
+
- lib/smart_health_cards_test_kit/utils/jws.rb
|
134
|
+
- lib/smart_health_cards_test_kit/utils/key.rb
|
135
|
+
- lib/smart_health_cards_test_kit/utils/key_set.rb
|
136
|
+
- lib/smart_health_cards_test_kit/utils/private_key.rb
|
137
|
+
- lib/smart_health_cards_test_kit/utils/public_key.rb
|
138
|
+
- lib/smart_health_cards_test_kit/utils/verification.rb
|
139
|
+
- lib/smart_health_cards_test_kit/utils/verifier.rb
|
140
|
+
- lib/smart_health_cards_test_kit/version.rb
|
141
|
+
- lib/smart_health_cards_test_kit/views/scan_qr_code.html
|
142
|
+
- lib/smart_health_cards_test_kit/views/upload_qr_code.html
|
143
|
+
homepage: https://github.com/inferno-framework/smart-health-cards-test-kit
|
144
|
+
licenses:
|
145
|
+
- Apache-2.0
|
146
|
+
metadata:
|
147
|
+
homepage_uri: https://github.com/inferno-framework/smart-health-cards-test-kit
|
148
|
+
source_code_uri: https://github.com/inferno-framework/smart-health-cards-test-kit
|
149
|
+
post_install_message:
|
150
|
+
rdoc_options: []
|
151
|
+
require_paths:
|
152
|
+
- lib
|
153
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
154
|
+
requirements:
|
155
|
+
- - ">="
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: 3.1.2
|
158
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
159
|
+
requirements:
|
160
|
+
- - ">="
|
161
|
+
- !ruby/object:Gem::Version
|
162
|
+
version: '0'
|
163
|
+
requirements: []
|
164
|
+
rubygems_version: 3.5.7
|
165
|
+
signing_key:
|
166
|
+
specification_version: 4
|
167
|
+
summary: Smart Health Cards test kit
|
168
|
+
test_files: []
|