Class: MAuth::Client

Inherits:
Object
  • Object
show all
Includes:
AuthenticatorBase, Signer
Defined in:
lib/mauth/client.rb,
lib/mauth/errors.rb,
lib/mauth/client/signer.rb,
lib/mauth/client/authenticator_base.rb,
lib/mauth/client/local_authenticator.rb,
lib/mauth/client/remote_authenticator.rb,
lib/mauth/client/security_token_cacher.rb

Overview

does operations which require a private key and corresponding app uuid. this is primarily:

  • signing outgoing requests and responses

  • authenticating incoming requests and responses, which may require retrieving the appropriate public key from mAuth (which requires a request to mAuth which is signed using the private key)

this nominally operates on request and response objects, but really the only requirements are that the object responds to the methods of MAuth::Signable and/or MAuth::Signed (as appropriate)

Defined Under Namespace

Modules: AuthenticatorBase, LocalAuthenticator, RemoteRequestAuthenticator, Signer Classes: ConfigurationError

Constant Summary collapse

MWS_TOKEN =
'MWS'
MWSV2_TOKEN =
'MWSV2'
AUTH_HEADER_DELIMITER =
';'
RACK_ENV_APP_UUID_KEY =
'mauth.app_uuid'
SIGNING_DIGEST =
OpenSSL::Digest.new('SHA512')

Constants included from Signer

Signer::UNABLE_TO_SIGN_ERR

Constants included from AuthenticatorBase

AuthenticatorBase::ALLOWED_DRIFT_SECONDS

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Signer

#signature_v1, #signature_v2, #signed, #signed_headers, #signed_headers_v1, #signed_headers_v2, #signed_v1, #signed_v2

Methods included from AuthenticatorBase

#authentic?, #authenticate!

Constructor Details

#initialize(config = {}) ⇒ Client

new client with the given App UUID and public key. config may include the following (all config keys may be strings or symbols):

  • private_key - required for signing and for authenticating responses. may be omitted if only remote authentication of requests is being performed (with MAuth::Rack::RequestAuthenticator). may be given as a string or a OpenSSL::PKey::RSA instance.

  • app_uuid - required in the same circumstances where a private_key is required

  • mauth_baseurl - required. needed for local authentication to retrieve public keys; needed for remote authentication for hopefully obvious reasons.

  • mauth_api_version - required. only ‘v1’ exists / is supported as of this writing.

  • logger - a Logger to which any useful information will be written. if this is omitted and Rails.logger exists, that will be used.

  • authenticator - this pretty much never needs to be specified. LocalAuthenticator or RemoteRequestAuthenticator will be used as appropriate.



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/mauth/client.rb', line 121

def initialize(config = {})
  # stringify symbol keys
  given_config = config.stringify_symbol_keys
  # build a configuration which discards any irrelevant parts of the given config (small memory usage matters here)
  @config = {}
  if given_config['private_key_file'] && !given_config['private_key']
    given_config['private_key'] = File.read(given_config['private_key_file'])
  end
  @config['private_key'] =
    case given_config['private_key']
    when nil
      nil
    when String
      OpenSSL::PKey::RSA.new(given_config['private_key'])
    when OpenSSL::PKey::RSA
      given_config['private_key']
    else
      raise MAuth::Client::ConfigurationError,
        "unrecognized value given for 'private_key' - this may be a " \
        "String, a OpenSSL::PKey::RSA, or omitted; instead got: #{given_config['private_key'].inspect}"
    end
  @config['app_uuid'] = given_config['app_uuid']
  @config['mauth_baseurl'] = given_config['mauth_baseurl']
  @config['mauth_api_version'] = given_config['mauth_api_version']
  @config['logger'] = given_config['logger'] || begin
    if Object.const_defined?(:Rails) && Rails.logger
      Rails.logger
    else
      require 'logger'
      is_win = RUBY_PLATFORM =~ /mswin|windows|mingw32|cygwin/i
      null_device = is_win ? 'NUL' : '/dev/null'
      ::Logger.new(File.open(null_device, File::WRONLY))
    end
  end

  request_config = { timeout: 10, open_timeout: 10 }
  request_config.merge!(symbolize_keys(given_config['faraday_options'])) if given_config['faraday_options']
  @config['faraday_options'] = { request: request_config } || {}
  @config['ssl_certs_path'] = given_config['ssl_certs_path'] if given_config['ssl_certs_path']
  @config['v2_only_authenticate'] = given_config['v2_only_authenticate'].to_s.casecmp('true').zero?
  @config['v2_only_sign_requests'] = given_config['v2_only_sign_requests'].to_s.casecmp('true').zero?
  @config['disable_fallback_to_v1_on_v2_failure'] =
    given_config['disable_fallback_to_v1_on_v2_failure'].to_s.casecmp('true').zero?
  @config['v1_only_sign_requests'] = given_config['v1_only_sign_requests'].to_s.casecmp('true').zero?

  if @config['v2_only_sign_requests'] && @config['v1_only_sign_requests']
    raise MAuth::Client::ConfigurationError, 'v2_only_sign_requests and v1_only_sign_requests may not both be true'
  end

  # if 'authenticator' was given, don't override that - including if it was given as nil / false
  if given_config.key?('authenticator')
    @config['authenticator'] = given_config['authenticator']
  elsif client_app_uuid && private_key
    @config['authenticator'] = LocalAuthenticator
  # MAuth::Client can authenticate locally if it's provided a client_app_uuid and private_key
  else
    # otherwise, it will authenticate remotely (requests only)
    @config['authenticator'] = RemoteRequestAuthenticator
  end
  extend @config['authenticator'] if @config['authenticator']
