mauth-client 6.2.0 → 6.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +53 -0
- data/.ruby-version +1 -0
- data/.travis.yml +23 -10
- data/Appraisals +6 -0
- data/CHANGELOG.md +15 -3
- data/Gemfile +2 -0
- data/Rakefile +4 -2
- data/examples/Gemfile +2 -0
- data/examples/Gemfile.lock +35 -13
- data/examples/get_user_info.rb +6 -6
- data/exe/mauth-client +88 -91
- data/exe/mauth-proxy +10 -10
- data/gemfiles/faraday_2.x.gemfile +7 -0
- data/lib/mauth/autoload.rb +2 -0
- data/lib/mauth/client/authenticator_base.rb +9 -6
- data/lib/mauth/client/local_authenticator.rb +12 -9
- data/lib/mauth/client/remote_authenticator.rb +20 -10
- data/lib/mauth/client/security_token_cacher.rb +36 -37
- data/lib/mauth/client/signer.rb +6 -4
- data/lib/mauth/client.rb +46 -37
- data/lib/mauth/core_ext.rb +2 -0
- data/lib/mauth/dice_bag/mauth_templates.rb +3 -1
- data/lib/mauth/errors.rb +2 -0
- data/lib/mauth/fake/rack.rb +17 -13
- data/lib/mauth/faraday.rb +5 -1
- data/lib/mauth/middleware.rb +4 -2
- data/lib/mauth/proxy.rb +12 -9
- data/lib/mauth/rack.rb +14 -6
- data/lib/mauth/request_and_response.rb +21 -13
- data/lib/mauth/version.rb +1 -1
- data/lib/mauth-client.rb +2 -0
- data/lib/rack/mauth.rb +2 -0
- data/mauth-client.gemspec +16 -11
- metadata +94 -69
data/exe/mauth-proxy
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
$LOAD_PATH.unshift File.expand_path('../lib', File.dirname(__FILE__))
|
4
5
|
|
5
|
-
require 'rubygems'
|
6
6
|
require 'mauth/proxy'
|
7
7
|
require 'rack'
|
8
8
|
|
9
9
|
headers = []
|
10
10
|
headers_index = ARGV.find_index('--header')
|
11
|
-
while
|
11
|
+
while headers_index
|
12
12
|
headers << ARGV[headers_index + 1]
|
13
13
|
ARGV.delete_at(headers_index + 1)
|
14
14
|
ARGV.delete_at(headers_index)
|
@@ -16,25 +16,25 @@ while(headers_index) do
|
|
16
16
|
end
|
17
17
|
|
18
18
|
authenticate_responses = !ARGV.delete('--no-authenticate')
|
19
|
-
browser_proxy = !
|
19
|
+
browser_proxy = !ARGV.delete('--browser_proxy').nil?
|
20
20
|
|
21
21
|
target_uri = browser_proxy ? ARGV : ARGV.pop
|
22
22
|
|
23
23
|
if !target_uri || target_uri.empty?
|
24
|
-
abort("Usage: mauth-proxy [rack options] --browser_proxy [--no-authenticate] <target URI> <target URI> ...\n"
|
25
|
-
|
24
|
+
abort("Usage: mauth-proxy [rack options] --browser_proxy [--no-authenticate] <target URI> <target URI> ...\n" \
|
25
|
+
'or: mauth-proxy [rack options] <target URI>')
|
26
26
|
end
|
27
27
|
|
28
28
|
rack_server_options = Rack::Server::Options.new.parse!(ARGV)
|
29
29
|
|
30
|
-
# for security, this rack server will only accept local connections, so override Host
|
30
|
+
# for security, this rack server will only accept local connections, so override Host
|
31
31
|
# to 127.0.0.1 (from the default of 0.0.0.0)
|
32
32
|
#
|
33
|
-
# this means that the '-o' / '--host' option to Rack::Server::Options is ignored.
|
34
|
-
rack_server_options[:Host] =
|
33
|
+
# this means that the '-o' / '--host' option to Rack::Server::Options is ignored.
|
34
|
+
rack_server_options[:Host] = '127.0.0.1'
|
35
35
|
|
36
|
-
rack_server_options[:app] = MAuth::Proxy.new(target_uri, :
|
37
|
-
:
|
36
|
+
rack_server_options[:app] = MAuth::Proxy.new(target_uri, authenticate_responses: authenticate_responses,
|
37
|
+
browser_proxy: browser_proxy, headers: headers)
|
38
38
|
|
39
39
|
mauth_proxy_server = Rack::Server.new(rack_server_options)
|
40
40
|
mauth_proxy_server.start
|
data/lib/mauth/autoload.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# methods common to RemoteRequestAuthenticator and LocalAuthenticator
|
2
4
|
|
3
5
|
module MAuth
|
@@ -21,7 +23,8 @@ module MAuth
|
|
21
23
|
# authenticate with v2 if the environment variable V2_ONLY_AUTHENTICATE
|
22
24
|
# is set. Otherwise will fall back to v1 when v2 authentication fails
|
23
25
|
def authenticate!(object)
|
24
|
-
|
26
|
+
case object.protocol_version
|
27
|
+
when 2
|
25
28
|
begin
|
26
29
|
authenticate_v2!(object)
|
27
30
|
rescue InauthenticError => e
|
@@ -33,9 +36,9 @@ module MAuth
|
|
33
36
|
|
34
37
|
log_authentication_request(object)
|
35
38
|
authenticate_v1!(object)
|
36
|
-
logger.warn(
|
39
|
+
logger.warn('Completed successful authentication attempt after fallback to v1')
|
37
40
|
end
|
38
|
-
|
41
|
+
when 1
|
39
42
|
if v2_only_authenticate?
|
40
43
|
# If v2 is required but not present and v1 is present we raise MissingV2Error
|
41
44
|
msg = 'This service requires mAuth v2 mcc-authentication header but only v1 x-mws-authentication is present'
|
@@ -54,13 +57,13 @@ module MAuth
|
|
54
57
|
|
55
58
|
private
|
56
59
|
|
57
|
-
#
|
60
|
+
# NOTE: This log is likely consumed downstream and the contents SHOULD NOT
|
58
61
|
# be changed without a thorough review of downstream consumers.
|
59
62
|
def log_authentication_request(object)
|
60
63
|
object_app_uuid = object.signature_app_uuid || '[none provided]'
|
61
64
|
object_token = object.signature_token || '[none provided]'
|
62
65
|
logger.info(
|
63
|
-
|
66
|
+
'Mauth-client attempting to authenticate request from app with mauth' \
|
64
67
|
" app uuid #{object_app_uuid} to app with mauth app uuid #{client_app_uuid}" \
|
65
68
|
" using version #{object_token}."
|
66
69
|
)
|
@@ -71,7 +74,7 @@ module MAuth
|
|
71
74
|
end
|
72
75
|
|
73
76
|
def time_within_valid_range!(object, time_signed, now = Time.now)
|
74
|
-
return if
|
77
|
+
return if (-ALLOWED_DRIFT_SECONDS..ALLOWED_DRIFT_SECONDS).cover?(now.to_i - time_signed)
|
75
78
|
|
76
79
|
msg = "Time verification failed. #{time_signed} not within #{ALLOWED_DRIFT_SECONDS} of #{now}"
|
77
80
|
log_inauthentic(object, msg)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'mauth/client/security_token_cacher'
|
2
4
|
require 'mauth/client/signer'
|
3
5
|
require 'openssl'
|
@@ -25,11 +27,13 @@ module MAuth
|
|
25
27
|
|
26
28
|
# do a simple percent reencoding variant of the path
|
27
29
|
object.attributes_for_signing[:request_url] = CGI.escape(original_request_uri.to_s)
|
28
|
-
expected_for_percent_reencoding = object.string_to_sign_v1(time: object.x_mws_time,
|
30
|
+
expected_for_percent_reencoding = object.string_to_sign_v1(time: object.x_mws_time,
|
31
|
+
app_uuid: object.signature_app_uuid)
|
29
32
|
|
30
33
|
# do a moderately complex Euresource-style reencoding of the path
|
31
34
|
object.attributes_for_signing[:request_url] = euresource_escape(original_request_uri.to_s)
|
32
|
-
expected_euresource_style_reencoding = object.string_to_sign_v1(time: object.x_mws_time,
|
35
|
+
expected_euresource_style_reencoding = object.string_to_sign_v1(time: object.x_mws_time,
|
36
|
+
app_uuid: object.signature_app_uuid)
|
33
37
|
|
34
38
|
# reset the object original request_uri, just in case we need it again
|
35
39
|
object.attributes_for_signing[:request_url] = original_request_uri
|
@@ -44,8 +48,8 @@ module MAuth
|
|
44
48
|
end
|
45
49
|
|
46
50
|
unless verify_signature_v1!(actual, expected_no_reencoding) ||
|
47
|
-
|
48
|
-
|
51
|
+
verify_signature_v1!(actual, expected_euresource_style_reencoding) ||
|
52
|
+
verify_signature_v1!(actual, expected_for_percent_reencoding)
|
49
53
|
msg = "Signature verification failed for #{object.class}"
|
50
54
|
log_inauthentic(object, msg)
|
51
55
|
raise InauthenticError, msg
|
@@ -53,7 +57,7 @@ module MAuth
|
|
53
57
|
end
|
54
58
|
|
55
59
|
def verify_signature_v1!(actual, expected_str_to_sign)
|
56
|
-
actual == Digest::SHA512.hexdigest(expected_str_to_sign)
|
60
|
+
actual == OpenSSL::Digest::SHA512.hexdigest(expected_str_to_sign)
|
57
61
|
end
|
58
62
|
|
59
63
|
def signature_valid_v2!(object)
|
@@ -93,8 +97,8 @@ module MAuth
|
|
93
97
|
actual = Base64.decode64(object.signature)
|
94
98
|
|
95
99
|
unless verify_signature_v2!(object, actual, pubkey, expected_no_reencoding) ||
|
96
|
-
|
97
|
-
|
100
|
+
verify_signature_v2!(object, actual, pubkey, expected_euresource_style_reencoding) ||
|
101
|
+
verify_signature_v2!(object, actual, pubkey, expected_for_percent_reencoding)
|
98
102
|
msg = "Signature inauthentic for #{object.class}"
|
99
103
|
log_inauthentic(object, msg)
|
100
104
|
raise InauthenticError, msg
|
@@ -113,7 +117,7 @@ module MAuth
|
|
113
117
|
raise InauthenticError, msg
|
114
118
|
end
|
115
119
|
|
116
|
-
#
|
120
|
+
# NOTE: RFC 3986 (https://www.ietf.org/rfc/rfc3986.txt) reserves the forward slash "/"
|
117
121
|
# and number sign "#" as component delimiters. Since these are valid URI components,
|
118
122
|
# they are decoded back into characters here to avoid signature invalidation
|
119
123
|
def euresource_escape(str)
|
@@ -139,7 +143,6 @@ module MAuth
|
|
139
143
|
def security_token_cacher
|
140
144
|
@security_token_cacher ||= SecurityTokenCacher.new(self)
|
141
145
|
end
|
142
|
-
|
143
146
|
end
|
144
147
|
end
|
145
148
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# methods for remotely authenticating a request by sending it to the mauth service
|
2
4
|
|
3
5
|
module MAuth
|
@@ -8,7 +10,11 @@ module MAuth
|
|
8
10
|
# takes an incoming request object (no support for responses currently), and errors if the
|
9
11
|
# object is not authentic according to its signature
|
10
12
|
def signature_valid_v1!(object)
|
11
|
-
|
13
|
+
unless object.is_a?(MAuth::Request)
|
14
|
+
raise ArgumentError,
|
15
|
+
"Remote Authenticator can only authenticate requests; received #{object.inspect}"
|
16
|
+
end
|
17
|
+
|
12
18
|
authentication_ticket = {
|
13
19
|
'verb' => object.attributes_for_signing[:verb],
|
14
20
|
'app_uuid' => object.signature_app_uuid,
|
@@ -41,15 +47,17 @@ module MAuth
|
|
41
47
|
|
42
48
|
def make_mauth_request(authentication_ticket)
|
43
49
|
begin
|
44
|
-
|
50
|
+
request_body = JSON.generate(authentication_ticket: authentication_ticket)
|
51
|
+
response = mauth_connection.post("/mauth/#{mauth_api_version}/authentication_tickets.json", request_body)
|
45
52
|
rescue ::Faraday::ConnectionFailed, ::Faraday::TimeoutError => e
|
46
53
|
msg = "mAuth service did not respond; received #{e.class}: #{e.message}"
|
47
54
|
logger.error("Unable to authenticate with MAuth. Exception #{msg}")
|
48
55
|
raise UnableToAuthenticateError, msg
|
49
56
|
end
|
50
|
-
|
57
|
+
case response.status
|
58
|
+
when 200..299
|
51
59
|
nil
|
52
|
-
|
60
|
+
when 412, 404
|
53
61
|
# the mAuth service responds with 412 when the given request is not authentically signed.
|
54
62
|
# older versions of the mAuth service respond with 404 when the given app_uuid
|
55
63
|
# does not exist, which is also considered to not be authentically signed. newer
|
@@ -62,12 +70,14 @@ module MAuth
|
|
62
70
|
end
|
63
71
|
|
64
72
|
def mauth_connection
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
73
|
+
@mauth_connection ||= begin
|
74
|
+
require 'faraday'
|
75
|
+
|
76
|
+
::Faraday.new(mauth_baseurl,
|
77
|
+
faraday_options.merge(headers: { 'Content-Type' => 'application/json' })) do |builder|
|
78
|
+
builder.use MAuth::Faraday::MAuthClientUserAgent
|
79
|
+
builder.adapter ::Faraday.default_adapter
|
80
|
+
end
|
71
81
|
end
|
72
82
|
end
|
73
83
|
end
|
@@ -1,44 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'faraday-http-cache'
|
2
|
-
require '
|
4
|
+
require 'mauth/faraday'
|
3
5
|
|
4
6
|
module MAuth
|
5
7
|
class Client
|
6
8
|
module LocalAuthenticator
|
7
9
|
class SecurityTokenCacher
|
8
|
-
|
9
10
|
def initialize(mauth_client)
|
10
11
|
@mauth_client = mauth_client
|
11
12
|
# TODO: should this be UnableToSignError?
|
12
|
-
@mauth_client.assert_private_key(
|
13
|
-
|
14
|
-
|
15
|
-
@cache_write_lock = Mutex.new
|
13
|
+
@mauth_client.assert_private_key(
|
14
|
+
UnableToAuthenticateError.new('Cannot fetch public keys from mAuth service without a private key!')
|
15
|
+
)
|
16
16
|
end
|
17
17
|
|
18
18
|
def get(app_uuid)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
end
|
34
|
-
elsif response.status == 404
|
35
|
-
# signing with a key mAuth doesn't know about is considered inauthentic
|
36
|
-
raise InauthenticError, "mAuth service responded with 404 looking up public key for #{app_uuid}"
|
37
|
-
else
|
38
|
-
@mauth_client.send(:mauth_service_response_error, response)
|
39
|
-
end
|
19
|
+
# url-encode the app_uuid to prevent trickery like escaping upward with ../../ in a malicious
|
20
|
+
# app_uuid - probably not exploitable, but this is the right way to do it anyway.
|
21
|
+
url_encoded_app_uuid = CGI.escape(app_uuid)
|
22
|
+
path = "/mauth/#{@mauth_client.mauth_api_version}/security_tokens/#{url_encoded_app_uuid}.json"
|
23
|
+
response = signed_mauth_connection.get(path)
|
24
|
+
|
25
|
+
case response.status
|
26
|
+
when 200
|
27
|
+
security_token_from(response.body)
|
28
|
+
when 404
|
29
|
+
# signing with a key mAuth doesn't know about is considered inauthentic
|
30
|
+
raise InauthenticError, "mAuth service responded with 404 looking up public key for #{app_uuid}"
|
31
|
+
else
|
32
|
+
@mauth_client.send(:mauth_service_response_error, response)
|
40
33
|
end
|
41
|
-
|
34
|
+
rescue ::Faraday::ConnectionFailed, ::Faraday::TimeoutError => e
|
35
|
+
msg = "mAuth service did not respond; received #{e.class}: #{e.message}"
|
36
|
+
@mauth_client.logger.error("Unable to authenticate with MAuth. Exception #{msg}")
|
37
|
+
raise UnableToAuthenticateError, msg
|
42
38
|
end
|
43
39
|
|
44
40
|
private
|
@@ -46,20 +42,23 @@ module MAuth
|
|
46
42
|
def security_token_from(response_body)
|
47
43
|
JSON.parse response_body
|
48
44
|
rescue JSON::ParserError => e
|
49
|
-
msg =
|
45
|
+
msg = "mAuth service responded with unparseable json: #{response_body}\n#{e.class}: #{e.message}"
|
50
46
|
@mauth_client.logger.error("Unable to authenticate with MAuth. Exception #{msg}")
|
51
47
|
raise UnableToAuthenticateError, msg
|
52
48
|
end
|
53
49
|
|
54
50
|
def signed_mauth_connection
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
51
|
+
@signed_mauth_connection ||= begin
|
52
|
+
if @mauth_client.ssl_certs_path
|
53
|
+
@mauth_client.faraday_options[:ssl] = { ca_path: @mauth_client.ssl_certs_path }
|
54
|
+
end
|
55
|
+
|
56
|
+
::Faraday.new(@mauth_client.mauth_baseurl, @mauth_client.faraday_options) do |builder|
|
57
|
+
builder.use MAuth::Faraday::MAuthClientUserAgent
|
58
|
+
builder.use MAuth::Faraday::RequestSigner, 'mauth_client' => @mauth_client
|
59
|
+
builder.use :http_cache, logger: MAuth::Client.new.logger, shared_cache: false
|
60
|
+
builder.adapter ::Faraday.default_adapter
|
61
|
+
end
|
63
62
|
end
|
64
63
|
end
|
65
64
|
end
|
data/lib/mauth/client/signer.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'openssl'
|
2
4
|
require 'mauth/errors'
|
3
5
|
|
@@ -5,7 +7,7 @@ require 'mauth/errors'
|
|
5
7
|
|
6
8
|
module MAuth
|
7
9
|
class Client
|
8
|
-
SIGNING_DIGEST = OpenSSL::Digest
|
10
|
+
SIGNING_DIGEST = OpenSSL::Digest.new('SHA512')
|
9
11
|
|
10
12
|
module Signer
|
11
13
|
UNABLE_TO_SIGN_ERR = UnableToSignError.new('mAuth client cannot sign without a private key!')
|
@@ -40,14 +42,14 @@ module MAuth
|
|
40
42
|
def signed_headers_v1(object, attributes = {})
|
41
43
|
attributes = { time: Time.now.to_i.to_s, app_uuid: client_app_uuid }.merge(attributes)
|
42
44
|
string_to_sign = object.string_to_sign_v1(attributes)
|
43
|
-
signature =
|
45
|
+
signature = signature_v1(string_to_sign)
|
44
46
|
{ 'X-MWS-Authentication' => "#{MWS_TOKEN} #{client_app_uuid}:#{signature}", 'X-MWS-Time' => attributes[:time] }
|
45
47
|
end
|
46
48
|
|
47
49
|
def signed_headers_v2(object, attributes = {})
|
48
50
|
attributes = { time: Time.now.to_i.to_s, app_uuid: client_app_uuid }.merge(attributes)
|
49
51
|
string_to_sign = object.string_to_sign_v2(attributes)
|
50
|
-
signature =
|
52
|
+
signature = signature_v2(string_to_sign)
|
51
53
|
{
|
52
54
|
'MCC-Authentication' => "#{MWSV2_TOKEN} #{client_app_uuid}:#{signature}#{AUTH_HEADER_DELIMITER}",
|
53
55
|
'MCC-Time' => attributes[:time]
|
@@ -56,7 +58,7 @@ module MAuth
|
|
56
58
|
|
57
59
|
def signature_v1(string_to_sign)
|
58
60
|
assert_private_key(UNABLE_TO_SIGN_ERR)
|
59
|
-
hashed_string_to_sign = Digest::SHA512.hexdigest(string_to_sign)
|
61
|
+
hashed_string_to_sign = OpenSSL::Digest::SHA512.hexdigest(string_to_sign)
|
60
62
|
Base64.encode64(private_key.private_encrypt(hashed_string_to_sign)).delete("\n")
|
61
63
|
end
|
62
64
|
|
data/lib/mauth/client.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'uri'
|
2
4
|
require 'openssl'
|
3
5
|
require 'base64'
|
@@ -24,9 +26,9 @@ module MAuth
|
|
24
26
|
# that the object responds to the methods of MAuth::Signable and/or MAuth::Signed (as
|
25
27
|
# appropriate)
|
26
28
|
class Client
|
27
|
-
MWS_TOKEN = 'MWS'
|
28
|
-
MWSV2_TOKEN = 'MWSV2'
|
29
|
-
AUTH_HEADER_DELIMITER = ';'
|
29
|
+
MWS_TOKEN = 'MWS'
|
30
|
+
MWSV2_TOKEN = 'MWSV2'
|
31
|
+
AUTH_HEADER_DELIMITER = ';'
|
30
32
|
|
31
33
|
include AuthenticatorBase
|
32
34
|
include Signer
|
@@ -53,7 +55,7 @@ module MAuth
|
|
53
55
|
# find the app_root (relative to which we look for yaml files). note that this
|
54
56
|
# is different than MAuth::Client.root, the root of the mauth-client library.
|
55
57
|
app_root = options['root'] || begin
|
56
|
-
if Object.const_defined?(
|
58
|
+
if Object.const_defined?(:Rails) && ::Rails.respond_to?(:root) && ::Rails.root
|
57
59
|
Rails.root
|
58
60
|
else
|
59
61
|
ENV['RAILS_ROOT'] || ENV['RACK_ROOT'] || ENV['APP_ROOT'] || '.'
|
@@ -62,7 +64,7 @@ module MAuth
|
|
62
64
|
|
63
65
|
# find the environment (with which yaml files are keyed)
|
64
66
|
env = options['environment'] || begin
|
65
|
-
if Object.const_defined?(
|
67
|
+
if Object.const_defined?(:Rails) && ::Rails.respond_to?(:environment)
|
66
68
|
Rails.environment
|
67
69
|
else
|
68
70
|
ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
|
@@ -82,16 +84,17 @@ module MAuth
|
|
82
84
|
errmessage = "#{mauth_config_yml} config has no key #{env} - it has keys #{whole_config.keys.inspect}"
|
83
85
|
whole_config[env] || raise(MAuth::Client::ConfigurationError, errmessage)
|
84
86
|
else
|
85
|
-
raise MAuth::Client::ConfigurationError,
|
87
|
+
raise MAuth::Client::ConfigurationError,
|
88
|
+
'could not find mauth config yaml file. this file may be ' \
|
86
89
|
"placed in #{default_loc}, specified with the mauth_config_yml option, or specified with the " \
|
87
|
-
|
90
|
+
'MAUTH_CONFIG_YML environment variable.'
|
88
91
|
end
|
89
92
|
end
|
90
93
|
|
91
94
|
unless mauth_config.key?('logger')
|
92
95
|
# the logger. Rails.logger if it exists, otherwise, no logger
|
93
96
|
mauth_config['logger'] = options['logger'] || begin
|
94
|
-
if Object.const_defined?(
|
97
|
+
if Object.const_defined?(:Rails) && ::Rails.respond_to?(:logger)
|
95
98
|
Rails.logger
|
96
99
|
end
|
97
100
|
end
|
@@ -122,22 +125,24 @@ module MAuth
|
|
122
125
|
if given_config['private_key_file'] && !given_config['private_key']
|
123
126
|
given_config['private_key'] = File.read(given_config['private_key_file'])
|
124
127
|
end
|
125
|
-
@config['private_key'] =
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
128
|
+
@config['private_key'] =
|
129
|
+
case given_config['private_key']
|
130
|
+
when nil
|
131
|
+
nil
|
132
|
+
when String
|
133
|
+
OpenSSL::PKey::RSA.new(given_config['private_key'])
|
134
|
+
when OpenSSL::PKey::RSA
|
135
|
+
given_config['private_key']
|
136
|
+
else
|
137
|
+
raise MAuth::Client::ConfigurationError,
|
138
|
+
"unrecognized value given for 'private_key' - this may be a " \
|
139
|
+
"String, a OpenSSL::PKey::RSA, or omitted; instead got: #{given_config['private_key'].inspect}"
|
140
|
+
end
|
136
141
|
@config['app_uuid'] = given_config['app_uuid']
|
137
142
|
@config['mauth_baseurl'] = given_config['mauth_baseurl']
|
138
143
|
@config['mauth_api_version'] = given_config['mauth_api_version']
|
139
144
|
@config['logger'] = given_config['logger'] || begin
|
140
|
-
if Object.const_defined?(
|
145
|
+
if Object.const_defined?(:Rails) && Rails.logger
|
141
146
|
Rails.logger
|
142
147
|
else
|
143
148
|
require 'logger'
|
@@ -151,26 +156,25 @@ module MAuth
|
|
151
156
|
request_config.merge!(symbolize_keys(given_config['faraday_options'])) if given_config['faraday_options']
|
152
157
|
@config['faraday_options'] = { request: request_config } || {}
|
153
158
|
@config['ssl_certs_path'] = given_config['ssl_certs_path'] if given_config['ssl_certs_path']
|
154
|
-
@config['v2_only_authenticate'] = given_config['v2_only_authenticate'].to_s.
|
155
|
-
@config['v2_only_sign_requests'] = given_config['v2_only_sign_requests'].to_s.
|
156
|
-
@config['disable_fallback_to_v1_on_v2_failure'] =
|
157
|
-
|
159
|
+
@config['v2_only_authenticate'] = given_config['v2_only_authenticate'].to_s.casecmp('true').zero?
|
160
|
+
@config['v2_only_sign_requests'] = given_config['v2_only_sign_requests'].to_s.casecmp('true').zero?
|
161
|
+
@config['disable_fallback_to_v1_on_v2_failure'] =
|
162
|
+
given_config['disable_fallback_to_v1_on_v2_failure'].to_s.casecmp('true').zero?
|
163
|
+
@config['v1_only_sign_requests'] = given_config['v1_only_sign_requests'].to_s.casecmp('true').zero?
|
158
164
|
|
159
165
|
if @config['v2_only_sign_requests'] && @config['v1_only_sign_requests']
|
160
|
-
raise MAuth::Client::ConfigurationError,
|
166
|
+
raise MAuth::Client::ConfigurationError, 'v2_only_sign_requests and v1_only_sign_requests may not both be true'
|
161
167
|
end
|
162
168
|
|
163
169
|
# if 'authenticator' was given, don't override that - including if it was given as nil / false
|
164
170
|
if given_config.key?('authenticator')
|
165
171
|
@config['authenticator'] = given_config['authenticator']
|
172
|
+
elsif client_app_uuid && private_key
|
173
|
+
@config['authenticator'] = LocalAuthenticator
|
174
|
+
# MAuth::Client can authenticate locally if it's provided a client_app_uuid and private_key
|
166
175
|
else
|
167
|
-
|
168
|
-
|
169
|
-
@config['authenticator'] = LocalAuthenticator
|
170
|
-
else
|
171
|
-
# otherwise, it will authenticate remotely (requests only)
|
172
|
-
@config['authenticator'] = RemoteRequestAuthenticator
|
173
|
-
end
|
176
|
+
# otherwise, it will authenticate remotely (requests only)
|
177
|
+
@config['authenticator'] = RemoteRequestAuthenticator
|
174
178
|
end
|
175
179
|
extend @config['authenticator'] if @config['authenticator']
|
176
180
|
end
|
@@ -184,11 +188,11 @@ module MAuth
|
|
184
188
|
end
|
185
189
|
|
186
190
|
def mauth_baseurl
|
187
|
-
@config['mauth_baseurl'] || raise(MAuth::Client::ConfigurationError,
|
191
|
+
@config['mauth_baseurl'] || raise(MAuth::Client::ConfigurationError, 'no configured mauth_baseurl!')
|
188
192
|
end
|
189
193
|
|
190
194
|
def mauth_api_version
|
191
|
-
@config['mauth_api_version'] || raise(MAuth::Client::ConfigurationError,
|
195
|
+
@config['mauth_api_version'] || raise(MAuth::Client::ConfigurationError, 'no configured mauth_api_version!')
|
192
196
|
end
|
193
197
|
|
194
198
|
def private_key
|
@@ -235,7 +239,7 @@ module MAuth
|
|
235
239
|
|
236
240
|
# Changes all keys in the top level of the hash to symbols. Does not affect nested hashes inside this one.
|
237
241
|
def symbolize_keys(hash)
|
238
|
-
hash.keys.each do |key|
|
242
|
+
hash.keys.each do |key| # rubocop:disable Style/HashEachMethods
|
239
243
|
hash[(key.to_sym rescue key) || key] = hash.delete(key)
|
240
244
|
end
|
241
245
|
hash
|
@@ -243,7 +247,7 @@ module MAuth
|
|
243
247
|
end
|
244
248
|
|
245
249
|
module ConfigFile
|
246
|
-
GITHUB_URL = 'https://github.com/mdsol/mauth-client-ruby'
|
250
|
+
GITHUB_URL = 'https://github.com/mdsol/mauth-client-ruby'
|
247
251
|
@config = {}
|
248
252
|
|
249
253
|
def self.load(path)
|
@@ -251,12 +255,17 @@ module MAuth
|
|
251
255
|
raise "File #{path} not found. Please visit #{GITHUB_URL} for details."
|
252
256
|
end
|
253
257
|
|
254
|
-
@config[path] ||=
|
258
|
+
@config[path] ||= yaml_safe_load_file(path)
|
255
259
|
unless @config[path]
|
256
260
|
raise "File #{path} does not contain proper YAML information. Visit #{GITHUB_URL} for details."
|
257
261
|
end
|
258
262
|
|
259
263
|
@config[path]
|
260
264
|
end
|
265
|
+
|
266
|
+
def self.yaml_safe_load_file(path)
|
267
|
+
yml_data = File.read(path)
|
268
|
+
YAML.safe_load(yml_data, aliases: true)
|
269
|
+
end
|
261
270
|
end
|
262
271
|
end
|
data/lib/mauth/core_ext.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'dice_bag'
|
2
4
|
|
3
5
|
class MauthTemplate < DiceBag::AvailableTemplates
|
@@ -14,6 +16,6 @@ class MauthInitializerTemplate < DiceBag::AvailableTemplates
|
|
14
16
|
end
|
15
17
|
|
16
18
|
def templates
|
17
|
-
[File.join(File.dirname(__FILE__), 'mauth.rb.dice')] if Object.const_defined?(
|
19
|
+
[File.join(File.dirname(__FILE__), 'mauth.rb.dice')] if Object.const_defined?(:Rails)
|
18
20
|
end
|
19
21
|
end
|
data/lib/mauth/errors.rb
CHANGED
data/lib/mauth/fake/rack.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'mauth/rack'
|
2
4
|
|
3
5
|
module MAuth
|
@@ -14,30 +16,32 @@ module MAuth
|
|
14
16
|
# (rather than switching to this fake one), as all this does is add those keys to the request env.
|
15
17
|
class RequestAuthenticationFaker < MAuth::Rack::RequestAuthenticator
|
16
18
|
class << self
|
17
|
-
def is_authentic?
|
19
|
+
def is_authentic? # rubocop:disable Naming/PredicateName
|
18
20
|
@is_authentic.nil? ? true : @is_authentic
|
19
21
|
end
|
20
22
|
|
21
|
-
def authentic=(is_auth = true)
|
23
|
+
def authentic=(is_auth = true) # rubocop:disable Style/OptionalBooleanParameter
|
22
24
|
@is_authentic = is_auth
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
26
28
|
def call(env)
|
27
29
|
retval = if should_authenticate?(env)
|
28
|
-
|
29
|
-
|
30
|
+
mauth_request = MAuth::Rack::Request.new(env)
|
31
|
+
env['mauth.protocol_version'] = mauth_request.protocol_version
|
30
32
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
33
|
+
if self.class.is_authentic?
|
34
|
+
@app.call(env.merge!('mauth.app_uuid' => mauth_request.signature_app_uuid,
|
35
|
+
'mauth.authentic' => true))
|
36
|
+
else
|
37
|
+
response_for_inauthentic_request(env)
|
38
|
+
end
|
39
|
+
else
|
40
|
+
@app.call(env)
|
41
|
+
end
|
39
42
|
|
40
|
-
# ensure that the next request is marked authenic unless the consumer of this middleware explicitly deems
|
43
|
+
# ensure that the next request is marked authenic unless the consumer of this middleware explicitly deems
|
44
|
+
# otherwise
|
41
45
|
self.class.authentic = true
|
42
46
|
|
43
47
|
retval
|