acme-client 2.0.2 → 2.0.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 05f87acfd3874c1d642f80201ef97d81abee4c36e97f194227f0a4cf3501f981
4
- data.tar.gz: 688ad48aca9ef6350f0350a8ed719f8efea00c611995124c56c1221160219152
3
+ metadata.gz: 718dac85c8139621711a030272097498d04414ca52c9d84544b8bac32179e8a1
4
+ data.tar.gz: 9e929450733261f291a62b96f5056ed89e8c235cae010805ddccfc3efb17188c
5
5
  SHA512:
6
- metadata.gz: e23b7f8b4116da8dd7bc9834ef4fb441bb90f2f3a54fae653bed0a3ea56d6d0a7b98642df443dd08834ffdd46b3f5b86b995d3b4eb4253f6f43511babe010592
7
- data.tar.gz: 706a2d72509b69a754c0a40d676652bde2f2ed284abdbed26982991c65ac42beb74586fddf2f790144de839f7ef570cae31617a882574459093a5ab157dc65a4
6
+ metadata.gz: f6e77c41a67b3f2fe9d6e373d58e928bfc0ee2523ac7b0ce2beb93167179a0950045cbb0ec4f5d8616527108814001acd0da7909a8bb0df8dac9b6ddde8bfbba
7
+ data.tar.gz: 9eba181b543cfe437e2043970b2e74cd691944457d43d3c3ea052cf10d3134e59b51aa8ee3522c36f27b25c212797e44e0e29b86f96b13361e98ca07d054ccac
data/.gitignore CHANGED
@@ -9,3 +9,4 @@
9
9
  /tmp/
10
10
  /vendor/bundle
11
11
  /.idea/
12
+ .tool-versions
@@ -7,133 +7,128 @@ AllCops:
7
7
  Rails:
8
8
  Enabled: false
9
9
 
10
- Style/FileName:
11
- Exclude:
12
- - 'lib/acme-client.rb'
10
+ Layout/AlignParameters:
11
+ EnforcedStyle: with_fixed_indentation
13
12
 
14
- Lint/AssignmentInCondition:
13
+ Layout/ElseAlignment:
15
14
  Enabled: false
16
15
 
17
- Style/ClassAndModuleChildren:
18
- Enabled: false
16
+ Layout/FirstParameterIndentation:
17
+ EnforcedStyle: consistent
19
18
 
20
- Style/Documentation:
19
+ Layout/IndentationWidth:
21
20
  Enabled: false
22
21
 
23
22
  Layout/MultilineOperationIndentation:
24
23
  Enabled: false
25
24
 
26
- Style/SignalException:
27
- EnforcedStyle: only_raise
28
-
29
- Layout/AlignParameters:
30
- EnforcedStyle: with_fixed_indentation
31
-
32
- Layout/ElseAlignment:
25
+ Layout/SpaceInsideBlockBraces:
33
26
  Enabled: false
34
27
 
35
- Style/MultipleComparison:
28
+ Lint/AmbiguousOperator:
36
29
  Enabled: false
37
30
 
38
- Layout/IndentationWidth:
31
+ Lint/AssignmentInCondition:
39
32
  Enabled: false
40
33
 
41
- Style/SymbolArray:
34
+ Lint/EndAlignment:
42
35
  Enabled: false
43
36
 
44
- Layout/FirstParameterIndentation:
45
- EnforcedStyle: consistent
46
-
47
- Style/TrailingCommaInArguments:
48
- Enabled: false
37
+ Lint/UnusedMethodArgument:
38
+ AllowUnusedKeywordArguments: true
49
39
 
50
- Style/PercentLiteralDelimiters:
40
+ Metrics/AbcSize:
51
41
  Enabled: false
52
42
 
53
43
  Metrics/BlockLength:
54
44
  Enabled: false
55
45
 
56
- Layout/SpaceInsideBlockBraces:
46
+ Metrics/ClassLength:
57
47
  Enabled: false
58
48
 
59
- Style/StringLiterals:
60
- Enabled: single_quotes
49
+ Metrics/CyclomaticComplexity:
50
+ Enabled: false
61
51
 
62
52
  Metrics/LineLength:
63
53
  Max: 140
64
54
 
