Skip to content

Commit

Permalink
New ca_cert parameter in FetchJwksUriSigningKey class
Browse files Browse the repository at this point in the history
Expected parameter type is OpenSSL::X509::Store
The parameter will allow to invoke http requests to endpoints providing self-signed certificate or certificate signed by 3rd party CA
  • Loading branch information
sashaCher committed Jan 6, 2022
1 parent 58c454a commit b7b1757
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class FetchJwksUriSigningKey
def initialize(
authenticator_input:,
fetch_signing_key:,
ca_cert: nil,
fetch_authenticator_secrets: Authentication::Util::FetchAuthenticatorSecrets.new,
http_lib: Net::HTTP,
create_jwks_from_http_response: CreateJwksFromHttpResponse.new,
Expand All @@ -23,6 +24,7 @@ def initialize(

@authenticator_input = authenticator_input
@fetch_signing_key = fetch_signing_key
@ca_cert = ca_cert
end

def call(force_fetch:)
Expand All @@ -36,6 +38,7 @@ def call(force_fetch:)
def fetch_signing_key
fetch_jwks_uri
fetch_jwks_keys
create_jwks_from_http_response
end

private
Expand All @@ -58,19 +61,63 @@ def jwks_uri_secret
end

def fetch_jwks_keys
begin
uri = URI(jwks_uri)
@logger.info(LogMessages::Authentication::AuthnJwt::FetchingJwksFromProvider.new(jwks_uri))
response = @http_lib.get_response(uri)
@logger.debug(LogMessages::Authentication::AuthnJwt::FetchJwtUriKeysSuccess.new)
rescue => e
jwks_keys
end

def jwks_keys
return @jwks_keys if defined?(@jwks_keys)

uri = URI(jwks_uri)
@logger.info(LogMessages::Authentication::AuthnJwt::FetchingJwksFromProvider.new(jwks_uri))
@jwks_keys = net_http_start(
uri.host,
uri.port,
uri.scheme == 'https'
) { |http| http.get(uri) }
@logger.debug(LogMessages::Authentication::AuthnJwt::FetchJwtUriKeysSuccess.new)
rescue => e
raise Errors::Authentication::AuthnJwt::FetchJwksKeysFailed.new(
jwks_uri,
e.inspect
)
end

def net_http_start(host, port, use_ssl, &block)
if @ca_cert && !use_ssl
raise Errors::Authentication::AuthnJwt::FetchJwksKeysFailed.new(
jwks_uri,
e.inspect
"TLS misconfiguration - ca-cert is provided but jwks-uri URI scheme is http"
)
end

@create_jwks_from_http_response.call(http_response: response)
if @ca_cert
net_http_start_with_ca_cert(host, port, use_ssl, &block)
else
net_http_start_without_ca_cert(host, port, use_ssl, &block)
end
end

def net_http_start_with_ca_cert(host, port, use_ssl, &block)
@http_lib.start(
host,
port,
use_ssl: use_ssl,
cert_store: @ca_cert,
&block
)
end

def net_http_start_without_ca_cert(host, port, use_ssl, &block)
@http_lib.start(
host,
port,
use_ssl: use_ssl,
&block
)
end

def create_jwks_from_http_response
@create_jwks_from_http_response.call(http_response: jwks_keys)
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,22 @@
let(:mocked_logger) { double("Mocked Logger") }
let(:mocked_fetch_signing_key) { double("MockedFetchSigningKey") }
let(:mocked_fetch_signing_key_refresh_value) { double("MockedFetchSigningKeyRefreshValue") }
let(:mocked_fetch_authenticator_secrets_exist_values) { double("MockedFetchAuthenticatorSecrets") }
let(:mocked_fetch_authenticator_secrets_empty_values) { double("MockedFetchAuthenticatorSecrets") }
let(:mocked_fetch_authenticator_secrets_exist_https) { double("MockedFetchAuthenticatorSecretsHttps") }
let(:mocked_fetch_authenticator_secrets_exist_http) { double("MockedFetchAuthenticatorSecretsHttp") }
let(:mocked_fetch_authenticator_secrets_empty_values) { double("MockedFetchAuthenticatorSecretsEmpty") }
let(:mocked_bad_http_response) { double("Mocked bad http response") }
let(:mocked_good_http_response) { double("Mocked good http response") }
let(:mocked_http_response_ca_cert_present) { double("MockedNet::HTTP.startCertStorePresent") }
let(:mocked_bad_response) { double("Mocked bad http body") }
let(:mocked_good_response) { double("Mocked good http body") }
let(:mocked_create_jwks_from_http_response) { double("Mocked good jwks") }
let(:mocked_create_jwks_from_http_responce_http_response) { double("MockedDummyJwks") }

let(:good_response) { "good-response"}
let(:bad_response) { "bad-response"}
let(:valid_jwks) { "valid-jwls" }
let(:cert_store_present) { "present" }
let(:cert_store_absent) { "absent" }

before(:each) do
allow(mocked_logger).to(
Expand All @@ -54,20 +59,41 @@
allow(mocked_fetch_signing_key).to receive(:call) { |params| params[:signing_key_provider].fetch_signing_key }
allow(mocked_fetch_signing_key_refresh_value).to receive(:call) { |params| params[:refresh] }

allow(mocked_fetch_authenticator_secrets_exist_values).to(
allow(mocked_fetch_authenticator_secrets_exist_https).to(
receive(:call).and_return('jwks-uri' => 'https://jwks-uri.com/jwks')
)

allow(mocked_fetch_authenticator_secrets_exist_http).to(
receive(:call).and_return('jwks-uri' => 'http://jwks-uri.com/jwks')
)

allow(mocked_fetch_authenticator_secrets_empty_values).to(
receive(:call).and_raise(required_secret_missing_error)
)

allow(mocked_bad_http_response).to(
receive(:get_response).and_return(bad_response)
receive(:start).and_return(bad_response)
)

allow(mocked_good_http_response).to(
receive(:get_response).and_return(good_response)
receive(:start).and_return(good_response)
)

allow(mocked_http_response_ca_cert_present).to(
receive(:start).with(
anything,
anything,
use_ssl: anything,
cert_store: cert_store_present
).and_return(cert_store_present)
)

allow(mocked_http_response_ca_cert_present).to(
receive(:start).with(
anything,
anything,
use_ssl: anything
).and_return(cert_store_absent)
)

allow(mocked_create_jwks_from_http_response).to(
Expand All @@ -77,6 +103,8 @@
allow(mocked_create_jwks_from_http_response).to(
receive(:call).with(http_response: bad_response).and_raise(bad_response_error)
)

allow(mocked_create_jwks_from_http_responce_http_response).to receive(:call) { |params| params[:http_response] }
end

# ____ _ _ ____ ____ ____ ___ ____ ___
Expand All @@ -90,7 +118,7 @@
::Authentication::AuthnJwt::SigningKey::FetchJwksUriSigningKey.new(authenticator_input: mocked_authenticator_input,
fetch_signing_key: mocked_fetch_signing_key_refresh_value,
logger: mocked_logger,
fetch_authenticator_secrets: mocked_fetch_authenticator_secrets_exist_values,
fetch_authenticator_secrets: mocked_fetch_authenticator_secrets_exist_https,
http_lib: mocked_bad_http_response,
create_jwks_from_http_response: mocked_create_jwks_from_http_response
).call(force_fetch: false)
Expand All @@ -106,7 +134,7 @@
::Authentication::AuthnJwt::SigningKey::FetchJwksUriSigningKey.new(authenticator_input: mocked_authenticator_input,
fetch_signing_key: mocked_fetch_signing_key_refresh_value,
logger: mocked_logger,
fetch_authenticator_secrets: mocked_fetch_authenticator_secrets_exist_values,
fetch_authenticator_secrets: mocked_fetch_authenticator_secrets_exist_https,
http_lib: mocked_bad_http_response,
create_jwks_from_http_response: mocked_create_jwks_from_http_response
).call(force_fetch: true)
Expand All @@ -117,12 +145,67 @@
end
end

context "processes ca_cert parameter" do
context "when it present" do
subject do
::Authentication::AuthnJwt::SigningKey::FetchJwksUriSigningKey.new(authenticator_input: mocked_authenticator_input,
ca_cert: cert_store_present,
fetch_signing_key: mocked_fetch_signing_key,
logger: mocked_logger,
fetch_authenticator_secrets: mocked_fetch_authenticator_secrets_exist_https,
http_lib: mocked_http_response_ca_cert_present,
create_jwks_from_http_response: mocked_create_jwks_from_http_responce_http_response
).call(force_fetch: false)
end

it "returns valid value" do
expect(subject).to eql(cert_store_present)
end
end

context "when it present but uri is http" do
subject do
::Authentication::AuthnJwt::SigningKey::FetchJwksUriSigningKey.new(authenticator_input: mocked_authenticator_input,
ca_cert: cert_store_present,
fetch_signing_key: mocked_fetch_signing_key,
logger: mocked_logger,
fetch_authenticator_secrets: mocked_fetch_authenticator_secrets_exist_http,
http_lib: mocked_http_response_ca_cert_present,
create_jwks_from_http_response: mocked_create_jwks_from_http_responce_http_response
).call(force_fetch: false)
end

it "raises error" do
expect { subject }.to raise_error(
Errors::Authentication::AuthnJwt::FetchJwksKeysFailed,
/CONJ00087E Failed to fetch JWKS from 'http:\/\/jwks-uri.com\/jwks'. Reason: 'TLS misconfiguration - ca-cert is provided but jwks-uri URI scheme is http'/
)
end
end

context "when it's absent" do
subject do
::Authentication::AuthnJwt::SigningKey::FetchJwksUriSigningKey.new(authenticator_input: mocked_authenticator_input,
fetch_signing_key: mocked_fetch_signing_key,
logger: mocked_logger,
fetch_authenticator_secrets: mocked_fetch_authenticator_secrets_exist_https,
http_lib: mocked_http_response_ca_cert_present,
create_jwks_from_http_response: mocked_create_jwks_from_http_responce_http_response
).call(force_fetch: false)
end

it "returns valid value" do
expect(subject).to eql(cert_store_absent)
end
end
end

context "'jwks-uri' secret is not valid" do
subject do
::Authentication::AuthnJwt::SigningKey::FetchJwksUriSigningKey.new(authenticator_input: mocked_authenticator_input,
fetch_signing_key: mocked_fetch_signing_key,
logger: mocked_logger,
fetch_authenticator_secrets: mocked_fetch_authenticator_secrets_exist_values,
fetch_authenticator_secrets: mocked_fetch_authenticator_secrets_exist_https,
http_lib: mocked_bad_http_response,
create_jwks_from_http_response: mocked_create_jwks_from_http_response
).call(force_fetch: false)
Expand All @@ -139,7 +222,7 @@
::Authentication::AuthnJwt::SigningKey::FetchJwksUriSigningKey.new(authenticator_input: mocked_authenticator_input,
fetch_signing_key: mocked_fetch_signing_key,
logger: mocked_logger,
fetch_authenticator_secrets: mocked_fetch_authenticator_secrets_exist_values,
fetch_authenticator_secrets: mocked_fetch_authenticator_secrets_exist_https,
http_lib: mocked_good_http_response,
create_jwks_from_http_response: mocked_create_jwks_from_http_response
).call(force_fetch: false)
Expand All @@ -155,7 +238,7 @@
::Authentication::AuthnJwt::SigningKey::FetchJwksUriSigningKey.new(authenticator_input: mocked_authenticator_input,
fetch_signing_key: mocked_fetch_signing_key,
logger: mocked_logger,
fetch_authenticator_secrets: mocked_fetch_authenticator_secrets_exist_values,
fetch_authenticator_secrets: mocked_fetch_authenticator_secrets_exist_https,
http_lib: mocked_bad_http_response,
create_jwks_from_http_response: mocked_create_jwks_from_http_response
).call(force_fetch: false)
Expand Down

0 comments on commit b7b1757

Please sign in to comment.