From cab0eaabd6fed5fc7145675632c97963296a3e76 Mon Sep 17 00:00:00 2001 From: Johnathan Martin Date: Tue, 14 Jan 2020 22:30:26 -0800 Subject: [PATCH 1/2] require that client specify auth token --- README.md | 8 +++--- lib/preservation/client.rb | 12 +++++++-- spec/preservation/client/catalog_spec.rb | 3 ++- spec/preservation/client/objects_spec.rb | 3 ++- .../client/versioned_api_service_spec.rb | 3 ++- spec/preservation/client_spec.rb | 25 ++++++++++++++++--- 6 files changed, 42 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index ea73de4..7999001 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ end private def client - @client ||= Preservation::Client.configure(url: Settings.preservation_catalog.url) + @client ||= Preservation::Client.configure(url: Settings.preservation_catalog.url, token: Settings.preservation_catalog.auth_token) end ``` @@ -47,7 +47,7 @@ OR require 'preservation/client' def initialize - Preservation::Client.configure(url: Settings.preservation_catalog.url) + Preservation::Client.configure(url: Settings.preservation_catalog.url, token: Settings.preservation_catalog.auth_token) end def do_the_thing @@ -55,7 +55,9 @@ def do_the_thing end ``` -Note that the client may **not** be used without first having been configured, and the `url` keyword is **required**. +Note that the client may **not** be used without first having been configured, and both the `url` and `token` keywords are **required**. + +See https://github.com/sul-dlss/preservation_catalog#api for info on obtaining a valid API token. Note that the preservation service is behind a firewall. diff --git a/lib/preservation/client.rb b/lib/preservation/client.rb index 2f2d016..ab94c9c 100644 --- a/lib/preservation/client.rb +++ b/lib/preservation/client.rb @@ -27,6 +27,7 @@ class UnexpectedResponseError < Error; end class ConnectionFailedError < Error; end DEFAULT_API_VERSION = 'v1' + TOKEN_HEADER = 'Authorization' include Singleton @@ -42,8 +43,10 @@ def catalog class << self # @param [String] url - def configure(url:) + # @param [String] token a bearer token for HTTP authentication + def configure(url:, token:) instance.url = url + instance.token = token # Force connection to be re-established when `.configure` is called instance.connection = nil @@ -54,7 +57,7 @@ def configure(url:) delegate :objects, :update, to: :instance end - attr_writer :url, :connection + attr_writer :url, :connection, :token delegate :update, to: :catalog private @@ -63,6 +66,10 @@ def url @url || raise(Error, 'url has not yet been configured') end + def token + @token || raise(Error, 'auth token has not been configured') + end + def connection @connection ||= Faraday.new(url) do |builder| builder.use ErrorFaradayMiddleware @@ -70,6 +77,7 @@ def connection builder.use Faraday::Response::RaiseError # raise exceptions on 40x, 50x responses builder.adapter Faraday.default_adapter builder.headers[:user_agent] = user_agent + builder.headers[TOKEN_HEADER] = "Bearer #{token}" end end diff --git a/spec/preservation/client/catalog_spec.rb b/spec/preservation/client/catalog_spec.rb index 97f8d9e..f9b0f6b 100644 --- a/spec/preservation/client/catalog_spec.rb +++ b/spec/preservation/client/catalog_spec.rb @@ -2,9 +2,10 @@ RSpec.describe Preservation::Client::Catalog do let(:prez_api_url) { 'https://prezcat.example.com' } + let(:auth_token) { 'my_secret_jwt_value' } before do - Preservation::Client.configure(url: prez_api_url) + Preservation::Client.configure(url: prez_api_url, token: auth_token) end let(:conn) { Preservation::Client.instance.send(:connection) } diff --git a/spec/preservation/client/objects_spec.rb b/spec/preservation/client/objects_spec.rb index e3df8df..ceace52 100644 --- a/spec/preservation/client/objects_spec.rb +++ b/spec/preservation/client/objects_spec.rb @@ -2,9 +2,10 @@ RSpec.describe Preservation::Client::Objects do let(:prez_api_url) { 'https://prezcat.example.com' } + let(:auth_token) { 'my_secret_jwt_value' } before do - Preservation::Client.configure(url: prez_api_url) + Preservation::Client.configure(url: prez_api_url, token: auth_token) end let(:conn) { Preservation::Client.instance.send(:connection) } diff --git a/spec/preservation/client/versioned_api_service_spec.rb b/spec/preservation/client/versioned_api_service_spec.rb index b1e2c6e..5179b32 100644 --- a/spec/preservation/client/versioned_api_service_spec.rb +++ b/spec/preservation/client/versioned_api_service_spec.rb @@ -2,11 +2,12 @@ RSpec.describe Preservation::Client::VersionedApiService do let(:prez_api_url) { 'https://prezcat.example.com' } + let(:auth_token) { 'my_secret_jwt_value' } let(:druid) { 'oo000oo0000' } let(:faraday_err_msg) { 'faraday is sad' } before do - Preservation::Client.configure(url: prez_api_url) + Preservation::Client.configure(url: prez_api_url, token: auth_token) end let(:conn) { Preservation::Client.instance.send(:connection) } diff --git a/spec/preservation/client_spec.rb b/spec/preservation/client_spec.rb index ebbb39f..f2eb637 100644 --- a/spec/preservation/client_spec.rb +++ b/spec/preservation/client_spec.rb @@ -2,13 +2,15 @@ RSpec.describe Preservation::Client do let(:prez_url) { 'https://prezcat.example.com' } + let(:auth_token) { 'my_secret_jwt_value' } + it 'has a version number' do expect(Preservation::Client::VERSION).not_to be nil end context 'once configured' do before do - described_class.configure(url: prez_url) + described_class.configure(url: prez_url, token: auth_token) end describe '.objects' do @@ -27,7 +29,7 @@ end describe '#configure' do - subject(:client) { described_class.configure(url: prez_url) } + subject(:client) { described_class.configure(url: prez_url, token: auth_token) } it 'returns Client class' do expect(client).to eq Preservation::Client @@ -35,11 +37,26 @@ it 'url is populated' do expect(client.instance.send(:url)).to eq prez_url end + it 'auth token is populated' do + expect(client.instance.send(:token)).to eq auth_token + end + it 'raises error if no url or token provided' do + expect { described_class.configure }.to raise_error(ArgumentError, /missing keywords: url, token/) + end it 'raises error if no url provided' do - expect { described_class.configure }.to raise_error(ArgumentError, /missing keyword: url/) + expect { described_class.configure(token: auth_token) }.to raise_error(ArgumentError, /missing keyword: url/) + end + it 'raises error if no token provided' do + expect { described_class.configure(url: prez_url) }.to raise_error(ArgumentError, /missing keyword: token/) end it 'connection is populated' do - expect(client.instance.send(:connection)).to be_instance_of(Faraday::Connection) + connection = client.instance.send(:connection) + expect(connection).to be_instance_of(Faraday::Connection) + expect(connection.url_prefix).to eq(URI(prez_url)) + expect(connection.headers).to include( + 'User-Agent' => "preservation-client #{Preservation::Client::VERSION}", + 'Authorization' => "Bearer #{auth_token}" + ) end end end From 39de52282fa0b0e562d78fd4f8602ad841da1cc1 Mon Sep 17 00:00:00 2001 From: Naomi Dushay Date: Wed, 15 Jan 2020 17:19:00 -0800 Subject: [PATCH 2/2] change 'auth_token' to just 'token' --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7999001..d1ba0ad 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ end private def client - @client ||= Preservation::Client.configure(url: Settings.preservation_catalog.url, token: Settings.preservation_catalog.auth_token) + @client ||= Preservation::Client.configure(url: Settings.preservation_catalog.url, token: Settings.preservation_catalog.token) end ``` @@ -47,7 +47,7 @@ OR require 'preservation/client' def initialize - Preservation::Client.configure(url: Settings.preservation_catalog.url, token: Settings.preservation_catalog.auth_token) + Preservation::Client.configure(url: Settings.preservation_catalog.url, token: Settings.preservation_catalog.token) end def do_the_thing