55
+ Metrics/MethodLength:
56
+ Max: 15
57
+ Enabled: false
58
+
65
59
  Metrics/ParameterLists:
66
60
  Max: 5
67
61
  CountKeywordArgs: false
68
62
 
69
- Lint/EndAlignment:
63
+ Metrics/PerceivedComplexity:
70
64
  Enabled: false
71
65
 
72
- Style/ParallelAssignment:
66
+ Security/JSONLoad:
73
67
  Enabled: false
74
68
 
75
- Style/ModuleFunction:
69
+ Style/AccessorMethodName:
76
70
  Enabled: false
77
71
 
78
- Style/TrivialAccessors:
79
- AllowPredicates: true
80
-
81
- Lint/UnusedMethodArgument:
82
- AllowUnusedKeywordArguments: true
72
+ Style/Alias:
73
+ Enabled: false
83
74
 
84
- Metrics/MethodLength:
85
- Max: 15
75
+ Style/BlockDelimiters:
76
+ EnforcedStyle: semantic
86
77
 
87
- Style/DoubleNegation:
78
+ Style/ClassAndModuleChildren:
88
79
  Enabled: false
89
80
 
90
- Style/IfUnlessModifier:
81
+ Style/Documentation:
91
82
  Enabled: false
92
83
 
93
- Style/MultilineBlockChain:
84
+ Style/DoubleNegation:
94
85
  Enabled: false
95
86
 
96
- Style/BlockDelimiters:
97
- EnforcedStyle: semantic
87
+ Style/FileName:
88
+ Exclude:
89
+ - 'lib/acme-client.rb'
98
90
 
99
- Style/Lambda:
91
+ Style/GlobalVars:
100
92
  Enabled: false
101
93
 
102
94
  Style/GuardClause:
103
95
  Enabled: false
104
96
 
105
- Style/Alias:
97
+ Style/IfUnlessModifier:
106
98
  Enabled: false
107
99
 
108
- Lint/AmbiguousOperator:
100
+ Style/Lambda:
109
101
  Enabled: false
110
102
 
111
- Metrics/MethodLength:
103
+ Style/ModuleFunction:
112
104
  Enabled: false
113
105
 
114
- Metrics/PerceivedComplexity:
106
+ Style/MultilineBlockChain:
115
107
  Enabled: false
116
108
 
117
- Metrics/CyclomaticComplexity:
109
+ Style/MultipleComparison:
118
110
  Enabled: false
119
111
 
120
- Metrics/AbcSize:
112
+ Style/MutableConstant:
121
113
  Enabled: false
122
114
 
123
- Metrics/ClassLength:
115
+ Style/ParallelAssignment:
124
116
  Enabled: false
125
117
 
126
- Style/MutableConstant:
118
+ Style/PercentLiteralDelimiters:
127
119
  Enabled: false
128
120
 
129
- Style/GlobalVars:
130
- Enabled: false
121
+ Style/SignalException:
122
+ EnforcedStyle: only_raise
131
123
 
132
- Style/ExpandPathArguments:
124
+ Style/SymbolArray:
133
125
  Enabled: false
134
126
 
135
- Security/JSONLoad:
136
- Enabled: false
127
+ Style/StringLiterals:
128
+ Enabled: single_quotes
137
129
 
138
- Style/AccessorMethodName:
130
+ Style/TrailingCommaInArguments:
139
131
  Enabled: false
132
+
133
+ Style/TrivialAccessors:
134
+ AllowPredicates: true
@@ -1,7 +1,10 @@
1
1
  language: ruby
2
2
  cache: bundler
3
3
  rvm:
4
- - 2.1
5
- - 2.2
6
- - 2.3.3
7
- - 2.4.0
4
+ - 2.5
5
+ - 2.6
6
+ - 2.7
7
+
8
+ before_install:
9
+ - gem uninstall -v '>= 2' -i $(rvm gemdir)@global -ax bundler || true
10
+ - gem install bundler -v '< 2'
@@ -1,10 +1,31 @@
1
+ ## `2.0.7`
2
+
3
+ * Add support for alternate certificate chain
4
+ * Change `Link` headers parsing to return array of value. This add support multiple entries at the same `rel`
5
+
6
+ ## `2.0.6`
7
+
8
+ * Allow Faraday up to `< 2.0`
9
+
10
+ ## `2.0.5`
11
+
12
+ * Use post-as-get
13
+ * Remove deprecated keyAuthorization
14
+
15
+ ## `2.0.4`
16
+
17
+ * Add an option to retry bad nonce errors
18
+
19
+ ## `2.0.3`
20
+
21
+ * Do not try to set the body on GET request
22
+
1
23
  ## `2.0.2`
