class Signet::OAuth1::Server
Attributes
@return [Proc] lookup the value from this Proc.
@return [Proc] lookup the value from this Proc.
@return [Proc] lookup the value from this Proc.
@return [Proc] lookup the value from this Proc.
@return [Proc] lookup the value from this Proc.
Public Class Methods
Creates an OAuth 1.0 server. @overload initialize(options)
@param [Proc] nonce_timestamp verify a nonce/timestamp pair. @param [Proc] client_credential find a client credential. @param [Proc] token_credential find a token credential. @param [Proc] temporary_credential find a temporary credential. @param [Proc] verifier validate a verifier value.
@example
server = Signet::OAuth1::Server.new( :nonce_timestamp => lambda { |n,t| OauthNonce.remember(n,t) }, :client_credential => lambda { |key| ClientCredential.find_by_key(key).to_hash }, :token_credential => lambda { |key| TokenCredential.find_by_key(key).to_hash }, :temporary_credential => lambda { |key| TemporaryCredential.find_by_key(key).to_hash }, :verifier => lambda {|verifier| Verifier.find_by_verifier(verifier).active? } )
# File lib/signet/oauth_1/server.rb, line 54 def initialize(options={}) [:nonce_timestamp, :client_credential, :token_credential, :temporary_credential, :verifier].each do |attr| instance_variable_set("@#{attr}", options[attr]) end end
Public Instance Methods
Authenticates a request for a protected resource. @overload authenticate_resource_request
(options)
@param [Hash] request The configuration parameters for the request. @param [String] method the HTTP method , defaults to <code>GET</code> @param [Addressable::URI, String] uri the URI . @param [Hash, Array] headers the HTTP headers. @param [StringIO, String] body The HTTP body. @param [Boolean] two_legged skip the token_credential lookup? @param [HTTPAdapter] adapter The HTTP adapter(optional).
@return [Hash] A hash of the credentials and realm for a valid request,
or <code>nil</code> if not valid.
# File lib/signet/oauth_1/server.rb, line 389 def authenticate_resource_request(options={}) verifications = { :client_credential => lambda do |x| ::Signet::OAuth1::Credential.new('Client credential key', 'Client credential secret') end } unless(options[:two_legged] == true) verifications.update( :token_credential => lambda do |x| ::Signet::OAuth1::Credential.new('Token credential key', 'Token credential secret') end ) end # Make sure all required state is set verifications.each do |(key, _value)| unless self.send(key) raise ArgumentError, "#{key} was not set." end end if(options[:request]) request_components = verify_request_components( :request=>options[:request], :adapter=>options[:adapter] ) else request_components = verify_request_components( :method=>options[:method], :uri=>options[:uri], :headers=>options[:headers], :body=>options[:body] ) end method = request_components[:method] uri = request_components[:uri] headers = request_components[:headers] body = request_components[:body] if !body.kind_of?(String) && body.respond_to?(:each) # Just in case we get a chunked body merged_body = StringIO.new body.each do |chunk| merged_body.write(chunk) end body = merged_body.string end if !body.kind_of?(String) raise TypeError, "Expected String, got #{body.class}." end media_type = nil headers.each do |(header, value)| if header.downcase == 'Content-Type'.downcase media_type = value.gsub(/^([^;]+)(;.*?)?$/, '\1') end end auth_hash = verify_auth_header_components(headers) auth_token = auth_hash['oauth_token'] unless(options[:two_legged]) return nil if(auth_token.nil?) return nil unless(token_credential = find_token_credential(auth_token)) token_credential_secret = token_credential.secret if token_credential end return nil unless(client_credential = find_client_credential(auth_hash['oauth_consumer_key'])) return nil unless validate_nonce_timestamp(auth_hash['oauth_nonce'], auth_hash['oauth_timestamp']) if(method == ('POST' || 'PUT') && media_type == 'application/x-www-form-urlencoded') request_components[:body] = body post_parameters = Addressable::URI.form_unencode(body) post_parameters.each {|param| param[1] = "" if param[1].nil?} # If the auth header doesn't have the same params as the body, it # can't have been signed correctly(5849#3.4.1.3) unless(post_parameters.sort == auth_hash.reject{|k,v| k.index('oauth_')}.to_a.sort) raise MalformedAuthorizationError.new( 'Request is of type application/x-www-form-urlencoded ' + 'but Authentication header did not include form values' ) end end client_credential_secret = client_credential.secret if client_credential computed_signature = ::Signet::OAuth1.sign_parameters( method, uri, # Realm isn't used, and will throw the signature off. auth_hash.reject{|k,v| k=='realm'}.to_a, client_credential_secret, token_credential_secret ) if(computed_signature == auth_hash['oauth_signature']) {:client_credential=>client_credential, :token_credential=>token_credential, :realm=>auth_hash['realm'] } else nil end end
Authenticates a temporary credential request. If no oauth_callback is present in the request, oob
will be returned.
@overload authenticate_temporary_credential_request
(options)
@param [Hash] request The configuration parameters for the request. @param [String] method the HTTP method , defaults to <code>GET</code> @param [Addressable::URI, String] uri the URI . @param [Hash, Array] headers the HTTP headers. @param [StringIO, String] body The HTTP body. @param [HTTPAdapter] adapter The HTTP adapter(optional).
@return [String] The oauth_callback value, or false
if not valid.
# File lib/signet/oauth_1/server.rb, line 245 def authenticate_temporary_credential_request(options={}) verifications = { :client_credential => lambda { |x| ::Signet::OAuth1::Credential.new('Client credential key', 'Client credential secret' ) } } verifications.each do |(key, _value)| raise ArgumentError, "#{key} was not set." unless self.send(key) end if(options[:request]) request_components = verify_request_components( :request=>options[:request], :adapter=>options[:adapter] ) else request_components = verify_request_components( :method=>options[:method], :uri=>options[:uri], :headers=>options[:headers] ) end # body should be blank; we don't care in any case. method = request_components[:method] uri = request_components[:uri] headers = request_components[:headers] auth_hash = verify_auth_header_components(headers) return false unless(client_credential = find_client_credential( auth_hash['oauth_consumer_key']) ) return false unless validate_nonce_timestamp(auth_hash['oauth_nonce'], auth_hash['oauth_timestamp']) client_credential_secret = client_credential.secret if client_credential computed_signature = ::Signet::OAuth1.sign_parameters( method, uri, # Realm isn't used, and will throw the signature off. auth_hash.reject{|k,v| k=='realm'}.to_a, client_credential_secret, nil ) if(computed_signature == auth_hash['oauth_signature']) if(auth_hash.fetch('oauth_callback', 'oob').empty?) 'oob' else auth_hash.fetch('oauth_callback') end else false end end
Authenticates a token credential request. @overload authenticate_token_credential_request
(options)
@param [Hash] request The configuration parameters for the request. @param [String] method the HTTP method , defaults to <code>GET</code> @param [Addressable::URI, String] uri the URI . @param [Hash, Array] headers the HTTP headers. @param [StringIO, String] body The HTTP body. @param [HTTPAdapter] adapter The HTTP adapter(optional).
@return [Hash] A hash of credentials and realm for a valid request,
or <code>nil</code> if not valid.
# File lib/signet/oauth_1/server.rb, line 311 def authenticate_token_credential_request(options={}) verifications = { :client_credential => lambda {|x| ::Signet::OAuth1::Credential.new('Client credential key', 'Client credential secret') }, :temporary_credential => lambda {|x| ::Signet::OAuth1::Credential.new('Temporary credential key', 'Temporary credential secret') }, :verifier => lambda {|x| 'Verifier' } } verifications.each do |(key, _value)| unless self.send(key) raise ArgumentError, "#{key} was not set." end end if(options[:request]) request_components = verify_request_components( :request=>options[:request], :adapter=>options[:adapter] ) else request_components = verify_request_components( :method=>options[:method], :uri=>options[:uri], :headers=>options[:headers], :body=>options[:body] ) end # body should be blank; we don't care in any case. method = request_components[:method] uri = request_components[:uri] headers = request_components[:headers] auth_hash = verify_auth_header_components(headers) return false unless( client_credential = find_client_credential(auth_hash['oauth_consumer_key']) ) return false unless( temporary_credential = find_temporary_credential(auth_hash['oauth_token']) ) return false unless validate_nonce_timestamp( auth_hash['oauth_nonce'], auth_hash['oauth_timestamp']) computed_signature = ::Signet::OAuth1.sign_parameters( method, uri, # Realm isn't used, and will throw the signature off. auth_hash.reject{|k,v| k=='realm'}.to_a, client_credential.secret, temporary_credential.secret ) if(computed_signature == auth_hash['oauth_signature']) {:client_credential=>client_credential, :temporary_credential=>temporary_credential, :realm=>auth_hash['realm'] } else nil end end
Call a credential lookup, and cast the result to a proper Credential
.
@param [Proc] credential to call. @param [String] key provided to the Proc in credential
@return [Signet::OAuth1::Credential] credential provided by
<code>credential</code> (if any).
# File lib/signet/oauth_1/server.rb, line 112 def call_credential_lookup(credential, key) cred = credential.call(key) if credential.respond_to?(:call) return nil if cred.nil? return nil unless (cred.respond_to?(:to_str) || cred.respond_to?(:to_ary) || cred.respond_to?(:to_hash) ) if(cred.instance_of?(::Signet::OAuth1::Credential)) cred else ::Signet::OAuth1::Credential.new(cred) end end
Find the appropriate client credential by calling the {#client_credential} Proc.
@param [String] key provided to the {#client_credential} Proc. @return [Signet::OAuth1::Credential] The client credential.
# File lib/signet/oauth_1/server.rb, line 81 def find_client_credential(key) call_credential_lookup(@client_credential, key) end
Find the appropriate client credential by calling the {#temporary_credential} Proc.
@param [String] key provided to the {#temporary_credential} Proc. @return [Signet::OAuth1::Credential] if the credential is found.
# File lib/signet/oauth_1/server.rb, line 101 def find_temporary_credential(key) call_credential_lookup(@temporary_credential, key) end
Find the appropriate client credential by calling the {#token_credential} Proc.
@param [String] key provided to the {#token_credential} Proc. @return [Signet::OAuth1::Credential] if the credential is found.
# File lib/signet/oauth_1/server.rb, line 91 def find_token_credential(key) call_credential_lookup(@token_credential, key) end
Determine if the verifier is valid by calling the Proc in {#verifier}.
@param [String] verifier Key provided to the {#verifier} Proc. @return [Boolean] if the verifier Proc returns anything other than
<code>nil</code> or <code>false</code>.
# File lib/signet/oauth_1/server.rb, line 132 def find_verifier(verifier) verified = @verifier.call(verifier) if @verifier.respond_to?(:call) verified ? true : false end
@overload request_realm
(options)
@param [Hash] request A pre-constructed request to verify. @param [String] method the HTTP method , defaults to <code>GET</code> @param [Addressable::URI, String] uri the URI . @param [Hash, Array] headers the HTTP headers. @param [StringIO, String] body The HTTP body. @param [HTTPAdapter] adapter The HTTP adapter(optional).
@return [String] The Authorization realm(see RFC 2617) of the request.
# File lib/signet/oauth_1/server.rb, line 211 def request_realm(options={}) if(options[:request]) request_components = verify_request_components( :request=>options[:request], :adapter=>options[:adapter] ) else request_components = verify_request_components( :method=>options[:method], :uri=>options[:uri], :headers=>options[:headers], :body=>options[:body] ) end auth_header = request_components[:headers].find{|x| x[0] == 'Authorization'} if(auth_header.nil? || auth_header[1] == '') raise MalformedAuthorizationError.new('Authorization header is missing') end auth_hash = ::Signet::OAuth1.parse_authorization_header( auth_header[1] ).inject({}) {|acc, (key,val)| acc[key.downcase] = val; acc} auth_hash['realm'] end
Determine if the supplied nonce/timestamp pair is valid by calling the {#nonce_timestamp} Proc.
@param [String, to_str] nonce value from the request @param [String, to_str] timestamp value from the request @return [Boolean] if the nonce/timestamp pair is valid.
# File lib/signet/oauth_1/server.rb, line 68 def validate_nonce_timestamp(nonce, timestamp) nonce = @nonce_timestamp.call(nonce, timestamp) if @nonce_timestamp.respond_to?(:call) nonce ? true : false end
Validate and normalize the HTTP Authorization header.
@param [Array] headers from HTTP request. @return [Hash] Hash of Authorization header.
# File lib/signet/oauth_1/server.rb, line 190 def verify_auth_header_components(headers) auth_header = headers.find{|x| x[0] == 'Authorization'} if(auth_header.nil? || auth_header[1] == '') raise MalformedAuthorizationError.new('Authorization header is missing') end auth_hash = ::Signet::OAuth1.parse_authorization_header( auth_header[1] ).inject({}) {|acc, (key,val)| acc[key.downcase] = val; acc} auth_hash end
Validate and normalize the components from an HTTP request. @overload verify_request_components
(options)
@param [Faraday::Request] request A pre-constructed request to verify. @param [String] method the HTTP method , defaults to <code>GET</code> @param [Addressable::URI, String] uri the URI . @param [Hash, Array] headers the HTTP headers. @param [StringIO, String] body The HTTP body. @param [HTTPAdapter] adapter The HTTP adapter(optional).
@return [Hash] normalized request components
# File lib/signet/oauth_1/server.rb, line 148 def verify_request_components(options={}) if options[:request] if options[:request].kind_of?(Faraday::Request) || options[:request].kind_of?(Array) request = options[:request] elsif options[:adapter] request = options[:adapter].adapt_request(options[:request]) end method = request.method uri = request.path headers = request.headers body = request.body else method = options[:method] || :get uri = options[:uri] headers = options[:headers] || [] body = options[:body] || '' end headers = headers.to_a if headers.kind_of?(Hash) method = method.to_s.upcase request_components = { :method => method, :uri => uri, :headers => headers } # Verify that we have all the pieces required to validate the HTTP request request_components.each do |(key, value)| unless value raise ArgumentError, "Missing :#{key} parameter." end end request_components[:body] = body request_components end