smart_app_launch_test_kit 0.1.3 → 0.1.6
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/smart_app_launch/app_launch_test.rb +14 -4
- data/lib/smart_app_launch/app_redirect_test.rb +9 -2
- data/lib/smart_app_launch/app_redirect_test_stu2.rb +45 -0
- data/lib/smart_app_launch/{discovery_group.rb → discovery_stu1_group.rb} +13 -82
- data/lib/smart_app_launch/discovery_stu2_group.rb +50 -0
- data/lib/smart_app_launch/ehr_launch_group_stu2.rb +56 -0
- data/lib/smart_app_launch/post_auth.html +35 -0
- data/lib/smart_app_launch/smart_stu1_suite.rb +154 -0
- data/lib/smart_app_launch/smart_stu2_suite.rb +160 -0
- data/lib/smart_app_launch/standalone_launch_group_stu2.rb +52 -0
- data/lib/smart_app_launch/token_refresh_test.rb +3 -3
- data/lib/smart_app_launch/token_response_body_test.rb +1 -1
- data/lib/smart_app_launch/version.rb +1 -1
- data/lib/smart_app_launch/well_known_capabilities_stu1_test.rb +39 -0
- data/lib/smart_app_launch/well_known_capabilities_stu2_test.rb +61 -0
- data/lib/smart_app_launch/well_known_endpoint_test.rb +46 -0
- data/lib/smart_app_launch_test_kit.rb +2 -172
- metadata +16 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c2dbada5c6124d5f5ca37325fd8e5a55c9a38ce827f9129362708559d32c3612
|
4
|
+
data.tar.gz: 55f25f85ffa99273ae104690985c7fbd58bcfc65fc6939b69c96e96c0e379c75
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 810a79b3a167dda729558308b165b24a6ed6ed431756584708b3b9ce8107a7361fa7881f3d86ec3f03d6fbc7fbf3fb33d4ef0dc669a696f0abdb7f96e27bffa7
|
7
|
+
data.tar.gz: bb60086d05486463f7b2c9b7b17dc7204e7d7cd0e2960d1df503549a72cabe9f30cae92fe55ae6c9cd4cf39a29601e9960f1e5c5879a859c17910c1205bb479f
|
@@ -12,13 +12,23 @@ module SMARTAppLaunch
|
|
12
12
|
|
13
13
|
config options: { launch_uri: "#{Inferno::Application['base_url']}/custom/smart/launch" }
|
14
14
|
|
15
|
+
def wait_message
|
16
|
+
return instance_exec(&config.options[:launch_message_proc]) if config.options[:launch_message_proc].present?
|
17
|
+
|
18
|
+
%(
|
19
|
+
### #{self.class.parent&.parent&.title}
|
20
|
+
|
21
|
+
Waiting for Inferno to be launched from the EHR.
|
22
|
+
|
23
|
+
Tests will resume once Inferno receives a launch request at
|
24
|
+
`#{config.options[:launch_uri]}` with an `iss` of `#{url}`.
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
15
28
|
run do
|
16
29
|
wait(
|
17
30
|
identifier: url,
|
18
|
-
message:
|
19
|
-
Waiting to receive a request at
|
20
|
-
`#{config.options[:launch_uri]}` with an `iss` of `#{url}`.
|
21
|
-
)
|
31
|
+
message: wait_message
|
22
32
|
)
|
23
33
|
end
|
24
34
|
end
|
@@ -58,10 +58,17 @@ module SMARTAppLaunch
|
|
58
58
|
end
|
59
59
|
|
60
60
|
def wait_message(auth_url)
|
61
|
+
if config.options[:redirect_message_proc].present?
|
62
|
+
return instance_exec(auth_url, &config.options[:redirect_message_proc])
|
63
|
+
end
|
64
|
+
|
61
65
|
%(
|
66
|
+
### #{self.class.parent&.parent&.title}
|
67
|
+
|
62
68
|
[Follow this link to authorize with the SMART server](#{auth_url}).
|
63
|
-
|
64
|
-
a
|
69
|
+
|
70
|
+
Tests will resume once Inferno receives a request at
|
71
|
+
`#{config.options[:redirect_uri]}` with a state of `#{state}`.
|
65
72
|
)
|
66
73
|
end
|
67
74
|
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require_relative 'app_redirect_test'
|
3
|
+
|
4
|
+
module SMARTAppLaunch
|
5
|
+
class AppRedirectTestSTU2 < AppRedirectTest
|
6
|
+
id :smart_app_redirect_stu2
|
7
|
+
description %(
|
8
|
+
Client browser redirected from OAuth server to redirect URI of client
|
9
|
+
app as described in SMART authorization sequence.
|
10
|
+
|
11
|
+
Client SHALL use either the HTTP GET or the HTTP POST method to send the
|
12
|
+
Authorization Request to the Authorization Server.
|
13
|
+
|
14
|
+
[Authorization Code
|
15
|
+
Request](http://hl7.org/fhir/smart-app-launch/STU2/app-launch.html#request-4)
|
16
|
+
)
|
17
|
+
|
18
|
+
input :authorization_method,
|
19
|
+
title: 'Authorization Method',
|
20
|
+
type: 'radio',
|
21
|
+
default: 'get',
|
22
|
+
options: {
|
23
|
+
list_options: [
|
24
|
+
{
|
25
|
+
label: 'GET',
|
26
|
+
value: 'get'
|
27
|
+
},
|
28
|
+
{
|
29
|
+
label: 'POST',
|
30
|
+
value: 'post'
|
31
|
+
}
|
32
|
+
]
|
33
|
+
}
|
34
|
+
|
35
|
+
def authorization_url_builder(url, params)
|
36
|
+
return super if authorization_method == 'get'
|
37
|
+
|
38
|
+
post_params = params.merge(auth_url: url)
|
39
|
+
|
40
|
+
post_url = URI(config.options[:post_authorization_uri])
|
41
|
+
post_url.query = URI.encode_www_form(post_params)
|
42
|
+
post_url.to_s
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -1,5 +1,8 @@
|
|
1
|
+
require_relative 'well_known_capabilities_stu1_test'
|
2
|
+
require_relative 'well_known_endpoint_test'
|
3
|
+
|
1
4
|
module SMARTAppLaunch
|
2
|
-
class
|
5
|
+
class DiscoverySTU1Group < Inferno::TestGroup
|
3
6
|
id :smart_discovery
|
4
7
|
title 'SMART on FHIR Discovery'
|
5
8
|
short_description 'Retrieve server\'s SMART on FHIR configuration.'
|
@@ -36,83 +39,10 @@ module SMARTAppLaunch
|
|
36
39
|
* [OpenID Connect Core](https://openid.net/specs/openid-connect-core-1_0.html)
|
37
40
|
)
|
38
41
|
|
39
|
-
test
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
be exposed as a Well-Known Uniform Resource Identifier
|
44
|
-
)
|
45
|
-
input :url,
|
46
|
-
title: 'FHIR Endpoint',
|
47
|
-
description: 'URL of the FHIR endpoint used by SMART applications'
|
48
|
-
output :well_known_configuration,
|
49
|
-
:well_known_authorization_url,
|
50
|
-
:well_known_introspection_url,
|
51
|
-
:well_known_management_url,
|
52
|
-
:well_known_registration_url,
|
53
|
-
:well_known_revocation_url,
|
54
|
-
:well_known_token_url
|
55
|
-
makes_request :smart_well_known_configuration
|
56
|
-
|
57
|
-
run do
|
58
|
-
well_known_configuration_url = "#{url.chomp('/')}/.well-known/smart-configuration"
|
59
|
-
get(well_known_configuration_url, name: :smart_well_known_configuration)
|
60
|
-
|
61
|
-
assert_response_status(200)
|
62
|
-
|
63
|
-
assert_valid_json(request.response_body)
|
64
|
-
|
65
|
-
config = JSON.parse(request.response_body)
|
66
|
-
output well_known_configuration: request.response_body,
|
67
|
-
well_known_authorization_url: config['authorization_endpoint'],
|
68
|
-
well_known_introspection_url: config['introspection_endpoint'],
|
69
|
-
well_known_management_url: config['management_endpoint'],
|
70
|
-
well_known_registration_url: config['registration_endpoint'],
|
71
|
-
well_known_revocation_url: config['revocation_endpoint'],
|
72
|
-
well_known_token_url: config['token_endpoint']
|
73
|
-
|
74
|
-
content_type = request.response_header('Content-Type')&.value
|
75
|
-
|
76
|
-
assert content_type.present?, 'No `Content-Type` header received.'
|
77
|
-
assert content_type.start_with?('application/json'),
|
78
|
-
"`Content-Type` must be `application/json`, but received: `#{content_type}`"
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
test do
|
83
|
-
title 'Well-known configuration contains required fields'
|
84
|
-
description %(
|
85
|
-
The JSON from .well-known/smart-configuration contains the following
|
86
|
-
required fields: `authorization_endpoint`, `token_endpoint`,
|
87
|
-
`capabilities`
|
88
|
-
)
|
89
|
-
input :well_known_configuration
|
90
|
-
|
91
|
-
run do
|
92
|
-
skip_if well_known_configuration.blank?, 'No well-known configuration found'
|
93
|
-
config = JSON.parse(well_known_configuration)
|
94
|
-
|
95
|
-
['authorization_endpoint', 'token_endpoint', 'capabilities'].each do |key|
|
96
|
-
assert config.key?(key), "Well-known configuration does not include `#{key}`"
|
97
|
-
assert config[key].present?, "Well-known configuration field `#{key}` is blank"
|
98
|
-
end
|
99
|
-
|
100
|
-
assert config['authorization_endpoint'].is_a?(String),
|
101
|
-
'Well-known `authorization_endpoint` field must be a string'
|
102
|
-
assert config['token_endpoint'].is_a?(String),
|
103
|
-
'Well-known `token_endpoint` field must be a string'
|
104
|
-
assert config['capabilities'].is_a?(Array),
|
105
|
-
'Well-known `capabilities` field must be an array'
|
106
|
-
|
107
|
-
non_string_capabilities = config['capabilities'].reject { |capability| capability.is_a? String }
|
108
|
-
|
109
|
-
assert non_string_capabilities.blank?, %(
|
110
|
-
Well-known `capabilities` field must be an array of strings, but found
|
111
|
-
non-string values:
|
112
|
-
#{non_string_capabilities.map { |value| "`#{value.nil? ? 'nil' : value}`" }.join(', ')}
|
113
|
-
)
|
114
|
-
end
|
115
|
-
end
|
42
|
+
test from: :well_known_endpoint,
|
43
|
+
id: 'Test01'
|
44
|
+
test from: :well_known_capabilities_stu1,
|
45
|
+
id: 'Test02'
|
116
46
|
|
117
47
|
test do
|
118
48
|
title 'Conformance/CapabilityStatement provides OAuth 2.0 endpoints'
|
@@ -141,13 +71,14 @@ module SMARTAppLaunch
|
|
141
71
|
resource
|
142
72
|
.rest
|
143
73
|
&.map(&:security)
|
74
|
+
&.compact
|
144
75
|
&.find do |security|
|
145
|
-
|
146
|
-
|
147
|
-
|
76
|
+
security.service&.any? do |service|
|
77
|
+
service.coding&.any? do |coding|
|
78
|
+
coding.code == 'SMART-on-FHIR'
|
79
|
+
end
|
148
80
|
end
|
149
81
|
end
|
150
|
-
end
|
151
82
|
&.extension
|
152
83
|
&.find do |extension|
|
153
84
|
extension.url == 'http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris'
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require_relative 'well_known_capabilities_stu2_test'
|
2
|
+
require_relative 'well_known_endpoint_test'
|
3
|
+
|
4
|
+
module SMARTAppLaunch
|
5
|
+
class DiscoverySTU2Group < Inferno::TestGroup
|
6
|
+
id :smart_discovery_stu2
|
7
|
+
title 'SMART on FHIR Discovery'
|
8
|
+
short_description 'Retrieve server\'s SMART on FHIR configuration.'
|
9
|
+
description %(
|
10
|
+
# Background
|
11
|
+
|
12
|
+
The #{title} Sequence test looks for authorization endpoints and SMART
|
13
|
+
capabilities as described by the [SMART App Launch
|
14
|
+
Framework](http://hl7.org/fhir/smart-app-launch/STU2/).
|
15
|
+
The SMART launch framework uses OAuth 2.0 to *authorize* apps, like
|
16
|
+
Inferno, to access certain information on a FHIR server. The
|
17
|
+
authorization service accessed at the endpoint allows users to give
|
18
|
+
these apps permission without sharing their credentials with the
|
19
|
+
application itself. Instead, the application receives an access token
|
20
|
+
which allows it to access resources on the server. The access token
|
21
|
+
itself has a limited lifetime and permission scopes associated with it.
|
22
|
+
A refresh token may also be provided to the application in order to
|
23
|
+
obtain another access token. Unlike access tokens, a refresh token is
|
24
|
+
not shared with the resource server. If OpenID Connect is used, an id
|
25
|
+
token may be provided as well. The id token can be used to
|
26
|
+
*authenticate* the user. The id token is digitally signed and allows the
|
27
|
+
identity of the user to be verified.
|
28
|
+
|
29
|
+
# Test Methodology
|
30
|
+
|
31
|
+
This test suite will examine the SMART on FHIR configuration contained
|
32
|
+
in the `/.well-known/smart-configuration` endpoint.
|
33
|
+
|
34
|
+
For more information see:
|
35
|
+
|
36
|
+
* [SMART App Launch Framework](http://hl7.org/fhir/smart-app-launch/STU2/)
|
37
|
+
* [The OAuth 2.0 Authorization Framework](https://tools.ietf.org/html/rfc6749)
|
38
|
+
* [OpenID Connect Core](https://openid.net/specs/openid-connect-core-1_0.html)
|
39
|
+
)
|
40
|
+
|
41
|
+
test from: :well_known_endpoint,
|
42
|
+
config: {
|
43
|
+
outputs: {
|
44
|
+
well_known_authorization_url: { name: :smart_authorization_url },
|
45
|
+
well_known_token_url: { name: :smart_token_url }
|
46
|
+
}
|
47
|
+
}
|
48
|
+
test from: :well_known_capabilities_stu2
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require_relative 'app_redirect_test_stu2'
|
2
|
+
require_relative 'ehr_launch_group'
|
3
|
+
|
4
|
+
module SMARTAppLaunch
|
5
|
+
class EHRLaunchGroupSTU2 < EHRLaunchGroup
|
6
|
+
id :smart_ehr_launch_stu2
|
7
|
+
description %(
|
8
|
+
# Background
|
9
|
+
|
10
|
+
The [EHR
|
11
|
+
Launch](http://hl7.org/fhir/smart-app-launch/STU2/app-launch.html#launch-app-ehr-launch)
|
12
|
+
is one of two ways in which an app can be launched, the other being
|
13
|
+
Standalone launch. In an EHR launch, the app is launched from an
|
14
|
+
existing EHR session or portal by a redirect to the registered launch
|
15
|
+
URL. The EHR provides the app two parameters:
|
16
|
+
|
17
|
+
* `iss` - Which contains the FHIR server url
|
18
|
+
* `launch` - An identifier needed for authorization
|
19
|
+
|
20
|
+
# Test Methodology
|
21
|
+
|
22
|
+
Inferno will wait for the EHR server redirect upon execution. When the
|
23
|
+
redirect is received Inferno will check for the presence of the `iss`
|
24
|
+
and `launch` parameters. The security of the authorization endpoint is
|
25
|
+
then checked and authorization is attempted using the provided `launch`
|
26
|
+
identifier.
|
27
|
+
|
28
|
+
For more information on the #{title} see:
|
29
|
+
|
30
|
+
* [SMART EHR Launch Sequence](http://hl7.org/fhir/smart-app-launch/STU2/app-launch.html#launch-app-ehr-launch)
|
31
|
+
)
|
32
|
+
|
33
|
+
config(
|
34
|
+
inputs: {
|
35
|
+
use_pkce: {
|
36
|
+
default: 'true',
|
37
|
+
locked: true
|
38
|
+
},
|
39
|
+
pkce_code_challenge_method: {
|
40
|
+
default: 'S256',
|
41
|
+
locked: true
|
42
|
+
},
|
43
|
+
requested_scopes: {
|
44
|
+
default: 'launch openid fhirUser offline_access user/*.rs'
|
45
|
+
}
|
46
|
+
}
|
47
|
+
)
|
48
|
+
|
49
|
+
test from: :smart_app_redirect_stu2 do
|
50
|
+
input :launch
|
51
|
+
end
|
52
|
+
|
53
|
+
redirect_index = children.find_index { |child| child.id.to_s.end_with? 'app_redirect' }
|
54
|
+
children[redirect_index] = children.pop
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<!-- Use the highest supported document mode of Internet Explorer -->
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
6
|
+
|
7
|
+
<meta charset="utf-8" />
|
8
|
+
<title>Inferno POST Authorization Redirect</title>
|
9
|
+
</head>
|
10
|
+
<body>
|
11
|
+
<noscript>You need to enable JavaScript to run this app.</noscript>
|
12
|
+
<form id="form" style="display:none;">
|
13
|
+
</form>
|
14
|
+
</body>
|
15
|
+
<script>
|
16
|
+
const params = Object.fromEntries(new URLSearchParams(window.location.search).entries());
|
17
|
+
const submitUrl = params.auth_url;
|
18
|
+
delete params.auth_url;
|
19
|
+
const form = document.getElementById('form');
|
20
|
+
form.method = 'POST';
|
21
|
+
form.action = submitUrl;
|
22
|
+
|
23
|
+
for (const property in params) {
|
24
|
+
let input = document.createElement('input');
|
25
|
+
input.setAttribute('name', property);
|
26
|
+
|
27
|
+
let value = params[property].replace(/\+/g, ' ');
|
28
|
+
input.setAttribute('value', decodeURIComponent(value));
|
29
|
+
|
30
|
+
form.appendChild(input);
|
31
|
+
}
|
32
|
+
|
33
|
+
form.submit();
|
34
|
+
</script>
|
35
|
+
</html>
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'tls_test_kit'
|
2
|
+
|
3
|
+
require_relative 'version'
|
4
|
+
require_relative 'discovery_stu1_group'
|
5
|
+
require_relative 'standalone_launch_group'
|
6
|
+
require_relative 'ehr_launch_group'
|
7
|
+
require_relative 'openid_connect_group'
|
8
|
+
require_relative 'token_refresh_group'
|
9
|
+
|
10
|
+
module SMARTAppLaunch
|
11
|
+
class SMARTSTU1Suite < Inferno::TestSuite
|
12
|
+
id 'smart'
|
13
|
+
title 'SMART App Launch STU1'
|
14
|
+
version VERSION
|
15
|
+
|
16
|
+
resume_test_route :get, '/launch' do
|
17
|
+
request.query_parameters['iss']
|
18
|
+
end
|
19
|
+
|
20
|
+
resume_test_route :get, '/redirect' do
|
21
|
+
request.query_parameters['state']
|
22
|
+
end
|
23
|
+
|
24
|
+
config options: {
|
25
|
+
redirect_uri: "#{Inferno::Application['base_url']}/custom/smart/redirect",
|
26
|
+
launch_uri: "#{Inferno::Application['base_url']}/custom/smart/launch"
|
27
|
+
}
|
28
|
+
|
29
|
+
group do
|
30
|
+
title 'Standalone Launch'
|
31
|
+
id :smart_full_standalone_launch
|
32
|
+
|
33
|
+
run_as_group
|
34
|
+
|
35
|
+
group from: :smart_discovery
|
36
|
+
group from: :smart_standalone_launch
|
37
|
+
|
38
|
+
group from: :smart_openid_connect,
|
39
|
+
config: {
|
40
|
+
inputs: {
|
41
|
+
id_token: { name: :standalone_id_token },
|
42
|
+
client_id: { name: :standalone_client_id },
|
43
|
+
requested_scopes: { name: :standalone_requested_scopes },
|
44
|
+
access_token: { name: :standalone_access_token },
|
45
|
+
smart_credentials: { name: :standalone_smart_credentials }
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
group from: :smart_token_refresh,
|
50
|
+
id: :smart_standalone_refresh_without_scopes,
|
51
|
+
title: 'SMART Token Refresh Without Scopes',
|
52
|
+
config: {
|
53
|
+
inputs: {
|
54
|
+
refresh_token: { name: :standalone_refresh_token },
|
55
|
+
client_id: { name: :standalone_client_id },
|
56
|
+
client_secret: { name: :standalone_client_secret },
|
57
|
+
received_scopes: { name: :standalone_received_scopes }
|
58
|
+
},
|
59
|
+
outputs: {
|
60
|
+
refresh_token: { name: :standalone_refresh_token },
|
61
|
+
received_scopes: { name: :standalone_received_scopes },
|
62
|
+
access_token: { name: :standalone_access_token },
|
63
|
+
token_retrieval_time: { name: :standalone_token_retrieval_time },
|
64
|
+
expires_in: { name: :standalone_expires_in },
|
65
|
+
smart_credentials: { name: :standalone_smart_credentials }
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
group from: :smart_token_refresh,
|
70
|
+
id: :smart_standalone_refresh_with_scopes,
|
71
|
+
title: 'SMART Token Refresh With Scopes',
|
72
|
+
config: {
|
73
|
+
options: { include_scopes: true },
|
74
|
+
inputs: {
|
75
|
+
refresh_token: { name: :standalone_refresh_token },
|
76
|
+
client_id: { name: :standalone_client_id },
|
77
|
+
client_secret: { name: :standalone_client_secret },
|
78
|
+
received_scopes: { name: :standalone_received_scopes }
|
79
|
+
},
|
80
|
+
outputs: {
|
81
|
+
refresh_token: { name: :standalone_refresh_token },
|
82
|
+
received_scopes: { name: :standalone_received_scopes },
|
83
|
+
access_token: { name: :standalone_access_token },
|
84
|
+
token_retrieval_time: { name: :standalone_token_retrieval_time },
|
85
|
+
expires_in: { name: :standalone_expires_in },
|
86
|
+
smart_credentials: { name: :standalone_smart_credentials }
|
87
|
+
}
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
group do
|
92
|
+
title 'EHR Launch'
|
93
|
+
id :smart_full_ehr_launch
|
94
|
+
|
95
|
+
run_as_group
|
96
|
+
|
97
|
+
group from: :smart_discovery
|
98
|
+
|
99
|
+
group from: :smart_ehr_launch
|
100
|
+
|
101
|
+
group from: :smart_openid_connect,
|
102
|
+
config: {
|
103
|
+
inputs: {
|
104
|
+
id_token: { name: :ehr_id_token },
|
105
|
+
client_id: { name: :ehr_client_id },
|
106
|
+
requested_scopes: { name: :ehr_requested_scopes },
|
107
|
+
access_token: { name: :ehr_access_token },
|
108
|
+
smart_credentials: { name: :ehr_smart_credentials }
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
group from: :smart_token_refresh,
|
113
|
+
id: :smart_ehr_refresh_without_scopes,
|
114
|
+
title: 'SMART Token Refresh Without Scopes',
|
115
|
+
config: {
|
116
|
+
inputs: {
|
117
|
+
refresh_token: { name: :ehr_refresh_token },
|
118
|
+
client_id: { name: :ehr_client_id },
|
119
|
+
client_secret: { name: :ehr_client_secret },
|
120
|
+
received_scopes: { name: :ehr_received_scopes }
|
121
|
+
},
|
122
|
+
outputs: {
|
123
|
+
refresh_token: { name: :ehr_refresh_token },
|
124
|
+
received_scopes: { name: :ehr_received_scopes },
|
125
|
+
access_token: { name: :ehr_access_token },
|
126
|
+
token_retrieval_time: { name: :ehr_token_retrieval_time },
|
127
|
+
expires_in: { name: :ehr_expires_in },
|
128
|
+
smart_credentials: { name: :ehr_smart_credentials }
|
129
|
+
}
|
130
|
+
}
|
131
|
+
|
132
|
+
group from: :smart_token_refresh,
|
133
|
+
id: :smart_ehr_refresh_with_scopes,
|
134
|
+
title: 'SMART Token Refresh With Scopes',
|
135
|
+
config: {
|
136
|
+
options: { include_scopes: true },
|
137
|
+
inputs: {
|
138
|
+
refresh_token: { name: :ehr_refresh_token },
|
139
|
+
client_id: { name: :ehr_client_id },
|
140
|
+
client_secret: { name: :ehr_client_secret },
|
141
|
+
received_scopes: { name: :ehr_received_scopes }
|
142
|
+
},
|
143
|
+
outputs: {
|
144
|
+
refresh_token: { name: :ehr_refresh_token },
|
145
|
+
received_scopes: { name: :ehr_received_scopes },
|
146
|
+
access_token: { name: :ehr_access_token },
|
147
|
+
token_retrieval_time: { name: :ehr_token_retrieval_time },
|
148
|
+
expires_in: { name: :ehr_expires_in },
|
149
|
+
smart_credentials: { name: :ehr_smart_credentials }
|
150
|
+
}
|
151
|
+
}
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
require 'tls_test_kit'
|
2
|
+
|
3
|
+
require_relative 'version'
|
4
|
+
require_relative 'discovery_stu2_group'
|
5
|
+
require_relative 'standalone_launch_group_stu2'
|
6
|
+
require_relative 'ehr_launch_group_stu2'
|
7
|
+
require_relative 'openid_connect_group'
|
8
|
+
require_relative 'token_refresh_group'
|
9
|
+
|
10
|
+
module SMARTAppLaunch
|
11
|
+
class SMARTSTU2Suite < Inferno::TestSuite
|
12
|
+
id 'smart_stu2'
|
13
|
+
title 'SMART App Launch STU2 (Work in Progress)'
|
14
|
+
version VERSION
|
15
|
+
|
16
|
+
resume_test_route :get, '/launch' do
|
17
|
+
request.query_parameters['iss']
|
18
|
+
end
|
19
|
+
|
20
|
+
resume_test_route :get, '/redirect' do
|
21
|
+
request.query_parameters['state']
|
22
|
+
end
|
23
|
+
|
24
|
+
@post_auth_page = File.read(File.join(__dir__, 'post_auth.html'))
|
25
|
+
post_auth_handler = proc { [200, {}, [@post_auth_page]] }
|
26
|
+
|
27
|
+
route :get, '/post_auth', post_auth_handler
|
28
|
+
|
29
|
+
config options: {
|
30
|
+
redirect_uri: "#{Inferno::Application['base_url']}/custom/smart_stu2/redirect",
|
31
|
+
launch_uri: "#{Inferno::Application['base_url']}/custom/smart_stu2/launch",
|
32
|
+
post_authorization_uri: "#{Inferno::Application['base_url']}/custom/smart_stu2/post_auth"
|
33
|
+
}
|
34
|
+
|
35
|
+
group do
|
36
|
+
title 'Standalone Launch'
|
37
|
+
id :smart_full_standalone_launch
|
38
|
+
|
39
|
+
run_as_group
|
40
|
+
|
41
|
+
group from: :smart_discovery_stu2
|
42
|
+
group from: :smart_standalone_launch_stu2
|
43
|
+
|
44
|
+
group from: :smart_openid_connect,
|
45
|
+
config: {
|
46
|
+
inputs: {
|
47
|
+
id_token: { name: :standalone_id_token },
|
48
|
+
client_id: { name: :standalone_client_id },
|
49
|
+
requested_scopes: { name: :standalone_requested_scopes },
|
50
|
+
access_token: { name: :standalone_access_token },
|
51
|
+
smart_credentials: { name: :standalone_smart_credentials }
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
group from: :smart_token_refresh,
|
56
|
+
id: :smart_standalone_refresh_without_scopes,
|
57
|
+
title: 'SMART Token Refresh Without Scopes',
|
58
|
+
config: {
|
59
|
+
inputs: {
|
60
|
+
refresh_token: { name: :standalone_refresh_token },
|
61
|
+
client_id: { name: :standalone_client_id },
|
62
|
+
client_secret: { name: :standalone_client_secret },
|
63
|
+
received_scopes: { name: :standalone_received_scopes }
|
64
|
+
},
|
65
|
+
outputs: {
|
66
|
+
refresh_token: { name: :standalone_refresh_token },
|
67
|
+
received_scopes: { name: :standalone_received_scopes },
|
68
|
+
access_token: { name: :standalone_access_token },
|
69
|
+
token_retrieval_time: { name: :standalone_token_retrieval_time },
|
70
|
+
expires_in: { name: :standalone_expires_in },
|
71
|
+
smart_credentials: { name: :standalone_smart_credentials }
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
group from: :smart_token_refresh,
|
76
|
+
id: :smart_standalone_refresh_with_scopes,
|
77
|
+
title: 'SMART Token Refresh With Scopes',
|
78
|
+
config: {
|
79
|
+
options: { include_scopes: true },
|
80
|
+
inputs: {
|
81
|
+
refresh_token: { name: :standalone_refresh_token },
|
82
|
+
client_id: { name: :standalone_client_id },
|
83
|
+
client_secret: { name: :standalone_client_secret },
|
84
|
+
received_scopes: { name: :standalone_received_scopes }
|
85
|
+
},
|
86
|
+
outputs: {
|
87
|
+
refresh_token: { name: :standalone_refresh_token },
|
88
|
+
received_scopes: { name: :standalone_received_scopes },
|
89
|
+
access_token: { name: :standalone_access_token },
|
90
|
+
token_retrieval_time: { name: :standalone_token_retrieval_time },
|
91
|
+
expires_in: { name: :standalone_expires_in },
|
92
|
+
smart_credentials: { name: :standalone_smart_credentials }
|
93
|
+
}
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
group do
|
98
|
+
title 'EHR Launch'
|
99
|
+
id :smart_full_ehr_launch
|
100
|
+
|
101
|
+
run_as_group
|
102
|
+
|
103
|
+
group from: :smart_discovery_stu2
|
104
|
+
|
105
|
+
group from: :smart_ehr_launch_stu2
|
106
|
+
|
107
|
+
group from: :smart_openid_connect,
|
108
|
+
config: {
|
109
|
+
inputs: {
|
110
|
+
id_token: { name: :ehr_id_token },
|
111
|
+
client_id: { name: :ehr_client_id },
|
112
|
+
requested_scopes: { name: :ehr_requested_scopes },
|
113
|
+
access_token: { name: :ehr_access_token },
|
114
|
+
smart_credentials: { name: :ehr_smart_credentials }
|
115
|
+
}
|
116
|
+
}
|
117
|
+
|
118
|
+
group from: :smart_token_refresh,
|
119
|
+
id: :smart_ehr_refresh_without_scopes,
|
120
|
+
title: 'SMART Token Refresh Without Scopes',
|
121
|
+
config: {
|
122
|
+
inputs: {
|
123
|
+
refresh_token: { name: :ehr_refresh_token },
|
124
|
+
client_id: { name: :ehr_client_id },
|
125
|
+
client_secret: { name: :ehr_client_secret },
|
126
|
+
received_scopes: { name: :ehr_received_scopes }
|
127
|
+
},
|
128
|
+
outputs: {
|
129
|
+
refresh_token: { name: :ehr_refresh_token },
|
130
|
+
received_scopes: { name: :ehr_received_scopes },
|
131
|
+
access_token: { name: :ehr_access_token },
|
132
|
+
token_retrieval_time: { name: :ehr_token_retrieval_time },
|
133
|
+
expires_in: { name: :ehr_expires_in },
|
134
|
+
smart_credentials: { name: :ehr_smart_credentials }
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
group from: :smart_token_refresh,
|
139
|
+
id: :smart_ehr_refresh_with_scopes,
|
140
|
+
title: 'SMART Token Refresh With Scopes',
|
141
|
+
config: {
|
142
|
+
options: { include_scopes: true },
|
143
|
+
inputs: {
|
144
|
+
refresh_token: { name: :ehr_refresh_token },
|
145
|
+
client_id: { name: :ehr_client_id },
|
146
|
+
client_secret: { name: :ehr_client_secret },
|
147
|
+
received_scopes: { name: :ehr_received_scopes }
|
148
|
+
},
|
149
|
+
outputs: {
|
150
|
+
refresh_token: { name: :ehr_refresh_token },
|
151
|
+
received_scopes: { name: :ehr_received_scopes },
|
152
|
+
access_token: { name: :ehr_access_token },
|
153
|
+
token_retrieval_time: { name: :ehr_token_retrieval_time },
|
154
|
+
expires_in: { name: :ehr_expires_in },
|
155
|
+
smart_credentials: { name: :ehr_smart_credentials }
|
156
|
+
}
|
157
|
+
}
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require_relative 'app_redirect_test_stu2'
|
2
|
+
require_relative 'standalone_launch_group'
|
3
|
+
|
4
|
+
module SMARTAppLaunch
|
5
|
+
class StandaloneLaunchGroupSTU2 < StandaloneLaunchGroup
|
6
|
+
id :smart_standalone_launch_stu2
|
7
|
+
description %(
|
8
|
+
# Background
|
9
|
+
|
10
|
+
The [Standalone
|
11
|
+
Launch Sequence](http://hl7.org/fhir/smart-app-launch/STU2/app-launch.html#launch-app-standalone-launch)
|
12
|
+
allows an app, like Inferno, to be launched independent of an
|
13
|
+
existing EHR session. It is one of the two launch methods described in
|
14
|
+
the SMART App Launch Framework alongside EHR Launch. The app will
|
15
|
+
request authorization for the provided scope from the authorization
|
16
|
+
endpoint, ultimately receiving an authorization token which can be used
|
17
|
+
to gain access to resources on the FHIR server.
|
18
|
+
|
19
|
+
# Test Methodology
|
20
|
+
|
21
|
+
Inferno will redirect the user to the the authorization endpoint so that
|
22
|
+
they may provide any required credentials and authorize the application.
|
23
|
+
Upon successful authorization, Inferno will exchange the authorization
|
24
|
+
code provided for an access token.
|
25
|
+
|
26
|
+
For more information on the #{title}:
|
27
|
+
|
28
|
+
* [Standalone Launch Sequence](http://hl7.org/fhir/smart-app-launch/STU2/app-launch.html#launch-app-standalone-launch)
|
29
|
+
)
|
30
|
+
|
31
|
+
config(
|
32
|
+
inputs: {
|
33
|
+
use_pkce: {
|
34
|
+
default: 'true',
|
35
|
+
locked: true
|
36
|
+
},
|
37
|
+
pkce_code_challenge_method: {
|
38
|
+
default: 'S256',
|
39
|
+
locked: true
|
40
|
+
},
|
41
|
+
requested_scopes: {
|
42
|
+
default: 'launch/patient openid fhirUser offline_access patient/*.rs'
|
43
|
+
}
|
44
|
+
}
|
45
|
+
)
|
46
|
+
|
47
|
+
test from: :smart_app_redirect_stu2
|
48
|
+
|
49
|
+
redirect_index = children.find_index { |child| child.id.to_s.end_with? 'app_redirect' }
|
50
|
+
children[redirect_index] = children.pop
|
51
|
+
end
|
52
|
+
end
|
@@ -16,7 +16,7 @@ module SMARTAppLaunch
|
|
16
16
|
the Pragma response header field with a value of no-cache to be
|
17
17
|
consistent with the requirements of the inital access token exchange.
|
18
18
|
)
|
19
|
-
input :
|
19
|
+
input :smart_token_url, :refresh_token, :client_id, :received_scopes
|
20
20
|
input :client_secret, optional: true
|
21
21
|
output :smart_credentials, :token_retrieval_time
|
22
22
|
makes_request :token_refresh
|
@@ -39,7 +39,7 @@ module SMARTAppLaunch
|
|
39
39
|
oauth2_params['client_id'] = client_id
|
40
40
|
end
|
41
41
|
|
42
|
-
post(
|
42
|
+
post(smart_token_url, body: oauth2_params, name: :token_refresh, headers: oauth2_headers)
|
43
43
|
|
44
44
|
assert_response_status(200)
|
45
45
|
assert_valid_json(request.response_body)
|
@@ -54,7 +54,7 @@ module SMARTAppLaunch
|
|
54
54
|
client_id: client_id,
|
55
55
|
client_secret: client_secret,
|
56
56
|
token_retrieval_time: token_retrieval_time,
|
57
|
-
token_url:
|
57
|
+
token_url: smart_token_url
|
58
58
|
}.to_json
|
59
59
|
end
|
60
60
|
end
|
@@ -43,7 +43,7 @@ module SMARTAppLaunch
|
|
43
43
|
validate_required_fields_present(token_response_body, ['access_token', 'token_type', 'expires_in', 'scope'])
|
44
44
|
validate_token_field_types(token_response_body)
|
45
45
|
validate_token_type(token_response_body)
|
46
|
-
check_for_missing_scopes(requested_scopes, token_response_body)
|
46
|
+
check_for_missing_scopes(requested_scopes, token_response_body) unless config.options[:ignore_missing_scopes_check]
|
47
47
|
|
48
48
|
assert access_token.present?, 'Token response did not contain an access token'
|
49
49
|
assert token_response_body['token_type']&.casecmp('Bearer')&.zero?,
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module SMARTAppLaunch
|
2
|
+
class WellKnownCapabilitiesSTU1Test < Inferno::Test
|
3
|
+
title 'Well-known configuration contains required fields'
|
4
|
+
id :well_known_capabilities_stu1
|
5
|
+
input :well_known_configuration
|
6
|
+
description %(
|
7
|
+
The JSON from .well-known/smart-configuration contains the following
|
8
|
+
required fields: `authorization_endpoint`, `token_endpoint`,
|
9
|
+
`capabilities`
|
10
|
+
)
|
11
|
+
|
12
|
+
def required_capabilities
|
13
|
+
{
|
14
|
+
'authorization_endpoint' => String,
|
15
|
+
'token_endpoint' => String,
|
16
|
+
'capabilities' => Array
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
run do
|
21
|
+
skip_if well_known_configuration.blank?, 'No well-known configuration found'
|
22
|
+
config = JSON.parse(well_known_configuration)
|
23
|
+
|
24
|
+
required_capabilities.each do |key, type|
|
25
|
+
assert config.key?(key), "Well-known configuration does not include `#{key}`"
|
26
|
+
assert config[key].present?, "Well-known configuration field `#{key}` is blank"
|
27
|
+
assert config[key].is_a?(type), "Well-known `#{key}` must be type: #{type.to_s.downcase}"
|
28
|
+
end
|
29
|
+
|
30
|
+
non_string_capabilities = config['capabilities'].reject { |capability| capability.is_a? String }
|
31
|
+
|
32
|
+
assert non_string_capabilities.blank?, %(
|
33
|
+
Well-known `capabilities` field must be an array of strings, but found
|
34
|
+
non-string values:
|
35
|
+
#{non_string_capabilities.map { |value| "`#{value.nil? ? 'nil' : value}`" }.join(', ')}
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module SMARTAppLaunch
|
2
|
+
class WellKnownCapabilitiesSTU2Test < Inferno::Test
|
3
|
+
title 'Well-known configuration contains required fields'
|
4
|
+
id :well_known_capabilities_stu2
|
5
|
+
input :well_known_configuration
|
6
|
+
description %(
|
7
|
+
The JSON from .well-known/smart-configuration contains the following
|
8
|
+
required fields: `authorization_endpoint`, `token_endpoint`,
|
9
|
+
`capabilities`, `grant_types_supported`, `code_challenge_methods_supported`.
|
10
|
+
If the `sso-openid-connect` capability is supported, then `issuer` and `jwks_uri` must be
|
11
|
+
present. If `sso-openid-connect` capability is not supported, then `issuer` must be omitted.
|
12
|
+
)
|
13
|
+
|
14
|
+
def required_capabilities
|
15
|
+
{
|
16
|
+
'authorization_endpoint' => String,
|
17
|
+
'token_endpoint' => String,
|
18
|
+
'capabilities' => Array,
|
19
|
+
'grant_types_supported' => Array,
|
20
|
+
'code_challenge_methods_supported' => Array
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
run do
|
25
|
+
skip_if well_known_configuration.blank?, 'No well-known configuration found'
|
26
|
+
config = JSON.parse(well_known_configuration)
|
27
|
+
|
28
|
+
required_capabilities.each do |key, type|
|
29
|
+
assert config.key?(key), "Well-known configuration does not include `#{key}`"
|
30
|
+
assert config[key].present?, "Well-known configuration field `#{key}` is blank"
|
31
|
+
assert config[key].is_a?(type), "Well-known `#{key}` must be type: #{type.to_s.downcase}"
|
32
|
+
end
|
33
|
+
|
34
|
+
assert config['grant_types_supported'].include?('authorization_code'),
|
35
|
+
'Well-known `grant_types_supported` must include `authorization_code` grant type to indicate SMART App Launch Support'
|
36
|
+
assert config['code_challenge_methods_supported'].include?('S256'),
|
37
|
+
'Well-known `code_challenge_methods_supported` must include `S256`'
|
38
|
+
assert config['code_challenge_methods_supported'].exclude?('plain'),
|
39
|
+
'Well-known `code_challenge_methods_support` must not include `plain`'
|
40
|
+
|
41
|
+
if config['capabilities'].include?('sso-openid-connect')
|
42
|
+
assert config['issuer'].is_a?(String),
|
43
|
+
'Well-known `issuer` field must be a string and present when server capabilities includes `sso-openid-connect`'
|
44
|
+
assert config['jwks_uri'].is_a?(String),
|
45
|
+
'Well-known `jwks_uri` field must be a string and present when server capabilites includes `sso-openid-coneect`'
|
46
|
+
else
|
47
|
+
warning do
|
48
|
+
assert config['issuer'].nil?, 'Well-known `issuer` is omitted when server capabilites does not include `sso-openid-connect`'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
non_string_capabilities = config['capabilities'].reject { |capability| capability.is_a? String }
|
53
|
+
|
54
|
+
assert non_string_capabilities.blank?, %(
|
55
|
+
Well-known `capabilities` field must be an array of strings, but found
|
56
|
+
non-string values:
|
57
|
+
#{non_string_capabilities.map { |value| "`#{value.nil? ? 'nil' : value}`" }.join(', ')}
|
58
|
+
)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module SMARTAppLaunch
|
2
|
+
class WellKnownEndpointTest < Inferno::Test
|
3
|
+
title 'FHIR server makes SMART configuration available from well-known endpoint'
|
4
|
+
id :well_known_endpoint
|
5
|
+
description %(
|
6
|
+
The authorization endpoints accepted by a FHIR resource server can
|
7
|
+
be exposed as a Well-Known Uniform Resource Identifier
|
8
|
+
)
|
9
|
+
input :url,
|
10
|
+
title: 'FHIR Endpoint',
|
11
|
+
description: 'URL of the FHIR endpoint used by SMART applications'
|
12
|
+
output :well_known_configuration,
|
13
|
+
:well_known_authorization_url,
|
14
|
+
:well_known_introspection_url,
|
15
|
+
:well_known_management_url,
|
16
|
+
:well_known_registration_url,
|
17
|
+
:well_known_revocation_url,
|
18
|
+
:well_known_token_url
|
19
|
+
makes_request :smart_well_known_configuration
|
20
|
+
|
21
|
+
run do
|
22
|
+
well_known_configuration_url = "#{url.chomp('/')}/.well-known/smart-configuration"
|
23
|
+
get(well_known_configuration_url,
|
24
|
+
name: :smart_well_known_configuration,
|
25
|
+
headers: { 'Accept' => 'application/json' })
|
26
|
+
assert_response_status(200)
|
27
|
+
|
28
|
+
assert_valid_json(request.response_body)
|
29
|
+
|
30
|
+
config = JSON.parse(request.response_body)
|
31
|
+
output well_known_configuration: request.response_body,
|
32
|
+
well_known_authorization_url: config['authorization_endpoint'],
|
33
|
+
well_known_introspection_url: config['introspection_endpoint'],
|
34
|
+
well_known_management_url: config['management_endpoint'],
|
35
|
+
well_known_registration_url: config['registration_endpoint'],
|
36
|
+
well_known_revocation_url: config['revocation_endpoint'],
|
37
|
+
well_known_token_url: config['token_endpoint']
|
38
|
+
|
39
|
+
content_type = request.response_header('Content-Type')&.value
|
40
|
+
|
41
|
+
assert content_type.present?, 'No `Content-Type` header received.'
|
42
|
+
assert content_type.start_with?('application/json'),
|
43
|
+
"`Content-Type` must be `application/json`, but received: `#{content_type}`"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -1,174 +1,4 @@
|
|
1
1
|
require 'tls_test_kit'
|
2
2
|
|
3
|
-
require_relative 'smart_app_launch/
|
4
|
-
require_relative 'smart_app_launch/
|
5
|
-
require_relative 'smart_app_launch/standalone_launch_group'
|
6
|
-
require_relative 'smart_app_launch/ehr_launch_group'
|
7
|
-
require_relative 'smart_app_launch/openid_connect_group'
|
8
|
-
require_relative 'smart_app_launch/token_refresh_group'
|
9
|
-
|
10
|
-
# TODO: Remove once this functionality is released in core:
|
11
|
-
# https://github.com/inferno-framework/inferno-core/pull/86
|
12
|
-
module Inferno
|
13
|
-
module DSL
|
14
|
-
module Runnable
|
15
|
-
def required_inputs(prior_outputs = [])
|
16
|
-
required_inputs =
|
17
|
-
inputs
|
18
|
-
.reject { |input| input_definitions[input][:optional] }
|
19
|
-
.map { |input| config.input_name(input) }
|
20
|
-
.reject { |input| prior_outputs.include?(input) }
|
21
|
-
children_required_inputs = children.flat_map { |child| child.required_inputs(prior_outputs) }
|
22
|
-
prior_outputs.concat(outputs.map { |output| config.output_name(output) })
|
23
|
-
(required_inputs + children_required_inputs).flatten.uniq
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
module SMARTAppLaunch
|
30
|
-
class SMARTSuite < Inferno::TestSuite
|
31
|
-
id 'smart'
|
32
|
-
title 'SMART App Launch STU1'
|
33
|
-
version VERSION
|
34
|
-
|
35
|
-
resume_test_route :get, '/launch' do
|
36
|
-
request.query_parameters['iss']
|
37
|
-
end
|
38
|
-
|
39
|
-
resume_test_route :get, '/redirect' do
|
40
|
-
request.query_parameters['state']
|
41
|
-
end
|
42
|
-
|
43
|
-
config options: {
|
44
|
-
redirect_uri: "#{Inferno::Application['base_url']}/custom/smart/redirect",
|
45
|
-
launch_uri: "#{Inferno::Application['base_url']}/custom/smart/launch"
|
46
|
-
}
|
47
|
-
|
48
|
-
group do
|
49
|
-
title 'Standalone Launch'
|
50
|
-
id :smart_full_standalone_launch
|
51
|
-
|
52
|
-
run_as_group
|
53
|
-
|
54
|
-
group from: :smart_discovery
|
55
|
-
|
56
|
-
group from: :smart_standalone_launch
|
57
|
-
|
58
|
-
group from: :smart_openid_connect,
|
59
|
-
config: {
|
60
|
-
inputs: {
|
61
|
-
id_token: { name: :standalone_id_token },
|
62
|
-
client_id: { name: :standalone_client_id },
|
63
|
-
requested_scopes: { name: :standalone_requested_scopes },
|
64
|
-
access_token: { name: :standalone_access_token },
|
65
|
-
smart_credentials: { name: :standalone_smart_credentials }
|
66
|
-
}
|
67
|
-
}
|
68
|
-
|
69
|
-
group from: :smart_token_refresh,
|
70
|
-
id: :smart_standalone_refresh_without_scopes,
|
71
|
-
title: 'SMART Token Refresh Without Scopes',
|
72
|
-
config: {
|
73
|
-
inputs: {
|
74
|
-
refresh_token: { name: :standalone_refresh_token },
|
75
|
-
client_id: { name: :standalone_client_id },
|
76
|
-
client_secret: { name: :standalone_client_secret },
|
77
|
-
received_scopes: { name: :standalone_received_scopes }
|
78
|
-
},
|
79
|
-
outputs: {
|
80
|
-
refresh_token: { name: :standalone_refresh_token },
|
81
|
-
received_scopes: { name: :standalone_received_scopes },
|
82
|
-
access_token: { name: :standalone_access_token },
|
83
|
-
token_retrieval_time: { name: :standalone_token_retrieval_time },
|
84
|
-
expires_in: { name: :standalone_expires_in },
|
85
|
-
smart_credentials: { name: :standalone_smart_credentials }
|
86
|
-
}
|
87
|
-
}
|
88
|
-
|
89
|
-
group from: :smart_token_refresh,
|
90
|
-
id: :smart_standalone_refresh_with_scopes,
|
91
|
-
title: 'SMART Token Refresh With Scopes',
|
92
|
-
config: {
|
93
|
-
options: { include_scopes: true },
|
94
|
-
inputs: {
|
95
|
-
refresh_token: { name: :standalone_refresh_token },
|
96
|
-
client_id: { name: :standalone_client_id },
|
97
|
-
client_secret: { name: :standalone_client_secret },
|
98
|
-
received_scopes: { name: :standalone_received_scopes }
|
99
|
-
},
|
100
|
-
outputs: {
|
101
|
-
refresh_token: { name: :standalone_refresh_token },
|
102
|
-
received_scopes: { name: :standalone_received_scopes },
|
103
|
-
access_token: { name: :standalone_access_token },
|
104
|
-
token_retrieval_time: { name: :standalone_token_retrieval_time },
|
105
|
-
expires_in: { name: :standalone_expires_in },
|
106
|
-
smart_credentials: { name: :standalone_smart_credentials }
|
107
|
-
}
|
108
|
-
}
|
109
|
-
end
|
110
|
-
|
111
|
-
group do
|
112
|
-
title 'EHR Launch'
|
113
|
-
id :smart_full_ehr_launch
|
114
|
-
|
115
|
-
run_as_group
|
116
|
-
|
117
|
-
group from: :smart_discovery
|
118
|
-
|
119
|
-
group from: :smart_ehr_launch
|
120
|
-
|
121
|
-
group from: :smart_openid_connect,
|
122
|
-
config: {
|
123
|
-
inputs: {
|
124
|
-
id_token: { name: :ehr_id_token },
|
125
|
-
client_id: { name: :ehr_client_id },
|
126
|
-
requested_scopes: { name: :ehr_requested_scopes },
|
127
|
-
access_token: { name: :ehr_access_token },
|
128
|
-
smart_credentials: { name: :ehr_smart_credentials }
|
129
|
-
}
|
130
|
-
}
|
131
|
-
|
132
|
-
group from: :smart_token_refresh,
|
133
|
-
id: :smart_ehr_refresh_without_scopes,
|
134
|
-
title: 'SMART Token Refresh Without Scopes',
|
135
|
-
config: {
|
136
|
-
inputs: {
|
137
|
-
refresh_token: { name: :ehr_refresh_token },
|
138
|
-
client_id: { name: :ehr_client_id },
|
139
|
-
client_secret: { name: :ehr_client_secret },
|
140
|
-
received_scopes: { name: :ehr_received_scopes }
|
141
|
-
},
|
142
|
-
outputs: {
|
143
|
-
refresh_token: { name: :ehr_refresh_token },
|
144
|
-
received_scopes: { name: :ehr_received_scopes },
|
145
|
-
access_token: { name: :ehr_access_token },
|
146
|
-
token_retrieval_time: { name: :ehr_token_retrieval_time },
|
147
|
-
expires_in: { name: :ehr_expires_in },
|
148
|
-
smart_credentials: { name: :ehr_smart_credentials }
|
149
|
-
}
|
150
|
-
}
|
151
|
-
|
152
|
-
group from: :smart_token_refresh,
|
153
|
-
id: :smart_ehr_refresh_with_scopes,
|
154
|
-
title: 'SMART Token Refresh With Scopes',
|
155
|
-
config: {
|
156
|
-
options: { include_scopes: true },
|
157
|
-
inputs: {
|
158
|
-
refresh_token: { name: :ehr_refresh_token },
|
159
|
-
client_id: { name: :ehr_client_id },
|
160
|
-
client_secret: { name: :ehr_client_secret },
|
161
|
-
received_scopes: { name: :ehr_received_scopes }
|
162
|
-
},
|
163
|
-
outputs: {
|
164
|
-
refresh_token: { name: :ehr_refresh_token },
|
165
|
-
received_scopes: { name: :ehr_received_scopes },
|
166
|
-
access_token: { name: :ehr_access_token },
|
167
|
-
token_retrieval_time: { name: :ehr_token_retrieval_time },
|
168
|
-
expires_in: { name: :ehr_expires_in },
|
169
|
-
smart_credentials: { name: :ehr_smart_credentials }
|
170
|
-
}
|
171
|
-
}
|
172
|
-
end
|
173
|
-
end
|
174
|
-
end
|
3
|
+
require_relative 'smart_app_launch/smart_stu1_suite'
|
4
|
+
require_relative 'smart_app_launch/smart_stu2_suite'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smart_app_launch_test_kit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stephen MacVicar
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-09-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: inferno_core
|
@@ -133,9 +133,12 @@ files:
|
|
133
133
|
- LICENSE
|
134
134
|
- lib/smart_app_launch/app_launch_test.rb
|
135
135
|
- lib/smart_app_launch/app_redirect_test.rb
|
136
|
+
- lib/smart_app_launch/app_redirect_test_stu2.rb
|
136
137
|
- lib/smart_app_launch/code_received_test.rb
|
137
|
-
- lib/smart_app_launch/
|
138
|
+
- lib/smart_app_launch/discovery_stu1_group.rb
|
139
|
+
- lib/smart_app_launch/discovery_stu2_group.rb
|
138
140
|
- lib/smart_app_launch/ehr_launch_group.rb
|
141
|
+
- lib/smart_app_launch/ehr_launch_group_stu2.rb
|
139
142
|
- lib/smart_app_launch/launch_received_test.rb
|
140
143
|
- lib/smart_app_launch/openid_connect_group.rb
|
141
144
|
- lib/smart_app_launch/openid_decode_id_token_test.rb
|
@@ -145,7 +148,11 @@ files:
|
|
145
148
|
- lib/smart_app_launch/openid_retrieve_jwks_test.rb
|
146
149
|
- lib/smart_app_launch/openid_token_header_test.rb
|
147
150
|
- lib/smart_app_launch/openid_token_payload_test.rb
|
151
|
+
- lib/smart_app_launch/post_auth.html
|
152
|
+
- lib/smart_app_launch/smart_stu1_suite.rb
|
153
|
+
- lib/smart_app_launch/smart_stu2_suite.rb
|
148
154
|
- lib/smart_app_launch/standalone_launch_group.rb
|
155
|
+
- lib/smart_app_launch/standalone_launch_group_stu2.rb
|
149
156
|
- lib/smart_app_launch/token_exchange_test.rb
|
150
157
|
- lib/smart_app_launch/token_payload_validation.rb
|
151
158
|
- lib/smart_app_launch/token_refresh_body_test.rb
|
@@ -154,6 +161,9 @@ files:
|
|
154
161
|
- lib/smart_app_launch/token_response_body_test.rb
|
155
162
|
- lib/smart_app_launch/token_response_headers_test.rb
|
156
163
|
- lib/smart_app_launch/version.rb
|
164
|
+
- lib/smart_app_launch/well_known_capabilities_stu1_test.rb
|
165
|
+
- lib/smart_app_launch/well_known_capabilities_stu2_test.rb
|
166
|
+
- lib/smart_app_launch/well_known_endpoint_test.rb
|
157
167
|
- lib/smart_app_launch_test_kit.rb
|
158
168
|
homepage: https://github.com/inferno_framework/smart-app-launch-test-kit
|
159
169
|
licenses:
|
@@ -161,7 +171,7 @@ licenses:
|
|
161
171
|
metadata:
|
162
172
|
homepage_uri: https://github.com/inferno_framework/smart-app-launch-test-kit
|
163
173
|
source_code_uri: https://github.com/inferno_framework/smart-app-launch-test-kit
|
164
|
-
post_install_message:
|
174
|
+
post_install_message:
|
165
175
|
rdoc_options: []
|
166
176
|
require_paths:
|
167
177
|
- lib
|
@@ -177,7 +187,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
177
187
|
version: '0'
|
178
188
|
requirements: []
|
179
189
|
rubygems_version: 3.1.6
|
180
|
-
signing_key:
|
190
|
+
signing_key:
|
181
191
|
specification_version: 4
|
182
192
|
summary: Inferno Tests for the SMART Application Launch Framework Implementation Guide
|
183
193
|
test_files: []
|