diff --git a/lib/acme/client.rb b/lib/acme/client.rb index d99bb4c..6abf550 100644 --- a/lib/acme/client.rb +++ b/lib/acme/client.rb @@ -44,7 +44,7 @@ def initialize(jwk: nil, kid: nil, private_key: nil, directory: DEFAULT_DIRECTOR @kid, @connection_options = kid, connection_options @bad_nonce_retry = bad_nonce_retry - @directory = Acme::Client::Resources::Directory.new(URI(directory), @connection_options) + @directory_url = URI(directory) @nonces ||= [] end @@ -229,28 +229,44 @@ def get_nonce true end + def directory + @directory ||= load_directory + end + def meta - @directory.meta + directory.meta end def terms_of_service - @directory.terms_of_service + directory.terms_of_service end def website - @directory.website + directory.website end def caa_identities - @directory.caa_identities + directory.caa_identities end def external_account_required - @directory.external_account_required + directory.external_account_required end private + def load_directory + Acme::Client::Resources::Directory.new(self, directory: fetch_directory) + end + + def fetch_directory + response = get(@directory_url) + response.body + rescue JSON::ParserError => exception + raise Acme::Client::Error::InvalidDirectory, + "Invalid directory url\n#{@directory_url} did not return a valid directory\n#{exception.inspect}" + end + def prepare_order_identifiers(identifiers) if identifiers.is_a?(Hash) [identifiers] @@ -351,6 +367,6 @@ def fetch_chain(response, limit = 10) end def endpoint_for(key) - @directory.endpoint_for(key) + directory.endpoint_for(key) end end diff --git a/lib/acme/client/resources/directory.rb b/lib/acme/client/resources/directory.rb index 6724215..2baf6fa 100644 --- a/lib/acme/client/resources/directory.rb +++ b/lib/acme/client/resources/directory.rb @@ -17,12 +17,13 @@ class Acme::Client::Resources::Directory external_account_required: 'externalAccountRequired' } - def initialize(url, connection_options) - @url, @connection_options = url, connection_options + def initialize(client, **arguments) + @client = client + assign_attributes(**arguments) end def endpoint_for(key) - directory.fetch(key) do |missing_key| + @directory.fetch(key) do |missing_key| raise Acme::Client::Error::UnsupportedOperation, "Directory at #{@url} does not include `#{missing_key}`" end @@ -45,31 +46,16 @@ def external_account_required end def meta - directory[:meta] + @directory[:meta] end private - def directory - @directory ||= load_directory - end - - def load_directory - body = fetch_directory - result = {} - result[:meta] = body.delete('meta') + def assign_attributes(directory:) + @directory = {} + @directory[:meta] = directory.delete('meta') DIRECTORY_RESOURCES.each do |key, entry| - result[key] = URI(body[entry]) if body[entry] + @directory[key] = URI(directory[entry]) if directory[entry] end - result - rescue JSON::ParserError => exception - raise Acme::Client::Error::InvalidDirectory, - "Invalid directory url\n#{@directory} did not return a valid directory\n#{exception.inspect}" - end - - def fetch_directory - http_client = Acme::Client::HTTPClient.new_acme_connection(url: @directory, options: @connection_options, client: nil, mode: nil) - response = http_client.get(@url) - response.body end end diff --git a/spec/cassettes/directory_endpoint_for.yml b/spec/cassettes/directory_endpoint_for.yml index b4be264..233ac8c 100644 --- a/spec/cassettes/directory_endpoint_for.yml +++ b/spec/cassettes/directory_endpoint_for.yml @@ -8,7 +8,9 @@ http_interactions: string: '' headers: User-Agent: - - Acme::Client v2.0.4 (https://github.com/unixcharles/acme-client) + - Acme::Client v2.0.15 (https://github.com/unixcharles/acme-client) + Content-Type: + - application/jose+json Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -23,15 +25,16 @@ http_interactions: Content-Type: - application/json; charset=utf-8 Date: - - Thu, 10 Oct 2019 03:08:14 GMT + - Tue, 16 Jan 2024 18:10:15 GMT Content-Length: - - '386' + - '396' body: encoding: UTF-8 string: |- { "keyChange": "/rollover-account-key", "meta": { + "externalAccountRequired": false, "termsOfService": "data:text/plain,Do%20what%20thou%20wilt" }, "newAccount": "/sign-me-up", @@ -39,6 +42,89 @@ http_interactions: "newOrder": "/order-plz", "revokeCert": "/revoke-cert" } - http_version: - recorded_at: Thu, 10 Oct 2019 03:08:14 GMT + http_version: + recorded_at: Tue, 16 Jan 2024 18:10:15 GMT +- request: + method: head + uri: "/nonce-plz" + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Acme::Client v2.0.15 (https://github.com/unixcharles/acme-client) + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Cache-Control: + - public, max-age=0, no-cache + Link: + - <>;rel="index" + Replay-Nonce: + - DXS4nYJl18Pid0N5tjRffw + Date: + - Tue, 16 Jan 2024 18:10:15 GMT + body: + encoding: UTF-8 + string: '' + http_version: + recorded_at: Tue, 16 Jan 2024 18:10:15 GMT +- request: + method: post + uri: "/sign-me-up" + body: + encoding: UTF-8 + string: '{"protected":"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzM4NCIsIm5vbmNlIjoiRFhTNG5ZSmwxOFBpZDBONXRqUmZmdyIsInVybCI6Imh0dHBzOi8vMC4wLjAuMDoxNDAwMC9zaWduLW1lLXVwIiwiandrIjp7ImNydiI6IlAtMzg0Iiwia3R5IjoiRUMiLCJ4IjoiMm5NOHg0aVR4UElLcUZqR01jX3RiY2QxOW1QT1pydld1S3ZpYlU3MGsxWlZwaU9LQ2p2c3N3Yl9OT0JNX29lYiIsInkiOiJHaHFaN3d2dXhRb05VRThJclBkVF9qVkVOQmJqRThjWVBjYnIwMkdrTk0tSHNhanNJWGRrNTYyNXZsNVpVdkhNIn19","payload":"eyJjb250YWN0IjpbIm1haWx0bzppbmZvQGV4YW1wbGUuY29tIl0sInRlcm1zT2ZTZXJ2aWNlQWdyZWVkIjp0cnVlfQ","signature":"_Ct6M9duFtXOxtZDWLwrxcVv49z3l2JJCDFGuQQ1HyUvbnBfoVPNuLssJEQ3bXz_Dh-F-W32DTPaOoPAvlIwWOV-WuHZEYuw1DeR8GwGS68cC-xbU2Fbs1c5Fu5bHhU-"}' + headers: + User-Agent: + - Acme::Client v2.0.15 (https://github.com/unixcharles/acme-client) + Content-Type: + - application/jose+json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 201 + message: Created + headers: + Cache-Control: + - public, max-age=0, no-cache + Content-Type: + - application/json; charset=utf-8 + Link: + - <>;rel="index" + Location: + - "/my-account/3011c37ad722d86" + Replay-Nonce: + - "-0Wz_OugGSEpZJRBPs8X2g" + Date: + - Tue, 16 Jan 2024 18:10:15 GMT + Content-Length: + - '360' + body: + encoding: UTF-8 + string: |- + { + "status": "valid", + "contact": [ + "mailto:info@example.com" + ], + "orders": "/list-orderz/3011c37ad722d86", + "key": { + "kty": "EC", + "crv": "P-384", + "x": "2nM8x4iTxPIKqFjGMc_tbcd19mPOZrvWuKvibU70k1ZVpiOKCjvsswb_NOBM_oeb", + "y": "GhqZ7wvuxQoNUE8IrPdT_jVENBbjE8cYPcbr02GkNM-HsajsIXdk5625vl5ZUvHM" + } + } + http_version: + recorded_at: Tue, 16 Jan 2024 18:10:15 GMT recorded_with: VCR 2.9.3 diff --git a/spec/cassettes/directory_meta.yml b/spec/cassettes/directory_meta.yml index b4be264..d8ca76b 100644 --- a/spec/cassettes/directory_meta.yml +++ b/spec/cassettes/directory_meta.yml @@ -8,7 +8,9 @@ http_interactions: string: '' headers: User-Agent: - - Acme::Client v2.0.4 (https://github.com/unixcharles/acme-client) + - Acme::Client v2.0.15 (https://github.com/unixcharles/acme-client) + Content-Type: + - application/jose+json Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 Accept: @@ -23,15 +25,16 @@ http_interactions: Content-Type: - application/json; charset=utf-8 Date: - - Thu, 10 Oct 2019 03:08:14 GMT + - Tue, 16 Jan 2024 18:10:51 GMT Content-Length: - - '386' + - '396' body: encoding: UTF-8 string: |- { "keyChange": "/rollover-account-key", "meta": { + "externalAccountRequired": false, "termsOfService": "data:text/plain,Do%20what%20thou%20wilt" }, "newAccount": "/sign-me-up", @@ -39,6 +42,89 @@ http_interactions: "newOrder": "/order-plz", "revokeCert": "/revoke-cert" } - http_version: - recorded_at: Thu, 10 Oct 2019 03:08:14 GMT + http_version: + recorded_at: Tue, 16 Jan 2024 18:10:51 GMT +- request: + method: head + uri: "/nonce-plz" + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Acme::Client v2.0.15 (https://github.com/unixcharles/acme-client) + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Cache-Control: + - public, max-age=0, no-cache + Link: + - <>;rel="index" + Replay-Nonce: + - Tuty2P8DM9lQfIoWYI_xIA + Date: + - Tue, 16 Jan 2024 18:10:51 GMT + body: + encoding: UTF-8 + string: '' + http_version: + recorded_at: Tue, 16 Jan 2024 18:10:51 GMT +- request: + method: post + uri: "/sign-me-up" + body: + encoding: UTF-8 + string: '{"protected":"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzM4NCIsIm5vbmNlIjoiVHV0eTJQOERNOWxRZklvV1lJX3hJQSIsInVybCI6Imh0dHBzOi8vMC4wLjAuMDoxNDAwMC9zaWduLW1lLXVwIiwiandrIjp7ImNydiI6IlAtMzg0Iiwia3R5IjoiRUMiLCJ4IjoiNkZ2SzNfZ3Ywei1mZ250b0VRQWFmUGxpZHVxUXd0QlpsMGdqTDRzc2ltdm5Cd0M4aWxYVlBiLWNNTlBxaFEwaSIsInkiOiJla2lEa29YTVlXTmhvSWwydTRUcEg2U3oyOXlXMkZpMm1qenhBYVRzTGpQczVIMnhQWW1GYlBRRmF1N3EzejVQIn19","payload":"eyJjb250YWN0IjpbIm1haWx0bzppbmZvQGV4YW1wbGUuY29tIl0sInRlcm1zT2ZTZXJ2aWNlQWdyZWVkIjp0cnVlfQ","signature":"TD8n-T2m1qE1mo9uZTl7AJax1D1LclTBixqXZmeyQi7xXQjSeGWdtBPpjvE6d0kCSpQNGv6GcBJgYHS-QoK-z-4YQnCDggCMetyn2d9MFeYuqiEhM9XrULchX58ZmiFe"}' + headers: + User-Agent: + - Acme::Client v2.0.15 (https://github.com/unixcharles/acme-client) + Content-Type: + - application/jose+json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 201 + message: Created + headers: + Cache-Control: + - public, max-age=0, no-cache + Content-Type: + - application/json; charset=utf-8 + Link: + - <>;rel="index" + Location: + - "/my-account/4530875a31aa979c" + Replay-Nonce: + - wgZvnNCEXH7AO9RaizC3Tw + Date: + - Tue, 16 Jan 2024 18:10:51 GMT + Content-Length: + - '361' + body: + encoding: UTF-8 + string: |- + { + "status": "valid", + "contact": [ + "mailto:info@example.com" + ], + "orders": "/list-orderz/4530875a31aa979c", + "key": { + "kty": "EC", + "crv": "P-384", + "x": "6FvK3_gv0z-fgntoEQAafPliduqQwtBZl0gjL4ssimvnBwC8ilXVPb-cMNPqhQ0i", + "y": "ekiDkoXMYWNhoIl2u4TpH6Sz29yW2Fi2mjzxAaTsLjPs5H2xPYmFbPQFau7q3z5P" + } + } + http_version: + recorded_at: Tue, 16 Jan 2024 18:10:51 GMT recorded_with: VCR 2.9.3 diff --git a/spec/directory_spec.rb b/spec/directory_spec.rb index 171ab06..af820c2 100644 --- a/spec/directory_spec.rb +++ b/spec/directory_spec.rb @@ -1,7 +1,14 @@ require 'spec_helper' describe Acme::Client::Resources::Directory do - let(:directory) { Acme::Client::Resources::Directory.new(DIRECTORY_URL, {}) } + let(:private_key) { generate_private_key } + let(:client) do + client = Acme::Client.new(private_key: private_key, directory: DIRECTORY_URL) + client.new_account(contact: 'mailto:info@example.com', terms_of_service_agreed: true) + client + end + + let(:directory) { client.directory } context 'endpoint_for', vcr: { cassette_name: 'directory_endpoint_for' } do it { expect(directory.endpoint_for(:new_nonce)).to be_a_kind_of(URI) } @@ -22,6 +29,6 @@ context 'meta', vcr: { cassette_name: 'directory_meta' } do it { expect(directory.meta).to be_a(Hash) } it { expect(directory.terms_of_service).to be_a(String) } - it { expect(directory.external_account_required).to be_nil } + it { expect(directory.external_account_required).to be false } end end