pax_global_header00006660000000000000000000000064124501116350014510gustar00rootroot0000000000000052 comment=4a375da4e213667e356e98a9e2bc3c3b8b969d67 simple_oauth-0.3.1/000077500000000000000000000000001245011163500142025ustar00rootroot00000000000000simple_oauth-0.3.1/.gitignore000066400000000000000000000001171245011163500161710ustar00rootroot00000000000000*.rbc .DS_Store .bundle .yardoc Gemfile.lock coverage doc measurement pkg rdoc simple_oauth-0.3.1/.rspec000066400000000000000000000000271245011163500153160ustar00rootroot00000000000000--color --order random simple_oauth-0.3.1/.rubocop.yml000066400000000000000000000015471245011163500164630ustar00rootroot00000000000000Metrics/AbcSize: Max: 16 Metrics/BlockNesting: Max: 1 Metrics/LineLength: AllowURI: true Enabled: false Metrics/MethodLength: CountComments: false Max: 7 Metrics/ParameterLists: Max: 4 CountKeywordArgs: true Style/AccessModifierIndentation: EnforcedStyle: outdent Style/CollectionMethods: PreferredMethods: map: 'collect' reduce: 'inject' find: 'detect' find_all: 'select' Style/Documentation: Enabled: false Style/DotPosition: EnforcedStyle: trailing Style/DoubleNegation: Enabled: false Style/EachWithObject: Enabled: false Style/Encoding: Enabled: false Style/HashSyntax: EnforcedStyle: hash_rockets Style/Lambda: Enabled: false Style/RaiseArgs: EnforcedStyle: compact Style/SpaceInsideHashLiteralBraces: EnforcedStyle: no_space Style/TrailingComma: EnforcedStyleForMultiline: 'comma' simple_oauth-0.3.1/.travis.yml000066400000000000000000000004571245011163500163210ustar00rootroot00000000000000language: ruby env: global: - JRUBY_OPTS="$JRUBY_OPTS --debug" rvm: - 1.8.7 - 1.9.3 - 2.0.0 - 2.1 - jruby-18mode - jruby-19mode - jruby-head - rbx-2 - ruby-head matrix: allow_failures: - rvm: jruby-head - rvm: rbx-2 - rvm: ruby-head fast_finish: true sudo: false simple_oauth-0.3.1/.yardopts000066400000000000000000000000711245011163500160460ustar00rootroot00000000000000--markup markdown - CONTRIBUTING.md LICENSE.md README.md simple_oauth-0.3.1/CONTRIBUTING.md000066400000000000000000000004331245011163500164330ustar00rootroot00000000000000## Contributing 1. Fork the project. 2. Create a topic branch. 3. Add failing tests. 4. Add code to pass the failing tests. 5. Run `bundle exec rake`. If failing, repeat step 4. 6. Commit and push your changes. 7. Submit a pull request. Please do not include changes to the gemspec. simple_oauth-0.3.1/Gemfile000066400000000000000000000006471245011163500155040ustar00rootroot00000000000000source 'https://rubygems.org' gem 'jruby-openssl', :platforms => :jruby gem 'rake' group :test do gem 'backports' gem 'coveralls' gem 'mime-types', '~> 1.25', :platforms => [:jruby, :ruby_18] gem 'rest-client', '~> 1.6.0', :platforms => [:jruby, :ruby_18] gem 'rspec', '>= 2.14' gem 'rubocop', '>= 0.28', :platforms => [:ruby_19, :ruby_20, :ruby_21] gem 'simplecov', '>= 0.9' gem 'yardstick' end gemspec simple_oauth-0.3.1/LICENSE.md000066400000000000000000000020721245011163500156070ustar00rootroot00000000000000Copyright (c) 2010-2013 Steve Richert, Erik Michaels-Ober Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. simple_oauth-0.3.1/README.md000066400000000000000000000034471245011163500154710ustar00rootroot00000000000000# simple_oauth [![Gem Version](http://img.shields.io/gem/v/simple_oauth.svg)][gem] [![Build Status](http://img.shields.io/travis/laserlemon/simple_oauth.svg)][travis] [![Dependency Status](http://img.shields.io/gemnasium/laserlemon/simple_oauth.svg)][gemnasium] [![Code Climate](http://img.shields.io/codeclimate/github/laserlemon/simple_oauth.svg)][codeclimate] [![Coverage Status](http://img.shields.io/coveralls/laserlemon/simple_oauth.svg)][coveralls] [gem]: https://rubygems.org/gems/simple_oauth [travis]: http://travis-ci.org/laserlemon/simple_oauth [gemnasium]: https://gemnasium.com/laserlemon/simple_oauth [codeclimate]: https://codeclimate.com/github/laserlemon/simple_oauth [coveralls]: https://coveralls.io/r/laserlemon/simple_oauth Simply builds and verifies OAuth headers ## Supported Rubies This library aims to support and is [tested against](http://travis-ci.org/laserlemon/simple_oauth) the following Ruby implementations: * Ruby 1.8.7 * Ruby 1.9.3 * Ruby 2.0.0 * Ruby 2.1 * [JRuby](http://jruby.org/) * [Rubinius](http://rubini.us/) If something doesn't work on one of these interpreters, it's a bug. This library may inadvertently work (or seem to work) on other Ruby implementations, however support will only be provided for the versions listed above. If you would like this library to support another Ruby version, you may volunteer to be a maintainer. Being a maintainer entails making sure all tests run and pass on that implementation. When something breaks on your implementation, you will be responsible for providing patches in a timely fashion. If critical issues for a particular implementation exist at the time of a major release, support for that Ruby version may be dropped. ## Copyright Copyright (c) 2010-2013 Steve Richert, Erik Michaels-Ober. See [LICENSE](LICENSE.md) for details. simple_oauth-0.3.1/Rakefile000066400000000000000000000010571245011163500156520ustar00rootroot00000000000000require 'bundler/gem_tasks' require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) task :test => :spec begin require 'rubocop/rake_task' RuboCop::RakeTask.new rescue LoadError task :rubocop do $stderr.puts 'RuboCop is disabled' end end require 'yardstick/rake/measurement' Yardstick::Rake::Measurement.new do |measurement| measurement.output = 'measurement/report.txt' end require 'yardstick/rake/verify' Yardstick::Rake::Verify.new do |verify| verify.threshold = 47 end task :default => [:spec, :rubocop, :verify_measurements] simple_oauth-0.3.1/lib/000077500000000000000000000000001245011163500147505ustar00rootroot00000000000000simple_oauth-0.3.1/lib/simple_oauth.rb000066400000000000000000000000361245011163500177650ustar00rootroot00000000000000require 'simple_oauth/header' simple_oauth-0.3.1/lib/simple_oauth/000077500000000000000000000000001245011163500174415ustar00rootroot00000000000000simple_oauth-0.3.1/lib/simple_oauth/header.rb000066400000000000000000000075131245011163500212240ustar00rootroot00000000000000require '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 IGNORED_KEYS = [:consumer_secret, :token_secret, :signature] unless defined? ::SimpleOAuth::Header::IGNORED_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 => unescape(match[2])) end end def escape(value) uri_parser.escape(value.to_s, /[^a-z0-9\-\.\_\~]/i) end alias_method :encode, :escape def unescape(value) uri_parser.unescape(value.to_s) end alias_method :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, _| k.to_s }.collect { |k, v| %(#{k}="#{self.class.escape(v)}") }.join(', ') end def attributes matching_keys, extra_keys = options.keys.partition { |key| ATTRIBUTE_KEYS.include?(key) } extra_keys -= IGNORED_KEYS if options[:ignore_extra_keys] || extra_keys.empty? Hash[options.select { |key, _| matching_keys.include?(key) }.collect { |key, value| [:"oauth_#{key}", value] }] else fail "SimpleOAuth: Found extra option keys not matching ATTRIBUTE_KEYS:\n [#{extra_keys.collect(&:inspect).join(', ')}]" end 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).collect { |v| self.class.escape(v) }.join('&') end alias_method :plaintext_signature, :secret def signature_base [method, url, normalized_params].collect { |v| self.class.escape(v) }.join('&') end def normalized_params signature_params.collect { |p| p.collect { |v| self.class.escape(v) } }.sort.collect { |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.collect { |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 simple_oauth-0.3.1/simple_oauth.gemspec000066400000000000000000000011251245011163500202370ustar00rootroot00000000000000Gem::Specification.new do |spec| spec.add_development_dependency 'bundler', '~> 1.0' spec.name = 'simple_oauth' spec.version = '0.3.1' spec.authors = ['Steve Richert', 'Erik Michaels-Ober'] spec.email = %w(steve.richert@gmail.com sferik@gmail.com) spec.description = 'Simply builds and verifies OAuth headers' spec.summary = spec.description spec.homepage = 'https://github.com/laserlemon/simple_oauth' spec.licenses = %w(MIT) spec.files = `git ls-files -z`.split("\x0").reject { |f| f.start_with?('spec/') } spec.require_paths = %w(lib) end simple_oauth-0.3.1/spec/000077500000000000000000000000001245011163500151345ustar00rootroot00000000000000simple_oauth-0.3.1/spec/helper.rb000066400000000000000000000011141245011163500167350ustar00rootroot00000000000000if RUBY_VERSION >= '1.9' require 'simplecov' require 'coveralls' SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[SimpleCov::Formatter::HTMLFormatter, Coveralls::SimpleCov::Formatter] SimpleCov.start do add_filter '/spec/' minimum_coverage(100) end end require 'simple_oauth' require 'rspec' def uri_parser @uri_parser ||= URI.const_defined?(:Parser) ? URI::Parser.new : URI end RSpec.configure do |config| config.expect_with :rspec do |c| c.syntax = :expect end end Dir[File.expand_path('../support/**/*.rb', __FILE__)].each { |f| require f } simple_oauth-0.3.1/spec/simple_oauth/000077500000000000000000000000001245011163500176255ustar00rootroot00000000000000simple_oauth-0.3.1/spec/simple_oauth/header_spec.rb000066400000000000000000000417001245011163500224160ustar00rootroot00000000000000# encoding: utf-8 require 'helper' describe SimpleOAuth::Header do describe '.default_options' do let(:default_options) { SimpleOAuth::Header.default_options } it 'is different every time' do expect(SimpleOAuth::Header.default_options).not_to eq default_options end it 'is used for new headers' do allow(SimpleOAuth::Header).to receive(:default_options).and_return(default_options) header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {}) expect(header.options).to eq default_options end it 'includes a signature method and an OAuth version' do expect(default_options[:signature_method]).not_to be_nil expect(default_options[:version]).not_to be_nil end end describe '.escape' do it 'escapes (most) non-word characters' do [' ', '!', '@', '#', '$', '%', '^', '&'].each do |character| escaped = SimpleOAuth::Header.escape(character) expect(escaped).not_to eq character expect(escaped).to eq uri_parser.escape(character, /.*/) end end it 'does not escape - . or ~' do ['-', '.', '~'].each do |character| escaped = SimpleOAuth::Header.escape(character) expect(escaped).to eq character end end def self.test_special_characters it 'escapes non-ASCII characters' do expect(SimpleOAuth::Header.escape('é')).to eq '%C3%A9' end it 'escapes multibyte characters' do expect(SimpleOAuth::Header.escape('あ')).to eq '%E3%81%82' end end if RUBY_VERSION >= '1.9' test_special_characters else %w(n N e E s S u U).each do |kcode| describe %(when $KCODE = "#{kcode}") do original_kcode = $KCODE # rubocop:disable GlobalVars begin $KCODE = kcode # rubocop:disable GlobalVars test_special_characters ensure $KCODE = original_kcode # rubocop:disable GlobalVars end end end end end describe '.unescape' do pending end describe '.parse' do let(:header) { SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}) } let(:parsed_options) { SimpleOAuth::Header.parse(header) } it 'returns a hash' do expect(parsed_options).to be_a(Hash) end it 'includes the options used to build the header' do expect(parsed_options.reject { |k, _| k == :signature }).to eq header.options end it 'includes a signature' do expect(header.options).not_to have_key(:signature) expect(parsed_options).to have_key(:signature) expect(parsed_options[:signature]).not_to be_nil end it "handles optional 'linear white space'" do parsed_header_with_spaces = SimpleOAuth::Header.parse 'OAuth oauth_consumer_key="abcd", oauth_nonce="oLKtec51GQy", oauth_signature="efgh%26mnop", oauth_signature_method="PLAINTEXT", oauth_timestamp="1286977095", oauth_token="ijkl", oauth_version="1.0"' expect(parsed_header_with_spaces).to be_a_kind_of(Hash) expect(parsed_header_with_spaces.keys.size).to eq 7 parsed_header_with_tabs = SimpleOAuth::Header.parse 'OAuth oauth_consumer_key="abcd", oauth_nonce="oLKtec51GQy", oauth_signature="efgh%26mnop", oauth_signature_method="PLAINTEXT", oauth_timestamp="1286977095", oauth_token="ijkl", oauth_version="1.0"' expect(parsed_header_with_tabs).to be_a_kind_of(Hash) expect(parsed_header_with_tabs.keys.size).to eq 7 parsed_header_with_spaces_and_tabs = SimpleOAuth::Header.parse 'OAuth oauth_consumer_key="abcd", oauth_nonce="oLKtec51GQy", oauth_signature="efgh%26mnop", oauth_signature_method="PLAINTEXT", oauth_timestamp="1286977095", oauth_token="ijkl", oauth_version="1.0"' expect(parsed_header_with_spaces_and_tabs).to be_a_kind_of(Hash) expect(parsed_header_with_spaces_and_tabs.keys.size).to eq 7 parsed_header_without_spaces = SimpleOAuth::Header.parse 'OAuth oauth_consumer_key="abcd",oauth_nonce="oLKtec51GQy",oauth_signature="efgh%26mnop",oauth_signature_method="PLAINTEXT",oauth_timestamp="1286977095",oauth_token="ijkl",oauth_version="1.0"' expect(parsed_header_without_spaces).to be_a_kind_of(Hash) expect(parsed_header_without_spaces.keys.size).to eq 7 end end describe '#initialize' do let(:header) { SimpleOAuth::Header.new(:get, 'HTTPS://api.TWITTER.com:443/1/statuses/friendships.json?foo=bar#anchor', {}) } it 'stringifies and uppercases the request method' do expect(header.method).to eq 'GET' end it 'downcases the scheme and authority' do expect(header.url).to match %r{^https://api\.twitter\.com/} end it 'ignores the query and fragment' do expect(header.url).to match %r{/1/statuses/friendships\.json$} end end describe '#valid?' do context 'using the HMAC-SHA1 signature method' do it 'requires consumer and token secrets' do secrets = {:consumer_secret => 'CONSUMER_SECRET', :token_secret => 'TOKEN_SECRET'} header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, secrets) parsed_header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, header) expect(parsed_header).not_to be_valid expect(parsed_header).to be_valid(secrets) end end context 'using the RSA-SHA1 signature method' do it 'requires an identical private key' do secrets = {:consumer_secret => rsa_private_key} header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, secrets.merge(:signature_method => 'RSA-SHA1')) parsed_header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, header) expect { parsed_header.valid? }.to raise_error expect(parsed_header).to be_valid(secrets) end end context 'using the PLAINTEXT signature method' do it 'requires consumer and token secrets' do secrets = {:consumer_secret => 'CONSUMER_SECRET', :token_secret => 'TOKEN_SECRET'} header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, secrets.merge(:signature_method => 'PLAINTEXT')) parsed_header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, header) expect(parsed_header).not_to be_valid expect(parsed_header).to be_valid(secrets) end end end describe '#normalized_attributes' do let(:header) { SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}) } let(:normalized_attributes) { header.send(:normalized_attributes) } it 'returns a sorted-key, quoted-value and comma-separated list' do allow(header).to receive(:signed_attributes).and_return(:d => 1, :c => 2, :b => 3, :a => 4) expect(normalized_attributes).to eq 'a="4", b="3", c="2", d="1"' end it 'URI encodes its values' do allow(header).to receive(:signed_attributes).and_return(1 => '!', 2 => '@', 3 => '#', 4 => '$') expect(normalized_attributes).to eq '1="%21", 2="%40", 3="%23", 4="%24"' end end describe '#signed_attributes' do it 'includes the OAuth signature' do header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}) expect(header.send(:signed_attributes)).to have_key(:oauth_signature) end end describe '#attributes' do let(:header) do options = {} SimpleOAuth::Header::ATTRIBUTE_KEYS.each { |k| options[k] = k.to_s.upcase } options[:other] = 'OTHER' SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {}, options) end it "prepends keys with 'oauth_'" do header.options[:ignore_extra_keys] = true expect(header.send(:attributes).keys).to be_all { |k| k.to_s =~ /^oauth_/ } end it 'excludes keys not included in the list of valid attributes' do header.options[:ignore_extra_keys] = true expect(header.send(:attributes).keys).to be_all { |k| k.is_a?(Symbol) } expect(header.send(:attributes)).not_to have_key(:oauth_other) end it 'preserves values for valid keys' do header.options[:ignore_extra_keys] = true expect(header.send(:attributes).size).to eq SimpleOAuth::Header::ATTRIBUTE_KEYS.size expect(header.send(:attributes)).to be_all { |k, v| k.to_s == "oauth_#{v.downcase}" } end it 'raises exception for extra keys' do expect { header.send(:attributes) }.to raise_error(RuntimeError, "SimpleOAuth: Found extra option keys not matching ATTRIBUTE_KEYS:\n [:other]") end end describe '#signature' do context 'calls the appropriate signature method' do specify 'when using HMAC-SHA1' do header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, :signature_method => 'HMAC-SHA1') expect(header).to receive(:hmac_sha1_signature).once.and_return('HMAC_SHA1_SIGNATURE') expect(header.send(:signature)).to eq 'HMAC_SHA1_SIGNATURE' end specify 'when using RSA-SHA1' do header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, :signature_method => 'RSA-SHA1') expect(header).to receive(:rsa_sha1_signature).once.and_return('RSA_SHA1_SIGNATURE') expect(header.send(:signature)).to eq 'RSA_SHA1_SIGNATURE' end specify 'when using PLAINTEXT' do header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, :signature_method => 'PLAINTEXT') expect(header).to receive(:plaintext_signature).once.and_return('PLAINTEXT_SIGNATURE') expect(header.send(:signature)).to eq 'PLAINTEXT_SIGNATURE' end end end describe '#hmac_sha1_signature' do it 'reproduces a successful Twitter GET' do options = { :consumer_key => '8karQBlMg6gFOwcf8kcoYw', :consumer_secret => '3d0vcHyUiiqADpWxolW8nlDIpSWMlyK7YNgc5Qna2M', :nonce => '547fed103e122eecf84c080843eedfe6', :signature_method => 'HMAC-SHA1', :timestamp => '1286830180', :token => '201425800-Sv4sTcgoffmHGkTCue0JnURT8vrm4DiFAkeFNDkh', :token_secret => 'T5qa1tF57tfDzKmpM89DHsNuhgOY4NT6DlNLsTFcuQ', } header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, options) expect(header.to_s).to eq 'OAuth oauth_consumer_key="8karQBlMg6gFOwcf8kcoYw", oauth_nonce="547fed103e122eecf84c080843eedfe6", oauth_signature="i9CT6ahDRAlfGX3hKYf78QzXsaw%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1286830180", oauth_token="201425800-Sv4sTcgoffmHGkTCue0JnURT8vrm4DiFAkeFNDkh", oauth_version="1.0"' end it 'reproduces a successful Twitter POST' do options = { :consumer_key => '8karQBlMg6gFOwcf8kcoYw', :consumer_secret => '3d0vcHyUiiqADpWxolW8nlDIpSWMlyK7YNgc5Qna2M', :nonce => 'b40a3e0f18590ecdcc0e273f7d7c82f8', :signature_method => 'HMAC-SHA1', :timestamp => '1286830181', :token => '201425800-Sv4sTcgoffmHGkTCue0JnURT8vrm4DiFAkeFNDkh', :token_secret => 'T5qa1tF57tfDzKmpM89DHsNuhgOY4NT6DlNLsTFcuQ', } header = SimpleOAuth::Header.new(:post, 'https://api.twitter.com/1/statuses/update.json', {:status => 'hi, again'}, options) expect(header.to_s).to eq 'OAuth oauth_consumer_key="8karQBlMg6gFOwcf8kcoYw", oauth_nonce="b40a3e0f18590ecdcc0e273f7d7c82f8", oauth_signature="mPqSFKejrWWk3ZT9bTQjhO5b2xI%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1286830181", oauth_token="201425800-Sv4sTcgoffmHGkTCue0JnURT8vrm4DiFAkeFNDkh", oauth_version="1.0"' end end describe '#secret' do let(:header) { SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {}) } let(:secret) { header.send(:secret) } it 'combines the consumer and token secrets with an ampersand' do allow(header).to receive(:options).and_return(:consumer_secret => 'CONSUMER_SECRET', :token_secret => 'TOKEN_SECRET') expect(secret).to eq 'CONSUMER_SECRET&TOKEN_SECRET' end it 'URI encodes each secret value before combination' do allow(header).to receive(:options).and_return(:consumer_secret => 'CONSUM#R_SECRET', :token_secret => 'TOKEN_S#CRET') expect(secret).to eq 'CONSUM%23R_SECRET&TOKEN_S%23CRET' end end describe '#signature_base' do let(:header) { SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {}) } let(:signature_base) { header.send(:signature_base) } it 'combines the request method, URL and normalized parameters using ampersands' do allow(header).to receive(:method).and_return('METHOD') allow(header).to receive(:url).and_return('URL') allow(header).to receive(:normalized_params).and_return('NORMALIZED_PARAMS') expect(signature_base).to eq 'METHOD&URL&NORMALIZED_PARAMS' end it 'URI encodes each value before combination' do allow(header).to receive(:method).and_return('ME#HOD') allow(header).to receive(:url).and_return('U#L') allow(header).to receive(:normalized_params).and_return('NORMAL#ZED_PARAMS') expect(signature_base).to eq 'ME%23HOD&U%23L&NORMAL%23ZED_PARAMS' end end describe '#normalized_params' do let(:header) do header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {}) allow(header).to receive(:signature_params).and_return([%w(A 4), %w(B 3), %w(B 2), %w(C 1), ['D[]', '0 ']]) header end let(:signature_params) { header.send(:signature_params) } let(:normalized_params) { header.send(:normalized_params) } it 'joins key/value pairs with equal signs and ampersands' do expect(normalized_params).to be_a(String) parts = normalized_params.split('&') expect(parts.size).to eq signature_params.size pairs = parts.collect { |p| p.split('=') } expect(pairs).to be_all { |p| p.size == 2 } end end describe '#signature_params' do let(:header) { SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {}) } let(:signature_params) { header.send(:signature_params) } it 'combines OAuth header attributes, body parameters and URL parameters into an flattened array of key/value pairs' do allow(header).to receive(:attributes).and_return(:attribute => 'ATTRIBUTE') allow(header).to receive(:params).and_return('param' => 'PARAM') allow(header).to receive(:url_params).and_return([%w(url_param 1), %w(url_param 2)]) expect(signature_params).to eq [ [:attribute, 'ATTRIBUTE'], %w(param PARAM), %w(url_param 1), %w(url_param 2), ] end end describe '#url_params' do it 'returns an empty array when the URL has no query parameters' do header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {}) expect(header.send(:url_params)).to eq [] end it 'returns an array of key/value pairs for each query parameter' do header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json?test=TEST', {}) expect(header.send(:url_params)).to eq [%w(test TEST)] end it 'sorts values for repeated keys' do header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json?test=3&test=1&test=2', {}) expect(header.send(:url_params)).to eq [%w(test 1), %w(test 2), %w(test 3)] end end describe '#rsa_sha1_signature' do it 'reproduces a successful OAuth example GET' do options = { :consumer_key => 'dpf43f3p2l4k3l03', :consumer_secret => rsa_private_key, :nonce => '13917289812797014437', :signature_method => 'RSA-SHA1', :timestamp => '1196666512', } header = SimpleOAuth::Header.new(:get, 'http://photos.example.net/photos', {:file => 'vacaction.jpg', :size => 'original'}, options) expect(header.to_s).to eq 'OAuth oauth_consumer_key="dpf43f3p2l4k3l03", oauth_nonce="13917289812797014437", oauth_signature="jvTp%2FwX1TYtByB1m%2BPbyo0lnCOLIsyGCH7wke8AUs3BpnwZJtAuEJkvQL2%2F9n4s5wUmUl4aCI4BwpraNx4RtEXMe5qg5T1LVTGliMRpKasKsW%2F%2Fe%2BRinhejgCuzoH26dyF8iY2ZZ%2F5D1ilgeijhV%2FvBka5twt399mXwaYdCwFYE%3D", oauth_signature_method="RSA-SHA1", oauth_timestamp="1196666512", oauth_version="1.0"' end end describe '#private_key' do pending end describe '#plaintext_signature' do it 'reproduces a successful OAuth example GET' do options = { :consumer_key => 'abcd', :consumer_secret => 'efgh', :nonce => 'oLKtec51GQy', :signature_method => 'PLAINTEXT', :timestamp => '1286977095', :token => 'ijkl', :token_secret => 'mnop', } header = SimpleOAuth::Header.new(:get, 'http://host.net/resource?name=value', {:name => 'value'}, options) expect(header.to_s).to eq 'OAuth oauth_consumer_key="abcd", oauth_nonce="oLKtec51GQy", oauth_signature="efgh%26mnop", oauth_signature_method="PLAINTEXT", oauth_timestamp="1286977095", oauth_token="ijkl", oauth_version="1.0"' end end end simple_oauth-0.3.1/spec/support/000077500000000000000000000000001245011163500166505ustar00rootroot00000000000000simple_oauth-0.3.1/spec/support/fixtures/000077500000000000000000000000001245011163500205215ustar00rootroot00000000000000simple_oauth-0.3.1/spec/support/fixtures/rsa-private-key000066400000000000000000000016231245011163500234710ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALRiMLAh9iimur8V A7qVvdqxevEuUkW4K+2KdMXmnQbG9Aa7k7eBjK1S+0LYmVjPKlJGNXHDGuy5Fw/d 7rjVJ0BLB+ubPK8iA/Tw3hLQgXMRRGRXXCn8ikfuQfjUS1uZSatdLB81mydBETlJ hI6GH4twrbDJCR2Bwy/XWXgqgGRzAgMBAAECgYBYWVtleUzavkbrPjy0T5FMou8H X9u2AC2ry8vD/l7cqedtwMPp9k7TubgNFo+NGvKsl2ynyprOZR1xjQ7WgrgVB+mm uScOM/5HVceFuGRDhYTCObE+y1kxRloNYXnx3ei1zbeYLPCHdhxRYW7T0qcynNmw rn05/KO2RLjgQNalsQJBANeA3Q4Nugqy4QBUCEC09SqylT2K9FrrItqL2QKc9v0Z zO2uwllCbg0dwpVuYPYXYvikNHHg+aCWF+VXsb9rpPsCQQDWR9TT4ORdzoj+Nccn qkMsDmzt0EfNaAOwHOmVJ2RVBspPcxt5iN4HI7HNeG6U5YsFBb+/GZbgfBT3kpNG WPTpAkBI+gFhjfJvRw38n3g/+UeAkwMI2TJQS4n8+hid0uus3/zOjDySH3XHCUno cn1xOJAyZODBo47E+67R4jV1/gzbAkEAklJaspRPXP877NssM5nAZMU0/O/NGCZ+ 3jPgDUno6WbJn5cqm8MqWhW1xGkImgRk+fkDBquiq4gPiT898jusgQJAd5Zrr6Q8 AO/0isr/3aa6O6NLQxISLKcPDk2NOccAfS/xOtfOz4sJYM3+Bs4Io9+dZGSDCA54 Lw03eHTNQghS0A== -----END PRIVATE KEY-----simple_oauth-0.3.1/spec/support/rsa.rb000066400000000000000000000003641245011163500177650ustar00rootroot00000000000000module RSAHelpers PRIVATE_KEY_PATH = File.expand_path('../fixtures/rsa-private-key', __FILE__) def rsa_private_key @rsa_private_key ||= File.read(PRIVATE_KEY_PATH) end end RSpec.configure do |config| config.include RSAHelpers end