acme-client 2.0.2 → 2.0.7

Sign up to get free protection for your applications and to get access to all the features.
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.