acme-client 2.0.30 → 2.0.31
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/CHANGELOG.md +7 -0
- data/acme-client.gemspec +2 -1
- data/lib/acme/client/error/rate_limited.rb +10 -5
- data/lib/acme/client/error.rb +33 -1
- data/lib/acme/client/http_client.rb +10 -2
- data/lib/acme/client/resources/authorization.rb +6 -3
- data/lib/acme/client/resources/challenges/base.rb +14 -3
- data/lib/acme/client/resources/order.rb +22 -7
- data/lib/acme/client/resources/renewal_info.rb +9 -2
- data/lib/acme/client/util.rb +18 -0
- data/lib/acme/client/version.rb +1 -1
- data/lib/acme/client.rb +18 -7
- metadata +19 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 55d60fbf24893ea26c59535b2ffe6041916e678d4bbea5982666c5f9eb0f2bae
|
|
4
|
+
data.tar.gz: b62818cd2dc3ca8b9676e7ec355dfcec40eaf90b3f9b7abc26b2dddea0cc4473
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b3311579f5f990f433e2ede868ce5baf6ce910eb38b19889107436a0dea91f7d96c36619e6039e0e4b4382402e8bc81dce939fee0f915c850b068b80ced67c1d
|
|
7
|
+
data.tar.gz: 45b7de2260bad0703cfa5f865a33e5367789cde176e0a906224fc7e7ad29ba5eeb9f756ce79b29719460c03264c26c5808702737be912d2f5df9cce38003b2f9
|
data/CHANGELOG.md
CHANGED
data/acme-client.gemspec
CHANGED
|
@@ -18,7 +18,8 @@ Gem::Specification.new do |spec|
|
|
|
18
18
|
|
|
19
19
|
spec.add_development_dependency 'rake', '~> 13.0'
|
|
20
20
|
spec.add_development_dependency 'rspec', '~> 3.9'
|
|
21
|
-
spec.add_development_dependency 'vcr', '~>
|
|
21
|
+
spec.add_development_dependency 'vcr', '~> 6.0'
|
|
22
|
+
spec.add_development_dependency 'bigdecimal'
|
|
22
23
|
spec.add_development_dependency 'webmock', '~> 3.8'
|
|
23
24
|
spec.add_development_dependency 'webrick', '~> 1.7'
|
|
24
25
|
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
class Acme::Client::Error::RateLimited < Acme::Client::Error::ServerError
|
|
2
|
-
attr_reader :retry_after
|
|
3
|
-
|
|
4
2
|
DEFAULT_MESSAGE = 'Error message: urn:ietf:params:acme:error:rateLimited'
|
|
3
|
+
DEFAULT_RETRY_SECONDS = 10
|
|
5
4
|
|
|
6
|
-
def initialize(message = DEFAULT_MESSAGE, retry_after =
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
def initialize(message = DEFAULT_MESSAGE, retry_after = nil, acme_error_body: nil, subproblems: nil)
|
|
6
|
+
retry_after_time = case retry_after
|
|
7
|
+
when Time then retry_after
|
|
8
|
+
when nil then Time.now + DEFAULT_RETRY_SECONDS
|
|
9
|
+
else Acme::Client::Util.parse_retry_after(retry_after) || Time.now + DEFAULT_RETRY_SECONDS
|
|
10
|
+
end
|
|
11
|
+
int_retry_after = retry_after.nil? ? DEFAULT_RETRY_SECONDS : [(retry_after_time - Time.now).ceil, 0].max
|
|
12
|
+
super(message, retry_after: int_retry_after, acme_error_body: acme_error_body, subproblems: subproblems)
|
|
13
|
+
@retry_after_time = retry_after_time
|
|
9
14
|
end
|
|
10
15
|
end
|
data/lib/acme/client/error.rb
CHANGED
|
@@ -1,4 +1,36 @@
|
|
|
1
1
|
class Acme::Client::Error < StandardError
|
|
2
|
+
attr_reader :retry_after, :retry_after_time, :subproblems, :acme_error_body
|
|
3
|
+
|
|
4
|
+
Subproblem = Struct.new(:type, :detail, :identifier, keyword_init: true) do
|
|
5
|
+
def to_h
|
|
6
|
+
{ type: type, detail: detail, identifier: identifier }
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def initialize(message = nil, retry_after: nil, acme_error_body: nil, subproblems: nil)
|
|
11
|
+
super(message)
|
|
12
|
+
@retry_after_time = Acme::Client::Util.parse_retry_after(retry_after)
|
|
13
|
+
@retry_after = @retry_after_time ? [(@retry_after_time - Time.now).ceil, 0].max : nil
|
|
14
|
+
@acme_error_body = acme_error_body
|
|
15
|
+
@subproblems = parse_subproblems(subproblems)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def parse_subproblems(raw)
|
|
21
|
+
return [] if raw.nil? || !raw.is_a?(Array)
|
|
22
|
+
|
|
23
|
+
raw.map do |sp|
|
|
24
|
+
Subproblem.new(
|
|
25
|
+
type: sp['type'],
|
|
26
|
+
detail: sp['detail'],
|
|
27
|
+
identifier: sp['identifier']
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
public
|
|
33
|
+
|
|
2
34
|
class Timeout < Acme::Client::Error; end
|
|
3
35
|
|
|
4
36
|
class ClientError < Acme::Client::Error; end
|
|
@@ -9,7 +41,7 @@ class Acme::Client::Error < StandardError
|
|
|
9
41
|
class CertificateNotReady < ClientError; end
|
|
10
42
|
class ForcedChainNotFound < ClientError; end
|
|
11
43
|
class OrderNotReady < ClientError; end
|
|
12
|
-
class
|
|
44
|
+
class OrderUrlNil < ClientError; end
|
|
13
45
|
|
|
14
46
|
class ServerError < Acme::Client::Error; end
|
|
15
47
|
class AlreadyReplaced < ServerError; end
|
|
@@ -101,10 +101,13 @@ module Acme::Client::HTTPClient
|
|
|
101
101
|
end
|
|
102
102
|
|
|
103
103
|
def raise_on_error!
|
|
104
|
+
retry_after = env.response_headers['retry-after']
|
|
105
|
+
body = env.body.is_a?(Hash) ? env.body : nil
|
|
106
|
+
subproblems = error_subproblems
|
|
104
107
|
if error_class == Acme::Client::Error::RateLimited
|
|
105
|
-
raise error_class.new(error_message,
|
|
108
|
+
raise error_class.new(error_message, retry_after, acme_error_body: body, subproblems: subproblems)
|
|
106
109
|
end
|
|
107
|
-
raise error_class,
|
|
110
|
+
raise error_class.new(error_message, retry_after: retry_after, acme_error_body: body, subproblems: subproblems)
|
|
108
111
|
end
|
|
109
112
|
|
|
110
113
|
def error_message
|
|
@@ -125,6 +128,11 @@ module Acme::Client::HTTPClient
|
|
|
125
128
|
env.body['type']
|
|
126
129
|
end
|
|
127
130
|
|
|
131
|
+
def error_subproblems
|
|
132
|
+
return unless env.body.is_a?(Hash)
|
|
133
|
+
env.body['subproblems']
|
|
134
|
+
end
|
|
135
|
+
|
|
128
136
|
def decode_body
|
|
129
137
|
content_type = env.response_headers['Content-Type'].to_s
|
|
130
138
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Acme::Client::Resources::Authorization
|
|
4
|
-
attr_reader :url, :identifier, :domain, :expires, :status, :wildcard
|
|
4
|
+
attr_reader :url, :identifier, :domain, :expires, :status, :wildcard, :retry_after, :retry_after_time
|
|
5
5
|
|
|
6
6
|
def initialize(client, **arguments)
|
|
7
7
|
@client = client
|
|
@@ -52,7 +52,8 @@ class Acme::Client::Resources::Authorization
|
|
|
52
52
|
status: status,
|
|
53
53
|
expires: expires,
|
|
54
54
|
challenges: @challenges,
|
|
55
|
-
wildcard: wildcard
|
|
55
|
+
wildcard: wildcard,
|
|
56
|
+
retry_after: retry_after
|
|
56
57
|
}
|
|
57
58
|
end
|
|
58
59
|
|
|
@@ -69,7 +70,7 @@ class Acme::Client::Resources::Authorization
|
|
|
69
70
|
Acme::Client::Resources::Challenges.new(@client, **arguments)
|
|
70
71
|
end
|
|
71
72
|
|
|
72
|
-
def assign_attributes(url:, status:, expires:, challenges:, identifier:, wildcard: false)
|
|
73
|
+
def assign_attributes(url:, status:, expires:, challenges:, identifier:, wildcard: false, retry_after: nil)
|
|
73
74
|
@url = url
|
|
74
75
|
@identifier = identifier
|
|
75
76
|
@domain = identifier.fetch('value')
|
|
@@ -77,5 +78,7 @@ class Acme::Client::Resources::Authorization
|
|
|
77
78
|
@expires = expires
|
|
78
79
|
@challenges = challenges
|
|
79
80
|
@wildcard = wildcard
|
|
81
|
+
@retry_after = retry_after
|
|
82
|
+
@retry_after_time = Acme::Client::Util.parse_retry_after(retry_after)
|
|
80
83
|
end
|
|
81
84
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Acme::Client::Resources::Challenges::Base
|
|
4
|
-
attr_reader :status, :url, :token, :error, :validated
|
|
4
|
+
attr_reader :status, :url, :token, :error, :validated, :retry_after, :retry_after_time
|
|
5
5
|
|
|
6
6
|
def initialize(client, **arguments)
|
|
7
7
|
@client = client
|
|
@@ -28,8 +28,17 @@ class Acme::Client::Resources::Challenges::Base
|
|
|
28
28
|
true
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
def typed_error
|
|
32
|
+
return nil unless error
|
|
33
|
+
|
|
34
|
+
error_type = error['type']
|
|
35
|
+
error_detail = error['detail'] || 'Unknown error'
|
|
36
|
+
error_class = Acme::Client::Error::ACME_ERRORS.fetch(error_type, Acme::Client::Error)
|
|
37
|
+
error_class.new(error_detail)
|
|
38
|
+
end
|
|
39
|
+
|
|
31
40
|
def to_h
|
|
32
|
-
{ status: status, url: url, token: token, error: error, validated: validated }
|
|
41
|
+
{ status: status, url: url, token: token, error: error, validated: validated, retry_after: retry_after }
|
|
33
42
|
end
|
|
34
43
|
|
|
35
44
|
private
|
|
@@ -40,11 +49,13 @@ class Acme::Client::Resources::Challenges::Base
|
|
|
40
49
|
).to_h
|
|
41
50
|
end
|
|
42
51
|
|
|
43
|
-
def assign_attributes(status:, url:, token:, error: nil, validated: nil)
|
|
52
|
+
def assign_attributes(status:, url:, token:, error: nil, validated: nil, retry_after: nil)
|
|
44
53
|
@status = status
|
|
45
54
|
@url = url
|
|
46
55
|
@token = token
|
|
47
56
|
@error = error
|
|
48
57
|
@validated = validated
|
|
58
|
+
@retry_after = retry_after
|
|
59
|
+
@retry_after_time = Acme::Client::Util.parse_retry_after(retry_after)
|
|
49
60
|
end
|
|
50
61
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Acme::Client::Resources::Order
|
|
4
|
-
attr_reader :url, :status, :contact, :finalize_url, :identifiers, :authorization_urls, :expires, :certificate_url, :profile
|
|
4
|
+
attr_reader :url, :status, :contact, :finalize_url, :identifiers, :authorization_urls, :expires, :certificate_url, :profile, :replaces, :retry_after, :retry_after_time
|
|
5
5
|
|
|
6
6
|
def initialize(client, **arguments)
|
|
7
7
|
@client = client
|
|
@@ -9,9 +9,7 @@ class Acme::Client::Resources::Order
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def reload
|
|
12
|
-
if url.nil?
|
|
13
|
-
raise Acme::Client::Error::OrderNotReloadable, 'Finalized orders are not reloadable for this CA'
|
|
14
|
-
end
|
|
12
|
+
raise Acme::Client::Error::OrderUrlNil, 'Cannot reload order with nil url.' if url.nil?
|
|
15
13
|
|
|
16
14
|
assign_attributes(**@client.order(url: url).to_h)
|
|
17
15
|
true
|
|
@@ -36,6 +34,18 @@ class Acme::Client::Resources::Order
|
|
|
36
34
|
end
|
|
37
35
|
end
|
|
38
36
|
|
|
37
|
+
def renew(replaces: nil, **arguments)
|
|
38
|
+
replaces ||= renewal_info.ari_id
|
|
39
|
+
|
|
40
|
+
@client.new_order(replaces: replaces, **to_h.slice(:identifiers, :profile).merge(arguments))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def renewal_info(certificate: nil, ari_id: nil)
|
|
44
|
+
certificate ||= self.certificate if ari_id.nil?
|
|
45
|
+
|
|
46
|
+
@client.renewal_info(certificate:, ari_id:)
|
|
47
|
+
end
|
|
48
|
+
|
|
39
49
|
def to_h
|
|
40
50
|
{
|
|
41
51
|
url: url,
|
|
@@ -45,14 +55,16 @@ class Acme::Client::Resources::Order
|
|
|
45
55
|
authorization_urls: authorization_urls,
|
|
46
56
|
identifiers: identifiers,
|
|
47
57
|
certificate_url: certificate_url,
|
|
48
|
-
profile: profile
|
|
58
|
+
profile: profile,
|
|
59
|
+
replaces: replaces,
|
|
60
|
+
retry_after: retry_after
|
|
49
61
|
}
|
|
50
62
|
end
|
|
51
63
|
|
|
52
64
|
private
|
|
53
65
|
|
|
54
|
-
def assign_attributes(url: nil, status:, expires:, finalize_url:, authorization_urls:, identifiers:, certificate_url: nil, profile: nil) # rubocop:disable Layout/LineLength,Metrics/ParameterLists
|
|
55
|
-
@url = url
|
|
66
|
+
def assign_attributes(url: nil, status:, expires:, finalize_url:, authorization_urls:, identifiers:, certificate_url: nil, profile: nil, replaces: nil, retry_after: nil) # rubocop:disable Layout/LineLength,Metrics/ParameterLists
|
|
67
|
+
@url = url unless url.nil?
|
|
56
68
|
@status = status
|
|
57
69
|
@expires = expires
|
|
58
70
|
@finalize_url = finalize_url
|
|
@@ -60,5 +72,8 @@ class Acme::Client::Resources::Order
|
|
|
60
72
|
@identifiers = identifiers
|
|
61
73
|
@certificate_url = certificate_url
|
|
62
74
|
@profile = profile
|
|
75
|
+
@replaces = replaces
|
|
76
|
+
@retry_after = retry_after
|
|
77
|
+
@retry_after_time = Acme::Client::Util.parse_retry_after(retry_after)
|
|
63
78
|
end
|
|
64
79
|
end
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Acme::Client::Resources::RenewalInfo
|
|
4
|
-
attr_reader :suggested_window, :explanation_url, :retry_after
|
|
4
|
+
attr_reader :ari_id, :suggested_window, :explanation_url, :retry_after, :retry_after_time
|
|
5
5
|
|
|
6
6
|
def initialize(client, **arguments)
|
|
7
7
|
@client = client
|
|
8
8
|
assign_attributes(**arguments)
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
def reload
|
|
12
|
+
assign_attributes(**@client.renewal_info(ari_id: ari_id).to_h)
|
|
13
|
+
end
|
|
14
|
+
|
|
11
15
|
def suggested_window_start
|
|
12
16
|
suggested_window&.fetch('start', nil)
|
|
13
17
|
end
|
|
@@ -31,6 +35,7 @@ class Acme::Client::Resources::RenewalInfo
|
|
|
31
35
|
|
|
32
36
|
def to_h
|
|
33
37
|
{
|
|
38
|
+
ari_id: ari_id,
|
|
34
39
|
suggested_window: suggested_window,
|
|
35
40
|
explanation_url: explanation_url,
|
|
36
41
|
retry_after: retry_after
|
|
@@ -39,9 +44,11 @@ class Acme::Client::Resources::RenewalInfo
|
|
|
39
44
|
|
|
40
45
|
private
|
|
41
46
|
|
|
42
|
-
def assign_attributes(suggested_window:, explanation_url: nil, retry_after: nil)
|
|
47
|
+
def assign_attributes(ari_id:, suggested_window:, explanation_url: nil, retry_after: nil)
|
|
48
|
+
@ari_id = ari_id
|
|
43
49
|
@suggested_window = suggested_window
|
|
44
50
|
@explanation_url = explanation_url
|
|
45
51
|
@retry_after = retry_after
|
|
52
|
+
@retry_after_time = Acme::Client::Util.parse_retry_after(retry_after)
|
|
46
53
|
end
|
|
47
54
|
end
|
data/lib/acme/client/util.rb
CHANGED
|
@@ -1,6 +1,24 @@
|
|
|
1
|
+
require 'time'
|
|
2
|
+
|
|
1
3
|
module Acme::Client::Util
|
|
2
4
|
extend self
|
|
3
5
|
|
|
6
|
+
# Parses a Retry-After header value into a Time.
|
|
7
|
+
# RFC 7231 §7.1.3: the value is either delay-seconds or an HTTP-date.
|
|
8
|
+
# Returns a Time, or nil if the value is nil or unparseable.
|
|
9
|
+
def parse_retry_after(value)
|
|
10
|
+
return nil if value.nil?
|
|
11
|
+
|
|
12
|
+
value = value.to_s
|
|
13
|
+
Integer(value, 10).then { |seconds| Time.now + seconds }
|
|
14
|
+
rescue ArgumentError, RangeError
|
|
15
|
+
begin
|
|
16
|
+
Time.httpdate(value)
|
|
17
|
+
rescue ArgumentError
|
|
18
|
+
nil
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
4
22
|
def urlsafe_base64(data)
|
|
5
23
|
Base64.urlsafe_encode64(data).sub(/[\s=]*\z/, '')
|
|
6
24
|
end
|
data/lib/acme/client/version.rb
CHANGED
data/lib/acme/client.rb
CHANGED
|
@@ -225,13 +225,18 @@ class Acme::Client
|
|
|
225
225
|
response.success?
|
|
226
226
|
end
|
|
227
227
|
|
|
228
|
-
def renewal_info(certificate:)
|
|
229
|
-
|
|
230
|
-
|
|
228
|
+
def renewal_info(certificate: nil, ari_id: nil)
|
|
229
|
+
if certificate.nil? && ari_id.nil?
|
|
230
|
+
raise ArgumentError, 'must specify certificate or ari_id'
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
ari_id ||= Acme::Client::Util.ari_certificate_identifier(certificate)
|
|
234
|
+
|
|
235
|
+
renewal_info_url = URI.join(endpoint_for(:renewal_info).to_s + '/', ari_id).to_s
|
|
231
236
|
|
|
232
237
|
response = get(renewal_info_url)
|
|
233
238
|
attributes = attributes_from_renewal_info_response(response)
|
|
234
|
-
Acme::Client::Resources::RenewalInfo.new(self, **attributes)
|
|
239
|
+
Acme::Client::Resources::RenewalInfo.new(self, ari_id: ari_id, **attributes)
|
|
235
240
|
end
|
|
236
241
|
|
|
237
242
|
def get_nonce
|
|
@@ -316,19 +321,25 @@ class Acme::Client
|
|
|
316
321
|
[:authorization_urls, 'authorizations'],
|
|
317
322
|
[:certificate_url, 'certificate'],
|
|
318
323
|
:identifiers,
|
|
319
|
-
:profile
|
|
324
|
+
:profile,
|
|
325
|
+
:replaces
|
|
320
326
|
)
|
|
321
327
|
|
|
322
328
|
attributes[:url] = response.headers[:location] if response.headers[:location]
|
|
329
|
+
attributes[:retry_after] = response.headers['retry-after'] if response.headers['retry-after']
|
|
323
330
|
attributes
|
|
324
331
|
end
|
|
325
332
|
|
|
326
333
|
def attributes_from_authorization_response(response)
|
|
327
|
-
extract_attributes(response.body, :identifier, :status, :expires, :challenges, :wildcard)
|
|
334
|
+
attributes = extract_attributes(response.body, :identifier, :status, :expires, :challenges, :wildcard)
|
|
335
|
+
attributes[:retry_after] = response.headers['retry-after'] if response.headers['retry-after']
|
|
336
|
+
attributes
|
|
328
337
|
end
|
|
329
338
|
|
|
330
339
|
def attributes_from_challenge_response(response)
|
|
331
|
-
extract_attributes(response.body, :status, :url, :token, :type, :error, :validated)
|
|
340
|
+
attributes = extract_attributes(response.body, :status, :url, :token, :type, :error, :validated)
|
|
341
|
+
attributes[:retry_after] = response.headers['retry-after'] if response.headers['retry-after']
|
|
342
|
+
attributes
|
|
332
343
|
end
|
|
333
344
|
|
|
334
345
|
def attributes_from_renewal_info_response(response)
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: acme-client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0.
|
|
4
|
+
version: 2.0.31
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Charles Barbier
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: rake
|
|
@@ -43,14 +43,28 @@ dependencies:
|
|
|
43
43
|
requirements:
|
|
44
44
|
- - "~>"
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '
|
|
46
|
+
version: '6.0'
|
|
47
47
|
type: :development
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
51
|
- - "~>"
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '
|
|
53
|
+
version: '6.0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: bigdecimal
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
54
68
|
- !ruby/object:Gem::Dependency
|
|
55
69
|
name: webmock
|
|
56
70
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -195,7 +209,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
195
209
|
- !ruby/object:Gem::Version
|
|
196
210
|
version: '0'
|
|
197
211
|
requirements: []
|
|
198
|
-
rubygems_version: 3.6.
|
|
212
|
+
rubygems_version: 3.6.9
|
|
199
213
|
specification_version: 4
|
|
200
214
|
summary: Client for the ACME protocol.
|
|
201
215
|
test_files: []
|