127 lines
3.3 KiB
Ruby
127 lines
3.3 KiB
Ruby
|
require 'openssl'
|
||
|
require 'uri'
|
||
|
require 'base64'
|
||
|
require 'cgi'
|
||
|
|
||
|
module SimpleOAuth
|
||
|
class Header
|
||
|
ATTRIBUTE_KEYS = [:callback, :consumer_key, :nonce, :signature_method, :timestamp, :token, :verifier, :version] unless defined? ::SimpleOAuth::Header::ATTRIBUTE_KEYS
|
||
|
attr_reader :method, :params, :options
|
||
|
|
||
|
class << self
|
||
|
def default_options
|
||
|
{
|
||
|
:nonce => OpenSSL::Random.random_bytes(16).unpack('H*')[0],
|
||
|
:signature_method => 'HMAC-SHA1',
|
||
|
:timestamp => Time.now.to_i.to_s,
|
||
|
:version => '1.0'
|
||
|
}
|
||
|
end
|
||
|
|
||
|
def parse(header)
|
||
|
header.to_s.sub(/^OAuth\s/, '').split(/,\s*/).inject({}) do |attributes, pair|
|
||
|
match = pair.match(/^(\w+)\=\"([^\"]*)\"$/)
|
||
|
attributes.merge(match[1].sub(/^oauth_/, '').to_sym => decode(match[2]))
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def escape(value)
|
||
|
uri_parser.escape(value.to_s, /[^a-z0-9\-\.\_\~]/i)
|
||
|
end
|
||
|
alias encode escape
|
||
|
|
||
|
def unescape(value)
|
||
|
uri_parser.unescape(value.to_s)
|
||
|
end
|
||
|
alias decode unescape
|
||
|
|
||
|
private
|
||
|
|
||
|
def uri_parser
|
||
|
@uri_parser ||= URI.const_defined?(:Parser) ? URI::Parser.new : URI
|
||
|
end
|
||
|
|
||
|
end
|
||
|
|
||
|
def initialize(method, url, params, oauth = {})
|
||
|
@method = method.to_s.upcase
|
||
|
@uri = URI.parse(url.to_s)
|
||
|
@uri.scheme = @uri.scheme.downcase
|
||
|
@uri.normalize!
|
||
|
@uri.fragment = nil
|
||
|
@params = params
|
||
|
@options = oauth.is_a?(Hash) ? self.class.default_options.merge(oauth) : self.class.parse(oauth)
|
||
|
end
|
||
|
|
||
|
def url
|
||
|
uri = @uri.dup
|
||
|
uri.query = nil
|
||
|
uri.to_s
|
||
|
end
|
||
|
|
||
|
def to_s
|
||
|
"OAuth #{normalized_attributes}"
|
||
|
end
|
||
|
|
||
|
def valid?(secrets = {})
|
||
|
original_options = options.dup
|
||
|
options.merge!(secrets)
|
||
|
valid = options[:signature] == signature
|
||
|
options.replace(original_options)
|
||
|
valid
|
||
|
end
|
||
|
|
||
|
def signed_attributes
|
||
|
attributes.merge(:oauth_signature => signature)
|
||
|
end
|
||
|
|
||
|
private
|
||
|
|
||
|
def normalized_attributes
|
||
|
signed_attributes.sort_by{|k,v| k.to_s }.map{|k,v| %(#{k}="#{self.class.encode(v)}") }.join(', ')
|
||
|
end
|
||
|
|
||
|
def attributes
|
||
|
ATTRIBUTE_KEYS.inject({}){|a,k| options[k] ? a.merge(:"oauth_#{k}" => options[k]) : a }
|
||
|
end
|
||
|
|
||
|
def signature
|
||
|
send(options[:signature_method].downcase.tr('-', '_') + '_signature')
|
||
|
end
|
||
|
|
||
|
def hmac_sha1_signature
|
||
|
Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::SHA1.new, secret, signature_base)).chomp.gsub(/\n/, '')
|
||
|
end
|
||
|
|
||
|
def secret
|
||
|
options.values_at(:consumer_secret, :token_secret).map{|v| self.class.encode(v) }.join('&')
|
||
|
end
|
||
|
alias_method :plaintext_signature, :secret
|
||
|
|
||
|
def signature_base
|
||
|
[method, url, normalized_params].map{|v| self.class.encode(v) }.join('&')
|
||
|
end
|
||
|
|
||
|
def normalized_params
|
||
|
signature_params.map{|p| p.map{|v| self.class.encode(v) } }.sort.map{|p| p.join('=') }.join('&')
|
||
|
end
|
||
|
|
||
|
def signature_params
|
||
|
attributes.to_a + params.to_a + url_params
|
||
|
end
|
||
|
|
||
|
def url_params
|
||
|
CGI.parse(@uri.query || '').inject([]){|p,(k,vs)| p + vs.sort.map{|v| [k, v] } }
|
||
|
end
|
||
|
|
||
|
def rsa_sha1_signature
|
||
|
Base64.encode64(private_key.sign(OpenSSL::Digest::SHA1.new, signature_base)).chomp.gsub(/\n/, '')
|
||
|
end
|
||
|
|
||
|
def private_key
|
||
|
OpenSSL::PKey::RSA.new(options[:consumer_secret])
|
||
|
end
|
||
|
|
||
|
end
|
||
|
end
|