end

Class Method Details

.default_config(options = {}) ⇒ Object

returns a configuration (to be passed to MAuth::Client.new) which is configured from information stored in standard places. all of which is overridable by options in case some defaults do not apply.

options (may be symbols or strings) - any or all may be omitted where your usage conforms to the defaults.

  • root: the path relative to which this method looks for configuration yaml files. defaults to Rails.root if ::Rails is defined, otherwise ENV, ENV, ENV, or ‘.’

  • environment: the environment, pertaining to top-level keys of the configuration yaml files. by default, tries Rails.environment, ENV, and ENV, and falls back to ‘development’ if none of these are set.

  • mauth_config - MAuth configuration. defaults to load this from a yaml file (see mauth_config_yml option) which is assumed to be keyed with the environment at the root. if this is specified, no yaml file is loaded, and the given config is passed through with any other defaults applied. at the moment, the only other default is to set the logger.

  • mauth_config_yml - specifies where a mauth configuration yaml file can be found. by default checks ENV or a file ‘config/mauth.yml’ relative to the root.

  • logger - by default checks ::Rails.logger



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/mauth/client.rb', line 53

def self.default_config(options = {})
  options = options.stringify_symbol_keys

  # find the app_root (relative to which we look for yaml files). note that this
  # is different than MAuth::Client.root, the root of the mauth-client library.
  app_root = options['root'] || begin
    if Object.const_defined?(:Rails) && ::Rails.respond_to?(:root) && ::Rails.root
      Rails.root
    else
      ENV['RAILS_ROOT'] || ENV['RACK_ROOT'] || ENV['APP_ROOT'] || '.'
    end
  end

  # find the environment (with which yaml files are keyed)
  env = options['environment'] || begin
    if Object.const_defined?(:Rails) && ::Rails.respond_to?(:environment)
      Rails.environment
    else
      ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
    end
  end

  # find mauth config, given on options, or in a file at
  # ENV['MAUTH_CONFIG_YML'] or config/mauth.yml in the app_root
  mauth_config = options['mauth_config'] || begin
    mauth_config_yml = options['mauth_config_yml']
    mauth_config_yml ||= ENV['MAUTH_CONFIG_YML']
    default_loc = 'config/mauth.yml'
    default_yml = File.join(app_root, default_loc)
    mauth_config_yml ||= default_yml if File.exist?(default_yml)
    if mauth_config_yml && File.exist?(mauth_config_yml)
      whole_config = ConfigFile.load(mauth_config_yml)
      errmessage = "#{mauth_config_yml} config has no key #{env} - it has keys #{whole_config.keys.inspect}"
      whole_config[env] || raise(MAuth::Client::ConfigurationError, errmessage)
    else
      raise MAuth::Client::ConfigurationError,
        'could not find mauth config yaml file. this file may be ' \
        "placed in #{default_loc}, specified with the mauth_config_yml option, or specified with the " \
        'MAUTH_CONFIG_YML environment variable.'
    end
  end

  unless mauth_config.key?('logger')
    # the logger. Rails.logger if it exists, otherwise, no logger
    mauth_config['logger'] = options['logger'] || begin
      if Object.const_defined?(:Rails) && ::Rails.respond_to?(:logger)
        Rails.logger
      end
    end
  end

  mauth_config
end

Instance Method Details

#assert_private_key(err) ⇒ Object



227
228
229
# File 'lib/mauth/client.rb', line 227

def assert_private_key(err)
  raise err unless private_key
end

#client_app_uuidObject



187
188
189
# File 'lib/mauth/client.rb', line 187

def client_app_uuid
  @config['app_uuid']
end

#disable_fallback_to_v1_on_v2_failure?Boolean

Returns:

  • (Boolean)


219
220
221
# File 'lib/mauth/client.rb', line 219

def disable_fallback_to_v1_on_v2_failure?
  @config['disable_fallback_to_v1_on_v2_failure']
end

#faraday_optionsObject



203
204
205
# File 'lib/mauth/client.rb', line 203

def faraday_options
  @config['faraday_options']
end

#loggerObject



183
184
185
# File 'lib/mauth/client.rb', line 183

def logger
  @config['logger']
end

#mauth_api_versionObject



195
196
197
# File 'lib/mauth/client.rb', line 195

def mauth_api_version
  @config['mauth_api_version'] || raise(MAuth::Client::ConfigurationError, 'no configured mauth_api_version!')
end

#mauth_baseurlObject



191
192
193
# File 'lib/mauth/client.rb', line 191

def mauth_baseurl
  @config['mauth_baseurl'] || raise(MAuth::Client::ConfigurationError, 'no configured mauth_baseurl!')
end

#private_keyObject



199
200
201
# File 'lib/mauth/client.rb', line 199

def private_key
  @config['private_key']
end

#ssl_certs_pathObject



207
208
209
# File 'lib/mauth/client.rb', line 207

def ssl_certs_path
  @config['ssl_certs_path']
end

#v1_only_sign_requests?Boolean

Returns:

  • (Boolean)


223
224
225
# File 'lib/mauth/client.rb', line 223

def v1_only_sign_requests?
  @config['v1_only_sign_requests']
end

#v2_only_authenticate?Boolean

Returns:

  • (Boolean)


215
216
217
# File 'lib/mauth/client.rb', line 215

def v2_only_authenticate?
  @config['v2_only_authenticate']
end

#v2_only_sign_requests?Boolean

Returns:

  • (Boolean)


211
212
213
# File 'lib/mauth/client.rb', line 211

def v2_only_sign_requests?
  @config['v2_only_sign_requests']
end