mauth-client 6.4.3 → 7.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,14 +4,135 @@ require 'mauth/client/security_token_cacher'
4
4
  require 'mauth/client/signer'
5
5
  require 'openssl'
6
6
 
7
- # methods to verify the authenticity of signed requests and responses locally, retrieving
8
- # public keys from the mAuth service as needed
7
+ # methods to verify the authenticity of signed requests and responses
9
8
 
10
9
  module MAuth
11
10
  class Client
12
- module LocalAuthenticator
11
+ module Authenticator
12
+ ALLOWED_DRIFT_SECONDS = 300
13
+
14
+ # takes an incoming request or response object, and returns whether
15
+ # the object is authentic according to its signature.
16
+ def authentic?(object)
17
+ log_authentication_request(object)
18
+ begin
19
+ authenticate!(object)
20
+ true
21
+ rescue InauthenticError, MAuthNotPresent, MissingV2Error
22
+ false
23
+ end
24
+ end
25
+
26
+ # raises InauthenticError unless the given object is authentic. Will only
27
+ # authenticate with v2 if the environment variable V2_ONLY_AUTHENTICATE
28
+ # is set. Otherwise will fall back to v1 when v2 authentication fails
29
+ def authenticate!(object)
30
+ case object.protocol_version
31
+ when 2
32
+ begin
33
+ authenticate_v2!(object)
34
+ rescue InauthenticError => e
35
+ raise e if v2_only_authenticate?
36
+ raise e if disable_fallback_to_v1_on_v2_failure?
37
+
38
+ object.fall_back_to_mws_signature_info
39
+ raise e unless object.signature
40
+
41
+ log_authentication_request(object)
42
+ authenticate_v1!(object)
43
+ logger.warn('Completed successful authentication attempt after fallback to v1')
44
+ end
45
+ when 1
46
+ if v2_only_authenticate?
47
+ # If v2 is required but not present and v1 is present we raise MissingV2Error
48
+ msg = 'This service requires mAuth v2 mcc-authentication header but only v1 x-mws-authentication is present'
49
+ logger.error(msg)
50
+ raise MissingV2Error, msg
51
+ end
52
+
53
+ authenticate_v1!(object)
54
+ else
55
+ sub_str = v2_only_authenticate? ? '' : 'X-MWS-Authentication header is blank, '
56
+ msg = "Authentication Failed. No mAuth signature present; #{sub_str}MCC-Authentication header is blank."
57
+ logger.warn("mAuth signature not present on #{object.class}. Exception: #{msg}")
58
+ raise MAuthNotPresent, msg
59
+ end
60
+ end
61
+
13
62
  private
14
63
 
