mauth-client 6.3.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 +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
|