2
24
 
3
25
  * Fix constant lookup on InvalidDirectory
4
26
  * Forward connection options when fetching nonce
5
27
  * Fix splats without parenthesis warning
6
28
 
7
-
8
29
  ## `2.0.1`
9
30
 
10
31
  * Properly require URI
data/Gemfile CHANGED
@@ -1,4 +1,5 @@
1
1
  source 'https://rubygems.org'
2
+
2
3
  gemspec
3
4
 
4
5
  group :development, :test do
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Build Status](https://travis-ci.org/unixcharles/acme-client.svg?branch=master)](https://travis-ci.org/unixcharles/acme-client)
4
4
 
5
- `acme-client` is a client implementation of the [ACMEv2](https://github.com/ietf-wg-acme/acme) protocol in Ruby.
5
+ `acme-client` is a client implementation of the ACMEv2 / [RFC 8555](https://tools.ietf.org/html/rfc8555) protocol in Ruby.
6
6
 
7
7
  You can find the ACME reference implementations of the [server](https://github.com/letsencrypt/boulder) in Go and the [client](https://github.com/certbot/certbot) in Python.
8
8
 
@@ -182,7 +182,10 @@ Certificate generation happens asynchronously. You may need to poll.
182
182
  ```ruby
183
183
  csr = Acme::Client::CertificateRequest.new(private_key: a_different_private_key, subject: { common_name: 'example.com' })
184
184
  order.finalize(csr: csr)
185
- sleep(1) while order.status == 'processing'
185
+ while order.status == 'processing'
186
+ sleep(1)
187
+ order.reload
188
+ end
186
189
  order.certificate # => PEM-formatted certificate
187
190
  ```
188
191
 
@@ -198,7 +201,7 @@ client.revoke(certificate: certificate)
198
201
 
199
202
  ### Certificate renewal
200
203
 
201
- The is no renewal process, just create a new order.
204
+ There is no renewal process, just create a new order.
202
205
 
203
206
 
204
207
  ## Not implemented
@@ -16,11 +16,15 @@ Gem::Specification.new do |spec|
16
16
 
17
17
  spec.required_ruby_version = '>= 2.1.0'
18
18
 
19
- spec.add_development_dependency 'bundler', '~> 1.6', '>= 1.6.9'
20
- spec.add_development_dependency 'rake', '~> 10.0'
21
- spec.add_development_dependency 'rspec', '~> 3.3', '>= 3.3.0'
22
- spec.add_development_dependency 'vcr', '~> 2.9', '>= 2.9.3'
23
- spec.add_development_dependency 'webmock', '~> 1.21', '>= 1.21.0'
19
+ spec.add_development_dependency 'bundler', '>= 1.17.3'
20
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.2')
21
+ spec.add_development_dependency 'rake', '>= 12.0'
22
+ else
23
+ spec.add_development_dependency 'rake', '~> 13.0'
24
+ end
25
+ spec.add_development_dependency 'rspec', '~> 3.9'
26
+ spec.add_development_dependency 'vcr', '~> 2.9'
27
+ spec.add_development_dependency 'webmock', '~> 3.8'
24
28
 
25
- spec.add_runtime_dependency 'faraday', '~> 0.9', '>= 0.9.1'
29
+ spec.add_runtime_dependency 'faraday', '>= 0.17', '< 2.0.0'
26
30
  end
@@ -13,6 +13,7 @@ def `(command)
13
13
  end
14
14
  end
15
15
 
16
+ `git add CHANGELOG.md`
16
17
  `git add lib/acme/client/version.rb`
17
18
  `git commit -m "bump to #{version}"`
18
19
  `git pull --rebase origin master`
@@ -20,6 +20,7 @@ require 'acme/client/faraday_middleware'
20
20
  require 'acme/client/jwk'
21
21
  require 'acme/client/error'
22
22
  require 'acme/client/util'
23
+ require 'acme/client/chain_identifier'
23
24
 
24
25
  class Acme::Client
25
26
  DEFAULT_DIRECTORY = 'http://127.0.0.1:4000/directory'.freeze
@@ -29,7 +30,7 @@ class Acme::Client
29
30
  pem: 'application/pem-certificate-chain'
30
31
  }
31
32
 
32
- def initialize(jwk: nil, kid: nil, private_key: nil, directory: DEFAULT_DIRECTORY, connection_options: {})
33
+ def initialize(jwk: nil, kid: nil, private_key: nil, directory: DEFAULT_DIRECTORY, connection_options: {}, bad_nonce_retry: 0)
33
34
  if jwk.nil? && private_key.nil?
34
35
  raise ArgumentError, 'must specify jwk or private_key'
35
36
  end
@@ -41,6 +42,7 @@ class Acme::Client
41
42
  end
42
43
 
43
44
  @kid, @connection_options = kid, connection_options
45
+ @bad_nonce_retry = bad_nonce_retry
44
46
  @directory = Acme::Client::Resources::Directory.new(URI(directory), @connection_options)
45
47
  @nonces ||= []
46
48
  end
@@ -89,7 +91,7 @@ class Acme::Client
89
91
  response.headers.fetch(:location)
90
92
  end
91
93
 
92
- response = post(@kid)
94
+ response = post_as_get(@kid)
93
95
  arguments = attributes_from_account_response(response)
94
96
  Acme::Client::Resources::Account.new(self, url: @kid, **arguments)
95
97
  end
@@ -100,13 +102,7 @@ class Acme::Client
100
102
 
101
103
  def new_order(identifiers:, not_before: nil, not_after: nil)
102
104
  payload = {}
103
- payload['identifiers'] = if identifiers.is_a?(Hash)
104
- identifiers
105
- else
106
- Array(identifiers).map do |identifier|
107
- { type: 'dns', value: identifier }
108
- end
109
- end
105
+ payload['identifiers'] = prepare_order_identifiers(identifiers)
110
106
  payload['notBefore'] = not_before if not_before
111
107
  payload['notAfter'] = not_after if not_after
112
108
 
@@ -116,7 +112,7 @@ class Acme::Client
116
112
  end
117
113
 
118
114
  def order(url:)
119
- response = get(url)
115
+ response = post_as_get(url)
120
116
  arguments = attributes_from_order_response(response)
121
117
  Acme::Client::Resources::Order.new(self, **arguments.merge(url: url))
122
118
  end
@@ -132,13 +128,28 @@ class Acme::Client
132
128
  Acme::Client::Resources::Order.new(self, **arguments)
133
129
  end
134
130
 
135
- def certificate(url:)
131
+ def certificate(url:, force_chain: nil)
136
132
  response = download(url, format: :pem)
137
- response.body
133
+ pem = response.body
134
+
135
+ return pem if force_chain.nil?
136
+
137
+ return pem if ChainIdentifier.new(pem).match_name?(force_chain)
138
+
139
+ alternative_urls = Array(response.headers.dig('link', 'alternate'))
140
+ alternative_urls.each do |alternate_url|
141
+ response = download(alternate_url, format: :pem)
142
+ pem = response.body
143
+ if ChainIdentifier.new(pem).match_name?(force_chain)
144
+ return pem
145
+ end
146
+ end
147
+
148
+ raise Acme::Client::Error::ForcedChainNotFound, "Could not find any matching chain for `#{force_chain}`"
138
149
  end
139
150
 
140
151
  def authorization(url:)
141
- response = get(url)
152
+ response = post_as_get(url)
142
153
  arguments = attributes_from_authorization_response(response)
143
154
  Acme::Client::Resources::Authorization.new(self, url: url, **arguments)
144
155
  end
@@ -150,13 +161,13 @@ class Acme::Client
150
161
  end
151
162
 
152
163
  def challenge(url:)
153
- response = get(url)
164
+ response = post_as_get(url)
154
165
  arguments = attributes_from_challenge_response(response)
155
166
  Acme::Client::Resources::Challenges.new(self, **arguments)
156
167
  end
157
168
 
158
- def request_challenge_validation(url:, key_authorization:)
159
- response = post(url, payload: { keyAuthorization: key_authorization })
169
+ def request_challenge_validation(url:, key_authorization: nil)
170
+ response = post(url, payload: {})
160
171
  arguments = attributes_from_challenge_response(response)
161
172
  Acme::Client::Resources::Challenges.new(self, **arguments)
162
173
  end
@@ -205,6 +216,20 @@ class Acme::Client
205
216
 
206
217
  private
207
218
 
219
+ def prepare_order_identifiers(identifiers)
220
+ if identifiers.is_a?(Hash)
221
+ [identifiers]
222
+ else
223
+ Array(identifiers).map do |identifier|
224
+ if identifier.is_a?(String)
225
+ { type: 'dns', value: identifier }
226
+ else
227
+ identifier
228
+ end
229
+ end
230
+ end
231
+ end
232
+
208
233
  def attributes_from_account_response(response)
209
234
  extract_attributes(
210
235
  response.body,
@@ -251,14 +276,19 @@ class Acme::Client
251
276
  connection.post(url, payload)
252
277
  end
253
278
 
279
+ def post_as_get(url, mode: :kid)
280
+ connection = connection_for(url: url, mode: mode)
281
+ connection.post(url, nil)
282
+ end
283
+
254
284
  def get(url, mode: :kid)
255
285
  connection = connection_for(url: url, mode: mode)
256
286
  connection.get(url)
257
287
  end
258
288
 
259
289
  def download(url, format:)
260
- connection = connection_for(url: url, mode: :download)
261
- connection.get do |request|
290
+ connection = connection_for(url: url, mode: :kid)
291
+ connection.post do |request|
262
292
  request.url(url)
263
293
  request.headers['Accept'] = CONTENT_TYPES.fetch(format)
264
294
  end
@@ -280,6 +310,12 @@ class Acme::Client
280
310
 
281
311
  def new_connection(endpoint:)
282
312
  Faraday.new(endpoint, **@connection_options) do |configuration|
313
+ if @bad_nonce_retry > 0
314
+ configuration.request(:retry,
315
+ max: @bad_nonce_retry,
316
+ methods: Faraday::Connection::METHODS,
317
+ exceptions: [Acme::Client::Error::BadNonce])
318
+ end
283
319
  yield(configuration) if block_given?
284
320
  configuration.adapter Faraday.default_adapter
285
321
  end
@@ -0,0 +1,27 @@
1
+ class Acme::Client
2
+ class ChainIdentifier
3
+ def initialize(pem_certificate_chain)
4
+ @pem_certificate_chain = pem_certificate_chain
5
+ end
6
+
7
+ def match_name?(name)
8
+ issuers.any? do |issuer|
9
+ issuer.include?(name)
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def issuers
16
+ x509_certificates.map(&:issuer).map(&:to_s)
17
+ end
18
+
19
+ def x509_certificates
20
+ @x509_certificates ||= splitted_pem_certificates.map { |pem| OpenSSL::X509::Certificate.new(pem) }
21
+ end
22
+
23
+ def splitted_pem_certificates
24
+ @pem_certificate_chain.each_line.slice_after(/END CERTIFICATE/).map(&:join)
25
+ end
26
+ end
27
+ end
@@ -7,6 +7,7 @@ class Acme::Client::Error < StandardError
7
7
  class UnsupportedChallengeType < ClientError; end
8
8
  class NotFound < ClientError; end
9
9
  class CertificateNotReady < ClientError; end
10
+ class ForcedChainNotFound < ClientError; end
10
11
 
11
12
  class ServerError < Acme::Client::Error; end
12
13
  class BadCSR < ServerError; end
@@ -16,7 +16,10 @@ class Acme::Client::FaradayMiddleware < Faraday::Middleware
16
16
  @env[:request_headers]['User-Agent'] = Acme::Client::USER_AGENT
17
17
  @env[:request_headers]['Content-Type'] = CONTENT_TYPE
18
18
 
19
- @env.body = client.jwk.jws(header: jws_header, payload: env.body)
19
+ if @env.method != :get
20
+ @env.body = client.jwk.jws(header: jws_header, payload: env.body)
21
+ end
22
+
20
23
  @app.call(env).on_complete { |response_env| on_complete(response_env) }
21
24
  rescue Faraday::TimeoutError, Faraday::ConnectionFailed
22
25
  raise Acme::Client::Error::Timeout
@@ -79,18 +82,10 @@ class Acme::Client::FaradayMiddleware < Faraday::Middleware
79
82
  end
80
83
  end
81
84
 
82
- LINK_MATCH = /<(.*?)>;rel="([\w-]+)"/
83
-
84
85
  def decode_link_headers
85
86
  return unless env.response_headers.key?('Link')
86
87
  link_header = env.response_headers['Link']
87
-
88
- links = link_header.split(', ').map { |entry|
89
- _, link, name = *entry.match(LINK_MATCH)
90
- [name, link]
91
- }
92
-
93
- Hash[*links.flatten]
88
+ Acme::Client::Util.decode_link_headers(link_header)
94
89
  end
95
90
 
96
91
  def store_nonce
@@ -14,10 +14,10 @@ class Acme::Client::JWK::Base
14
14
  # payload - A Hash of payload data.
15
15
  #
16
16
  # Returns a JSON String.
17
- def jws(header: {}, payload: {})
17
+ def jws(header: {}, payload:)
18
18
  header = jws_header(header)
19
19
  encoded_header = Acme::Client::Util.urlsafe_base64(header.to_json)
20
- encoded_payload = Acme::Client::Util.urlsafe_base64(payload.to_json)
20
+ encoded_payload = Acme::Client::Util.urlsafe_base64(payload.nil? ? '' : payload.to_json)
21
21
 
22
22
  signature_data = "#{encoded_header}.#{encoded_payload}"
23
23
  signature = sign(signature_data)
@@ -5,7 +5,7 @@ class Acme::Client::Resources::Account
5
5
 
6
6
  def initialize(client, **arguments)
7
7
  @client = client
8
- assign_attributes(arguments)
8
+ assign_attributes(**arguments)
9
9
  end
10
10
 
11
11
  def kid
@@ -5,7 +5,7 @@ class Acme::Client::Resources::Authorization
5
5
 
6
6
  def initialize(client, **arguments)
7
7
  @client = client
8
- assign_attributes(arguments)
8
+ assign_attributes(**arguments)
9
9
  end
10
10
 
11
11
  def deactivate
@@ -4,6 +4,7 @@ module Acme::Client::Resources::Challenges
4
4
  require 'acme/client/resources/challenges/base'
5
5
  require 'acme/client/resources/challenges/http01'
6
6
  require 'acme/client/resources/challenges/dns01'
7
+ require 'acme/client/resources/challenges/unsupported_challenge'
7
8
 
8
9
  CHALLENGE_TYPES = {
9
10
  'http-01' => Acme::Client::Resources::Challenges::HTTP01,
@@ -11,11 +12,6 @@ module Acme::Client::Resources::Challenges
11
12
  }
12
13
 
13
14
  def self.new(client, type:, **arguments)
14
- klass = CHALLENGE_TYPES[type]
15
- if klass
16
- klass.new(client, **arguments)
17
- else
18
- { type: type }.merge(arguments)
19
- end
15
+ CHALLENGE_TYPES.fetch(type, Unsupported).new(client, **arguments)
20
16
  end
21
17
  end
@@ -5,7 +5,7 @@ class Acme::Client::Resources::Challenges::Base
5
5
 
6
6
  def initialize(client, **arguments)
7
7
  @client = client
8
- assign_attributes(arguments)
8
+ assign_attributes(**arguments)
9
9
  end
10
10
 
11
11
  def challenge_type
@@ -21,17 +21,9 @@ class Acme::Client::Resources::Challenges::Base
21
21
  true
22
22
  end
23
23
 
24
- def send_challenge_vallidation(url:, key_authorization:)
25
- @client.request_challenge_validation(
26
- url: url,
27
- key_authorization: key_authorization
28
- ).to_h
29
- end
30
-
31
24
  def request_validation
32
- assign_attributes(**send_challenge_vallidation(
33
- url: url,
34
- key_authorization: key_authorization
25
+ assign_attributes(**send_challenge_validation(
26
+ url: url
35
27
  ))
36
28
  true
37
29
  end
@@ -42,6 +34,12 @@ class Acme::Client::Resources::Challenges::Base
42
34
 
43
35
  private
44
36
 
37
+ def send_challenge_validation(url:)
38
+ @client.request_challenge_validation(
39
+ url: url
40
+ ).to_h
41
+ end
42
+
45
43
  def assign_attributes(status:, url:, token:, error: nil)
46
44
  @status = status
47
45
  @url = url
@@ -0,0 +1,2 @@
1
+ class Acme::Client::Resources::Challenges::Unsupported < Acme::Client::Resources::Challenges::Base
2
+ end
@@ -5,7 +5,7 @@ class Acme::Client::Resources::Order
5
5
 
6
6
  def initialize(client, **arguments)
7
7
  @client = client
8
- assign_attributes(arguments)
8
+ assign_attributes(**arguments)
9
9
  end
10
10
 
11
11
  def reload
@@ -24,9 +24,9 @@ class Acme::Client::Resources::Order
24
24
  true
25
25
  end
26
26
 
27
- def certificate
27
+ def certificate(force_chain: nil)
28
28
  if certificate_url
29
- @client.certificate(url: certificate_url)
29
+ @client.certificate(url: certificate_url, force_chain: force_chain)
30
30
  else
31
31
  raise Acme::Client::Error::CertificateNotReady, 'No certificate_url to collect the order'
32
32
  end
@@ -3,6 +3,17 @@ module Acme::Client::Util
3
3
  Base64.urlsafe_encode64(data).sub(/[\s=]*\z/, '')
4
4
  end
5
5
 
6
+ LINK_MATCH = /<(.*?)>\s?;\s?rel="([\w-]+)"/
7
+
8
+ # See RFC 8288 - https://tools.ietf.org/html/rfc8288#section-3
9
+ def decode_link_headers(link_header)
10
+ link_header.split(',').each_with_object({}) { |entry, hash|
11
+ _, link, name = *entry.match(LINK_MATCH)
12
+ hash[name] ||= []
13
+ hash[name].push(link)
14
+ }
15
+ end
16
+
6
17
  # Sets public key on CSR or cert.
7
18
  #
8
19
  # obj - An OpenSSL::X509::Certificate or OpenSSL::X509::Request instance.
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Acme
4
4
  class Client
5
- VERSION = '2.0.2'.freeze
5
+ VERSION = '2.0.7'.freeze
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,69 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acme-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: 2.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Charles Barbier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-02-19 00:00:00.000000000 Z
11
+ date: 2020-09-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '1.6'
20
17
  - - ">="
21
18
  - !ruby/object:Gem::Version
22
- version: 1.6.9
19
+ version: 1.17.3
23
20
  type: :development
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
- - - "~>"
28
- - !ruby/object:Gem::Version
29
- version: '1.6'
30
24
  - - ">="
31
25
  - !ruby/object:Gem::Version
32
- version: 1.6.9
26
+ version: 1.17.3
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: rake
35
29
  requirement: !ruby/object:Gem::Requirement
36
30
  requirements:
37
31
  - - "~>"
38
32
  - !ruby/object:Gem::Version
39
- version: '10.0'
33
+ version: '13.0'
40
34
  type: :development
41
35
  prerelease: false
42
36
  version_requirements: !ruby/object:Gem::Requirement
43
37
  requirements:
44
38
  - - "~>"
45
39
  - !ruby/object:Gem::Version
46
- version: '10.0'
40
+ version: '13.0'
47
41
  - !ruby/object:Gem::Dependency
48
42
  name: rspec
49
43
  requirement: !ruby/object:Gem::Requirement
50
44
  requirements:
51
- - - ">="
52
- - !ruby/object:Gem::Version
53
- version: 3.3.0
54
45
  - - "~>"
55
46
  - !ruby/object:Gem::Version
56
- version: '3.3'
47
+ version: '3.9'
57
48
  type: :development
58
49
  prerelease: false
59
50
  version_requirements: !ruby/object:Gem::Requirement
60
51
  requirements:
61
- - - ">="
62
- - !ruby/object:Gem::Version
63
- version: 3.3.0
64
52
  - - "~>"
65
53
  - !ruby/object:Gem::Version
66
- version: '3.3'
54
+ version: '3.9'
67
55
  - !ruby/object:Gem::Dependency
68
56
  name: vcr
69
57
  requirement: !ruby/object:Gem::Requirement
@@ -71,9 +59,6 @@ dependencies:
71
59
  - - "~>"
72
60
  - !ruby/object:Gem::Version
73
61
  version: '2.9'
74
- - - ">="
75
- - !ruby/object:Gem::Version
76
- version: 2.9.3
77
62
  type: :development
78
63
  prerelease: false
79
64
  version_requirements: !ruby/object:Gem::Requirement
@@ -81,49 +66,40 @@ dependencies:
81
66
  - - "~>"
82
67
  - !ruby/object:Gem::Version
83
68
  version: '2.9'
84
- - - ">="
85
- - !ruby/object:Gem::Version
86
- version: 2.9.3
87
69
  - !ruby/object:Gem::Dependency
88
70
  name: webmock
89
71
  requirement: !ruby/object:Gem::Requirement
90
72
  requirements:
91
- - - ">="
92
- - !ruby/object:Gem::Version
93
- version: 1.21.0
94
73
  - - "~>"
95
74
  - !ruby/object:Gem::Version
96
- version: '1.21'
75
+ version: '3.8'
97
76
  type: :development
98
77
  prerelease: false
99
78
  version_requirements: !ruby/object:Gem::Requirement
100
79
  requirements:
101
- - - ">="
102
- - !ruby/object:Gem::Version
103
- version: 1.21.0
104
80
  - - "~>"
105
81
  - !ruby/object:Gem::Version
106
- version: '1.21'
82
+ version: '3.8'
107
83
  - !ruby/object:Gem::Dependency
108
84
  name: faraday
109
85
  requirement: !ruby/object:Gem::Requirement
110
86
  requirements:
111
- - - "~>"
112
- - !ruby/object:Gem::Version
113
- version: '0.9'
114
87
  - - ">="
115
88
  - !ruby/object:Gem::Version
116
- version: 0.9.1
89
+ version: '0.17'
90
+ - - "<"
91
+ - !ruby/object:Gem::Version
92
+ version: 2.0.0
117
93
  type: :runtime
118
94
  prerelease: false
119
95
  version_requirements: !ruby/object:Gem::Requirement
120
96
  requirements:
121
- - - "~>"
122
- - !ruby/object:Gem::Version
123
- version: '0.9'
124
97
  - - ">="
125
98
  - !ruby/object:Gem::Version
126
- version: 0.9.1
99
+ version: '0.17'
100
+ - - "<"
101
+ - !ruby/object:Gem::Version
102
+ version: 2.0.0
127
103
  description:
128
104
  email:
129
105
  - unixcharles@gmail.com
@@ -148,6 +124,7 @@ files:
148
124
  - lib/acme/client.rb
149
125
  - lib/acme/client/certificate_request.rb
150
126
  - lib/acme/client/certificate_request/ec_key_patch.rb
127
+ - lib/acme/client/chain_identifier.rb
151
128
  - lib/acme/client/error.rb
152
129
  - lib/acme/client/faraday_middleware.rb
153
130
  - lib/acme/client/jwk.rb
@@ -161,6 +138,7 @@ files:
161
138
  - lib/acme/client/resources/challenges/base.rb
162
139
  - lib/acme/client/resources/challenges/dns01.rb
163
140
  - lib/acme/client/resources/challenges/http01.rb
141
+ - lib/acme/client/resources/challenges/unsupported_challenge.rb
164
142
  - lib/acme/client/resources/directory.rb
165
143
  - lib/acme/client/resources/order.rb
166
144
  - lib/acme/client/self_sign_certificate.rb
@@ -185,7 +163,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
185
163
  - !ruby/object:Gem::Version
186
164
  version: '0'
187
165
  requirements: []
188
- rubygems_version: 3.0.2
166
+ rubygems_version: 3.1.2
189
167
  signing_key:
190
168
  specification_version: 4
191
169
  summary: Client for the ACME protocol.