64
+ # NOTE: This log is likely consumed downstream and the contents SHOULD NOT
65
+ # be changed without a thorough review of downstream consumers.
66
+ def log_authentication_request(object)
67
+ object_app_uuid = object.signature_app_uuid || '[none provided]'
68
+ object_token = object.signature_token || '[none provided]'
69
+ logger.info(
70
+ 'Mauth-client attempting to authenticate request from app with mauth ' \
71
+ "app uuid #{object_app_uuid} to app with mauth app uuid #{client_app_uuid} " \
72
+ "using version #{object_token}."
73
+ )
74
+ end
75
+
76
+ def log_inauthentic(object, message)
77
+ logger.error("mAuth signature authentication failed for #{object.class}. Exception: #{message}")
78
+ end
79
+
80
+ def time_within_valid_range!(object, time_signed, now = Time.now)
81
+ return if (-ALLOWED_DRIFT_SECONDS..ALLOWED_DRIFT_SECONDS).cover?(now.to_i - time_signed)
82
+
83
+ msg = "Time verification failed. #{time_signed} not within #{ALLOWED_DRIFT_SECONDS} of #{now}"
84
+ log_inauthentic(object, msg)
85
+ raise InauthenticError, msg
86
+ end
87
+
88
+ # V1 helpers
89
+ def authenticate_v1!(object)
90
+ time_valid_v1!(object)
91
+ token_valid_v1!(object)
92
+ signature_valid_v1!(object)
93
+ end
94
+
95
+ def time_valid_v1!(object)
96
+ if object.x_mws_time.nil?
97
+ msg = 'Time verification failed. No x-mws-time present.'
98
+ log_inauthentic(object, msg)
99
+ raise InauthenticError, msg
100
+ end
101
+ time_within_valid_range!(object, object.x_mws_time.to_i)
102
+ end
103
+
104
+ def token_valid_v1!(object)
105
+ return if object.signature_token == MWS_TOKEN
106
+
107
+ msg = "Token verification failed. Expected #{MWS_TOKEN}; token was #{object.signature_token}"
108
+ log_inauthentic(object, msg)
109
+ raise InauthenticError, msg
110
+ end
111
+
112
+ # V2 helpers
113
+ def authenticate_v2!(object)
114
+ time_valid_v2!(object)
115
+ token_valid_v2!(object)
116
+ signature_valid_v2!(object)
117
+ end
118
+
119
+ def time_valid_v2!(object)
120
+ if object.mcc_time.nil?
121
+ msg = 'Time verification failed. No MCC-Time present.'
122
+ log_inauthentic(object, msg)
123
+ raise InauthenticError, msg
124
+ end
125
+ time_within_valid_range!(object, object.mcc_time.to_i)
126
+ end
127
+
128
+ def token_valid_v2!(object)
129
+ return if object.signature_token == MWSV2_TOKEN
130
+
131
+ msg = "Token verification failed. Expected #{MWSV2_TOKEN}; token was #{object.signature_token}"
132
+ log_inauthentic(object, msg)
133
+ raise InauthenticError, msg
134
+ end
135
+
15
136
  def signature_valid_v1!(object)
16
137
  # We are in an unfortunate situation in which Euresource is percent-encoding parts of paths, but not
17
138
  # all of them. In particular, Euresource is percent-encoding all special characters save for '/'.
@@ -1,16 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'faraday-http-cache'
4
+ require 'faraday/retry'
5
+ if Gem::Version.new(Faraday::VERSION) >= Gem::Version.new('2.0')
6
+ require 'faraday/net_http_persistent'
7
+ else
8
+ require 'net/http/persistent'
9
+ end
4
10
  require 'mauth/faraday'
5
11
 
6
12
  module MAuth
7
13
  class Client
8
- module LocalAuthenticator
14
+ module Authenticator
9
15
  class SecurityTokenCacher
16
+ attr_reader :mauth_client
17
+
10
18
  def initialize(mauth_client)
11
19
  @mauth_client = mauth_client
12
20
  # TODO: should this be UnableToSignError?
