module Signet::OAuth1

Constants

OUT_OF_BAND

Public Class Methods

encode(value) click to toggle source

Converts a value to a percent-encoded String according to the rules given in RFC 5849. All non-unreserved characters are percent-encoded.

@param [Symbol, to_str] value The value to be encoded.

@return [String] The percent-encoded value.

# File lib/signet/oauth_1.rb, line 18
def self.encode(value)
  value = value.to_s if value.kind_of?(Symbol)
  return Addressable::URI.encode_component(
    value,
    Addressable::URI::CharacterClasses::UNRESERVED
  )
end
extract_credential_key_option(credential_type, options) click to toggle source

Processes an options Hash to find a credential key value. Allows for greater flexibility in configuration.

@param [Symbol] credential_type

One of <code>:client</code>, <code>:temporary</code>,
<code>:token</code>, <code>:consumer</code>, <code>:request</code>,
or <code>:access</code>.

@return [String] The credential key value.

# File lib/signet/oauth_1.rb, line 65
def self.extract_credential_key_option(credential_type, options)
  # Normalize key to String to allow indifferent access.
  options = options.inject({}) { |accu, (k, v)| accu[k.to_s] = v; accu }
  credential_key = "#{credential_type}_credential_key"
  credential = "#{credential_type}_credential"
  if options[credential_key]
    credential_key = options[credential_key]
  elsif options[credential]
    require 'signet/oauth_1/credential'
    if !options[credential].respond_to?(:key)
      raise TypeError,
        "Expected Signet::OAuth1::Credential, " +
        "got #{options[credential].class}."
    end
    credential_key = options[credential].key
  elsif options["client"]
    require 'signet/oauth_1/client'
    if !options["client"].kind_of?(::Signet::OAuth1::Client)
      raise TypeError,
        "Expected Signet::OAuth1::Client, got #{options["client"].class}."
    end
    credential_key = options["client"].send(credential_key)
  else
    credential_key = nil
  end
  if credential_key != nil && !credential_key.kind_of?(String)
    raise TypeError,
      "Expected String, got #{credential_key.class}."
  end
  return credential_key
end
extract_credential_secret_option(credential_type, options) click to toggle source

Processes an options Hash to find a credential secret value. Allows for greater flexibility in configuration.

@param [Symbol] credential_type

One of <code>:client</code>, <code>:temporary</code>,
<code>:token</code>, <code>:consumer</code>, <code>:request</code>,
or <code>:access</code>.

@return [String] The credential secret value.

# File lib/signet/oauth_1.rb, line 107
def self.extract_credential_secret_option(credential_type, options)
  # Normalize key to String to allow indifferent access.
  options = options.inject({}) { |accu, (k, v)| accu[k.to_s] = v; accu }
  credential_secret = "#{credential_type}_credential_secret"
  credential = "#{credential_type}_credential"
  if options[credential_secret]
    credential_secret = options[credential_secret]
  elsif options[credential]
    require 'signet/oauth_1/credential'
    if !options[credential].respond_to?(:secret)
      raise TypeError,
        "Expected Signet::OAuth1::Credential, " +
        "got #{options[credential].class}."
    end
    credential_secret = options[credential].secret
  elsif options["client"]
    require 'signet/oauth_1/client'
    if !options["client"].kind_of?(::Signet::OAuth1::Client)
      raise TypeError,
        "Expected Signet::OAuth1::Client, got #{options["client"].class}."
    end
    credential_secret = options["client"].send(credential_secret)
  else
    credential_secret = nil
  end
  if credential_secret != nil && !credential_secret.kind_of?(String)
    raise TypeError,
      "Expected String, got #{credential_secret.class}."
  end
  return credential_secret
end
generate_authorization_header(parameters, realm=nil) click to toggle source

Generates an Authorization header from a parameter list according to the rules given in RFC 5849.

@param [Enumerable] parameters The OAuth parameter list. @param [String] realm

The <code>Authorization</code> realm.  See RFC 2617.

@return [String] The Authorization header.

