safire 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.claude/skills/release-safire/SKILL.md +153 -0
- data/.rubocop.yml +11 -1
- data/.tool-versions +1 -1
- data/CHANGELOG.md +107 -1
- data/CONTRIBUTION.md +6 -1
- data/Gemfile +1 -2
- data/Gemfile.lock +47 -37
- data/README.md +60 -5
- data/ROADMAP.md +61 -12
- data/docs/Gemfile.lock +49 -45
- data/docs/_config.yml +2 -2
- data/docs/adr/ADR-001-activesupport-dependency.md +2 -2
- data/docs/adr/ADR-002-facade-and-forwardable.md +21 -6
- data/docs/adr/ADR-003-protocol-vs-client-type.md +13 -6
- data/docs/adr/ADR-004-clientconfig-immutability-and-entity-masking.md +34 -8
- data/docs/adr/ADR-006-lazy-discovery.md +64 -5
- data/docs/adr/ADR-007-https-only-redirects-and-localhost-exception.md +40 -10
- data/docs/adr/ADR-009-oauth-error-hierarchy.md +131 -0
- data/docs/adr/ADR-010-optional-client-id-dcr-temp-client.md +90 -0
- data/docs/adr/ADR-011-udap-stu2-discovery-conformance.md +113 -0
- data/docs/adr/ADR-012-udap-signed-metadata-validation.md +104 -0
- data/docs/adr/ADR-013-udap-registration-request-model.md +106 -0
- data/docs/adr/ADR-014-udap-software-statement-signing.md +122 -0
- data/docs/adr/index.md +9 -3
- data/docs/advanced.md +22 -25
- data/docs/configuration/client-setup.md +126 -10
- data/docs/configuration/index.md +11 -7
- data/docs/index.md +13 -6
- data/docs/installation.md +3 -2
- data/docs/security.md +44 -5
- data/docs/smart-on-fhir/backend-services/index.md +2 -2
- data/docs/smart-on-fhir/backend-services/token-request.md +1 -1
- data/docs/smart-on-fhir/confidential-asymmetric/index.md +2 -2
- data/docs/smart-on-fhir/confidential-symmetric/index.md +1 -1
- data/docs/smart-on-fhir/discovery/capability-checks.md +7 -0
- data/docs/smart-on-fhir/dynamic-client-registration/index.md +103 -0
- data/docs/smart-on-fhir/dynamic-client-registration/registration.md +160 -0
- data/docs/smart-on-fhir/dynamic-client-registration/response.md +161 -0
- data/docs/smart-on-fhir/index.md +2 -1
- data/docs/smart-on-fhir/post-based-authorization.md +1 -1
- data/docs/smart-on-fhir/public-client/index.md +1 -1
- data/docs/troubleshooting/auth-errors.md +20 -0
- data/docs/troubleshooting/client-errors.md +143 -0
- data/docs/troubleshooting/index.md +61 -4
- data/docs/udap/dynamic-client-registration/index.md +198 -0
- data/docs/udap/dynamic-client-registration/lifecycle.md +115 -0
- data/docs/udap/dynamic-client-registration/registration-metadata.md +133 -0
- data/docs/udap/dynamic-client-registration/software-statement.md +115 -0
- data/docs/udap.md +187 -64
- data/gemfiles/activesupport_71.gemfile +30 -0
- data/gemfiles/activesupport_71.gemfile.lock +291 -0
- data/lib/safire/client.rb +141 -44
- data/lib/safire/client_config.rb +114 -35
- data/lib/safire/client_config_builder.rb +18 -0
- data/lib/safire/errors.rb +100 -44
- data/lib/safire/http_client.rb +7 -2
- data/lib/safire/middleware/https_only_redirects.rb +10 -4
- data/lib/safire/protocols/behaviours.rb +12 -1
- data/lib/safire/protocols/oauth_response_handling.rb +48 -0
- data/lib/safire/protocols/smart.rb +108 -36
- data/lib/safire/protocols/smart_metadata.rb +6 -0
- data/lib/safire/protocols/udap.rb +534 -0
- data/lib/safire/protocols/udap_metadata.rb +429 -0
- data/lib/safire/protocols/udap_registration_metadata.rb +368 -0
- data/lib/safire/protocols/udap_signed_metadata_validator.rb +349 -0
- data/lib/safire/protocols/udap_software_statement.rb +348 -0
- data/lib/safire/protocols.rb +6 -0
- data/lib/safire/uri_validation.rb +102 -0
- data/lib/safire/version.rb +1 -1
- data/lib/safire.rb +2 -0
- data/safire.gemspec +7 -8
- metadata +42 -13
data/docs/udap.md
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
layout: default
|
|
3
3
|
title: UDAP
|
|
4
4
|
nav_order: 5
|
|
5
|
-
|
|
5
|
+
permalink: /udap/
|
|
6
|
+
description: "UDAP Security STU2 discovery and Dynamic Client Registration in Safire."
|
|
7
|
+
has_children: true
|
|
6
8
|
---
|
|
7
9
|
|
|
8
10
|
# UDAP
|
|
@@ -10,70 +12,191 @@ description: "Overview of planned UDAP Security protocol support in Safire, incl
|
|
|
10
12
|
{: .no_toc }
|
|
11
13
|
|
|
12
14
|
<div class="code-example" markdown="1">
|
|
13
|
-
|
|
15
|
+
Safire implements UDAP Security STU2 discovery, signed metadata validation,
|
|
16
|
+
community scoping, and certificate-backed Dynamic Client Registration for new
|
|
17
|
+
registration, modification, and cancellation. JWT client authentication and
|
|
18
|
+
Tiered OAuth remain planned.
|
|
14
19
|
</div>
|
|
15
20
|
|
|
16
|
-
## Table of contents
|
|
17
|
-
{: .no_toc .text-delta }
|
|
18
|
-
|
|
19
|
-
1. TOC
|
|
20
|
-
{:toc}
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
24
21
|
## Overview
|
|
25
22
|
|
|
26
|
-
UDAP (Unified Data Access Profiles) is a security framework for healthcare data
|
|
27
|
-
|
|
28
|
-
UDAP
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
-
|
|
79
|
-
|
|
23
|
+
UDAP (Unified Data Access Profiles) is a security framework for healthcare data
|
|
24
|
+
exchange defined by the
|
|
25
|
+
[UDAP Security STU2 / v2.0.0 Implementation Guide](https://hl7.org/fhir/us/udap-security/STU2/index.html).
|
|
26
|
+
It adds X.509-based identity, signed metadata, dynamic registration, and trust
|
|
27
|
+
community semantics to OAuth-based workflows.
|
|
28
|
+
|
|
29
|
+
Select UDAP independently from SMART client authentication types:
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
client = Safire::Client.new(
|
|
33
|
+
{ base_url: 'https://fhir.example.com' },
|
|
34
|
+
protocol: :udap
|
|
35
|
+
)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`client_type:` is not applicable to UDAP. Passing one explicitly, or assigning
|
|
39
|
+
`client.client_type = ...` on a UDAP client, raises
|
|
40
|
+
`Safire::Errors::ConfigurationError`.
|
|
41
|
+
|
|
42
|
+
## Discovery
|
|
43
|
+
|
|
44
|
+
`server_metadata` fetches `/.well-known/udap`, validates the required
|
|
45
|
+
`signed_metadata` JWT, and merges authoritative signed endpoint claims over
|
|
46
|
+
their unsigned JSON counterparts before returning `UdapMetadata`.
|
|
47
|
+
|
|
48
|
+
### Server trust and revocation
|
|
49
|
+
|
|
50
|
+
Production discovery requires caller-supplied trust anchors plus an explicit
|
|
51
|
+
revocation policy. Use CRLs or a custom checker:
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
ca_cert = OpenSSL::X509::Certificate.new(File.read('udap-ca.pem'))
|
|
55
|
+
ca_crl = OpenSSL::X509::CRL.new(File.read('udap-ca.crl'))
|
|
56
|
+
|
|
57
|
+
metadata = client.server_metadata(
|
|
58
|
+
trusted_anchors: [ca_cert],
|
|
59
|
+
crls: [ca_crl]
|
|
60
|
+
)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Safire verifies the JWT signature, certificate chain and revocation status,
|
|
64
|
+
issuer/SAN relationship, time claims, `jti`, and signed endpoint claims. Results
|
|
65
|
+
are cached per community and trust policy, but cache hits revalidate the signed
|
|
66
|
+
metadata before reuse. An expired or newly untrusted entry is discarded and
|
|
67
|
+
refetched.
|
|
68
|
+
|
|
69
|
+
{: .warning }
|
|
70
|
+
> `verify_chain: false` skips X.509 chain and revocation validation. Use it only
|
|
71
|
+
> for development and tests. Local HTTP loopback servers separately require
|
|
72
|
+
> `allow_insecure_localhost: true` in `ClientConfig`; this permits HTTP only on
|
|
73
|
+
> `localhost` and `127.0.0.1` and is not production UDAP conformance.
|
|
74
|
+
|
|
75
|
+
### Community-scoped discovery
|
|
76
|
+
|
|
77
|
+
Pass a trust-community URI when a server supports multiple communities:
|
|
78
|
+
|
|
79
|
+
```ruby
|
|
80
|
+
metadata = client.server_metadata(
|
|
81
|
+
community: 'https://udap.example.org/community1',
|
|
82
|
+
trusted_anchors: [ca_cert],
|
|
83
|
+
crls: [ca_crl]
|
|
84
|
+
)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Different community, trust-anchor, CRL, revocation-checker, or chain-verification
|
|
88
|
+
arguments use separate cache entries.
|
|
89
|
+
|
|
90
|
+
### Structural and capability helpers
|
|
91
|
+
|
|
92
|
+
`UdapMetadata#valid?` checks required fields, exact STU2 fixed values, array and
|
|
93
|
+
string types, conditional fields, subset rules, and endpoint URL shape. It logs
|
|
94
|
+
each violation and returns `false`; it does not repeat cryptographic validation.
|
|
95
|
+
|
|
96
|
+
```ruby
|
|
97
|
+
unless metadata.valid?
|
|
98
|
+
# Safire.logger.warn has already recorded each structural violation.
|
|
99
|
+
raise 'UDAP metadata is structurally non-conformant'
|
|
100
|
+
end
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Profile-only helpers inspect raw profile advertisements. Capability helpers add
|
|
104
|
+
the endpoint or grant preconditions Safire can verify locally:
|
|
105
|
+
|
|
106
|
+
| Capability helper | Checks |
|
|
107
|
+
|-------------------|--------|
|
|
108
|
+
| `supports_dynamic_registration?` | `udap_dcr` profile plus a valid `registration_endpoint` |
|
|
109
|
+
| `supports_jwt_client_auth?` | `udap_authn` profile plus a valid `token_endpoint` |
|
|
110
|
+
| `supports_client_authorization?` | `udap_authz`, `client_credentials`, and a valid `token_endpoint` |
|
|
111
|
+
| `supports_authorization_code?` | `authorization_code` grant advertisement |
|
|
112
|
+
| `supports_refresh_token?` | `refresh_token` grant advertisement |
|
|
113
|
+
| `supports_tiered_oauth?` | `udap_to` profile advertisement |
|
|
114
|
+
| `supports_signed_metadata?` | Compact-JWS-shaped `signed_metadata` value |
|
|
115
|
+
|
|
116
|
+
The corresponding profile helpers are `dynamic_registration_profile?`,
|
|
117
|
+
`jwt_client_auth_profile?`, `client_authorization_profile?`, and
|
|
118
|
+
`tiered_oauth_profile?`.
|
|
119
|
+
|
|
120
|
+
Use `signed_metadata_valid?(base_url:, ...)` only when explicitly revalidating
|
|
121
|
+
an existing metadata object against another trust policy. Metadata returned by
|
|
122
|
+
`server_metadata` is already cryptographically validated.
|
|
123
|
+
|
|
124
|
+
### Discovery failures
|
|
125
|
+
|
|
126
|
+
| Condition | Safire behavior |
|
|
127
|
+
|-----------|-----------------|
|
|
128
|
+
| `404 Not Found` | Raises `DiscoveryError`; clients treat the server as not supporting UDAP workflows |
|
|
129
|
+
| `204 No Content` | Raises `DiscoveryError` before body parsing; no UDAP workflow is available for that community |
|
|
130
|
+
| Invalid `signed_metadata` | Raises `DiscoveryError` after validation warnings |
|
|
131
|
+
| Malformed DER in JOSE `x5c` | Raises `CertificateError` |
|
|
132
|
+
| Invalid `community:` | Raises `ConfigurationError` before HTTP |
|
|
133
|
+
| Connection, TLS, timeout, or blocked redirect failure | Raises `NetworkError` |
|
|
134
|
+
|
|
135
|
+
## Dynamic Client Registration
|
|
136
|
+
|
|
137
|
+
UDAP DCR is discovery-bound. Safire validates discovery metadata, signs
|
|
138
|
+
caller-controlled registration metadata into a short-lived X.509-backed
|
|
139
|
+
`software_statement`, and POSTs the fixed UDAP envelope to the authoritative
|
|
140
|
+
signed `registration_endpoint`.
|
|
141
|
+
|
|
142
|
+
```ruby
|
|
143
|
+
client = Safire::Client.new(
|
|
144
|
+
{
|
|
145
|
+
base_url: 'https://fhir.example.com',
|
|
146
|
+
private_key: File.read('client-key.pem'),
|
|
147
|
+
certificate_chain: [File.read('client-cert.pem')]
|
|
148
|
+
},
|
|
149
|
+
protocol: :udap
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
registration = client.register_client(
|
|
153
|
+
{
|
|
154
|
+
client_name: 'Example Backend Service',
|
|
155
|
+
contacts: ['mailto:security@example.com'],
|
|
156
|
+
grant_types: ['client_credentials'],
|
|
157
|
+
scope: 'system/Patient.rs'
|
|
158
|
+
},
|
|
159
|
+
client_uri: 'https://client.example.com',
|
|
160
|
+
trusted_anchors: [ca_cert],
|
|
161
|
+
crls: [ca_crl]
|
|
162
|
+
)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Use `register_client` again with the same client URI and community to request a
|
|
166
|
+
modification. Use `cancel_registration` to send a fresh software statement with
|
|
167
|
+
an empty `grant_types` array. Safire accepts cancellation only when a successful
|
|
168
|
+
response contains a non-blank `client_id` and an empty `grant_types` array.
|
|
169
|
+
|
|
170
|
+
Pass `certifications:` when required by community policy. Certification and
|
|
171
|
+
endorsement JWTs may be signed by the client operator or a third party. Safire
|
|
172
|
+
validates their compact-JWS shape but leaves issuer, signature, claims, and
|
|
173
|
+
policy evaluation to the authorization server.
|
|
174
|
+
|
|
175
|
+
See [Dynamic Client Registration]({% link udap/dynamic-client-registration/index.md %})
|
|
176
|
+
for prerequisites and public API usage, [Registration Metadata]({% link udap/dynamic-client-registration/registration-metadata.md %})
|
|
177
|
+
for input rules, [Software Statements]({% link udap/dynamic-client-registration/software-statement.md %})
|
|
178
|
+
for signing behavior, and [Registration Lifecycle]({% link udap/dynamic-client-registration/lifecycle.md %})
|
|
179
|
+
for modification and cancellation semantics.
|
|
180
|
+
|
|
181
|
+
## Scope and Protocol Choice
|
|
182
|
+
|
|
183
|
+
Safire does not yet implement UDAP JWT client authentication, token acquisition,
|
|
184
|
+
authorization-code handling, or Tiered OAuth. Those methods continue to raise
|
|
185
|
+
`NotImplementedError` on a UDAP client. Dynamic registration obtains a
|
|
186
|
+
`client_id`, but the later UDAP authentication and authorization flows remain
|
|
187
|
+
separate roadmap work.
|
|
188
|
+
|
|
189
|
+
| Feature | SMART App Launch in Safire | UDAP Security in Safire |
|
|
190
|
+
|---------|----------------------------|--------------------------|
|
|
191
|
+
| Discovery | SMART configuration JSON | Signed UDAP metadata with X.509 trust validation |
|
|
192
|
+
| Dynamic registration | RFC 7591 metadata request | STU2 X.509-backed software statement |
|
|
193
|
+
| Implemented client flows | App Launch and Backend Services | Registration lifecycle only |
|
|
194
|
+
| Client selection | `client_type:` | `protocol: :udap`; no `client_type:` |
|
|
195
|
+
| Trust model | Per-server client registration and registered keys/secrets | Certificate trust communities and signed metadata |
|
|
196
|
+
|
|
197
|
+
Resources:
|
|
198
|
+
|
|
199
|
+
- [UDAP Security STU2 / v2.0.0 IG](https://hl7.org/fhir/us/udap-security/STU2/index.html)
|
|
200
|
+
- [UDAP Dynamic Client Registration STU1](https://www.udap.org/udap-dynamic-client-registration-stu1.html)
|
|
201
|
+
- [UDAP Certifications and Endorsements STU1](https://www.udap.org/udap-certifications-and-endorsements-stu1.html)
|
|
202
|
+
- [Safire roadmap](https://github.com/vanessuniq/safire/blob/main/ROADMAP.md)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
source 'https://rubygems.org'
|
|
4
|
+
|
|
5
|
+
gem 'activesupport', '~> 7.1.0'
|
|
6
|
+
|
|
7
|
+
gemspec path: '..'
|
|
8
|
+
|
|
9
|
+
group :development, :test do
|
|
10
|
+
gem 'bundler-audit', '~> 0.9'
|
|
11
|
+
gem 'debug'
|
|
12
|
+
gem 'pry'
|
|
13
|
+
gem 'pry-byebug'
|
|
14
|
+
gem 'rspec', '~> 3.12'
|
|
15
|
+
gem 'rubocop', '~> 1.57'
|
|
16
|
+
gem 'rubocop-rspec', '~> 3.0', require: false
|
|
17
|
+
gem 'timecop', '~> 0.9'
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
group :development do
|
|
21
|
+
gem 'yard', '~> 0.9'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
group :test do
|
|
25
|
+
gem 'dotenv', '~> 3.0'
|
|
26
|
+
gem 'simplecov', require: false
|
|
27
|
+
gem 'simplecov-cobertura', require: false
|
|
28
|
+
gem 'sinatra', '~> 4.0'
|
|
29
|
+
gem 'webmock', '~> 3.18'
|
|
30
|
+
end
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: ..
|
|
3
|
+
specs:
|
|
4
|
+
safire (0.3.0)
|
|
5
|
+
activesupport (>= 7.1, < 9)
|
|
6
|
+
addressable (~> 2.8)
|
|
7
|
+
faraday (~> 2.14)
|
|
8
|
+
faraday-follow_redirects (~> 0.4)
|
|
9
|
+
jwt (~> 2.8)
|
|
10
|
+
|
|
11
|
+
GEM
|
|
12
|
+
remote: https://rubygems.org/
|
|
13
|
+
specs:
|
|
14
|
+
activesupport (7.1.6)
|
|
15
|
+
base64
|
|
16
|
+
benchmark (>= 0.3)
|
|
17
|
+
bigdecimal
|
|
18
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
19
|
+
connection_pool (>= 2.2.5)
|
|
20
|
+
drb
|
|
21
|
+
i18n (>= 1.6, < 2)
|
|
22
|
+
logger (>= 1.4.2)
|
|
23
|
+
minitest (>= 5.1)
|
|
24
|
+
mutex_m
|
|
25
|
+
securerandom (>= 0.3)
|
|
26
|
+
tzinfo (~> 2.0)
|
|
27
|
+
addressable (2.9.0)
|
|
28
|
+
public_suffix (>= 2.0.2, < 8.0)
|
|
29
|
+
ast (2.4.3)
|
|
30
|
+
base64 (0.3.0)
|
|
31
|
+
benchmark (0.5.0)
|
|
32
|
+
bigdecimal (4.1.2)
|
|
33
|
+
bundler-audit (0.9.3)
|
|
34
|
+
bundler (>= 1.2.0)
|
|
35
|
+
thor (~> 1.0)
|
|
36
|
+
byebug (13.0.0)
|
|
37
|
+
reline (>= 0.6.0)
|
|
38
|
+
coderay (1.1.3)
|
|
39
|
+
concurrent-ruby (1.3.7)
|
|
40
|
+
connection_pool (3.0.2)
|
|
41
|
+
crack (1.0.1)
|
|
42
|
+
bigdecimal
|
|
43
|
+
rexml
|
|
44
|
+
date (3.5.1)
|
|
45
|
+
debug (1.11.1)
|
|
46
|
+
irb (~> 1.10)
|
|
47
|
+
reline (>= 0.3.8)
|
|
48
|
+
diff-lcs (1.6.2)
|
|
49
|
+
docile (1.4.1)
|
|
50
|
+
dotenv (3.2.0)
|
|
51
|
+
drb (2.2.3)
|
|
52
|
+
erb (6.0.4)
|
|
53
|
+
faraday (2.14.3)
|
|
54
|
+
faraday-net_http (>= 2.0, < 3.5)
|
|
55
|
+
json
|
|
56
|
+
logger
|
|
57
|
+
faraday-follow_redirects (0.5.0)
|
|
58
|
+
faraday (>= 1, < 3)
|
|
59
|
+
faraday-net_http (3.4.4)
|
|
60
|
+
net-http (~> 0.5)
|
|
61
|
+
hashdiff (1.2.1)
|
|
62
|
+
i18n (1.15.0)
|
|
63
|
+
concurrent-ruby (~> 1.0)
|
|
64
|
+
io-console (0.8.2)
|
|
65
|
+
irb (1.18.0)
|
|
66
|
+
pp (>= 0.6.0)
|
|
67
|
+
prism (>= 1.3.0)
|
|
68
|
+
rdoc (>= 4.0.0)
|
|
69
|
+
reline (>= 0.4.2)
|
|
70
|
+
json (2.19.9)
|
|
71
|
+
jwt (2.10.3)
|
|
72
|
+
base64
|
|
73
|
+
language_server-protocol (3.17.0.5)
|
|
74
|
+
lint_roller (1.1.0)
|
|
75
|
+
logger (1.7.0)
|
|
76
|
+
method_source (1.1.0)
|
|
77
|
+
minitest (5.27.0)
|
|
78
|
+
mustermann (3.1.1)
|
|
79
|
+
mutex_m (0.3.0)
|
|
80
|
+
net-http (0.9.1)
|
|
81
|
+
uri (>= 0.11.1)
|
|
82
|
+
parallel (2.1.0)
|
|
83
|
+
parser (3.3.11.1)
|
|
84
|
+
ast (~> 2.4.1)
|
|
85
|
+
racc
|
|
86
|
+
pp (0.6.3)
|
|
87
|
+
prettyprint
|
|
88
|
+
prettyprint (0.2.0)
|
|
89
|
+
prism (1.9.0)
|
|
90
|
+
pry (0.16.0)
|
|
91
|
+
coderay (~> 1.1)
|
|
92
|
+
method_source (~> 1.0)
|
|
93
|
+
reline (>= 0.6.0)
|
|
94
|
+
pry-byebug (3.12.0)
|
|
95
|
+
byebug (~> 13.0)
|
|
96
|
+
pry (>= 0.13, < 0.17)
|
|
97
|
+
psych (5.4.0)
|
|
98
|
+
date
|
|
99
|
+
stringio
|
|
100
|
+
public_suffix (7.0.5)
|
|
101
|
+
racc (1.8.1)
|
|
102
|
+
rack (3.2.6)
|
|
103
|
+
rack-protection (4.2.1)
|
|
104
|
+
base64 (>= 0.1.0)
|
|
105
|
+
logger (>= 1.6.0)
|
|
106
|
+
rack (>= 3.0.0, < 4)
|
|
107
|
+
rack-session (2.1.2)
|
|
108
|
+
base64 (>= 0.1.0)
|
|
109
|
+
rack (>= 3.0.0)
|
|
110
|
+
rainbow (3.1.1)
|
|
111
|
+
rdoc (7.2.0)
|
|
112
|
+
erb
|
|
113
|
+
psych (>= 4.0.0)
|
|
114
|
+
tsort
|
|
115
|
+
regexp_parser (2.12.0)
|
|
116
|
+
reline (0.6.3)
|
|
117
|
+
io-console (~> 0.5)
|
|
118
|
+
rexml (3.4.4)
|
|
119
|
+
rspec (3.13.2)
|
|
120
|
+
rspec-core (~> 3.13.0)
|
|
121
|
+
rspec-expectations (~> 3.13.0)
|
|
122
|
+
rspec-mocks (~> 3.13.0)
|
|
123
|
+
rspec-core (3.13.6)
|
|
124
|
+
rspec-support (~> 3.13.0)
|
|
125
|
+
rspec-expectations (3.13.5)
|
|
126
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
127
|
+
rspec-support (~> 3.13.0)
|
|
128
|
+
rspec-mocks (3.13.8)
|
|
129
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
130
|
+
rspec-support (~> 3.13.0)
|
|
131
|
+
rspec-support (3.13.7)
|
|
132
|
+
rubocop (1.88.0)
|
|
133
|
+
json (~> 2.3)
|
|
134
|
+
language_server-protocol (~> 3.17.0.2)
|
|
135
|
+
lint_roller (~> 1.1.0)
|
|
136
|
+
parallel (>= 1.10)
|
|
137
|
+
parser (>= 3.3.0.2)
|
|
138
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
139
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
|
140
|
+
rubocop-ast (>= 1.49.0, < 2.0)
|
|
141
|
+
ruby-progressbar (~> 1.7)
|
|
142
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
|
143
|
+
rubocop-ast (1.49.1)
|
|
144
|
+
parser (>= 3.3.7.2)
|
|
145
|
+
prism (~> 1.7)
|
|
146
|
+
rubocop-rspec (3.10.2)
|
|
147
|
+
lint_roller (~> 1.1)
|
|
148
|
+
regexp_parser (>= 2.0)
|
|
149
|
+
rubocop (~> 1.86, >= 1.86.2)
|
|
150
|
+
ruby-progressbar (1.13.0)
|
|
151
|
+
securerandom (0.4.1)
|
|
152
|
+
simplecov (0.22.0)
|
|
153
|
+
docile (~> 1.1)
|
|
154
|
+
simplecov-html (~> 0.11)
|
|
155
|
+
simplecov_json_formatter (~> 0.1)
|
|
156
|
+
simplecov-cobertura (3.2.0)
|
|
157
|
+
rexml
|
|
158
|
+
simplecov (~> 0.19)
|
|
159
|
+
simplecov-html (0.13.2)
|
|
160
|
+
simplecov_json_formatter (0.1.4)
|
|
161
|
+
sinatra (4.2.1)
|
|
162
|
+
logger (>= 1.6.0)
|
|
163
|
+
mustermann (~> 3.0)
|
|
164
|
+
rack (>= 3.0.0, < 4)
|
|
165
|
+
rack-protection (= 4.2.1)
|
|
166
|
+
rack-session (>= 2.0.0, < 3)
|
|
167
|
+
tilt (~> 2.0)
|
|
168
|
+
stringio (3.2.0)
|
|
169
|
+
thor (1.5.0)
|
|
170
|
+
tilt (2.7.0)
|
|
171
|
+
timecop (0.9.11)
|
|
172
|
+
tsort (0.2.0)
|
|
173
|
+
tzinfo (2.0.6)
|
|
174
|
+
concurrent-ruby (~> 1.0)
|
|
175
|
+
unicode-display_width (3.2.0)
|
|
176
|
+
unicode-emoji (~> 4.1)
|
|
177
|
+
unicode-emoji (4.2.0)
|
|
178
|
+
uri (1.1.1)
|
|
179
|
+
webmock (3.26.2)
|
|
180
|
+
addressable (>= 2.8.0)
|
|
181
|
+
crack (>= 0.3.2)
|
|
182
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
|
183
|
+
yard (0.9.44)
|
|
184
|
+
|
|
185
|
+
PLATFORMS
|
|
186
|
+
ruby
|
|
187
|
+
x86_64-linux
|
|
188
|
+
|
|
189
|
+
DEPENDENCIES
|
|
190
|
+
activesupport (~> 7.1.0)
|
|
191
|
+
bundler-audit (~> 0.9)
|
|
192
|
+
debug
|
|
193
|
+
dotenv (~> 3.0)
|
|
194
|
+
pry
|
|
195
|
+
pry-byebug
|
|
196
|
+
rspec (~> 3.12)
|
|
197
|
+
rubocop (~> 1.57)
|
|
198
|
+
rubocop-rspec (~> 3.0)
|
|
199
|
+
safire!
|
|
200
|
+
simplecov
|
|
201
|
+
simplecov-cobertura
|
|
202
|
+
sinatra (~> 4.0)
|
|
203
|
+
timecop (~> 0.9)
|
|
204
|
+
webmock (~> 3.18)
|
|
205
|
+
yard (~> 0.9)
|
|
206
|
+
|
|
207
|
+
CHECKSUMS
|
|
208
|
+
activesupport (7.1.6) sha256=7f12140a813b1c4922a322663e547129aef1840fc512fa262378f6d7e7fd3a7c
|
|
209
|
+
addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af
|
|
210
|
+
ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
|
|
211
|
+
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
|
|
212
|
+
benchmark (0.5.0) sha256=465df122341aedcb81a2a24b4d3bd19b6c67c1530713fd533f3ff034e419236c
|
|
213
|
+
bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd
|
|
214
|
+
bundler-audit (0.9.3) sha256=81c8766c71e47d0d28a0f98c7eed028539f21a6ea3cd8f685eb6f42333c9b4e9
|
|
215
|
+
byebug (13.0.0) sha256=d2263efe751941ca520fa29744b71972d39cbc41839496706f5d9b22e92ae05d
|
|
216
|
+
coderay (1.1.3) sha256=dc530018a4684512f8f38143cd2a096c9f02a1fc2459edcfe534787a7fc77d4b
|
|
217
|
+
concurrent-ruby (1.3.7) sha256=4412caec3a5ea2e5fdc52076724c071a81f2c0593d83b2ac8cbb8ca63b3151b0
|
|
218
|
+
connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
|
|
219
|
+
crack (1.0.1) sha256=ff4a10390cd31d66440b7524eb1841874db86201d5b70032028553130b6d4c7e
|
|
220
|
+
date (3.5.1) sha256=750d06384d7b9c15d562c76291407d89e368dda4d4fff957eb94962d325a0dc0
|
|
221
|
+
debug (1.11.1) sha256=2e0b0ac6119f2207a6f8ac7d4a73ca8eb4e440f64da0a3136c30343146e952b6
|
|
222
|
+
diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
|
|
223
|
+
docile (1.4.1) sha256=96159be799bfa73cdb721b840e9802126e4e03dfc26863db73647204c727f21e
|
|
224
|
+
dotenv (3.2.0) sha256=e375b83121ea7ca4ce20f214740076129ab8514cd81378161f11c03853fe619d
|
|
225
|
+
drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
|
|
226
|
+
erb (6.0.4) sha256=38e3803694be357fe2bfe312487c74beaf9fb4e5beb3e22498952fe1645b95d9
|
|
227
|
+
faraday (2.14.3) sha256=1882247e6766615c8220b4392bf1d27f6ebb63d8e28267587cef1fb0bf37f278
|
|
228
|
+
faraday-follow_redirects (0.5.0) sha256=5cde93c894b30943a5d2b93c2fe9284216a6b756f7af406a1e55f211d97d10ad
|
|
229
|
+
faraday-net_http (3.4.4) sha256=0e78af151747ed1b00f33e25973b4bc220d7f16c00c39676817c8b12331eb588
|
|
230
|
+
hashdiff (1.2.1) sha256=9c079dbc513dfc8833ab59c0c2d8f230fa28499cc5efb4b8dd276cf931457cd1
|
|
231
|
+
i18n (1.15.0) sha256=ed20037ed7ca75dd36ec486b5fb9696e8e0cfe48f4f80980017932d93e70527f
|
|
232
|
+
io-console (0.8.2) sha256=d6e3ae7a7cc7574f4b8893b4fca2162e57a825b223a177b7afa236c5ef9814cc
|
|
233
|
+
irb (1.18.0) sha256=de9454a0703a54704b9811a5ef31a60c86949fbf4013fcf244fabc7c775248e3
|
|
234
|
+
json (2.19.9) sha256=9b9025b7cdddafa38d316eca0b2358488e42d417045c1b90d216a9fefe46b79a
|
|
235
|
+
jwt (2.10.3) sha256=e4d9352fbc7309b1a7448c7dd713dfe4d8c47077af80759cdbed8f878ea0b484
|
|
236
|
+
language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc
|
|
237
|
+
lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
|
|
238
|
+
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
|
|
239
|
+
method_source (1.1.0) sha256=181301c9c45b731b4769bc81e8860e72f9161ad7d66dd99103c9ab84f560f5c5
|
|
240
|
+
minitest (5.27.0) sha256=2d3b17f8a36fe7801c1adcffdbc38233b938eb0b4966e97a6739055a45fa77d5
|
|
241
|
+
mustermann (3.1.1) sha256=4c6170c7234d5499c345562ba7c7dfe73e1754286dcc1abb053064d66a127198
|
|
242
|
+
mutex_m (0.3.0) sha256=cfcb04ac16b69c4813777022fdceda24e9f798e48092a2b817eb4c0a782b0751
|
|
243
|
+
net-http (0.9.1) sha256=25ba0b67c63e89df626ed8fac771d0ad24ad151a858af2cc8e6a716ca4336996
|
|
244
|
+
parallel (2.1.0) sha256=b35258865c2e31134c5ecb708beaaf6772adf9d5efae28e93e99260877b09356
|
|
245
|
+
parser (3.3.11.1) sha256=d17ace7aabe3e72c3cc94043714be27cc6f852f104d81aa284c2281aecc65d54
|
|
246
|
+
pp (0.6.3) sha256=2951d514450b93ccfeb1df7d021cae0da16e0a7f95ee1e2273719669d0ab9df6
|
|
247
|
+
prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193
|
|
248
|
+
prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
|
|
249
|
+
pry (0.16.0) sha256=d76c69065698ed1f85e717bd33d7942c38a50868f6b0673c636192b3d1b6054e
|
|
250
|
+
pry-byebug (3.12.0) sha256=594e094ae8a8390a7ad4c7b36ae36e13304ed02664c67417d108dc5f7213d1b7
|
|
251
|
+
psych (5.4.0) sha256=14f72d69a611af663d7d70e4a7b67d9eb1f3ae9f8d916b478961d5a0075ba5b7
|
|
252
|
+
public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623
|
|
253
|
+
racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
|
|
254
|
+
rack (3.2.6) sha256=5ed78e1f73b2e25679bec7d45ee2d4483cc4146eb1be0264fc4d94cb5ef212c2
|
|
255
|
+
rack-protection (4.2.1) sha256=cf6e2842df8c55f5e4d1a4be015e603e19e9bc3a7178bae58949ccbb58558bac
|
|
256
|
+
rack-session (2.1.2) sha256=595434f8c0c3473ae7d7ac56ecda6cc6dfd9d37c0b2b5255330aa1576967ffe8
|
|
257
|
+
rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
|
|
258
|
+
rdoc (7.2.0) sha256=8650f76cd4009c3b54955eb5d7e3a075c60a57276766ebf36f9085e8c9f23192
|
|
259
|
+
regexp_parser (2.12.0) sha256=35a916a1d63190ab5c9009457136ae5f3c0c7512d60291d0d1378ba18ce08ebb
|
|
260
|
+
reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835
|
|
261
|
+
rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142
|
|
262
|
+
rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
|
|
263
|
+
rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d
|
|
264
|
+
rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
|
|
265
|
+
rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47
|
|
266
|
+
rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
|
|
267
|
+
rubocop (1.88.0) sha256=e420ddf1662d0ef34bc8a2910ac4b396a7ddda0b51a708264405241734b08e0b
|
|
268
|
+
rubocop-ast (1.49.1) sha256=4412f3ee70f6fe4546cc489548e0f6fcf76cafcfa80fa03af67098ffed755035
|
|
269
|
+
rubocop-rspec (3.10.2) sha256=0b3e2ecc592cd10ecbf0095bb58d1e357905276e069643523cc19eb7495f65e2
|
|
270
|
+
ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
|
|
271
|
+
safire (0.3.0)
|
|
272
|
+
securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
|
|
273
|
+
simplecov (0.22.0) sha256=fe2622c7834ff23b98066bb0a854284b2729a569ac659f82621fc22ef36213a5
|
|
274
|
+
simplecov-cobertura (3.2.0) sha256=70d702658677fcb20e5aceb6915ccf8bc62ff2ccd38b62b3ad5c9db5c0888740
|
|
275
|
+
simplecov-html (0.13.2) sha256=bd0b8e54e7c2d7685927e8d6286466359b6f16b18cb0df47b508e8d73c777246
|
|
276
|
+
simplecov_json_formatter (0.1.4) sha256=529418fbe8de1713ac2b2d612aa3daa56d316975d307244399fa4838c601b428
|
|
277
|
+
sinatra (4.2.1) sha256=b7aeb9b11d046b552972ade834f1f9be98b185fa8444480688e3627625377080
|
|
278
|
+
stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1
|
|
279
|
+
thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73
|
|
280
|
+
tilt (2.7.0) sha256=0d5b9ba69f6a36490c64b0eee9f6e9aad517e20dcc848800a06eb116f08c6ab3
|
|
281
|
+
timecop (0.9.11) sha256=41284dc6e5041f2184f781ace766f942108c842f8d8c1386a26e6343decc7542
|
|
282
|
+
tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
|
|
283
|
+
tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b
|
|
284
|
+
unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42
|
|
285
|
+
unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f
|
|
286
|
+
uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6
|
|
287
|
+
webmock (3.26.2) sha256=774556f2ea6371846cca68c01769b2eac0d134492d21f6d0ab5dd643965a4c90
|
|
288
|
+
yard (0.9.44) sha256=eb087e9b631ccd887b049f303d489963945452d5e2a7eb49a5a74a7cf6887f28
|
|
289
|
+
|
|
290
|
+
BUNDLED WITH
|
|
291
|
+
4.0.10
|