13
- @mauth_client.assert_private_key(
21
+ mauth_client.assert_private_key(
14
22
  UnableToAuthenticateError.new('Cannot fetch public keys from mAuth service without a private key!')
15
23
  )
16
24
  end
@@ -19,7 +27,7 @@ module MAuth
19
27
  # url-encode the app_uuid to prevent trickery like escaping upward with ../../ in a malicious
20
28
  # app_uuid - probably not exploitable, but this is the right way to do it anyway.
21
29
  url_encoded_app_uuid = CGI.escape(app_uuid)
22
- path = "/mauth/#{@mauth_client.mauth_api_version}/security_tokens/#{url_encoded_app_uuid}.json"
30
+ path = "/mauth/#{mauth_client.mauth_api_version}/security_tokens/#{url_encoded_app_uuid}.json"
23
31
  response = signed_mauth_connection.get(path)
24
32
 
25
33
  case response.status
@@ -29,11 +37,11 @@ module MAuth
29
37
  # signing with a key mAuth doesn't know about is considered inauthentic
30
38
  raise InauthenticError, "mAuth service responded with 404 looking up public key for #{app_uuid}"
31
39
  else
32
- @mauth_client.send(:mauth_service_response_error, response)
40
+ mauth_client.send(:mauth_service_response_error, response)
33
41
  end
34
42
  rescue ::Faraday::ConnectionFailed, ::Faraday::TimeoutError => e
35
43
  msg = "mAuth service did not respond; received #{e.class}: #{e.message}"
36
- @mauth_client.logger.error("Unable to authenticate with MAuth. Exception #{msg}")
44
+ mauth_client.logger.error("Unable to authenticate with MAuth. Exception #{msg}")
37
45
  raise UnableToAuthenticateError, msg
38
46
  end
39
47
 
@@ -43,21 +51,20 @@ module MAuth
43
51
  JSON.parse response_body
44
52
  rescue JSON::ParserError => e
45
53
  msg = "mAuth service responded with unparseable json: #{response_body}\n#{e.class}: #{e.message}"
46
- @mauth_client.logger.error("Unable to authenticate with MAuth. Exception #{msg}")
54
+ mauth_client.logger.error("Unable to authenticate with MAuth. Exception #{msg}")
47
55
  raise UnableToAuthenticateError, msg
48
56
  end
49
57
 
50
58
  def signed_mauth_connection
51
59
  @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
60
+ mauth_client.faraday_options[:ssl] = { ca_path: mauth_client.ssl_certs_path } if mauth_client.ssl_certs_path
55
61
 
56
- ::Faraday.new(@mauth_client.mauth_baseurl, @mauth_client.faraday_options) do |builder|
62
+ ::Faraday.new(mauth_client.mauth_baseurl, mauth_client.faraday_options) do |builder|
57
63
  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
64
+ builder.use MAuth::Faraday::RequestSigner, 'mauth_client' => mauth_client
65
+ builder.use :http_cache, store: mauth_client.cache_store, logger: mauth_client.logger, shared_cache: false
66
+ builder.request :retry, max: 2
67
+ builder.adapter :net_http_persistent
61
68
  end
62
69
  end
63
70
  end
data/lib/mauth/client.rb CHANGED
@@ -7,13 +7,12 @@ require 'json'
7
7
  require 'yaml'
8
8
  require 'mauth/core_ext'
9
9
  require 'mauth/autoload'
10
- require 'mauth/dice_bag/mauth_templates'
11
10
  require 'mauth/version'
12
- require 'mauth/client/authenticator_base'
13
- require 'mauth/client/local_authenticator'
14
- require 'mauth/client/remote_authenticator'
11
+ require 'mauth/client/authenticator'
15
12
  require 'mauth/client/signer'
13
+ require 'mauth/config_env'
16
14
  require 'mauth/errors'
15
+ require 'mauth/private_key_helper'
17
16
 
18
17
  module MAuth
19
18
  # does operations which require a private key and corresponding app uuid. this is primarily:
@@ -31,66 +30,22 @@ module MAuth
31
30
  AUTH_HEADER_DELIMITER = ';'
32
31
  RACK_ENV_APP_UUID_KEY = 'mauth.app_uuid'
33
32
 
34
- include AuthenticatorBase
33
+ include Authenticator
35
34
  include Signer
36
35
 
37
36
  # returns a configuration (to be passed to MAuth::Client.new) which is configured from information stored in
38
37
  # standard places. all of which is overridable by options in case some defaults do not apply.
39
38
  #
40
39
  # options (may be symbols or strings) - any or all may be omitted where your usage conforms to the defaults.
41
- # - root: the path relative to which this method looks for configuration yaml files. defaults to Rails.root
42
- # if ::Rails is defined, otherwise ENV['RAILS_ROOT'], ENV['RACK_ROOT'], ENV['APP_ROOT'], or '.'
43
- # - environment: the environment, pertaining to top-level keys of the configuration yaml files. by default,
44
- # tries Rails.environment, ENV['RAILS_ENV'], and ENV['RACK_ENV'], and falls back to 'development' if none
45
- # of these are set.
46
- # - mauth_config - MAuth configuration. defaults to load this from a yaml file (see mauth_config_yml option)
47
- # which is assumed to be keyed with the environment at the root. if this is specified, no yaml file is
48
- # loaded, and the given config is passed through with any other defaults applied. at the moment, the only
49
- # other default is to set the logger.
50
- # - mauth_config_yml - specifies where a mauth configuration yaml file can be found. by default checks
51
- # ENV['MAUTH_CONFIG_YML'] or a file 'config/mauth.yml' relative to the root.
40
+ # - mauth_config - MAuth configuration. defaults to load this from environment variables. if this is specified,
41
+ # no environment variable is loaded, and the given config is passed through with any other defaults applied.
42
+ # at the moment, the only other default is to set the logger.
52
43
  # - logger - by default checks ::Rails.logger
53
44
  def self.default_config(options = {})
54
45
  options = options.stringify_symbol_keys
55
46
 
56
- # find the app_root (relative to which we look for yaml files). note that this
57
- # is different than MAuth::Client.root, the root of the mauth-client library.
58
- app_root = options['root'] || begin
59
- if Object.const_defined?(:Rails) && ::Rails.respond_to?(:root) && ::Rails.root
60
- Rails.root
61
- else
62
- ENV['RAILS_ROOT'] || ENV['RACK_ROOT'] || ENV['APP_ROOT'] || '.'
63
- end
64
- end
65
-
66
- # find the environment (with which yaml files are keyed)
67
- env = options['environment'] || begin
68
- if Object.const_defined?(:Rails) && ::Rails.respond_to?(:environment)
69
- Rails.environment
70
- else
71
- ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
72
- end
73
- end
74
-
75
- # find mauth config, given on options, or in a file at
76
- # ENV['MAUTH_CONFIG_YML'] or config/mauth.yml in the app_root
77
- mauth_config = options['mauth_config'] || begin
78
- mauth_config_yml = options['mauth_config_yml']
79
- mauth_config_yml ||= ENV['MAUTH_CONFIG_YML']
80
- default_loc = 'config/mauth.yml'
81
- default_yml = File.join(app_root, default_loc)
82
- mauth_config_yml ||= default_yml if File.exist?(default_yml)
83
- if mauth_config_yml && File.exist?(mauth_config_yml)
84
- whole_config = ConfigFile.load(mauth_config_yml)
85
- errmessage = "#{mauth_config_yml} config has no key #{env} - it has keys #{whole_config.keys.inspect}"
86
- whole_config[env] || raise(MAuth::Client::ConfigurationError, errmessage)
87
- else
88
- raise MAuth::Client::ConfigurationError,
89
- 'could not find mauth config yaml file. this file may be ' \
90
- "placed in #{default_loc}, specified with the mauth_config_yml option, or specified with the " \
91
- 'MAUTH_CONFIG_YML environment variable.'
92
- end
93
- end
47
+ # find mauth config
48
+ mauth_config = options['mauth_config'] || ConfigEnv.load
94
49
 
95
50
  unless mauth_config.key?('logger')
96
51
  # the logger. Rails.logger if it exists, otherwise, no logger
@@ -106,18 +61,13 @@ module MAuth
106
61
 
107
62
  # new client with the given App UUID and public key. config may include the following (all
108
63
  # config keys may be strings or symbols):
109
- # - private_key - required for signing and for authenticating responses. may be omitted if
110
- # only remote authentication of requests is being performed (with
111
- # MAuth::Rack::RequestAuthenticator). may be given as a string or a OpenSSL::PKey::RSA
112
- # instance.
64
+ # - private_key - required for signing and for authentication.
65
+ # may be given as a string or a OpenSSL::PKey::RSA instance.
113
66
  # - app_uuid - required in the same circumstances where a private_key is required
114
- # - mauth_baseurl - required. needed for local authentication to retrieve public keys; needed
115
- # for remote authentication for hopefully obvious reasons.
67
+ # - mauth_baseurl - required. needed to retrieve public keys.
116
68
  # - mauth_api_version - required. only 'v1' exists / is supported as of this writing.
117
69
  # - logger - a Logger to which any useful information will be written. if this is omitted and
118
70
  # Rails.logger exists, that will be used.
119
- # - authenticator - this pretty much never needs to be specified. LocalAuthenticator or
120
- # RemoteRequestAuthenticator will be used as appropriate.
121
71
  def initialize(config = {})
122
72
  # stringify symbol keys
123
73
  given_config = config.stringify_symbol_keys
@@ -131,7 +81,7 @@ module MAuth
131
81
  when nil
132
82
  nil
133
83
  when String
134
- OpenSSL::PKey::RSA.new(given_config['private_key'])
84
+ PrivateKeyHelper.load(given_config['private_key'])
135
85
  when OpenSSL::PKey::RSA
136
86
  given_config['private_key']
137
87
  else
@@ -153,31 +103,20 @@ module MAuth
153
103
  end
154
104
  end
155
105
 
156
- request_config = { timeout: 10, open_timeout: 10 }
106
+ request_config = { timeout: 10, open_timeout: 3 }
157
107
  request_config.merge!(symbolize_keys(given_config['faraday_options'])) if given_config['faraday_options']
158
108
  @config['faraday_options'] = { request: request_config } || {}
159
109
  @config['ssl_certs_path'] = given_config['ssl_certs_path'] if given_config['ssl_certs_path']
160
110
  @config['v2_only_authenticate'] = given_config['v2_only_authenticate'].to_s.casecmp('true').zero?
161
111
  @config['v2_only_sign_requests'] = given_config['v2_only_sign_requests'].to_s.casecmp('true').zero?
162
- @config['disable_fallback_to_v1_on_v2_failure'] =
163
- given_config['disable_fallback_to_v1_on_v2_failure'].to_s.casecmp('true').zero?
164
112
  @config['v1_only_sign_requests'] = given_config['v1_only_sign_requests'].to_s.casecmp('true').zero?
165
-
166
113
  if @config['v2_only_sign_requests'] && @config['v1_only_sign_requests']
167
114
  raise MAuth::Client::ConfigurationError, 'v2_only_sign_requests and v1_only_sign_requests may not both be true'
168
115
  end
169
116
 
170
- # if 'authenticator' was given, don't override that - including if it was given as nil / false
171
- if given_config.key?('authenticator')
172
- @config['authenticator'] = given_config['authenticator']
173
- elsif client_app_uuid && private_key
174
- @config['authenticator'] = LocalAuthenticator
175
- # MAuth::Client can authenticate locally if it's provided a client_app_uuid and private_key
176
- else
177
- # otherwise, it will authenticate remotely (requests only)
178
- @config['authenticator'] = RemoteRequestAuthenticator
179
- end
180
- extend @config['authenticator'] if @config['authenticator']
117
+ @config['disable_fallback_to_v1_on_v2_failure'] =
118
+ given_config['disable_fallback_to_v1_on_v2_failure'].to_s.casecmp('true').zero?
119
+ @config['use_rails_cache'] = given_config['use_rails_cache']
181
120
  end
182
121
 
183
122
  def logger
@@ -228,6 +167,10 @@ module MAuth
228
167
  raise err unless private_key
229
168
  end
230
169
 
170
+ def cache_store
171
+ Rails.cache if @config['use_rails_cache'] && Object.const_defined?(:Rails) && ::Rails.respond_to?(:cache)
172
+ end
173
+
231
174
  private
232
175
 
233
176
  def mauth_service_response_error(response)
@@ -246,27 +189,4 @@ module MAuth
246
189
  hash
247
190
  end
248
191
  end
249
-
250
- module ConfigFile
251
- GITHUB_URL = 'https://github.com/mdsol/mauth-client-ruby'
252
- @config = {}
253
-
254
- def self.load(path)
255
- unless File.exist?(path)
256
- raise "File #{path} not found. Please visit #{GITHUB_URL} for details."
257
- end
258
-
259
- @config[path] ||= yaml_safe_load_file(path)
260
- unless @config[path]
261
- raise "File #{path} does not contain proper YAML information. Visit #{GITHUB_URL} for details."
262
- end
263
-
264
- @config[path]
265
- end
266
-
267
- def self.yaml_safe_load_file(path)
268
- yml_data = File.read(path)
269
- YAML.safe_load(yml_data, aliases: true)
270
- end
271
- end
272
192
  end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MAuth
4
+ class ConfigEnv
5
+ GITHUB_URL = 'https://github.com/mdsol/mauth-client-ruby'
6
+
7
+ ENV_STUFF = {
8
+ 'MAUTH_URL' => nil,
9
+ 'MAUTH_API_VERSION' => 'v1',
10
+ 'MAUTH_APP_UUID' => nil,
11
+ 'MAUTH_PRIVATE_KEY' => nil,
12
+ 'MAUTH_PRIVATE_KEY_FILE' => 'config/mauth_key',
13
+ 'MAUTH_V2_ONLY_AUTHENTICATE' => false,
14
+ 'MAUTH_V2_ONLY_SIGN_REQUESTS' => false,
15
+ 'MAUTH_DISABLE_FALLBACK_TO_V1_ON_V2_FAILURE' => false,
16
+ 'MAUTH_V1_ONLY_SIGN_REQUESTS' => true,
17
+ 'MAUTH_USE_RAILS_CACHE' => false
18
+ }.freeze
19
+
20
+ class << self
21
+ def load
22
+ validate! if production?
23
+
24
+ {
25
+ 'mauth_baseurl' => env[:mauth_url] || 'http://localhost:7000',
26
+ 'mauth_api_version' => env[:mauth_api_version],
27
+ 'app_uuid' => env[:mauth_app_uuid] || 'fb17460e-9868-11e1-8399-0090f5ccb4d3',
28
+ 'private_key' => private_key || PrivateKeyHelper.generate.to_s,
29
+ 'v2_only_authenticate' => env[:mauth_v2_only_authenticate],
30
+ 'v2_only_sign_requests' => env[:mauth_v2_only_sign_requests],
31
+ 'disable_fallback_to_v1_on_v2_failure' => env[:mauth_disable_fallback_to_v1_on_v2_failure],
32
+ 'v1_only_sign_requests' => env[:mauth_v1_only_sign_requests],
33
+ 'use_rails_cache' => env[:mauth_use_rails_cache]
34
+ }
35
+ end
36
+
37
+ private
38
+
39
+ def validate!
40
+ errors = []
41
+ errors << 'The MAUTH_URL environment variable must be set' if env[:mauth_url].nil?
42
+ errors << 'The MAUTH_APP_UUID environment variable must be set' if env[:mauth_app_uuid].nil?
43
+ errors << 'The MAUTH_PRIVATE_KEY environment variable must be set' if env[:mauth_private_key].nil?
44
+ return if errors.empty?
45
+
46
+ errors.map! { |err| "#{err} => See #{GITHUB_URL}" }
47
+ errors.unshift('Invalid MAuth Client configuration:')
48
+ raise errors.join("\n")
49
+ end
50
+
51
+ def env
52
+ @env ||= ENV_STUFF.each_with_object({}) do |(key, default), hsh|
53
+ env_key = key.downcase.to_sym
54
+ hsh[env_key] = ENV.fetch(key, default)
55
+
56
+ case default
57
+ when TrueClass, FalseClass
58
+ hsh[env_key] = hsh[env_key].to_s.casecmp('true').zero?
59
+ end
60
+ end
61
+ end
62
+
63
+ def production?
64
+ environment.to_s.casecmp('production').zero?
65
+ end
66
+
67
+ def environment
68
+ return Rails.environment if Object.const_defined?(:Rails) && ::Rails.respond_to?(:environment)
69
+
70
+ ENV.fetch('RAILS_ENV') { ENV.fetch('RACK_ENV', 'development') }
71
+ end
72
+
73
+ def private_key
74
+ return env[:mauth_private_key] if env[:mauth_private_key]
75
+ return nil unless env[:mauth_private_key_file] && File.readable?(env[:mauth_private_key_file])
76
+
77
+ File.read(env[:mauth_private_key_file])
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
4
+
5
+ module MAuth
6
+ module PrivateKeyHelper
7
+ HEADER = '-----BEGIN RSA PRIVATE KEY-----'
8
+ FOOTER = '-----END RSA PRIVATE KEY-----'
9
+
10
+ module_function
11
+
12
+ def generate
13
+ OpenSSL::PKey::RSA.generate(2048)
14
+ end
15
+
16
+ def load(key)
17
+ OpenSSL::PKey::RSA.new(to_rsa_format(key))
18
+ rescue OpenSSL::PKey::RSAError
19
+ raise 'The private key provided is invalid'
20
+ end
21
+
22
+ def to_rsa_format(key)
23
+ return key if key.include?("\n")
24
+
25
+ body = key.strip.delete_prefix(HEADER).delete_suffix(FOOTER).strip
26
+ body = body.include?("\s") ? body.tr("\s", "\n") : body.scan(/.{1,64}/).join("\n")
27
+ "#{HEADER}\n#{body}\n#{FOOTER}"
28
+ end
29
+ end
30
+ end
data/lib/mauth/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MAuth
4
- VERSION = '6.4.3'
4
+ VERSION = '7.1.0'
5
5
  end
data/mauth-client.gemspec CHANGED
@@ -14,7 +14,7 @@ Gem::Specification.new do |spec|
14
14
  'Includes middleware for Rack and Faraday for incoming and outgoing requests and responses.'
15
15
  spec.homepage = 'https://github.com/mdsol/mauth-client-ruby'
16
16
  spec.license = 'MIT'
17
- spec.required_ruby_version = '>= 2.6.0'
17
+ spec.required_ruby_version = '>= 2.7.0'
18
18
 
19
19
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
20
  spec.bindir = 'exe'
@@ -23,23 +23,11 @@ Gem::Specification.new do |spec|
23
23
 
24
24
  spec.add_dependency 'addressable', '~> 2.0'
25
25
  spec.add_dependency 'coderay', '~> 1.0'
26
- spec.add_dependency 'dice_bag', '>= 0.9', '< 2.0'
27
- spec.add_dependency 'faraday', '>= 0.9', '< 3.0'
26
+ spec.add_dependency 'faraday', '>= 1.9', '< 3.0'
28
27
  spec.add_dependency 'faraday-http-cache', '>= 2.0', '< 3.0'
28
+ spec.add_dependency 'faraday-net_http_persistent'
29
+ spec.add_dependency 'faraday-retry'
30
+ spec.add_dependency 'net-http-persistent', '>= 3.1'
29
31
  spec.add_dependency 'rack', '> 2.2.3'
30
32
  spec.add_dependency 'term-ansicolor', '~> 1.0'
31
-
32
- spec.add_development_dependency 'appraisal'
33
- spec.add_development_dependency 'benchmark-ips', '~> 2.7'
34
- spec.add_development_dependency 'bundler', '>= 1.17'
35
- spec.add_development_dependency 'byebug'
36
- spec.add_development_dependency 'rack-test', '~> 1.1.0'
37
- spec.add_development_dependency 'rake', '~> 12.0'
38
- spec.add_development_dependency 'rspec', '~> 3.8'
39
- spec.add_development_dependency 'rubocop', '= 1.25.1'
40
- spec.add_development_dependency 'rubocop-mdsol', '~> 0.1'
41
- spec.add_development_dependency 'rubocop-performance', '= 1.13.2'
42
- spec.add_development_dependency 'simplecov', '~> 0.16'
43
- spec.add_development_dependency 'timecop', '~> 0.9'
44
- spec.add_development_dependency 'webmock', '~> 3.0'
45
33
  end