# File lib/signet/oauth_1.rb, line 204
def self.generate_authorization_header(parameters, realm=nil)
  if !parameters.kind_of?(Enumerable) || parameters.kind_of?(String)
    raise TypeError, "Expected Enumerable, got #{parameters.class}."
  end
  parameter_list = parameters.map do |k, v|
    if k == 'realm'
      raise ArgumentError,
        'The "realm" parameter must be specified as a separate argument.'
    end
    "#{self.encode(k)}=\"#{self.encode(v)}\""
  end
  if realm
    realm = realm.gsub('"', '\"')
    parameter_list.unshift("realm=\"#{realm}\"")
  end
  return 'OAuth ' + parameter_list.join(", ")
end
generate_authorization_uri(authorization_uri, options={}) click to toggle source

Appends the optional 'oauth_token' and 'oauth_callback' parameters to the base authorization URI.

@param [Addressable::URI, String, to_str] authorization_uri

The base authorization URI.

@return [String] The authorization URI to redirect the user to.

# File lib/signet/oauth_1.rb, line 359
def self.generate_authorization_uri(authorization_uri, options={})
  options = {
    :callback => nil,
    :additional_parameters => {}
  }.merge(options)
  temporary_credential_key =
    self.extract_credential_key_option(:temporary, options)
  parsed_uri = Addressable::URI.parse(authorization_uri).dup
  query_values = parsed_uri.query_values || {}
  if options[:additional_parameters]
    query_values = query_values.merge(
      options[:additional_parameters].inject({}) { |h,(k,v)| h[k]=v; h }
    )
  end
  if temporary_credential_key
    query_values['oauth_token'] = temporary_credential_key
  end
  if options[:callback]
    query_values['oauth_callback'] = options[:callback]
  end
  parsed_uri.query_values = query_values
  return parsed_uri.normalize.to_s
end
generate_base_string(method, uri, parameters) click to toggle source

Generates a signature base string according to the algorithm given in RFC 5849. Joins the method, URI, and normalized parameter string with '&' characters.

@param [String] method The HTTP method. @param [Addressable::URI, String, to_str] uri The URI. @param [Enumerable] parameters The OAuth parameter list.

@return [String] The signature base string.

# File lib/signet/oauth_1.rb, line 170
def self.generate_base_string(method, uri, parameters)
  if !parameters.kind_of?(Enumerable)
    raise TypeError, "Expected Enumerable, got #{parameters.class}."
  end
  method = method.to_s.upcase
  parsed_uri = Addressable::URI.parse(uri)
  uri = Addressable::URI.new(
    :scheme => parsed_uri.normalized_scheme,
    :authority => parsed_uri.normalized_authority,
    :path => parsed_uri.path,
    :query => parsed_uri.query,
    :fragment => parsed_uri.fragment
  )
  uri_parameters = uri.query_values.to_a
  uri = uri.omit(:query, :fragment).to_s
  merged_parameters =
    uri_parameters.concat(parameters.map { |k, v| [k, v] })
  parameter_string = self.normalize_parameters(merged_parameters)
  return [
    self.encode(method),
    self.encode(uri),
    self.encode(parameter_string)
  ].join('&')
end
generate_nonce() click to toggle source

Returns a nonce suitable for use as an 'oauth_nonce' value.

@return [String] A random nonce.

# File lib/signet/oauth_1.rb, line 51
def self.generate_nonce()
  return SecureRandom.random_bytes(16).unpack('H*').join('')
end
generate_timestamp() click to toggle source

Returns a timestamp suitable for use as an 'oauth_timestamp' value.

@return [String] The current timestamp.

# File lib/signet/oauth_1.rb, line 42
def self.generate_timestamp()
  return Time.now.to_i.to_s
end
normalize_parameters(parameters) click to toggle source

Normalizes a set of OAuth parameters according to the algorithm given in RFC 5849. Sorts key/value pairs lexically by byte order, first by key, then by value, joins key/value pairs with the '=' character, then joins the entire parameter list with '&' characters.

@param [Enumerable] parameters The OAuth parameter list.

@return [String] The normalized parameter list.

# File lib/signet/oauth_1.rb, line 148
def self.normalize_parameters(parameters)
  if !parameters.kind_of?(Enumerable)
    raise TypeError, "Expected Enumerable, got #{parameters.class}."
  end
  parameter_list = parameters.map do |k, v|
    next if k == "oauth_signature"
    # This is probably the wrong place to try to exclude the realm
    "#{self.encode(k)}=#{self.encode(v)}"
  end
  return parameter_list.compact.sort.join("&")
