mauth-client 6.3.0 → 6.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +53 -0
- data/.ruby-version +1 -0
- data/.travis.yml +12 -5
- data/Appraisals +6 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +2 -0
- data/Rakefile +4 -2
- data/examples/Gemfile +2 -0
- data/examples/Gemfile.lock +14 -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 +11 -8
- data/lib/mauth/client/remote_authenticator.rb +20 -10
- data/lib/mauth/client/security_token_cacher.rb +4 -3
- data/lib/mauth/client/signer.rb +5 -3
- 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 +20 -12
- 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 +15 -10
- metadata +87 -62
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
|
@@ -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,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'faraday-http-cache'
|
2
4
|
require 'mauth/faraday'
|
3
5
|
|
@@ -5,12 +7,11 @@ 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
13
|
@mauth_client.assert_private_key(
|
13
|
-
UnableToAuthenticateError.new(
|
14
|
+
UnableToAuthenticateError.new('Cannot fetch public keys from mAuth service without a private key!')
|
14
15
|
)
|
15
16
|
end
|
16
17
|
|
@@ -41,7 +42,7 @@ module MAuth
|
|
41
42
|
def security_token_from(response_body)
|
42
43
|
JSON.parse response_body
|
43
44
|
rescue JSON::ParserError => e
|
44
|
-
msg =
|
45
|
+
msg = "mAuth service responded with unparseable json: #{response_body}\n#{e.class}: #{e.message}"
|
45
46
|
@mauth_client.logger.error("Unable to authenticate with MAuth. Exception #{msg}")
|
46
47
|
raise UnableToAuthenticateError, msg
|
47
48
|
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]
|
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
|
data/lib/mauth/faraday.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'mauth/middleware'
|
2
4
|
require 'mauth/request_and_response'
|
3
5
|
|
@@ -31,6 +33,7 @@ module MAuth
|
|
31
33
|
# passed to a Mauth::Client for signing
|
32
34
|
class Request < MAuth::Request
|
33
35
|
attr_reader :request_env
|
36
|
+
|
34
37
|
def initialize(request_env)
|
35
38
|
@request_env = request_env
|
36
39
|
end
|
@@ -59,6 +62,7 @@ module MAuth
|
|
59
62
|
class Response < MAuth::Response
|
60
63
|
include Signed
|
61
64
|
attr_reader :response_env
|
65
|
+
|
62
66
|
def initialize(response_env)
|
63
67
|
@response_env = response_env
|
64
68
|
end
|
@@ -86,7 +90,7 @@ module MAuth
|
|
86
90
|
|
87
91
|
# add MAuth-Client's user-agent to a request
|
88
92
|
class MAuthClientUserAgent
|
89
|
-
def initialize(app, agent_base =
|
93
|
+
def initialize(app, agent_base = 'Mauth-Client')
|
90
94
|
@app = app
|
91
95
|
@agent_base = agent_base
|
92
96
|
end
|
data/lib/mauth/middleware.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'mauth/core_ext'
|
2
4
|
module MAuth
|
3
5
|
# base class for middleware, common to both Faraday and Rack
|
@@ -15,9 +17,9 @@ module MAuth
|
|
15
17
|
# this method may be overloaded to provide more flexibility in providing a MAuth::Client
|
16
18
|
def mauth_client
|
17
19
|
require 'mauth/client'
|
18
|
-
# @
|
20
|
+
# @mauth_client ivar only used here for caching; should not be used by other methods, in
|
19
21
|
# order that overloading #mauth_client will work
|
20
|
-
@
|
22
|
+
@mauth_client ||= @config['mauth_client'] || MAuth::Client.new(@config)
|
21
23
|
end
|
22
24
|
end
|
23
25
|
end
|
data/lib/mauth/proxy.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'mauth/client'
|
2
4
|
require 'faraday'
|
3
5
|
require 'rack'
|
@@ -7,6 +9,8 @@ module MAuth
|
|
7
9
|
# proxy them to a target URI. the responses from the target may be authenticated, with MAuth
|
8
10
|
# (and are by default).
|
9
11
|
class Proxy
|
12
|
+
EXCLUDED_RESPONSE_HEADERS = %w[Content-Length Transfer-Encoding].map(&:downcase)
|
13
|
+
|
10
14
|
# target_uri is the base relative to which requests are made.
|
11
15
|
#
|
12
16
|
# options:
|
@@ -38,12 +42,11 @@ module MAuth
|
|
38
42
|
end
|
39
43
|
end
|
40
44
|
@persistent_headers = {}
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
end
|
45
|
+
options[:headers]&.each do |cur|
|
46
|
+
raise 'Headers must be in the format of [key]:[value]' unless cur.include?(':')
|
47
|
+
|
48
|
+
key, _throw_away, value = cur.partition(':')
|
49
|
+
@persistent_headers[key.strip] = value.strip
|
47
50
|
end
|
48
51
|
end
|
49
52
|
|
@@ -56,20 +59,20 @@ module MAuth
|
|
56
59
|
request_headers = {}
|
57
60
|
request_env.each do |k, v|
|
58
61
|
if k.start_with?('HTTP_') && k != 'HTTP_HOST'
|
59
|
-
name = k.
|
62
|
+
name = k.delete_prefix('HTTP_')
|
60
63
|
request_headers[name] = v
|
61
64
|
end
|
62
65
|
end
|
63
66
|
request_headers.merge!(@persistent_headers)
|
64
67
|
if @browser_proxy
|
65
|
-
target_uri = request_env[
|
68
|
+
target_uri = request_env['REQUEST_URI']
|
66
69
|
connection = @target_uris.any? { |u| target_uri.start_with? u } ? @signer_connection : @unsigned_connection
|
67
70
|
response = connection.run_request(request_method, target_uri, request_body, request_headers)
|
68
71
|
else
|
69
72
|
response = @connection.run_request(request_method, request.fullpath, request_body, request_headers)
|
70
73
|
end
|
71
74
|
response_headers = response.headers.reject do |name, _value|
|
72
|
-
|
75
|
+
EXCLUDED_RESPONSE_HEADERS.include?(name.downcase)
|
73
76
|
end
|
74
77
|
[response.status, response_headers, [response.body || '']]
|
75
78
|
end
|