end
parse_authorization_header(field_value) click to toggle source

Parses an Authorization header into its component parameters. Parameter keys and values are decoded according to the rules given in RFC 5849.

# File lib/signet/oauth_1.rb, line 226
def self.parse_authorization_header(field_value)
  if !field_value.kind_of?(String)
    raise TypeError, "Expected String, got #{field_value.class}."
  end
  auth_scheme = field_value[/^([-._0-9a-zA-Z]+)/, 1]
  case auth_scheme
  when /^OAuth$/i
    # Other token types may be supported eventually
    pairs = Signet.parse_auth_param_list(field_value[/^OAuth\s+(.*)$/i, 1])
    return (pairs.inject([]) do |accu, (k, v)|
      if k != 'realm'
        k = self.unencode(k)
        v = self.unencode(v)
      end
      accu << [k, v]
      accu
    end)
  else
    raise ParseError,
      'Parsing non-OAuth Authorization headers is out of scope.'
  end
end
parse_form_encoded_credentials(body) click to toggle source

Parses an application/x-www-form-urlencoded HTTP response body into an OAuth key/secret pair.

@param [String] body The response body.

@return [Signet::OAuth1::Credential] The OAuth credentials.

# File lib/signet/oauth_1.rb, line 256
def self.parse_form_encoded_credentials(body)
  if !body.kind_of?(String)
    raise TypeError, "Expected String, got #{body.class}."
  end
  return Signet::OAuth1::Credential.new(
    Addressable::URI.form_unencode(body)
  )
end
sign_parameters(method, uri, parameters, client_credential_secret, token_credential_secret=nil) click to toggle source

Generates an OAuth signature using the signature method indicated in the parameter list. Unsupported signature methods will result in a NotImplementedError exception being raised.

@param [String] method The HTTP method. @param [Addressable::URI, String, to_str] uri The URI. @param [Enumerable] parameters The OAuth parameter list. @param [String] client_credential_secret The client credential secret. @param [String] token_credential_secret

The token credential secret.  Omitted when unavailable.

@return [String] The signature.

# File lib/signet/oauth_1.rb, line 278
def self.sign_parameters(method, uri, parameters,
    client_credential_secret, token_credential_secret=nil)
  # Technically, the token_credential_secret parameter here may actually
  # be a temporary credential secret when obtaining a token credential
  # for the first time
  base_string = self.generate_base_string(method, uri, parameters)
  parameters = parameters.inject({}) { |h,(k,v)| h[k.to_s]=v; h }
  signature_method = parameters['oauth_signature_method']
  case signature_method
  when 'HMAC-SHA1'
    require 'signet/oauth_1/signature_methods/hmac_sha1'
    return Signet::OAuth1::HMACSHA1.generate_signature(
      base_string, client_credential_secret, token_credential_secret
    )
  when 'RSA-SHA1'
    require 'signet/oauth_1/signature_methods/rsa_sha1'
    return Signet::OAuth1::RSASHA1.generate_signature(
      base_string, client_credential_secret, token_credential_secret
    )
  when 'PLAINTEXT'
    require 'signet/oauth_1/signature_methods/plaintext'
    return Signet::OAuth1::PLAINTEXT.generate_signature(
      base_string, client_credential_secret, token_credential_secret
    )
  else
    raise NotImplementedError,
      "Unsupported signature method: #{signature_method}"
  end
end
unencode(value) click to toggle source

Converts a percent-encoded String to an unencoded value.

@param [#to_str] value

The percent-encoded <code>String</code> to be unencoded.

@return [String] The unencoded value.

# File lib/signet/oauth_1.rb, line 33
def self.unencode(value)
  return Addressable::URI.unencode_component(value)
end
unsigned_resource_parameters(options={}) click to toggle source

Generates an OAuth parameter list to be used when requesting a protected resource.

@param [Hash] options

The configuration parameters for the request.
- <code>:client_credential_key</code> -
  The client credential key.
- <code>:token_credential_key</code> -
  The token credential key.
- <code>:signature_method</code> -
  The signature method.  Defaults to <code>'HMAC-SHA1'</code>.
- <code>:two_legged</code> -
  A switch for two-legged OAuth.  Defaults to <code>false</code>.

@return [Array]

The parameter list as an <code>Array</code> of key/value pairs.
# File lib/signet/oauth_1.rb, line 448
def self.unsigned_resource_parameters(options={})
  options = {
    :signature_method => 'HMAC-SHA1',
    :two_legged => false
  }.merge(options)
  client_credential_key =
    self.extract_credential_key_option(:client, options)
  if client_credential_key == nil
    raise ArgumentError, "Missing :client_credential_key parameter."
  end
  unless options[:two_legged]
    token_credential_key =
      self.extract_credential_key_option(:token, options)
    if token_credential_key == nil
      raise ArgumentError, "Missing :token_credential_key parameter."
    end
  end
  parameters = [
    ["oauth_consumer_key", client_credential_key],
    ["oauth_signature_method", options[:signature_method]],
    ["oauth_timestamp", self.generate_timestamp()],
    ["oauth_nonce", self.generate_nonce()],
    ["oauth_version", "1.0"]
  ]
  unless options[:two_legged]
    parameters << ["oauth_token", token_credential_key]
  end
  # No additional parameters allowed here
  return parameters
end
unsigned_temporary_credential_parameters(options={}) click to toggle source

Generates an OAuth parameter list to be used when obtaining a set of temporary credentials.

@param [Hash] options

The configuration parameters for the request.
- <code>:client_credential_key</code> -
  The client credential key.
- <code>:callback</code> -
  The OAuth callback.  Defaults to {Signet::OAuth1::OUT_OF_BAND}.
- <code>:signature_method</code> -
  The signature method.  Defaults to <code>'HMAC-SHA1'</code>.
- <code>:additional_parameters</code> -
  Non-standard additional parameters.

@return [Array]

The parameter list as an <code>Array</code> of key/value pairs.
# File lib/signet/oauth_1.rb, line 325
def self.unsigned_temporary_credential_parameters(options={})
  options = {
    :callback => ::Signet::OAuth1::OUT_OF_BAND,
    :signature_method => 'HMAC-SHA1',
    :additional_parameters => []
  }.merge(options)
  client_credential_key =
    self.extract_credential_key_option(:client, options)
  if client_credential_key == nil
    raise ArgumentError, "Missing :client_credential_key parameter."
  end
  parameters = [
    ["oauth_consumer_key", client_credential_key],
    ["oauth_signature_method", options[:signature_method]],
    ["oauth_timestamp", self.generate_timestamp()],
    ["oauth_nonce", self.generate_nonce()],
    ["oauth_version", "1.0"],
    ["oauth_callback", options[:callback]]
  ]
  # Works for any Enumerable
  options[:additional_parameters].each do |key, value|
    parameters << [key, value]
  end
  return parameters
end
unsigned_token_credential_parameters(options={}) click to toggle source

Generates an OAuth parameter list to be used when obtaining a set of token credentials.

@param [Hash] options

The configuration parameters for the request.
- <code>:client_credential_key</code> -
  The client credential key.
- <code>:temporary_credential_key</code> -
  The temporary credential key.
- <code>:verifier</code> -
  The OAuth verifier.
- <code>:signature_method</code> -
  The signature method.  Defaults to <code>'HMAC-SHA1'</code>.

@return [Array]

The parameter list as an <code>Array</code> of key/value pairs.
# File lib/signet/oauth_1.rb, line 400
def self.unsigned_token_credential_parameters(options={})
  options = {
    :signature_method => 'HMAC-SHA1',
    :verifier => nil
  }.merge(options)
  client_credential_key =
    self.extract_credential_key_option(:client, options)
  temporary_credential_key =
    self.extract_credential_key_option(:temporary, options)
  if client_credential_key == nil
    raise ArgumentError, "Missing :client_credential_key parameter."
  end
  if temporary_credential_key == nil
    raise ArgumentError, "Missing :temporary_credential_key parameter."
  end
  if options[:verifier] == nil
    raise ArgumentError, "Missing :verifier parameter."
  end
  parameters = [
    ["oauth_consumer_key", client_credential_key],
    ["oauth_token", temporary_credential_key],
    ["oauth_signature_method", options[:signature_method]],
    ["oauth_timestamp", self.generate_timestamp()],
    ["oauth_nonce", self.generate_nonce()],
    ["oauth_verifier", options[:verifier]],
    ["oauth_version", "1.0"]
  ]
  # No additional parameters allowed here
  return parameters
end