signet-0.11.0/ 0000755 0000041 0000041 00000000000 13410324340 013104 5 ustar www-data www-data signet-0.11.0/README.md 0000644 0000041 0000041 00000003715 13410324340 014371 0 ustar www-data www-data # Signet
- Homepage
- http://code.google.com/p/oauth-signet/
- Author
- Bob Aman
- Copyright
- Copyright © 2010 Google, Inc.
- License
- Apache 2.0
[](https://badge.fury.io/rb/signet)
[](http://travis-ci.org/google/signet)
## Description
Signet is an OAuth 1.0 / OAuth 2.0 implementation.
## Reference
- {Signet::OAuth1}
- {Signet::OAuth1::Client}
- {Signet::OAuth1::Credential}
- {Signet::OAuth1::Server}
- {Signet::OAuth2}
- {Signet::OAuth2::Client}
## Example Usage for Google
# Initialize the client
``` ruby
require 'signet/oauth_2/client'
client = Signet::OAuth2::Client.new(
:authorization_uri => 'https://accounts.google.com/o/oauth2/auth',
:token_credential_uri => 'https://oauth2.googleapis.com/token',
:client_id => '44410190108-74nkm6jc5e3vvjqis803frkvmu88cu3a.apps.googleusercontent.com',
:client_secret => 'X1NUhvO-rQr9sm8uUSMY8i7v',
:scope => 'email profile',
:redirect_uri => 'https://example.client.com/oauth'
)
```
# Request an authorization code
```
redirect_to(client.authorization_uri)
```
# Obtain an access token
```
client.code = request.query['code']
client.fetch_access_token!
```
## Install
`gem install signet`
Be sure `https://rubygems.org` is in your gem sources.
## Supported Ruby Versions
This library is currently supported on Ruby 1.9+.
However, Ruby 2.4 or later is strongly recommended, as earlier releases have
reached or are nearing end-of-life. After March 31, 2019, Google will provide
official support only for Ruby versions that are considered current and
supported by Ruby Core (that is, Ruby versions that are either in normal
maintenance or in security maintenance).
See https://www.ruby-lang.org/en/downloads/branches/ for further details.
signet-0.11.0/tasks/ 0000755 0000041 0000041 00000000000 13410324340 014231 5 ustar www-data www-data signet-0.11.0/tasks/git.rake 0000644 0000041 0000041 00000002162 13410324340 015661 0 ustar www-data www-data namespace :git do
namespace :tag do
desc "List tags from the Git repository"
task :list do
tags = `git tag -l`
tags.gsub!("\r", "")
tags = tags.split("\n").sort {|a, b| b <=> a }
puts tags.join("\n")
end
desc "Create a new tag in the Git repository"
task :create do
changelog = File.open("CHANGELOG.md", "r") { |file| file.read }
puts "-" * 80
puts changelog
puts "-" * 80
puts
v = ENV["VERSION"] or abort "Must supply VERSION=x.y.z"
abort "Versions don't match #{v} vs #{PKG_VERSION}" if v != PKG_VERSION
tag = "#{PKG_NAME}-#{PKG_VERSION}"
msg = "Release #{PKG_NAME}-#{PKG_VERSION}"
existing_tags = `git tag -l #{PKG_NAME}-*`.split("\n")
if existing_tags.include?(tag)
warn("Tag already exists, deleting...")
unless system "git tag -d #{tag}"
abort "Tag deletion failed."
end
end
puts "Creating git tag '#{tag}'..."
unless system "git tag -a -m \"#{msg}\" #{tag}"
abort "Tag creation failed."
end
end
end
end
task "gem:release" => "git:tag:create"
signet-0.11.0/tasks/clobber.rake 0000644 0000041 0000041 00000000060 13410324340 016501 0 ustar www-data www-data desc "Remove all build products"
task "clobber"
signet-0.11.0/tasks/metrics.rake 0000644 0000041 0000041 00000002223 13410324340 016542 0 ustar www-data www-data namespace :metrics do
task :lines do
lines, codelines, total_lines, total_codelines = 0, 0, 0, 0
for file_name in FileList["lib/**/*.rb"]
f = File.open(file_name)
while line = f.gets
lines += 1
next if line =~ /^\s*$/
next if line =~ /^\s*#/
codelines += 1
end
puts "L: #{sprintf("%4d", lines)}, " +
"LOC #{sprintf("%4d", codelines)} | #{file_name}"
total_lines += lines
total_codelines += codelines
lines, codelines = 0, 0
end
puts "Code: Lines #{total_lines}, LOC #{total_codelines}\n\n"
lines, codelines, total_lines, total_codelines = 0, 0, 0, 0
for file_name in FileList["spec/**/*_spec.rb"]
f = File.open(file_name)
while line = f.gets
lines += 1
next if line =~ /^\s*$/
next if line =~ /^\s*#/
codelines += 1
end
puts "L: #{sprintf("%4d", lines)}, " +
"LOC #{sprintf("%4d", codelines)} | #{file_name}"
total_lines += lines
total_codelines += codelines
lines, codelines = 0, 0
end
puts "Specs: Lines #{total_lines}, LOC #{total_codelines}\n\n"
end
end
signet-0.11.0/tasks/spec.rake 0000644 0000041 0000041 00000001664 13410324340 016036 0 ustar www-data www-data require 'rspec/core/rake_task'
require 'rake/clean'
CLOBBER.include('coverage', 'specdoc')
namespace :spec do
RSpec::Core::RakeTask.new(:normal) do |t|
t.pattern = FileList['spec/**/*_spec.rb']
t.rspec_opts = ['--color', '--format', 'documentation']
end
RSpec::Core::RakeTask.new(:all) do |t|
t.pattern = FileList['spec/**/*_spec.rb']
t.rspec_opts = ['--color', '--format', 'documentation']
end
desc "Generate HTML Specdocs for all specs"
RSpec::Core::RakeTask.new(:specdoc) do |t|
specdoc_path = File.expand_path(
File.join(File.dirname(__FILE__), '..', 'documentation')
)
Dir.mkdir(specdoc_path) if !File.exist?(specdoc_path)
output_file = File.join(specdoc_path, 'index.html')
t.pattern = FileList['spec/**/*_spec.rb']
t.rspec_opts = ["--format", "\"html:#{output_file}\"", "--diff"]
t.fail_on_error = false
end
end
desc "Alias to spec:normal"
task "spec" => "spec:normal"
signet-0.11.0/tasks/yard.rake 0000644 0000041 0000041 00000000701 13410324340 016032 0 ustar www-data www-data require 'rake/clean'
CLOBBER.include('doc')
require 'yard'
require 'yard/rake/yardoc_task'
namespace :doc do
desc 'Generate Yardoc documentation'
YARD::Rake::YardocTask.new do |yardoc|
yardoc.name = 'yard'
yardoc.options = ['--verbose', '--markup', 'markdown']
yardoc.files = [
'lib/**/*.rb', 'ext/**/*.c', '-',
'README.md', 'CHANGELOG.md', 'LICENSE'
]
end
end
desc 'Alias to doc:yard'
task 'doc' => 'doc:yard'
signet-0.11.0/tasks/gem.rake 0000644 0000041 0000041 00000001461 13410324340 015647 0 ustar www-data www-data require "rubygems/package_task"
namespace :gem do
desc "Build the gem"
task :build do
system "gem build signet.gemspec"
end
desc "Install the gem"
task :install => ["clobber", "gem:package"] do
sh "#{SUDO} gem install --local pkg/#{GEM_SPEC.full_name}"
end
desc "Uninstall the gem"
task :uninstall do
installed_list = Gem.source_index.find_name(PKG_NAME)
if installed_list &&
(installed_list.collect { |s| s.version.to_s}.include?(PKG_VERSION))
sh(
"#{SUDO} gem uninstall --version '#{PKG_VERSION}' " +
"--ignore-dependencies --executables #{PKG_NAME}"
)
end
end
desc "Reinstall the gem"
task :reinstall => [:uninstall, :install]
end
desc "Alias to gem:package"
task "gem" => "gem:package"
task "clobber" => ["gem:clobber_package"]
signet-0.11.0/tasks/wiki.rake 0000644 0000041 0000041 00000001656 13410324340 016050 0 ustar www-data www-data $LOAD_PATH.unshift(
File.expand_path(File.join(File.dirname(__FILE__), '../yard/lib'))
)
$LOAD_PATH.unshift(File.expand_path('.'))
$LOAD_PATH.uniq!
require 'rake'
require 'rake/clean'
CLOBBER.include('wiki')
begin
require 'yard'
require 'yard/rake/wikidoc_task'
namespace :wiki do
desc 'Generate Wiki Documentation with YARD'
YARD::Rake::WikidocTask.new do |yardoc|
yardoc.name = 'reference'
yardoc.options = [
'--verbose',
'--markup', 'markdown',
'-e', 'yard/lib/yard-google-code.rb',
'-p', 'yard/templates',
'-f', 'wiki',
'-o', 'wiki'
]
yardoc.files = [
'lib/**/*.rb', 'ext/**/*.c', '-', 'README.md', 'CHANGELOG.md'
]
end
task 'generate' => ['wiki:reference', 'wiki:supported_apis']
end
rescue LoadError
# If yard isn't available, it's not the end of the world
warn('YARD unavailable. Cannot fully generate wiki.')
end
signet-0.11.0/spec/ 0000755 0000041 0000041 00000000000 13410324340 014036 5 ustar www-data www-data signet-0.11.0/spec/signet_spec.rb 0000644 0000041 0000041 00000006104 13410324340 016667 0 ustar www-data www-data # Copyright (C) 2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'
require 'signet/oauth_2'
describe Signet do
describe 'when parsing an auth param list' do
it 'should correctly handle commas' do
parameters = Signet.parse_auth_param_list(
'a="1, 2" , b="3,4",c="5 , 6" ,d="7 ,8"'
).inject({}) { |h,(k,v)| h[k]=v; h }
expect(parameters['a']).to eq '1, 2'
expect(parameters['b']).to eq '3,4'
expect(parameters['c']).to eq '5 , 6'
expect(parameters['d']).to eq '7 ,8'
end
it 'should correctly handle backslash-escaped pairs' do
parameters = Signet.parse_auth_param_list(
'token="\t\o\k\e\n" sigalg="\s\i\g\a\l\g" data="\d\a\t\a"'
).inject({}) { |h,(k,v)| h[k]=v; h }
expect(parameters['token']).to eq 'token'
expect(parameters['sigalg']).to eq 'sigalg'
expect(parameters['data']).to eq 'data'
end
it 'should liberally handle space-separated auth-param lists' do
parameters = Signet.parse_auth_param_list(
'token="token" sigalg="sigalg" data="data" sig="sig"'
).inject({}) { |h,(k,v)| h[k]=v; h }
expect(parameters['token']).to eq 'token'
expect(parameters['sigalg']).to eq 'sigalg'
expect(parameters['data']).to eq 'data'
expect(parameters['sig']).to eq 'sig'
end
it 'should liberally handle single-quoted auth-param lists' do
parameters = Signet.parse_auth_param_list(
'token=\'token\' sigalg=\'sigalg\' data=\'data\' sig=\'sig\''
).inject({}) { |h,(k,v)| h[k]=v; h }
expect(parameters['token']).to eq 'token'
expect(parameters['sigalg']).to eq 'sigalg'
expect(parameters['data']).to eq 'data'
expect(parameters['sig']).to eq 'sig'
end
it 'should liberally handle unquoted auth-param lists' do
parameters = Signet.parse_auth_param_list(
'token=token sigalg=sigalg data=data sig=sig'
).inject({}) { |h,(k,v)| h[k]=v; h }
expect(parameters['token']).to eq 'token'
expect(parameters['sigalg']).to eq 'sigalg'
expect(parameters['data']).to eq 'data'
expect(parameters['sig']).to eq 'sig'
end
it 'should liberally handle auth-param lists with empty sections' do
parameters = Signet.parse_auth_param_list(
'token=token, , sigalg=sigalg,, data=data, sig=sig'
).inject({}) { |h,(k,v)| h[k]=v; h }
expect(parameters['token']).to eq 'token'
expect(parameters['sigalg']).to eq 'sigalg'
expect(parameters['data']).to eq 'data'
expect(parameters['sig']).to eq 'sig'
end
end
end
signet-0.11.0/spec/spec.opts 0000644 0000041 0000041 00000000040 13410324340 015671 0 ustar www-data www-data --colour
--format documentation
signet-0.11.0/spec/signet/ 0000755 0000041 0000041 00000000000 13410324340 015327 5 ustar www-data www-data signet-0.11.0/spec/signet/oauth_1/ 0000755 0000041 0000041 00000000000 13410324340 016667 5 ustar www-data www-data signet-0.11.0/spec/signet/oauth_1/signature_methods/ 0000755 0000041 0000041 00000000000 13410324340 022413 5 ustar www-data www-data signet-0.11.0/spec/signet/oauth_1/signature_methods/hmac_sha1_spec.rb 0000644 0000041 0000041 00000004545 13410324340 025606 0 ustar www-data www-data # Copyright (C) 2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'
require 'signet'
require 'signet/oauth_1'
require 'signet/oauth_1/signature_methods/hmac_sha1'
describe Signet::OAuth1::HMACSHA1 do
it 'should correctly generate a signature' do
method = "GET"
uri = "http://photos.example.net/photos"
parameters = {
"oauth_consumer_key" => "dpf43f3p2l4k3l03",
"oauth_token" => "nnch734d00sl2jdk",
"oauth_signature_method" => "HMAC-SHA1",
"oauth_timestamp" => "1191242096",
"oauth_nonce" => "kllo9940pd9333jh",
"oauth_version" => "1.0",
"file" => "vacation.jpg",
"size" => "original"
}
client_credential_secret = "kd94hf93k423kf44"
token_credential_secret = "pfkkdhi9sl3r4s00"
base_string = Signet::OAuth1.generate_base_string(method, uri, parameters)
expect(Signet::OAuth1::HMACSHA1.generate_signature(
base_string, client_credential_secret, token_credential_secret
)).to eq "tR3+Ty81lMeYAr/Fid0kMTYa/WM="
end
it 'should correctly generate a signature' do
method = "GET"
uri = "http://photos.example.net/photos"
parameters = {
"oauth_consumer_key" => "www.example.com",
"oauth_token" => "4/QL2GT6b5uznYem1ZGH6v+-9mMvRL",
"oauth_signature_method" => "HMAC-SHA1",
"oauth_timestamp" => "1191242096",
"oauth_nonce" => "kllo9940pd9333jh",
"oauth_version" => "1.0",
"file" => "vacation.jpg",
"size" => "original"
}
client_credential_secret = "Kv+o2XXL/9RxkQW3lO3QTVlH"
token_credential_secret = "QllSuL9eQ5FXFO1Z/HcgL4ON"
base_string = Signet::OAuth1.generate_base_string(method, uri, parameters)
expect(Signet::OAuth1::HMACSHA1.generate_signature(
base_string, client_credential_secret, token_credential_secret
)).to eq "G/nkdbmbpEA+6RD1Sc5uIefhFfQ="
end
end
signet-0.11.0/spec/signet/oauth_1/signature_methods/plaintext_spec.rb 0000644 0000041 0000041 00000004666 13410324340 025776 0 ustar www-data www-data # Copyright (C) 2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'
require 'signet'
require 'signet/oauth_1'
require 'signet/oauth_1/signature_methods/plaintext'
describe Signet::OAuth1::PLAINTEXT do
it 'should correctly generate a signature' do
method = "GET"
uri = "http://photos.example.net/photos"
parameters = {
"oauth_consumer_key" => "dpf43f3p2l4k3l03",
"oauth_token" => "nnch734d00sl2jdk",
"oauth_signature_method" => "HMAC-SHA1",
"oauth_timestamp" => "1191242096",
"oauth_nonce" => "kllo9940pd9333jh",
"oauth_version" => "1.0",
"file" => "vacation.jpg",
"size" => "original"
}
client_credential_secret = "kd94hf93k423kf44"
token_credential_secret = "pfkkdhi9sl3r4s00"
base_string = Signet::OAuth1.generate_base_string(method, uri, parameters)
expect(Signet::OAuth1::PLAINTEXT.generate_signature(
base_string, client_credential_secret, token_credential_secret
)).to eq "kd94hf93k423kf44%26pfkkdhi9sl3r4s00"
end
it 'should correctly generate a signature' do
method = "GET"
uri = "http://photos.example.net/photos"
parameters = {
"oauth_consumer_key" => "www.example.com",
"oauth_token" => "4/QL2GT6b5uznYem1ZGH6v+-9mMvRL",
"oauth_signature_method" => "HMAC-SHA1",
"oauth_timestamp" => "1191242096",
"oauth_nonce" => "kllo9940pd9333jh",
"oauth_version" => "1.0",
"file" => "vacation.jpg",
"size" => "original"
}
client_credential_secret = "Kv+o2XXL/9RxkQW3lO3QTVlH"
token_credential_secret = "QllSuL9eQ5FXFO1Z/HcgL4ON"
base_string = Signet::OAuth1.generate_base_string(method, uri, parameters)
expect(Signet::OAuth1::PLAINTEXT.generate_signature(
base_string, client_credential_secret, token_credential_secret
)).to eq "Kv%252Bo2XXL%252F9RxkQW3lO3QTVlH%26QllSuL9eQ5FXFO1Z%252FHcgL4ON"
end
end
signet-0.11.0/spec/signet/oauth_1/signature_methods/rsa_sha1_spec.rb 0000644 0000041 0000041 00000014120 13410324340 025451 0 ustar www-data www-data # Copyright (C) 2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'
require 'signet'
require 'signet/oauth_1'
require 'signet/oauth_1/signature_methods/rsa_sha1'
describe Signet::OAuth1::RSASHA1 do
it 'should correctly generate a signature' do
method = "GET"
uri = "http://term.ie/oauth/example/request_token.php"
parameters = {
"oauth_consumer_key" => "key",
"oauth_signature_method" => "RSA-SHA1",
"oauth_timestamp" => "1377815426",
"oauth_nonce" => "c3839c47cb204a20e042b11a5cc9f971",
"oauth_version" => "1.0"
}
client_credential_secret = "-----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-----"
token_credential_secret = "pfkkdhi9sl3r4s00"
base_string = Signet::OAuth1.generate_base_string(method, uri, parameters)
expect(Signet::OAuth1::RSASHA1.generate_signature(
base_string, client_credential_secret, token_credential_secret
)).to eq "P72T4RS8dVBneQPJSY71D3iLEjge2tiivxEasPVoaoDldDgPdwpQfhS1q0th19jB3B3+9P6tBWjpWaVPxrNZe3ssBCiwS/EmXZ/6VCJGU3YoDHMtz+0jCd36NjHj5I6TpLVQ8/rtfy6+EzpdUMz7ydnhKXYqJFPOWnNv8HM1W7I="
end
end
describe Signet::OAuth1::RSASHA1 do
it 'should correctly generate a signature' do
method = "GET"
uri = "http://photos.example.net/photos"
parameters = {
"oauth_consumer_key" => "dpf43f3p2l4k3l03",
"oauth_signature_method" => "RSA-SHA1",
"oauth_timestamp" => "1196666512",
"oauth_nonce" => "13917289812797014437",
"oauth_version" => "1.0",
"file" => "vacaction.jpg",
"size" => "original"
}
client_credential_secret = "-----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-----"
token_credential_secret = "pfkkdhi9sl3r4s00"
base_string = Signet::OAuth1.generate_base_string(method, uri, parameters)
expect(Signet::OAuth1::RSASHA1.generate_signature(
base_string, client_credential_secret, token_credential_secret
)).to eq "jvTp/wX1TYtByB1m+Pbyo0lnCOLIsyGCH7wke8AUs3BpnwZJtAuEJkvQL2/9n4s5wUmUl4aCI4BwpraNx4RtEXMe5qg5T1LVTGliMRpKasKsW//e+RinhejgCuzoH26dyF8iY2ZZ/5D1ilgeijhV/vBka5twt399mXwaYdCwFYE="
end
it 'should correctly generate a signature' do
method = "GET"
uri = "http://term.ie/oauth/example/access_token.php"
parameters = {
"oauth_consumer_key" => "key",
"oauth_token" => "requestkey",
"oauth_signature_method" => "RSA-SHA1",
"oauth_timestamp" => "1377815426",
"oauth_nonce" => "8ae9ac8192dd3cd7372e0324bf879602",
"oauth_version" => "1.0",
}
client_credential_secret = "-----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-----"
token_credential_secret = "QllSuL9eQ5FXFO1Z/HcgL4ON"
base_string = Signet::OAuth1.generate_base_string(method, uri, parameters)
expect(Signet::OAuth1::RSASHA1.generate_signature(
base_string, client_credential_secret, token_credential_secret
)).to eq "Q1O7Ovi0jdacl/OTJoH3MAyOO/9H/tTXmoJzP/YqiKEJ+/wfShXo1RXX0xmlcjDR1XYxB1RMgHkFWQYYwz1qGCUhkXlH1c/to2qxPksptfPHRe7PJTxRClrdqLFOlhN7w2kO7tHVCeEp8IJIKON9q7cdXroTP7ctPPS+Q883SS0="
end
end
signet-0.11.0/spec/signet/oauth_1/server_spec.rb 0000644 0000041 0000041 00000076626 13410324340 021555 0 ustar www-data www-data # Copyright (C) 2011 The Yakima Herald-Republic.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'
require 'signet/oauth_1/server'
require 'signet/oauth_1/client'
require 'addressable/uri'
require 'stringio'
def merge_body(chunked_body)
merged_body = StringIO.new
chunked_body.each do |chunk|
merged_body.write(chunk)
end
return merged_body.string
end
def make_oauth_signature_header(real_headers={})
[oauth_headers({'oauth_signature' => 'oauth_signature'}.merge(real_headers))]
end
def make_oauth_token_header(real_headers={})
[oauth_headers({'oauth_token' => 'oauth_token'}.merge(real_headers))]
end
def oauth_headers(real_headers={})
headers = {}
%w[oauth_consumer_key oauth_timestamp oauth_nonce].each do |key|
headers[key] = key
end
headers['oauth_signature_method'] = 'HMAC-SHA1'
headers['oauth_version'] = '1.0'
headers.merge!(real_headers)
['Authorization', ::Signet::OAuth1.generate_authorization_header(headers, nil)]
end
def make_temporary_credential_request(client, callback=nil, uri=nil, realm=nil)
client.callback = callback if callback
client.temporary_credential_uri = uri || 'http://photos.example.net/initiate'
client.generate_temporary_credential_request(:realm=>realm)
end
def make_token_credential_request(client, verifier=nil, realm=nil, uri=nil)
client.token_credential_uri = uri || 'http://photos.example.net/token'
client.generate_token_credential_request(:verifier=>verifier || '12345',
:realm=>realm
)
end
def make_resource_request(client, real_request={}, realm=nil)
client.generate_authenticated_request(
:method => real_request[:method] || 'GET',
:uri => real_request[:uri] || 'http://photos.example.net/photos',
:body=> real_request[:body],
:headers=>real_request[:headers],
:realm=>realm
)
end
describe Signet::OAuth1::Server, 'unconfigured' do
before do
@server = Signet::OAuth1::Server.new
end
it 'should not have a client_credential Proc' do
expect(@server.client_credential).to eq nil
end
it 'should not have a token_credential Proc' do
expect(@server.token_credential).to eq nil
end
it 'should not have a nonce_timestamp Proc' do
expect(@server.nonce_timestamp).to eq nil
end
it 'should not have a verifier Proc' do
expect(@server.verifier).to eq nil
end
end
describe Signet::OAuth1::Server, 'configured' do
before do
@server = Signet::OAuth1::Server.new
@client_credential_key = 'dpf43f3p2l4k3l03'
@client_credential_secret = 'kd94hf93k423kf44'
@token_credential_key = 'nnch734d00sl2jdk'
@token_credential_secret = 'pfkkdhi9sl3r4s00'
@temporary_credential_key = 'hh5s93j4hdidpola'
@temporary_credential_secret = 'hdhd0244k9j7ao03'
@verifier = 'hfdp7dh39dks9884'
@server.client_credential =
lambda do |x|
x.nil? ? nil : Signet::OAuth1::Credential.new(@client_credential_key,
@client_credential_secret)
end
@server.token_credential =
lambda do |x|
x.nil? ? nil : Signet::OAuth1::Credential.new(@token_credential_key,
@token_credential_secret)
end
@server.temporary_credential =
lambda do |x|
x.nil? ? nil : Signet::OAuth1::Credential.new(@temporary_credential_key,
@temporary_credential_secret)
end
@server.nonce_timestamp =
lambda do |nonce, timestamp|
!(nonce.nil? && timestamp.nil?)
end
@server.verifier = lambda { |x| x == @verifier }
end
it 'should raise an error if the client credential Proc is not set' do
@server.client_credential = nil
expect(lambda do
@server.authenticate_resource_request
end).to raise_error(ArgumentError)
end
it "should raise an error if the token credential Proc is not set" do
@server.token_credential = nil
expect(lambda do
@server.authenticate_resource_request
end).to raise_error(ArgumentError)
end
it "should raise an error if the temporary token credential Proc is not set" do
@server.temporary_credential = nil
expect(lambda do
@server.authenticate_token_credential_request
end).to raise_error(ArgumentError)
end
it "should raise an error if the verifier Proc is not set for a token request" do
@server.verifier = nil
expect(lambda do
@server.authenticate_token_credential_request
end).to raise_error(ArgumentError)
end
it 'should raise an error if no request is provided' do
expect(lambda do
@server.authenticate_resource_request
end).to raise_error(ArgumentError)
end
it 'should raise an error if a bogus request is provided' do
expect(lambda do
@server.authenticate_resource_request(
:request => []
)
end).to raise_error(ArgumentError)
end
it 'should raise an error if no Authentication header is provided' do
expect(lambda do
@server.authenticate_resource_request(
:method => 'GET',
:uri => 'https://photos.example.net/photos',
:headers => [['Authorization', '']],
:body => ''
)
end).to raise_error(Signet::MalformedAuthorizationError)
end
it 'should raise an error if no URI is provided' do
expect(lambda do
@server.authenticate_resource_request(
:method => 'GET',
:headers => [],
:body => ''
)
end).to raise_error(ArgumentError)
end
it 'should reject a request with the wrong signature method' do
bad_method = 'FOO'
expect(lambda do
@server.authenticate_resource_request(
:method => 'GET',
:uri => 'http://photos.example.net/photos',
:headers=>make_oauth_token_header({'oauth_signature_method'=>bad_method})
)
end).to raise_error(NotImplementedError,
"Unsupported signature method: #{bad_method}"
)
end
describe 'calling find_temporary_credential' do
it 'should return a Signet credential if the Proc provides one' do
@server.temporary_credential =
lambda do |x|
x.nil? ? nil : Signet::OAuth1::Credential.new(
@temporary_credential_key, @temporary_credential_secret
)
end
expect(@server.find_temporary_credential(@temporary_credential_key)).to eq(
Signet::OAuth1::Credential.new(@temporary_credential_key,
@temporary_credential_secret))
end
it 'should return a Signet credential if the Proc provides a key/secret pair' do
@server.temporary_credential =
lambda do |x|
{:key=>@temporary_credential_key, :secret=>@temporary_credential_secret}
end
expect(@server.find_temporary_credential(@temporary_credential_key)).to eq(
Signet::OAuth1::Credential.new(@temporary_credential_key,
@temporary_credential_secret))
end
it 'should return a Signet credential if the Proc provides ' +
'a key/secret Enumerable' do
@server.temporary_credential =
lambda do |x|
[@temporary_credential_key, @temporary_credential_secret]
end
expect(@server.find_temporary_credential(@temporary_credential_key)).to eq(
Signet::OAuth1::Credential.new(@temporary_credential_key,
@temporary_credential_secret))
end
it 'should return nil if the Proc does not provide a usable response' do
@server.temporary_credential = lambda {|x| nil }
expect(@server.find_temporary_credential(@temporary_credential_key)).to eq nil
end
end
describe 'calling find_client_credential' do
it 'should return a Signet credential if the Proc provides one' do
@server.client_credential =
lambda do |x|
x.nil? ? nil : Signet::OAuth1::Credential.new(@client_credential_key,
@client_credential_secret)
end
expect(@server.find_client_credential(@client_credential_key)).to eq(
Signet::OAuth1::Credential.new(@client_credential_key,
@client_credential_secret))
end
it 'should return a Signet credential if the Proc provides a key/secret pair' do
@server.client_credential =
lambda do |x|
{:key=>@client_credential_key, :secret=>@client_credential_secret}
end
expect(@server.find_client_credential(@client_credential_key)).to eq(
Signet::OAuth1::Credential.new(@client_credential_key,
@client_credential_secret))
end
it 'should return a Signet credential if the Proc provides ' +
'a key/secret Enumerable' do
@server.client_credential =
lambda do |x|
[@client_credential_key, @client_credential_secret]
end
expect(@server.find_client_credential(@client_credential_key)).to eq(
Signet::OAuth1::Credential.new(@client_credential_key,
@client_credential_secret))
end
it 'should return nil if the Proc does not provide a usable response' do
@server.client_credential = lambda {|x| nil }
expect(@server.find_client_credential(@client_credential_key)).to be_nil
end
end
describe 'calling find_token_credential' do
it 'should return a Signet credential if the Proc provides one' do
@server.token_credential =
lambda do |x|
x.nil? ? nil : Signet::OAuth1::Credential.new(@token_credential_key,
@token_credential_secret)
end
expect(@server.find_token_credential(@token_credential_key)).to eq(
Signet::OAuth1::Credential.new(@token_credential_key,
@token_credential_secret))
end
it 'should return a Signet credential if the Proc provides a key/secret pair' do
@server.token_credential =
lambda do |x|
{:key=>@token_credential_key, :secret=>@token_credential_secret}
end
expect(@server.find_token_credential(@token_credential_key)).to eq(
Signet::OAuth1::Credential.new(@token_credential_key,
@token_credential_secret))
end
it 'should return a Signet credential if the Proc provides ' +
'a key/secret Enumerable' do
@server.token_credential =
lambda do |x|
[@token_credential_key, @token_credential_secret]
end
expect(@server.find_token_credential(@token_credential_key)).to eq(
Signet::OAuth1::Credential.new(@token_credential_key,
@token_credential_secret))
end
it 'should return nil if the Proc does not provide a usable response' do
@server.token_credential = lambda {|x| nil }
expect(@server.find_token_credential(@token_credential_key)).to be_nil
end
end
describe 'calling find_verifier' do
it 'should return false if server verifier returns false' do
@server.verifier = lambda {|x| false }
expect(@server.find_verifier(@verifier)).to eq false
end
it 'should return false if server verifier returns nil' do
@server.verifier = lambda {|x| nil }
expect(@server.find_verifier(@verifier)).to eq false
end
it 'should return true if server verifier returns a random object' do
@server.verifier = lambda {|x| x.succ}
expect(@server.find_verifier(@verifier)).to eq true
end
end
describe 'calling validate_nonce_timestamp' do
it 'should return false if nonce_timestamp Proc returns false' do
@server.nonce_timestamp = lambda {|n,t| false}
expect(@server.validate_nonce_timestamp('nonce', 'timestamp')).to be false
end
it 'should return false if nonce_timestamp Proc returns nil' do
@server.nonce_timestamp = lambda {|n,t| nil}
expect(@server.validate_nonce_timestamp('nonce', 'timestamp')).to be false
end
it 'should return true if nonce_timestamp Proc returns a random object' do
@server.nonce_timestamp = lambda {|n,t| n+t.to_s}
expect(@server.validate_nonce_timestamp('nonce', 'timestamp')).to be true
end
end
describe 'expecting a request for a temporary credential' do
before do
@client = Signet::OAuth1::Client.new(
:client_credential_key=>@client_credential_key,
:client_credential_secret=>@client_credential_secret,
:temporary_credential_uri=>
'http://photos.example.net/initiate')
end
it 'should raise an error if the client credential Proc is not set' do
@server.client_credential = nil
expect(lambda do
@server.authenticate_temporary_credential_request(
:request=>make_temporary_credential_request(@client)
)
end).to raise_error(ArgumentError)
end
it 'should reject an malformed request' do
bad_request = make_temporary_credential_request(@client, nil, 'https://photos.example.net/photos')
bad_request.headers['Authorization'].gsub!(/(OAuth)(.+)/, "#{$1}")
expect(lambda do
@server.authenticate_temporary_credential_request(
:request=>bad_request
)
end).to raise_error(Signet::MalformedAuthorizationError)
end
it 'should call a user-supplied Proc to validate a nonce/timestamp pair' do
nonce_callback = double('nonce')
expect(nonce_callback).to receive(:call).once.with(an_instance_of(String),
an_instance_of(String)
).and_return(true)
@server.nonce_timestamp = nonce_callback
@server.authenticate_temporary_credential_request(
:request=>make_temporary_credential_request(@client)
)
end
it "should return 'oob' for a valid request without an oauth_callback" do
bad_request = make_temporary_credential_request(@client)
expect(@server.authenticate_temporary_credential_request(
:request=>bad_request
)).to eq 'oob'
end
it 'should return the oauth_callback for a valid request ' +
'with an oauth_callback' do
callback = 'http://printer.example.com/ready'
expect(@server.authenticate_temporary_credential_request(
:request=>make_temporary_credential_request(@client, callback)
)).to eq callback
end
it 'should return false for an unauthenticated request' do
bad_request = make_temporary_credential_request(@client)
bad_request.headers["Authorization"].gsub!(/oauth_signature=\".+\"/,
"oauth_signature=\"foobar\"")
expect(@server.authenticate_temporary_credential_request(
:request=>bad_request
)).to eq false
end
it 'should return nil from #request_realm if no realm is provided' do
req = make_temporary_credential_request(@client)
expect(@server.request_realm(
:request=>req
)).to eq nil
end
describe 'with a Realm provided' do
it 'should return the realm from #request_realm' do
req = make_temporary_credential_request(@client, nil, nil, 'Photos')
expect(@server.request_realm(
:request=>req
)).to eq 'Photos'
end
it 'should return "oob" with a valid request without an oauth_callback' do
req = make_temporary_credential_request(@client, nil, nil, 'Photos')
expect(@server.authenticate_temporary_credential_request(
:request=>req
)).to eq 'oob'
end
end
end
describe 'expecting a request for a token credential' do
before do
@client = Signet::OAuth1::Client.new(
:client_credential_key=>@client_credential_key,
:client_credential_secret=>@client_credential_secret,
:temporary_credential_key=>@temporary_credential_key,
:temporary_credential_secret=>@temporary_credential_secret,
:token_credential_uri=>'http://photos.example.net/token'
)
@return_hash = {:client_credential=>Signet::OAuth1::Credential.new(@client_credential_key, @client_credential_secret),
:temporary_credential=>Signet::OAuth1::Credential.new(@temporary_credential_key, @temporary_credential_secret),
:realm=>nil
}
end
it 'should reject an malformed request' do
bad_request = make_token_credential_request(@client)
bad_request.headers["Authorization"].gsub!(/(OAuth)(.+)/, "#{$1}")
expect(lambda do
@server.authenticate_token_credential_request(
:request=>bad_request
)
end).to raise_error(Signet::MalformedAuthorizationError)
end
it 'should call a user-supplied Proc to validate a nonce/timestamp pair' do
nonce_callback = double('nonce')
expect(nonce_callback).to receive(:call).once.with(
an_instance_of(String), an_instance_of(String)
).and_return(true)
@server.nonce_timestamp = nonce_callback
@server.authenticate_token_credential_request(
:request=>make_token_credential_request(@client)
)
end
it 'should return an informational hash for a valid request' do
expect(@server.authenticate_token_credential_request(
:request=>make_token_credential_request(@client)
)).to eq @return_hash
end
it 'should return nil for an unauthenticated request' do
bad_request = make_token_credential_request(@client)
bad_request.headers["Authorization"].gsub!(/oauth_signature=\".+\"/,
"oauth_signature=\"foobar\"")
expect(@server.authenticate_token_credential_request(
:request=>bad_request
)).to eq nil
end
it 'should call a user-supplied Proc to fetch the client credential' do
client_cred = Signet::OAuth1::Credential.new(@client_credential_key,
@client_credential_secret )
key_callback = double('client_cred')
expect(key_callback).to receive(:call).at_least(:once).with(
@client_credential_key
).and_return(client_cred)
@server.client_credential = key_callback
@server.authenticate_token_credential_request(
:request=>make_token_credential_request(@client)
)
end
it 'should call a user-supplied Proc to fetch the temporary token credential' do
temp_cred = Signet::OAuth1::Credential.new(@temporary_credential_key,
@temporary_credential_secret)
temp_callback = double('temp_cred')
expect(temp_callback).to receive(:call).at_least(:once).with(
@temporary_credential_key
).and_return(temp_cred)
@server.temporary_credential = temp_callback
@server.authenticate_token_credential_request(
:request=>make_token_credential_request(@client)
)
end
it 'should return nil from #request_realm if no realm is provided' do
req = make_token_credential_request(@client)
expect(@server.request_realm(
:request=>req
)).to eq nil
end
describe 'with a Realm provided' do
before do
@realm = 'Photos'
@return_hash[:realm] = @realm
end
it 'should return the realm from #request_realm' do
req = make_token_credential_request(@client, nil, @realm)
expect(@server.request_realm(
:request=>req
)).to eq @realm
end
it 'should an informational hash with a valid request' do
req = make_token_credential_request(@client, nil, @realm)
expect(@server.authenticate_token_credential_request(
:request=>req
)).to eq @return_hash
end
end
end
describe 'expecting a request for a protected resource' do
before(:each) do
@client = Signet::OAuth1::Client.new(
:client_credential_key=>@client_credential_key,
:client_credential_secret=>@client_credential_secret,
:token_credential_key=>@token_credential_key,
:token_credential_secret=>@token_credential_secret
)
@return_hash = {:client_credential=>Signet::OAuth1::Credential.new(@client_credential_key, @client_credential_secret),
:token_credential=>Signet::OAuth1::Credential.new(@token_credential_key, @token_credential_secret),
:realm=>nil
}
end
it 'should not raise an error if a request body is chunked(as Array)' do
approved = @server.authenticate_resource_request(
:method => 'POST',
:uri => 'https://photos.example.net/photos',
:body => ['A chunked body.'],
:headers => make_oauth_signature_header
)
expect(approved).to eq nil
end
it 'should not raise an error if a request body is chunked(as StringIO)' do
chunked_body = StringIO.new
chunked_body.write('A chunked body.')
chunked_body.rewind
approved = @server.authenticate_resource_request(
:method => 'POST',
:uri => 'https://photos.example.net/photos',
:body => chunked_body,
:headers => make_oauth_signature_header
)
expect(approved).to eq nil
end
it 'should raise an error if a request body is of a bogus type' do
expect(lambda do
@server.authenticate_resource_request(
:method => 'POST',
:uri => 'https://photos.example.net/photos',
:body => 42,
:headers => make_oauth_signature_header
)
end).to raise_error(TypeError)
end
it 'should use form parameters in signature if request is a POSTed form' do
req = make_resource_request(
@client,
{:method=>'POST',
:headers=>{'Content-Type'=>'application/x-www-form-urlencoded'},
:body=>'c2&a3=2+q'})
expect(@server.authenticate_resource_request(:request=>req)).to eq @return_hash
end
it 'should raise an error if signature is x-www-form-encoded ' +
'but does not send form parameters in header' do
# Make a full request so that we can sign against the form parameters
# that will be removed.
req = make_resource_request(
@client,
{:method=>'POST',
:headers=>{'Content-Type'=>'application/x-www-form-urlencoded'},
:body=>'c2&a3=2+q'})
req.headers["Authorization"].gsub!(/c2=\"\", a3=\"2%20q\", /, '')
expect(lambda do
@server.authenticate_resource_request(:request=>req)
end).to raise_error(Signet::MalformedAuthorizationError,
'Request is of type application/x-www-form-urlencoded but ' +
'Authentication header did not include form values'
)
end
it 'should call a user-supplied Proc to validate a nonce/timestamp pair' do
nonce_callback = double('nonce')
expect(nonce_callback).to receive(:call).once.with(
an_instance_of(String), an_instance_of(String)
).and_return(true)
@server.nonce_timestamp = nonce_callback
@server.authenticate_resource_request(
:request=>make_resource_request(@client)
)
end
it 'should call a user-supplied Proc to fetch the client credential' do
client_cred = Signet::OAuth1::Credential.new(@client_credential_key,
@client_credential_secret )
key_callback = double('client_cred' )
expect(key_callback).to receive(:call).at_least(:once).with(
@client_credential_key
).and_return(client_cred)
@server.client_credential = key_callback
@server.authenticate_resource_request(
:request=>make_resource_request(@client)
)
end
it 'should call a user-supplied Proc to fetch the token credential' do
token_cred = Signet::OAuth1::Credential.new(@token_credential_key,
@token_credential_secret)
key_callback = double('token_cred' )
expect(key_callback).to receive(:call).at_least(:once).with(
@token_credential_key
).and_return(token_cred)
@server.token_credential = key_callback
@server.authenticate_resource_request(
:request=>make_resource_request(@client)
)
end
it 'should return a Hash for a valid request' do
expect(@server.authenticate_resource_request(
:request=>make_resource_request(@client)
)).to eq @return_hash
end
it 'should return nil for a unauthenticated request' do
bad_request = make_resource_request(@client)
bad_request.headers["Authorization"].gsub!(/oauth_signature=\".+\"/,
"oauth_signature=\"foobar\"")
expect(@server.authenticate_resource_request(:request=>bad_request)).to eq nil
end
it 'should return nil from #request_realm if no realm is provided' do
req = make_resource_request(@client)
expect(@server.request_realm(
:request=>req
)).to eq nil
end
describe 'with a Realm provided' do
before do
@realm = 'Photos'
@return_hash[:realm] = @realm
end
it 'should return the realm from #request_realm' do
req = make_resource_request(@client, {}, @realm)
expect(@server.request_realm(
:request=>req
)).to eq @realm
end
it 'should return a hash containing the realm with a valid request' do
req = make_resource_request(@client, {}, @realm)
expect(@server.authenticate_resource_request(
:request=>req
)).to eq @return_hash
end
end
end
describe "expecting a two-legged request for a protected resource" do
before do
@client = Signet::OAuth1::Client.new(
:client_credential_key=>@client_credential_key,
:client_credential_secret=>@client_credential_secret,
:two_legged=>true)
@return_hash = {:client_credential=>Signet::OAuth1::Credential.new(@client_credential_key, @client_credential_secret),
:token_credential=>nil,
:realm=>nil
}
end
it 'should not raise an error if a request body is chunked(as Array)' do
approved = @server.authenticate_resource_request(
:method => 'POST',
:uri => 'https://photos.example.net/photos',
:body => ['A chunked body.'],
:headers => make_oauth_signature_header,
:two_legged=>true
)
expect(approved).to eq nil
end
it 'should not raise an error if a request body is chunked(as StringIO)' do
chunked_body = StringIO.new
chunked_body.write('A chunked body.')
chunked_body.rewind
approved = @server.authenticate_resource_request(
:method => 'POST',
:uri => 'https://photos.example.net/photos',
:body => chunked_body,
:headers => make_oauth_signature_header,
:two_legged=>true
)
expect(approved).to eq nil
end
it 'should raise an error if a request body is of a bogus type' do
expect(lambda do
@server.authenticate_resource_request(
:method => 'POST',
:uri => 'https://photos.example.net/photos',
:body => 42,
:headers => make_oauth_signature_header,
:two_legged=>true
)
end).to raise_error(TypeError)
end
it 'should use form parameters in signature if request is a POSTed form' do
req = make_resource_request(
@client,
{:method=>'POST',
:headers=>{'Content-Type'=>'application/x-www-form-urlencoded'},
:body=>'c2&a3=2+q'}
)
expect(@server.authenticate_resource_request(
:request=>req, :two_legged=>true
)).to eq @return_hash
end
it 'should raise an error if signature is x-www-form-encoded '+
'but does not send form parameters in header' do
# Make a full request so that we can sign against the form parameters
# that will be removed.
req = make_resource_request(
@client,
{:method=>'POST',
:headers=>{'Content-Type'=>'application/x-www-form-urlencoded'},
:body=>'c2&a3=2+q'}
)
req.headers["Authorization"].gsub!(/c2=\"\", a3=\"2%20q\", /, '')
expect(lambda do
@server.authenticate_resource_request(:request=>req, :two_legged=>true)
end).to raise_error(Signet::MalformedAuthorizationError,
'Request is of type application/x-www-form-urlencoded but '+
'Authentication header did not include form values'
)
end
it 'should call a user-supplied Proc to validate a nonce/timestamp pair' do
nonce_callback = double('nonce')
expect(nonce_callback).to receive(:call).once.with(
an_instance_of(String), an_instance_of(String)
).and_return(true)
@server.nonce_timestamp = nonce_callback
@server.authenticate_resource_request(
:request=>make_resource_request(@client), :two_legged=>true
)
end
it 'should call a user-supplied Proc to fetch the client credential' do
client_cred = Signet::OAuth1::Credential.new(@client_credential_key,
@client_credential_secret )
key_callback = double('client_cred')
expect(key_callback).to receive(:call).at_least(:once).with(
@client_credential_key
).and_return(client_cred)
@server.client_credential = key_callback
@server.authenticate_resource_request(
:request=>make_resource_request(@client), :two_legged=>true
)
end
it 'should return a informational hash for a valid request' do
expect(@server.authenticate_resource_request(
:request=>make_resource_request(@client), :two_legged=>true
)).to eq @return_hash
end
it 'should return false for a unauthenticated request' do
bad_request = make_resource_request(@client)
bad_request.headers["Authorization"].gsub!(/oauth_signature=\".+\"/,
"oauth_signature=\"foobar\"")
expect(@server.authenticate_resource_request(:request=>bad_request)).to eq nil
end
it 'should return nil from #request_realm if no realm is provided' do
req = make_resource_request(@client)
expect(@server.request_realm(
:request=>req
)).to eq nil
end
describe 'with a Realm provided' do
before do
@realm = 'Photos'
@return_hash[:realm] = @realm
end
it 'should return the realm from #request_realm' do
req = make_resource_request(@client, {}, @realm)
expect(@server.request_realm(
:request=>req, :two_legged=>true
)).to eq @realm
end
it 'should return a hash containing the realm with a valid request' do
req = make_resource_request(@client, {}, @realm)
expect(@server.authenticate_resource_request(
:request=>req, :two_legged=>true
)).to eq @return_hash
end
end
end
end
signet-0.11.0/spec/signet/oauth_1/client_spec.rb 0000644 0000041 0000041 00000074003 13410324340 021510 0 ustar www-data www-data # Copyright (C) 2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'
require 'multi_json'
require 'signet/oauth_1/client'
require 'addressable/uri'
require 'stringio'
conn = Faraday.default_connection
def merge_body(chunked_body)
if chunked_body == nil
raise ArgumentError, "Expected chunked body, got nil."
end
merged_body = StringIO.new
chunked_body.each do |chunk|
merged_body.write(chunk)
end
return merged_body.string
end
describe Signet::OAuth1::Client, 'unconfigured' do
before do
@client = Signet::OAuth1::Client.new
end
it 'should have no temporary_credential_uri' do
expect(@client.temporary_credential_uri).to be_nil
end
it 'should allow the temporary_credential_uri to be set to a String' do
@client.temporary_credential_uri = "http://example.com/"
expect(@client.temporary_credential_uri.to_s).to eq "http://example.com/"
end
it 'should allow the temporary_credential_uri to be set to a URI' do
@client.temporary_credential_uri =
Addressable::URI.parse("http://example.com/")
expect(@client.temporary_credential_uri.to_s).to eq "http://example.com/"
end
it 'should have no authorization_uri' do
expect(@client.authorization_uri).to be_nil
end
it 'should allow the authorization_uri to be set to a String' do
@client.authorization_uri = 'http://example.com/authorize'
expect(@client.authorization_uri.to_s).to include(
'http://example.com/authorize'
)
end
it 'should allow the authorization_uri to be set to a Hash' do
@client.authorization_uri = {
:scheme => 'http', :host => 'example.com', :path => '/authorize'
}
expect(@client.authorization_uri.to_s).to include(
'http://example.com/authorize'
)
end
it 'should allow the authorization_uri to be set to a URI' do
@client.authorization_uri =
Addressable::URI.parse('http://example.com/authorize')
expect(@client.authorization_uri.to_s).to include(
'http://example.com/authorize'
)
end
it 'should have no token_credential_uri' do
expect(@client.token_credential_uri).to be_nil
end
it 'should allow the token_credential_uri to be set to a String' do
@client.token_credential_uri = "http://example.com/"
expect(@client.token_credential_uri.to_s).to eq "http://example.com/"
end
it 'should allow the token_credential_uri to be set to a Hash' do
@client.token_credential_uri = {
:scheme => 'http', :host => 'example.com', :path => '/token'
}
expect(@client.token_credential_uri.to_s).to eq 'http://example.com/token'
end
it 'should allow the token_credential_uri to be set to a URI' do
@client.token_credential_uri =
Addressable::URI.parse("http://example.com/")
expect(@client.token_credential_uri.to_s).to eq "http://example.com/"
end
it 'should have no client_credential' do
expect(@client.client_credential).to be_nil
end
it 'should raise an error for partially set client credentials' do
@client.client_credential_key = "12345"
@client.client_credential_secret = nil
expect(lambda do
@client.client_credential
end).to raise_error(ArgumentError)
end
it 'should raise an error for partially set client credentials' do
@client.client_credential_key = nil
@client.client_credential_secret = "54321"
expect(lambda do
@client.client_credential
end).to raise_error(ArgumentError)
end
it 'should allow the client_credential to be set to a ' +
'Signet::OAuth1::Credential' do
@client.client_credential =
Signet::OAuth1::Credential.new("12345", "54321")
expect(@client.client_credential_key).to eq "12345"
expect(@client.client_credential_secret).to eq "54321"
expect(@client.client_credential).to eq Signet::OAuth1::Credential.new("12345", "54321")
end
it 'should allow the client_credential to be set to nil' do
@client.client_credential_key = "12345"
@client.client_credential_secret = "54321"
expect(@client.client_credential_key).to eq "12345"
expect(@client.client_credential_secret).to eq "54321"
@client.client_credential = nil
expect(@client.client_credential).to be_nil
expect(@client.client_credential_key).to be_nil
expect(@client.client_credential_secret).to be_nil
end
it 'should not allow the client_credential to be set to a bogus value' do
expect(lambda do
@client.client_credential = 42
end).to raise_error(TypeError)
end
it 'should have no client_credential_key' do
expect(@client.client_credential_key).to be_nil
end
it 'should allow the client_credential_key to be set to a String' do
@client.client_credential_key = "12345"
expect(@client.client_credential_key).to eq "12345"
end
it 'should not allow the client_credential_key to be set to a non-String' do
expect(lambda do
@client.client_credential_key = 12345
end).to raise_error(TypeError)
end
it 'should have no client_credential_secret' do
expect(@client.client_credential_secret).to be_nil
end
it 'should allow the client_credential_secret to be set to a String' do
@client.client_credential_secret = "54321"
expect(@client.client_credential_secret).to eq "54321"
end
it 'should not allow the client_credential_secret ' +
'to be set to a non-String' do
expect(lambda do
@client.client_credential_secret = 54321
end).to raise_error(TypeError)
end
it 'should have an out-of-band callback' do
expect(@client.callback).to eq ::Signet::OAuth1::OUT_OF_BAND
end
it 'should allow the callback to be set to a String' do
@client.callback = "http://example.com/callback"
expect(@client.callback).to eq "http://example.com/callback"
end
it 'should allow the callback to be set to a URI' do
@client.callback =
Addressable::URI.parse("http://example.com/callback")
expect(@client.callback).to eq "http://example.com/callback"
end
it 'should not allow the callback to be set to a non-String' do
expect(lambda do
@client.callback = 12345
end).to raise_error(TypeError)
end
it 'should raise an error if the temporary credentials URI is not set' do
@client.client_credential_key = 'dpf43f3p2l4k3l03'
@client.client_credential_secret = 'kd94hf93k423kf44'
expect(lambda do
@client.generate_temporary_credential_request
end).to raise_error(ArgumentError)
end
it 'should raise an error if the client credential key is not set' do
@client.temporary_credential_uri =
'http://example.com/temporary_credentials'
@client.client_credential_secret = 'kd94hf93k423kf44'
expect(lambda do
@client.generate_temporary_credential_request
end).to raise_error(ArgumentError)
end
it 'should raise an error if the client credential secret is not set' do
@client.temporary_credential_uri =
'http://example.com/temporary_credentials'
@client.client_credential_key = 'dpf43f3p2l4k3l03'
expect(lambda do
@client.generate_temporary_credential_request
end).to raise_error(ArgumentError)
end
it 'should have no temporary_credential' do
expect(@client.temporary_credential).to be_nil
end
it 'should raise an error for partially set temporary credentials' do
@client.temporary_credential_key = "12345"
@client.temporary_credential_secret = nil
expect(lambda do
@client.temporary_credential
end).to raise_error(ArgumentError)
end
it 'should raise an error for partially set temporary credentials' do
@client.temporary_credential_key = nil
@client.temporary_credential_secret = "54321"
expect(lambda do
@client.temporary_credential
end).to raise_error(ArgumentError)
end
it 'should allow the temporary_credential to be set to a ' +
'Signet::OAuth1::Credential' do
@client.temporary_credential =
Signet::OAuth1::Credential.new("12345", "54321")
expect(@client.temporary_credential_key).to eq "12345"
expect(@client.temporary_credential_secret).to eq "54321"
expect(@client.temporary_credential).to eq Signet::OAuth1::Credential.new("12345", "54321")
end
it 'should allow the temporary_credential to be set to nil' do
@client.temporary_credential_key = "12345"
@client.temporary_credential_secret = "54321"
expect(@client.temporary_credential_key).to eq "12345"
expect(@client.temporary_credential_secret).to eq "54321"
@client.temporary_credential = nil
expect(@client.temporary_credential).to be_nil
expect(@client.temporary_credential_key).to be_nil
expect(@client.temporary_credential_secret).to be_nil
end
it 'should not allow the temporary_credential to be set to a bogus value' do
expect(lambda do
@client.temporary_credential = 42
end).to raise_error(TypeError)
end
it 'should have no temporary_credential_key' do
expect(@client.temporary_credential_key).to be_nil
end
it 'should allow the temporary_credential_key to be set to a String' do
@client.temporary_credential_key = "12345"
expect(@client.temporary_credential_key).to eq "12345"
end
it 'should not allow the temporary_credential_key ' +
'to be set to a non-String' do
expect(lambda do
@client.temporary_credential_key = 12345
end).to raise_error(TypeError)
end
it 'should have no temporary_credential_secret' do
expect(@client.temporary_credential_secret).to be_nil
end
it 'should allow the temporary_credential_secret to be set to a String' do
@client.temporary_credential_secret = "54321"
expect(@client.temporary_credential_secret).to eq "54321"
end
it 'should not allow the temporary_credential_secret ' +
'to be set to a non-String' do
expect(lambda do
@client.temporary_credential_secret = 54321
end).to raise_error(TypeError)
end
it 'should have no token_credential' do
expect(@client.token_credential).to be_nil
end
it 'should raise an error for partially set token credentials' do
@client.token_credential_key = "12345"
@client.token_credential_secret = nil
expect(lambda do
@client.token_credential
end).to raise_error(ArgumentError)
end
it 'should raise an error for partially set token credentials' do
@client.token_credential_key = nil
@client.token_credential_secret = "54321"
expect(lambda do
@client.token_credential
end).to raise_error(ArgumentError)
end
it 'should allow the token_credential to be set to a ' +
'Signet::OAuth1::Credential' do
@client.token_credential =
Signet::OAuth1::Credential.new("12345", "54321")
expect(@client.token_credential_key).to eq "12345"
expect(@client.token_credential_secret).to eq "54321"
expect(@client.token_credential).to eq Signet::OAuth1::Credential.new("12345", "54321")
end
it 'should allow the token_credential to be set to nil' do
@client.token_credential_key = "12345"
@client.token_credential_secret = "54321"
expect(@client.token_credential_key).to eq "12345"
expect(@client.token_credential_secret).to eq "54321"
@client.token_credential = nil
expect(@client.token_credential).to be_nil
expect(@client.token_credential_key).to be_nil
expect(@client.token_credential_secret).to be_nil
end
it 'should not allow the token_credential to be set to a bogus value' do
expect(lambda do
@client.token_credential = 42
end).to raise_error(TypeError)
end
it 'should have no token_credential_key' do
expect(@client.token_credential_key).to be_nil
end
it 'should allow the token_credential_key to be set to a String' do
@client.token_credential_key = "12345"
expect(@client.token_credential_key).to eq "12345"
end
it 'should not allow the token_credential_key ' +
'to be set to a non-String' do
expect(lambda do
@client.token_credential_key = 12345
end).to raise_error(TypeError)
end
it 'should have no token_credential_secret' do
expect(@client.token_credential_secret).to be_nil
end
it 'should allow the token_credential_secret to be set to a String' do
@client.token_credential_secret = "54321"
expect(@client.token_credential_secret).to eq "54321"
end
it 'should not allow the token_credential_secret ' +
'to be set to a non-String' do
expect(lambda do
@client.token_credential_secret = 54321
end).to raise_error(TypeError)
end
it 'should not allow the two_legged flag ' +
'to be set to a non-Boolean' do
expect(lambda do
@client.two_legged = 42
end).to raise_error(TypeError)
end
end
describe Signet::OAuth1::Client, 'configured' do
before do
@client = Signet::OAuth1::Client.new
@client.temporary_credential_uri =
'http://example.com/temporary_credentials'
@client.authorization_uri =
'http://example.com/authorize'
@client.token_credential_uri =
'http://example.com/token_credentials'
@client.callback = 'http://example.com/callback'
@client.client_credential_key = 'dpf43f3p2l4k3l03'
@client.client_credential_secret = 'kd94hf93k423kf44'
@client.temporary_credential_key = 'hh5s93j4hdidpola'
@client.temporary_credential_secret = 'hdhd0244k9j7ao03'
@client.token_credential_key = 'nnch734d00sl2jdk'
@client.token_credential_secret = 'pfkkdhi9sl3r4s00'
end
it 'should generate a JSON representation of the client' do
json = @client.to_json
expect(json).not_to be_nil
deserialized = MultiJson.load(json)
expect(deserialized["temporary_credential_uri"]).to eq 'http://example.com/temporary_credentials'
expect(deserialized["authorization_uri"]).to include(
'http://example.com/authorize')
expect(deserialized["token_credential_uri"]).to eq 'http://example.com/token_credentials'
expect(deserialized["callback"]).to eq 'http://example.com/callback'
expect(deserialized["client_credential_key"]).to eq 'dpf43f3p2l4k3l03'
expect(deserialized["client_credential_secret"]).to eq 'kd94hf93k423kf44'
expect(deserialized["temporary_credential_key"]).to eq 'hh5s93j4hdidpola'
expect(deserialized["temporary_credential_secret"]).to eq 'hdhd0244k9j7ao03'
expect(deserialized["token_credential_key"]).to eq 'nnch734d00sl2jdk'
expect(deserialized["token_credential_secret"]).to eq 'pfkkdhi9sl3r4s00'
end
it 'should generate an authorization URI with a callback' do
@client.temporary_credential_key = nil
expect(@client.authorization_uri.to_s).to eq 'http://example.com/authorize?oauth_callback=http://example.com/callback'
end
it 'should generate an authorization URI with a temporary credential' do
@client.callback = nil
expect(@client.authorization_uri.to_s).to include(
'oauth_token=hh5s93j4hdidpola'
)
end
it 'should generate an authorization URI both a callback and ' +
'a temporary credential' do
expect(@client.authorization_uri.to_s).to include(
'oauth_callback=http://example.com/callback'
)
expect(@client.authorization_uri.to_s).to include(
'oauth_token=hh5s93j4hdidpola'
)
end
it 'should generate an authorization URI with additional parameters' do
authorization_uri = @client.authorization_uri(
:additional_parameters => {:domain => 'www.example.com'}
)
expect(authorization_uri.to_s).to include(
'oauth_callback=http://example.com/callback'
)
expect(authorization_uri.to_s).to include(
'oauth_token=hh5s93j4hdidpola'
)
expect(authorization_uri.to_s).to include(
'domain=www.example.com'
)
end
it 'should raise an error if the verifier is not provided' do
expect(lambda do
@client.generate_token_credential_request
end).to raise_error(ArgumentError)
expect(lambda do
@client.generate_token_credential_request(:verifier => nil)
end).to raise_error(ArgumentError)
end
it 'should raise an error if the token credentials URI is not set' do
@client.token_credential_uri = nil
expect(lambda do
@client.generate_token_credential_request(:verifier => '12345')
end).to raise_error(ArgumentError)
end
it 'should raise an error if the client credential key is not set' do
@client.client_credential_key = nil
expect(lambda do
@client.generate_token_credential_request(:verifier => '12345')
end).to raise_error(ArgumentError)
end
it 'should raise an error if the client credential secret is not set' do
@client.client_credential_secret = nil
expect(lambda do
@client.generate_token_credential_request(:verifier => '12345')
end).to raise_error(ArgumentError)
end
it 'should raise an error if the temporary credential key is not set' do
@client.temporary_credential_key = nil
expect(lambda do
@client.generate_token_credential_request(:verifier => '12345')
end).to raise_error(ArgumentError)
end
it 'should raise an error if the temporary credential secret is not set' do
@client.temporary_credential_secret = nil
expect(lambda do
@client.generate_token_credential_request(:verifier => '12345')
end).to raise_error(ArgumentError)
end
it 'should raise an error if the client credential key is not set' do
@client.client_credential_key = nil
expect(lambda do
@client.generate_authenticated_request
end).to raise_error(ArgumentError)
end
it 'should raise an error if the client credential secret is not set' do
@client.client_credential_secret = nil
expect(lambda do
@client.generate_authenticated_request
end).to raise_error(ArgumentError)
end
it 'should raise an error if the token credential key is not set' do
@client.token_credential_key = nil
expect(lambda do
@client.generate_authenticated_request
end).to raise_error(ArgumentError)
end
it 'should raise an error if the token credential secret is not set' do
@client.token_credential_secret = nil
expect(lambda do
@client.generate_authenticated_request
end).to raise_error(ArgumentError)
end
it 'should raise an error if no request is provided' do
expect(lambda do
@client.generate_authenticated_request
end).to raise_error(ArgumentError)
end
it 'should raise an error if a bogus request is provided' do
expect {
@client.generate_authenticated_request(
:request => []
)
}.to raise_error(ArgumentError)
end
it 'should not raise an error if a request is ' +
'provided without a connection' do
request = @client.generate_authenticated_request(
:request => conn.build_request(:get) do |req|
req.url('http://www.example.com/')
end
)
end
it 'should raise an error if no URI is provided' do
expect(lambda do
@client.generate_authenticated_request(
:method => 'GET',
:headers => [],
:body => ''
)
end).to raise_error(ArgumentError)
end
it 'should not raise an error if a request body is chunked' do
request = @client.generate_authenticated_request(
:method => 'POST',
:uri => 'https://photos.example.net/photos',
:body => ['A chunked body.']
)
expect(request).to be_kind_of(Faraday::Request)
expect(request.body).to eq 'A chunked body.'
end
it 'should not raise an error if a request body is chunked' do
chunked_body = StringIO.new
chunked_body.write('A chunked body.')
chunked_body.rewind
request = @client.generate_authenticated_request(
:method => 'POST',
:uri => 'https://photos.example.net/photos',
:body => chunked_body
)
expect(request).to be_kind_of(Faraday::Request)
expect(request.body).to eq 'A chunked body.'
end
it 'should raise an error if a request body is of a bogus type' do
expect(lambda do
@client.generate_authenticated_request(
:method => 'POST',
:uri => 'https://photos.example.net/photos',
:body => 42
)
end).to raise_error(TypeError)
end
it 'should correctly fetch the temporary credentials' do
# Repeat this because signatures change from test to test
10.times do
request = @client.generate_temporary_credential_request
expect(request.method).to eq :post
expect(request.path).to eq 'http://example.com/temporary_credentials'
authorization_header = request.headers['Authorization']
parameters = ::Signet::OAuth1.parse_authorization_header(
authorization_header
).inject({}) { |h,(k,v)| h[k]=v; h }
expect(parameters).not_to have_key('oauth_client_credential_key')
expect(parameters).not_to have_key('oauth_temporary_credential_key')
expect(parameters).not_to have_key('oauth_token')
expect(parameters['oauth_nonce']).to match(/^\w+$/)
expect(parameters['oauth_callback']).to eq @client.callback
expect(parameters['oauth_timestamp']).to match(/^\d+$/)
expect(parameters['oauth_signature_method']).to eq 'HMAC-SHA1'
expect(parameters['oauth_consumer_key']).to eq @client.client_credential_key
expect(parameters['oauth_signature']).to match(/^[a-zA-Z0-9\=\/\+]+$/)
expect(parameters['oauth_version']).to eq '1.0'
end
end
it 'should correctly fetch the token credentials' do
# Repeat this because signatures change from test to test
10.times do
request = @client.generate_token_credential_request(
:verifier => '473f82d3'
)
expect(request.method).to eq :post
expect(request.path).to eq 'http://example.com/token_credentials'
authorization_header = request.headers['Authorization']
parameters = ::Signet::OAuth1.parse_authorization_header(
authorization_header
).inject({}) { |h,(k,v)| h[k]=v; h }
expect(parameters).not_to have_key('oauth_client_credential_key')
expect(parameters).not_to have_key('oauth_temporary_credential_key')
expect(parameters).not_to have_key('oauth_callback')
expect(parameters['oauth_nonce']).to match(/^\w+$/)
expect(parameters['oauth_timestamp']).to match(/^\d+$/)
expect(parameters['oauth_signature_method']).to eq 'HMAC-SHA1'
expect(parameters['oauth_consumer_key']).to eq @client.client_credential_key
expect(parameters['oauth_token']).to eq @client.temporary_credential_key
expect(parameters['oauth_signature']).to match(/^[a-zA-Z0-9\=\/\+]+$/)
expect(parameters['oauth_verifier']).to eq '473f82d3'
expect(parameters['oauth_version']).to eq '1.0'
end
end
it 'should correctly fetch the protected resource' do
# Repeat this because signatures change from test to test
10.times do
original_request = [
'GET',
'https://photos.example.net/photos?file=vacation.jpg&size=original',
[['Host', 'photos.example.net']],
['']
]
signed_request = @client.generate_authenticated_request(
:request => original_request
)
expect(signed_request.method).to eq :get
expect(signed_request.path).to eq 'https://photos.example.net/photos'
expect(signed_request.params).to eq({"file"=>"vacation.jpg", "size"=>"original"})
authorization_header = signed_request.headers['Authorization']
expect(signed_request.body).to eq ''
parameters = ::Signet::OAuth1.parse_authorization_header(
authorization_header
).inject({}) { |h,(k,v)| h[k]=v; h }
expect(parameters).not_to have_key('oauth_client_credential_key')
expect(parameters).not_to have_key('oauth_temporary_credential_key')
expect(parameters).not_to have_key('oauth_token_credential_key')
expect(parameters).not_to have_key('oauth_callback')
expect(parameters['oauth_nonce']).to match(/^\w+$/)
expect(parameters['oauth_timestamp']).to match(/^\d+$/)
expect(parameters['oauth_signature_method']).to eq 'HMAC-SHA1'
expect(parameters['oauth_consumer_key']).to eq @client.client_credential_key
expect(parameters['oauth_token']).to eq @client.token_credential_key
expect(parameters['oauth_signature']).to match(/^[a-zA-Z0-9\=\/\+]+$/)
expect(parameters['oauth_version']).to eq '1.0'
end
end
it 'should correctly fetch the protected resource' do
# Repeat this because signatures change from test to test
10.times do
original_request = [
'POST',
'https://photos.example.net/photos',
[
['Host', 'photos.example.net'],
['Content-Type', 'application/x-www-form-urlencoded; charset=utf-8'],
['Content-Length', '31'],
],
['file=vacation.jpg&size=original']
]
signed_request = @client.generate_authenticated_request(
:request => original_request
)
expect(signed_request.method).to eq :post
expect(signed_request.path).to eq 'https://photos.example.net/photos'
authorization_header = signed_request.headers['Authorization']
expect(signed_request.body).to eq 'file=vacation.jpg&size=original'
parameters = ::Signet::OAuth1.parse_authorization_header(
authorization_header
).inject({}) { |h,(k,v)| h[k]=v; h }
expect(parameters).not_to have_key('oauth_client_credential_key')
expect(parameters).not_to have_key('oauth_temporary_credential_key')
expect(parameters).not_to have_key('oauth_token_credential_key')
expect(parameters).not_to have_key('oauth_callback')
expect(parameters['oauth_nonce']).to match(/^\w+$/)
expect(parameters['oauth_timestamp']).to match(/^\d+$/)
expect(parameters['oauth_signature_method']).to eq 'HMAC-SHA1'
expect(parameters['oauth_consumer_key']).to eq @client.client_credential_key
expect(parameters['oauth_token']).to eq @client.token_credential_key
expect(parameters['oauth_signature']).to match(/^[a-zA-Z0-9\=\/\+]+$/)
expect(parameters['oauth_version']).to eq '1.0'
end
end
describe 'with Faraday requests' do
it 'should correctly get the protected resource' do
# Repeat this because signatures change from test to test
10.times do
original_request = conn.build_request(:get) do |req|
req.url(
'https://photos.example.net/photos?file=vacation.jpg&size=original'
)
req.headers = Faraday::Utils::Headers.new(
[['Host', 'photos.example.net']]
)
req.body = ''
end
signed_request = @client.generate_authenticated_request(
:request => original_request
)
# Should be same request object
expect(original_request['Authorization']).to eq signed_request['Authorization']
expect(signed_request.method).to eq :get
expect(signed_request.path).to eq 'https://photos.example.net/photos'
expect(signed_request.params).to eq ({"file"=>"vacation.jpg", "size"=>"original"})
authorization_header = signed_request.headers['Authorization']
expect(signed_request.body).to eq ''
parameters = ::Signet::OAuth1.parse_authorization_header(
authorization_header
).inject({}) { |h,(k,v)| h[k]=v; h }
expect(parameters).not_to have_key('oauth_client_credential_key')
expect(parameters).not_to have_key('oauth_temporary_credential_key')
expect(parameters).not_to have_key('oauth_token_credential_key')
expect(parameters).not_to have_key('oauth_callback')
expect(parameters['oauth_nonce']).to match(/^\w+$/)
expect(parameters['oauth_timestamp']).to match(/^\d+$/)
expect(parameters['oauth_signature_method']).to eq 'HMAC-SHA1'
expect(parameters['oauth_consumer_key']).to eq @client.client_credential_key
expect(parameters['oauth_token']).to eq @client.token_credential_key
expect(parameters['oauth_signature']).to match(/^[a-zA-Z0-9\=\/\+]+$/)
expect(parameters['oauth_version']).to eq '1.0'
end
end
it 'should correctly post the protected resource' do
# Repeat this because signatures change from test to test
10.times do
original_request = conn.build_request(:post) do |req|
req.url('https://photos.example.net/photos')
req.headers = Faraday::Utils::Headers.new([
['Host', 'photos.example.net'],
['Content-Type', 'application/x-www-form-urlencoded; charset=utf-8'],
['Content-Length', '31'],
])
req.body = {
'size' => 'original',
'file' => 'vacation.jpg'
}
end
signed_request = @client.generate_authenticated_request(
:request => original_request
)
# Should be same request object
expect(original_request['Authorization']).to eq signed_request['Authorization']
expect(signed_request.method).to eq :post
expect(signed_request.path).to eq 'https://photos.example.net/photos'
authorization_header = signed_request.headers['Authorization']
# Can't rely on the order post parameters are encoded in.
expect(signed_request.body).to include('file=vacation.jpg')
expect(signed_request.body).to include('size=original')
parameters = ::Signet::OAuth1.parse_authorization_header(
authorization_header
).inject({}) { |h,(k,v)| h[k]=v; h }
expect(parameters).not_to have_key('oauth_client_credential_key')
expect(parameters).not_to have_key('oauth_temporary_credential_key')
expect(parameters).not_to have_key('oauth_token_credential_key')
expect(parameters).not_to have_key('oauth_callback')
expect(parameters['oauth_nonce']).to match(/^\w+$/)
expect(parameters['oauth_timestamp']).to match(/^\d+$/)
expect(parameters['oauth_signature_method']).to eq 'HMAC-SHA1'
expect(parameters['oauth_consumer_key']).to eq @client.client_credential_key
expect(parameters['oauth_token']).to eq @client.token_credential_key
expect(parameters['oauth_signature']).to match(/^[a-zA-Z0-9\=\/\+]+$/)
expect(parameters['oauth_version']).to eq '1.0'
end
end
end
end
signet-0.11.0/spec/signet/oauth_1/credential_spec.rb 0000644 0000041 0000041 00000012272 13410324340 022344 0 ustar www-data www-data # Copyright (C) 2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'
require 'signet/oauth_1/credential'
describe Signet::OAuth1::Credential, 'with a Hash for initialization' do
it 'should accept "oauth_token" and "oauth_token_secret" pairs' do
token = Signet::OAuth1::Credential.new({
"oauth_token" => "dpf43f3p2l4k3l03",
"oauth_token_secret" => "kd94hf93k423kf44"
})
expect(token.key).to eq "dpf43f3p2l4k3l03"
expect(token.secret).to eq "kd94hf93k423kf44"
end
it 'should accept :oauth_token and :oauth_token_secret pairs' do
token = Signet::OAuth1::Credential.new({
:oauth_token => "dpf43f3p2l4k3l03",
:oauth_token_secret => "kd94hf93k423kf44"
})
expect(token.key).to eq "dpf43f3p2l4k3l03"
expect(token.secret).to eq "kd94hf93k423kf44"
end
it 'should accept "key" and "secret" pairs' do
token = Signet::OAuth1::Credential.new({
"key" => "dpf43f3p2l4k3l03",
"secret" => "kd94hf93k423kf44"
})
expect(token.key).to eq "dpf43f3p2l4k3l03"
expect(token.secret).to eq "kd94hf93k423kf44"
end
it 'should accept :key and :secret pairs' do
token = Signet::OAuth1::Credential.new(
:key => "dpf43f3p2l4k3l03",
:secret => "kd94hf93k423kf44"
)
expect(token.key).to eq "dpf43f3p2l4k3l03"
expect(token.secret).to eq "kd94hf93k423kf44"
end
it 'should not complain about additional parameters' do
token = Signet::OAuth1::Credential.new({
"oauth_token" => "dpf43f3p2l4k3l03",
"oauth_token_secret" => "kd94hf93k423kf44",
"oauth_version" => "1.0"
})
expect(token.key).to eq "dpf43f3p2l4k3l03"
expect(token.secret).to eq "kd94hf93k423kf44"
end
it 'should allow parameters to be specified as an implicit Hash' do
class ParameterHashSet
def initialize(parameters)
@parameters = parameters.inject({}) { |h,(k,v)| h[k]=v; h }
end
def to_hash
return @parameters
end
end
token = Signet::OAuth1::Credential.new(ParameterHashSet.new({
"oauth_token" => "dpf43f3p2l4k3l03",
"oauth_token_secret" => "kd94hf93k423kf44",
"oauth_version" => "1.0"
}))
expect(token.key).to eq "dpf43f3p2l4k3l03"
expect(token.secret).to eq "kd94hf93k423kf44"
end
it 'should allow parameters to be specified as an Enumerable' do
token = Signet::OAuth1::Credential.new([
["oauth_token", "dpf43f3p2l4k3l03"],
["oauth_token_secret", "kd94hf93k423kf44"],
["oauth_version", "1.0"]
])
expect(token.key).to eq "dpf43f3p2l4k3l03"
expect(token.secret).to eq "kd94hf93k423kf44"
end
it 'should allow parameters to be specified as an implicit Array' do
class ParameterArraySet
def initialize(parameters)
@parameters = parameters
end
def to_ary
return @parameters
end
end
token = Signet::OAuth1::Credential.new(ParameterArraySet.new([
["oauth_token", "dpf43f3p2l4k3l03"],
["oauth_token_secret", "kd94hf93k423kf44"],
["oauth_version", "1.0"]
]))
expect(token.key).to eq "dpf43f3p2l4k3l03"
expect(token.secret).to eq "kd94hf93k423kf44"
end
it 'should raise an error if key and secret are not present' do
expect(lambda do
Signet::OAuth1::Credential.new({})
end).to raise_error(ArgumentError)
end
it 'should allow key and secret to be passed in as a tuple' do
token = Signet::OAuth1::Credential.new(
["dpf43f3p2l4k3l03", "kd94hf93k423kf44"]
)
expect(token.key).to eq "dpf43f3p2l4k3l03"
expect(token.secret).to eq "kd94hf93k423kf44"
end
it 'should allow key and secret to be passed in as normal parameters' do
token = Signet::OAuth1::Credential.new(
"dpf43f3p2l4k3l03", "kd94hf93k423kf44"
)
expect(token.key).to eq "dpf43f3p2l4k3l03"
expect(token.secret).to eq "kd94hf93k423kf44"
end
it 'should raise an error if key or secret are of the wrong type' do
expect(lambda do
Signet::OAuth1::Credential.new("dpf43f3p2l4k3l03", 42)
end).to raise_error(TypeError)
expect(lambda do
Signet::OAuth1::Credential.new(42, "kd94hf93k423kf44")
end).to raise_error(TypeError)
end
it 'should raise an error if the wrong number of arguments are passed' do
expect(lambda do
Signet::OAuth1::Credential.new(
"dpf43f3p2l4k3l03", "kd94hf93k423kf44", "something else"
)
end).to raise_error(ArgumentError)
end
it 'should convert to a Hash object' do
token = Signet::OAuth1::Credential.new(
"dpf43f3p2l4k3l03", "kd94hf93k423kf44"
)
parameters = token.to_h
expect(parameters['oauth_token']).to eq "dpf43f3p2l4k3l03"
expect(parameters['oauth_token_secret']).to eq "kd94hf93k423kf44"
end
end
signet-0.11.0/spec/signet/oauth_2/ 0000755 0000041 0000041 00000000000 13410324340 016670 5 ustar www-data www-data signet-0.11.0/spec/signet/oauth_2/client_spec.rb 0000644 0000041 0000041 00000125341 13410324340 021513 0 ustar www-data www-data # Copyright (C) 2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'
require 'signet/oauth_2/client'
require 'openssl'
require 'jwt'
require 'date'
conn = Faraday.default_connection
def build_json_response(payload)
[200, { "Content-Type" => "application/json; charset=utf-8" }, MultiJson.dump(payload)]
end
def build_form_encoded_response(payload)
[200, { "Content-Type" => "application/json; charset=utf-8" }, Addressable::URI.form_encode(payload)]
end
describe Signet::OAuth2::Client, 'unconfigured' do
before do
@client = Signet::OAuth2::Client.new
end
it 'should allow additional paraemters to be set.' do
@client.additional_parameters['type'] = 'web_server'
expect(@client.additional_parameters).to eq({'type' => 'web_server'})
end
it 'should raise an error if a bogus scope is provided' do
expect(lambda do
@client = Signet::OAuth2::Client.new(:scope => :bogus)
end).to raise_error(TypeError)
end
it 'should raise an error if a scope array is provided with spaces' do
expect(lambda do
@client = Signet::OAuth2::Client.new(:scope => [
'legit',
'bogus bogus'
])
end).to raise_error(ArgumentError)
end
it 'should allow the scope to be set to a String' do
@client.scope = 'legit'
expect(@client.scope).to eq ['legit']
@client.scope = 'legit alsolegit'
expect(@client.scope).to eq ['legit', 'alsolegit']
end
it 'should allow the scope to be set to an Array' do
@client.scope = ['legit']
expect(@client.scope).to eq ['legit']
@client.scope = ['legit', 'alsolegit']
expect(@client.scope).to eq ['legit', 'alsolegit']
end
it 'should raise an error if a bogus redirect URI is provided' do
expect(lambda do
@client = Signet::OAuth2::Client.new(:redirect_uri => :bogus)
end).to raise_error(TypeError)
end
it 'should raise an error if a relative redirect URI is provided' do
expect(lambda do
@client = Signet::OAuth2::Client.new(:redirect_uri => '/relative/path')
end).to raise_error(ArgumentError)
end
it 'should allow "postmessage" as a redirect URI (Google hack)' do
@client.authorization_uri = 'https://example.com/authorize'
@client.client_id = 's6BhdRkqt3'
@client.redirect_uri = 'postmessage'
expect(@client.authorization_uri.query_values['redirect_uri']).to eq 'postmessage'
end
it 'should allow oob values as a redirect URI (for installed apps)' do
@client.authorization_uri = 'https://example.com/authorize'
@client.client_id = 's6BhdRkqt3'
@client.redirect_uri = 'urn:ietf:wg:oauth:2.0:oob'
expect(@client.authorization_uri.query_values['redirect_uri']).to eq 'urn:ietf:wg:oauth:2.0:oob'
@client.redirect_uri = 'oob'
expect(@client.authorization_uri.query_values['redirect_uri']).to eq 'oob'
end
it 'should have no authorization_uri' do
expect(@client.authorization_uri).to eq nil
end
it 'should allow the authorization_uri to be set to a String' do
@client.authorization_uri = 'https://example.com/authorize'
@client.client_id = 's6BhdRkqt3'
@client.redirect_uri = 'https://example.client.com/callback'
expect(@client.authorization_uri.to_s).to include(
'https://example.com/authorize'
)
expect(@client.authorization_uri.query_values['client_id']).to eq 's6BhdRkqt3'
expect(@client.authorization_uri.query_values['redirect_uri']).to eq (
'https://example.client.com/callback'
)
end
it 'should allow the authorization_uri to be set to a Hash' do
@client.authorization_uri = {
:scheme => 'https', :host => 'example.com', :path => '/authorize'
}
@client.client_id = 's6BhdRkqt3'
@client.redirect_uri = 'https://example.client.com/callback'
expect(@client.authorization_uri.to_s).to include(
'https://example.com/authorize'
)
expect(@client.authorization_uri.query_values['client_id']).to eq 's6BhdRkqt3'
expect(@client.authorization_uri.query_values['redirect_uri']).to eq (
'https://example.client.com/callback'
)
end
it 'should allow the authorization_uri to be set to a URI' do
@client.authorization_uri =
Addressable::URI.parse('https://example.com/authorize')
@client.client_id = 's6BhdRkqt3'
@client.redirect_uri =
Addressable::URI.parse('https://example.client.com/callback')
expect(@client.authorization_uri.to_s).to include(
'https://example.com/authorize'
)
expect(@client.authorization_uri.query_values['client_id']).to eq 's6BhdRkqt3'
expect(@client.authorization_uri.query_values['redirect_uri']).to eq (
'https://example.client.com/callback'
)
end
it 'should require a redirect URI when getting the authorization_uri' do
@client.authorization_uri =
Addressable::URI.parse('https://example.com/authorize')
@client.client_id = 's6BhdRkqt3'
expect(lambda do
@client.authorization_uri
end).to raise_error(ArgumentError)
end
it 'should require a client ID when getting the authorization_uri' do
@client.authorization_uri =
Addressable::URI.parse('https://example.com/authorize')
@client.redirect_uri =
Addressable::URI.parse('https://example.client.com/callback')
expect(lambda do
@client.authorization_uri
end).to raise_error(ArgumentError)
end
it 'should have no token_credential_uri' do
expect(@client.token_credential_uri).to eq nil
end
it 'should allow the token_credential_uri to be set to a String' do
@client.token_credential_uri = "https://example.com/token"
expect(@client.token_credential_uri.to_s).to eq "https://example.com/token"
end
it 'should allow the token_credential_uri to be set to a Hash' do
@client.token_credential_uri = {
:scheme => 'https', :host => 'example.com', :path => '/token'
}
expect(@client.token_credential_uri.to_s).to eq 'https://example.com/token'
end
it 'should allow the token_credential_uri to be set to a URI' do
@client.token_credential_uri =
Addressable::URI.parse("https://example.com/token")
expect(@client.token_credential_uri.to_s).to eq "https://example.com/token"
end
end
describe Signet::OAuth2::Client, 'configured for assertions profile' do
describe 'when using RSA keys' do
before do
@key = OpenSSL::PKey::RSA.new 2048
@client = Signet::OAuth2::Client.new(
:token_credential_uri =>
'https://oauth2.googleapis.com/token',
:scope => 'https://www.googleapis.com/auth/userinfo.profile',
:issuer => 'app@example.com',
:audience => 'https://oauth2.googleapis.com/token',
:signing_key => @key
)
end
it 'should generate valid JWTs' do
jwt = @client.to_jwt
expect(jwt).not_to be_nil
claim, header = JWT.decode(jwt, @key.public_key, true, algorithm: 'RS256')
expect(claim["iss"]).to eq 'app@example.com'
expect(claim["scope"]).to eq 'https://www.googleapis.com/auth/userinfo.profile'
expect(claim["aud"]).to eq 'https://oauth2.googleapis.com/token'
end
it 'should generate valid JWTs for impersonation' do
@client.principal = 'user@example.com'
jwt = @client.to_jwt
expect(jwt).not_to be_nil
claim, header = JWT.decode(jwt, @key.public_key, true, algorithm: 'RS256')
expect(claim["iss"]).to eq 'app@example.com'
expect(claim["prn"]).to eq 'user@example.com'
expect(claim["scope"]).to eq 'https://www.googleapis.com/auth/userinfo.profile'
expect(claim["aud"]).to eq 'https://oauth2.googleapis.com/token'
end
it 'should generate valid JWTs for impersonation using deprecated person attribute' do
@client.person = 'user@example.com'
jwt = @client.to_jwt
expect(jwt).not_to be_nil
claim, header = JWT.decode(jwt, @key.public_key, true, algorithm: 'RS256')
expect(claim["iss"]).to eq 'app@example.com'
expect(claim["prn"]).to eq 'user@example.com'
expect(claim["scope"]).to eq 'https://www.googleapis.com/auth/userinfo.profile'
expect(claim["aud"]).to eq 'https://oauth2.googleapis.com/token'
end
it 'should generate valid JWTs for impersonation using the sub attribute' do
@client.sub = 'user@example.com'
jwt = @client.to_jwt
expect(jwt).not_to be_nil
claim, header = JWT.decode(jwt, @key.public_key, true, algorithm: 'RS256')
expect(claim["iss"]).to eq 'app@example.com'
expect(claim["sub"]).to eq 'user@example.com'
expect(claim["scope"]).to eq 'https://www.googleapis.com/auth/userinfo.profile'
expect(claim["aud"]).to eq 'https://oauth2.googleapis.com/token'
end
it 'should generate a JSON representation of the client' do
@client.principal = 'user@example.com'
json = @client.to_json
expect(json).not_to be_nil
deserialized = MultiJson.load(json)
expect(deserialized["token_credential_uri"]).to eq 'https://oauth2.googleapis.com/token'
expect(deserialized["scope"]).to eq ['https://www.googleapis.com/auth/userinfo.profile']
expect(deserialized["issuer"]).to eq 'app@example.com'
expect(deserialized["audience"]).to eq 'https://oauth2.googleapis.com/token'
expect(deserialized["signing_key"]).to eq @key.to_s
end
it 'should send valid access token request' do
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.post('/token') do |env|
params = Addressable::URI.form_unencode(env[:body])
claim, header = JWT.decode(params.assoc("assertion").last, @key.public_key, true, algorithm: 'RS256')
expect(params.assoc("grant_type")).to eq ['grant_type','urn:ietf:params:oauth:grant-type:jwt-bearer']
build_json_response({
"access_token" => "1/abcdef1234567890",
"token_type" => "Bearer",
"expires_in" => 3600
})
end
end
connection = Faraday.new(:url => 'https://www.google.com') do |builder|
builder.adapter(:test, stubs)
end
@client.fetch_access_token!(:connection => connection)
expect(@client.access_token).to eq "1/abcdef1234567890"
stubs.verify_stubbed_calls
end
end
describe 'when using shared secrets' do
before do
@key = 'my secret key'
@client = Signet::OAuth2::Client.new(
:token_credential_uri =>
'https://oauth2.googleapis.com/token',
:scope => 'https://www.googleapis.com/auth/userinfo.profile',
:issuer => 'app@example.com',
:audience => 'https://oauth2.googleapis.com/token',
:signing_key => @key
)
end
it 'should generate valid JWTs' do
jwt = @client.to_jwt
expect(jwt).not_to be_nil
claim, header = JWT.decode(jwt, @key, true, algorithm: 'HS256')
expect(claim["iss"]).to eq 'app@example.com'
expect(claim["scope"]).to eq 'https://www.googleapis.com/auth/userinfo.profile'
expect(claim["aud"]).to eq 'https://oauth2.googleapis.com/token'
end
end
end
describe Signet::OAuth2::Client, 'configured for Google userinfo API' do
before do
@client = Signet::OAuth2::Client.new(
:authorization_uri =>
'https://accounts.google.com/o/oauth2/auth',
:token_credential_uri =>
'https://oauth2.googleapis.com/token',
:scope => 'https://www.googleapis.com/auth/userinfo.profile'
)
end
it 'should not have a grant type by default' do
expect(@client.grant_type).to eq nil
end
it 'should use the authorization_code grant type if given code' do
@client.code = '00000'
@client.redirect_uri = 'http://www.example.com/'
expect(@client.grant_type).to eq 'authorization_code'
end
it 'should use the refresh_token grant type if given refresh token' do
@client.refresh_token = '54321'
expect(@client.grant_type).to eq 'refresh_token'
end
it 'should use the password grant type if given username and password' do
@client.username = 'johndoe'
@client.password = 'incognito'
expect(@client.grant_type).to eq 'password'
end
it 'should allow the grant type to be set manually' do
@client.grant_type = 'authorization_code'
expect(@client.grant_type).to eq 'authorization_code'
@client.grant_type = 'refresh_token'
expect(@client.grant_type).to eq 'refresh_token'
@client.grant_type = 'password'
expect(@client.grant_type).to eq 'password'
end
it 'should allow the grant type to be set to an extension' do
@client.grant_type = 'urn:ietf:params:oauth:grant-type:saml2-bearer'
@client.extension_parameters['assertion'] =
'PEFzc2VydGlvbiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDU'
expect(@client.grant_type).to eq Addressable::URI.parse('urn:ietf:params:oauth:grant-type:saml2-bearer')
expect(@client.extension_parameters).to eq ({'assertion' => 'PEFzc2VydGlvbiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDU'})
end
it 'should raise an error if extension parameters are bogus' do
expect(lambda do
@client.extension_parameters = :bogus
end).to raise_error(TypeError)
end
it 'should include extension parameters in token request' do
@client.grant_type = 'urn:ietf:params:oauth:grant-type:saml2-bearer'
@client.extension_parameters['assertion'] =
'PEFzc2VydGlvbiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDU'
request = @client.generate_access_token_request
expect(request).to include('assertion' => 'PEFzc2VydGlvbiBJc3N1ZUluc3RhbnQ9IjIwMTEtMDU')
end
it 'should include the scope in token request' do
@client.scope = ['https://www.googleapis.com/auth/userinfo.profile']
request = @client.generate_access_token_request(:use_configured_scope => true)
expect(request).to include('scope' => ['https://www.googleapis.com/auth/userinfo.profile'])
end
it 'should allow the token to be updated' do
issued_at = Time.now
@client.update_token!(
:access_token => '12345',
:refresh_token => '54321',
:expires_in => 3600,
:issued_at => issued_at
)
expect(@client.access_token).to eq '12345'
expect(@client.refresh_token).to eq '54321'
expect(@client.expires_in).to eq 3600
expect(@client.issued_at).to eq issued_at
expect(@client).to_not be_expired
end
it 'should handle expires as equivalent to expires_in' do
issued_at = Time.now
@client.update_token!(
:access_token => '12345',
:refresh_token => '54321',
:expires => 600,
:issued_at => issued_at
)
expect(@client.expires_in).to eq 600
end
it 'should allow the token to be updated without an expiration' do
@client.update_token!(
:access_token => '12345',
:refresh_token => '54321'
)
expect(@client.access_token).to eq '12345'
expect(@client.refresh_token).to eq '54321'
expect(@client.expires_in).to eq nil
expect(@client.issued_at).to eq nil
expect(@client).to_not be_expired
end
it 'should allow the token expiration to be cleared' do
issued_at = Time.now
@client.update_token!(
:access_token => '12345',
:refresh_token => '54321',
:expires_in => 3600,
:issued_at => issued_at
)
@client.expires_in = nil
@client.issued_at = nil
expect(@client).to_not be_expired
end
it 'should allow the expires_at time to be updated' do
expires_at = Time.now
@client.update_token!(
:expires_at => expires_at.to_i,
:expires_in => nil
)
expect(@client.expires_at).to be_within(1).of(expires_at)
expect(@client).to be_expired
end
it 'should calculate the expires_at from issued_at when issued_at is set' do
expires_in = 3600
issued_at = Time.now - expires_in
@client.update_token!(
:issued_at => issued_at,
:expires_in => expires_in
)
expect(@client.expires_at).to eq issued_at + expires_in
expect(@client).to be_expired
end
it 'should calculate expires_at from Time.now when issed_at is NOT set' do
expires_in = 3600
expires_at = Time.now + expires_in
@client.update_token! :expires_in => expires_in
expect(@client.expires_at).to be_within(1).of(expires_at)
expect(@client).to_not be_expired
end
# This test is to document the way that expires_in has always been used:
# If expires_in is set on the client, it always resets the issued_at time
# to Time.now
it 'sets issued_at to Time.now when expires_in is not set through update_token!' do
one_hour = 3600
issued_at = Time.now - (2 * one_hour)
current_time = Time.now
@client.issued_at = issued_at
@client.expires_in = one_hour
expect(@client.issued_at).to_not eq issued_at
expect(@client.issued_at).to be_within(1).of(current_time)
expect(@client).to_not be_expired
end
it 'should allow setting expires_at manually' do
expires_at = Time.now+100
@client.expires_at = expires_at.to_i
expect(@client.expires_at).to be_within(1).of(expires_at)
expect(@client).to_not be_expired
end
it 'should normalize values of expires_at to instances of time' do
time_formats = [DateTime.new, "12:00", 100, Time.new]
normalized_time_formats = []
time_formats.each do |time|
@client.expires_at = time
normalized_time_formats << @client.expires_at
end
normalized_time_formats.each do |time|
expect(time).to be_an_instance_of(Time)
end
end
it 'should set expires_in when expires_at is set' do
issued_at = Time.now
expires_at = Time.now+100
@client.expires_at = expires_at.to_i
@client.issued_at = issued_at
expect(@client.expires_in).to be_within(1).of (expires_at - issued_at).to_i
@client.expires_at = nil
expect(@client.expires_in).to be_nil
end
it 'should set expires_in to nil when expires_at is set to nil' do
@client.expires_at = nil
expect(@client.expires_in).to be_nil
end
it 'should set expires_at when expires_in is set' do
expires_in = 100
@client.expires_in = expires_in
expect(@client.expires_at).to eq (@client.issued_at + expires_in)
@client.expires_in = nil
expect(@client.expires_at).to be_nil
end
it 'should set expires_at to nil when expires_in is set to nil' do
@client.expires_in = nil
expect(@client.expires_at).to be_nil
end
it 'should indicate the token is not expired if expired_at nil' do
@client.expires_at = nil
expect(@client.expires_within?(60)).to be false
expect(@client.expired?).to be false
end
it 'should indicate the token is not expiring when expiry beyond window' do
@client.expires_at = Time.now+100
expect(@client.expires_within?(60)).to be false
end
it 'should indicate the token is expiring soon when expiry within window' do
@client.expires_at = Time.now+30
expect(@client.expires_within?(60)).to be true
end
it 'should raise an error if the authorization endpoint is not secure' do
@client.client_id = 'client-12345'
@client.client_secret = 'secret-12345'
@client.redirect_uri = 'http://www.example.com/'
@client.authorization_uri = 'http://accounts.google.com/o/oauth2/auth'
expect(lambda do
@client.authorization_uri
end).to raise_error(Signet::UnsafeOperationError)
end
it 'should raise an error if token credential URI is missing' do
@client.token_credential_uri = nil
expect(lambda do
@client.fetch_access_token!
end).to raise_error(ArgumentError)
end
it 'should raise an error if unauthorized' do
@client.client_id = 'client-12345'
@client.client_secret = 'secret-12345'
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.post('/token') do
[401, {}, 'User authorization failed or something.']
end
end
expect(lambda do
connection = Faraday.new(:url => 'https://www.google.com') do |builder|
builder.adapter(:test, stubs)
end
@client.fetch_access_token!(
:connection => connection
)
end).to raise_error(Signet::AuthorizationError)
stubs.verify_stubbed_calls
end
it 'should raise a remote server error if the server gives a 5xx status' do
@client.client_id = 'client-12345'
@client.client_secret = 'secret-12345'
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.post('/token') do
[509, {}, 'Rate limit hit or something.']
end
end
expect(lambda do
connection = Faraday.new(:url => 'https://www.google.com') do |builder|
builder.adapter(:test, stubs)
end
@client.fetch_access_token!(
:connection => connection
)
end).to raise_error(Signet::RemoteServerError)
stubs.verify_stubbed_calls
end
it 'should raise an unexpected error if the token server gives an unexpected status' do
@client.client_id = 'client-12345'
@client.client_secret = 'secret-12345'
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.post('/token') do
[309, {}, 'Rate limit hit or something.']
end
end
expect(lambda do
connection = Faraday.new(:url => 'https://www.google.com') do |builder|
builder.adapter(:test, stubs)
end
@client.fetch_access_token!(
:connection => connection
)
end).to raise_error(Signet::UnexpectedStatusError)
stubs.verify_stubbed_calls
end
it 'should correctly fetch an access token' do
@client.client_id = 'client-12345'
@client.client_secret = 'secret-12345'
@client.code = '00000'
@client.redirect_uri = 'https://www.example.com/'
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.post('/token') do
build_json_response({
'access_token' => '12345',
'refresh_token' => '54321',
'expires_in' => '3600'
})
end
end
connection = Faraday.new(:url => 'https://www.google.com') do |builder|
builder.adapter(:test, stubs)
end
@client.fetch_access_token!(
:connection => connection
)
expect(@client.access_token).to eq '12345'
expect(@client.refresh_token).to eq '54321'
expect(@client.expires_in).to eq 3600
stubs.verify_stubbed_calls
end
it 'should correctly fetch an access token with a password' do
@client.client_id = 'client-12345'
@client.client_secret = 'secret-12345'
@client.username = 'johndoe'
@client.password = 'incognito'
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.post('/token') do
build_json_response({
'access_token' => '12345',
'refresh_token' => '54321',
'expires_in' => '3600'
})
end
end
connection = Faraday.new(:url => 'https://www.google.com') do |builder|
builder.adapter(:test, stubs)
end
@client.fetch_access_token!(
:connection => connection
)
expect(@client.access_token).to eq '12345'
expect(@client.refresh_token).to eq '54321'
expect(@client.expires_in).to eq 3600
stubs.verify_stubbed_calls
end
it 'should correctly refresh an access token' do
@client.client_id = 'client-12345'
@client.client_secret = 'secret-12345'
@client.refresh_token = '54321'
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.post('/token') do
build_json_response({
'access_token' => '12345',
'refresh_token' => '54321',
'expires_in' => '3600'
})
end
end
connection = Faraday.new(:url => 'https://www.google.com') do |builder|
builder.adapter(:test, stubs)
end
@client.fetch_access_token!(
:connection => connection
)
expect(@client.access_token).to eq '12345'
expect(@client.refresh_token).to eq '54321'
expect(@client.expires_in).to eq 3600
stubs.verify_stubbed_calls
end
it 'should detect unintential grant type of none' do
@client.client_id = 'client-12345'
@client.client_secret = 'secret-12345'
@client.redirect_uri = 'https://www.example.com/'
expect(lambda do
@client.fetch_access_token!
end).to raise_error(ArgumentError)
end
it 'should correctly fetch protected resources' do
@client.client_id = 'client-12345'
@client.client_secret = 'secret-12345'
@client.access_token = '12345'
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.get('/oauth2/v1/userinfo?alt=json') do
[200, {}, <<-JSON]
{
"id": "116452824309856782163",
"name": "Bob Aman",
"given_name": "Bob",
"family_name": "Aman",
"link": "https://plus.google.com/116452824309856782163"
}
JSON
end
end
connection = Faraday.new(:url => 'https://www.googleapis.com') do |builder|
builder.adapter(:test, stubs)
end
response = @client.fetch_protected_resource(
:connection => connection,
:uri => 'https://www.googleapis.com/oauth2/v1/userinfo?alt=json'
)
expect(response.status).to eq 200
expect(response.body).to eq <<-JSON
{
"id": "116452824309856782163",
"name": "Bob Aman",
"given_name": "Bob",
"family_name": "Aman",
"link": "https://plus.google.com/116452824309856782163"
}
JSON
stubs.verify_stubbed_calls
end
it 'should correctly send the realm in the Authorization header' do
@client.client_id = 'client-12345'
@client.client_secret = 'secret-12345'
@client.access_token = '12345'
connection = Faraday.new(:url => 'https://www.googleapis.com') do |builder|
builder.adapter(:test)
end
request = @client.generate_authenticated_request(
:connection => connection,
:realm => 'Example',
:request => conn.build_request(:get) do |req|
req.url('https://www.googleapis.com/oauth2/v1/userinfo?alt=json')
end
)
expect(request.headers['Authorization']).to eq 'Bearer 12345, realm="Example"'
end
it 'should correctly send the realm in the Authorization header' do
@client.client_id = 'client-12345'
@client.client_secret = 'secret-12345'
@client.access_token = '12345'
connection = Faraday.new(:url => 'https://www.googleapis.com') do |builder|
builder.adapter(:test)
end
request = @client.generate_authenticated_request(
:connection => connection,
:realm => 'Example',
:request => [
'GET',
'https://www.googleapis.com/oauth2/v1/userinfo?alt=json',
{},
['']
]
)
expect(request.headers['Authorization']).to eq 'Bearer 12345, realm="Example"'
end
it 'should not raise an error if a request is ' +
'provided without a connection' do
@client.client_id = 'client-12345'
@client.client_secret = 'secret-12345'
@client.access_token = '12345'
request = @client.generate_authenticated_request(
:realm => 'Example',
:request => conn.build_request(:get) do |req|
req.url('https://www.googleapis.com/oauth2/v1/userinfo?alt=json')
end
)
end
it 'should raise an error if not enough information ' +
'is supplied to create a request' do
@client.client_id = 'client-12345'
@client.client_secret = 'secret-12345'
@client.access_token = '12345'
expect(lambda do
@client.generate_authenticated_request(
:realm => 'Example',
:method => 'POST'
)
end).to raise_error(ArgumentError)
end
it 'should raise an error if the client does not have an access token' do
@client.client_id = 'client-12345'
@client.client_secret = 'secret-12345'
expect(lambda do
@client.fetch_protected_resource
end).to raise_error(ArgumentError)
end
it 'should not raise an error if the API server gives an error status' do
@client.client_id = 'client-12345'
@client.client_secret = 'secret-12345'
@client.access_token = '12345'
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.get('/oauth2/v1/userinfo?alt=json') do
[509, {}, 'Rate limit hit or something.']
end
end
connection = Faraday.new(:url => 'https://www.googleapis.com') do |builder|
builder.adapter(:test, stubs)
end
response = @client.fetch_protected_resource(
:connection => connection,
:uri => 'https://www.googleapis.com/oauth2/v1/userinfo?alt=json'
)
expect(response.status).to eq 509
expect(response.body).to eq 'Rate limit hit or something.'
stubs.verify_stubbed_calls
end
it 'should only raise an error if the API server ' +
'gives an authorization failed status' do
@client.client_id = 'client-12345'
@client.client_secret = 'secret-12345'
@client.access_token = '12345'
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.get('/oauth2/v1/userinfo?alt=json') do
[401, {}, 'User authorization failed or something.']
end
end
expect(lambda do
connection = Faraday.new(
:url => 'https://www.googleapis.com'
) do |builder|
builder.adapter(:test, stubs)
end
@client.fetch_protected_resource(
:connection => connection,
:uri => 'https://www.googleapis.com/oauth2/v1/userinfo?alt=json'
)
end).to raise_error(Signet::AuthorizationError)
stubs.verify_stubbed_calls
end
it 'should correctly handle an ID token' do
@client.client_id = 'client-12345'
@client.client_secret = 'secret-12345'
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.post('/token') do
build_json_response({
'access_token' => '12345',
'refresh_token' => '54321',
'expires_in' => '3600',
'id_token' => (
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl9oYXNoIjoidGdoRD' +
'lKN244VjBOMnZjdzZlTWlqZyIsImF1ZCI6ImNsaWVudC0xMjM0NSIsImlkIjoiM' +
'TIzNDUiLCJpYXQiOjEzMjA2NzA5NzgsImV4cCI6MTMyMDY3NDg3OCwiY2lkIjoi' +
'Y2xpZW50LTEyMzQ1IiwiaXNzIjoiZXhhbXBsZS5jb20ifQ.tsF3srlBaAh6pV3U' +
'wfRrHSA3-jwnvOw6MMsQ6sO4kjc'
)
})
end
end
connection = Faraday.new(:url => 'https://www.google.com') do |builder|
builder.adapter(:test, stubs)
end
@client.fetch_access_token!(
:connection => connection
)
expect(@client.access_token).to eq '12345'
expect(@client.refresh_token).to eq '54321'
expect(@client.decoded_id_token(nil, :verify_expiration => false)).to eq ({
"token_hash" => "tghD9J7n8V0N2vcw6eMijg",
"id" => "12345",
"aud" => "client-12345",
"iat" => 1320670978,
"exp" => 1320674878,
"cid" => "client-12345",
"iss" => "example.com"
})
expect(@client.expires_in).to eq 3600
stubs.verify_stubbed_calls
end
it 'should raise an error decoding an ID token if ' +
'audience does not match client ID' do
@client.client_id = 'client-54321'
@client.client_secret = 'secret-12345'
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.post('/token') do
build_json_response({
'access_token' => '12345',
'refresh_token' => '54321',
'expires_in' => '3600',
'id_token' => (
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl9oYXNoIjoidGdoRD' +
'lKN244VjBOMnZjdzZlTWlqZyIsImF1ZCI6ImNsaWVudC0xMjM0NSIsImlkIjoiM' +
'TIzNDUiLCJpYXQiOjEzMjA2NzA5NzgsImV4cCI6MTMyMDY3NDg3OCwiY2lkIjoi' +
'Y2xpZW50LTEyMzQ1IiwiaXNzIjoiZXhhbXBsZS5jb20ifQ.tsF3srlBaAh6pV3U' +
'wfRrHSA3-jwnvOw6MMsQ6sO4kjc'
)
})
end
end
connection = Faraday.new(:url => 'https://www.google.com') do |builder|
builder.adapter(:test, stubs)
end
@client.fetch_access_token!(
:connection => connection
)
expect(@client.access_token).to eq '12345'
expect(@client.refresh_token).to eq '54321'
expect(@client.expires_in).to eq 3600
expect(lambda do
@client.decoded_id_token(nil, :verify_expiration => false)
end).to raise_error(Signet::UnsafeOperationError)
stubs.verify_stubbed_calls
end
it 'should raise an error decoding an ID token if ' +
'audience is missing' do
@client.client_id = 'client-12345'
@client.client_secret = 'secret-12345'
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.post('/token') do
build_json_response({
'access_token' => '12345',
'refresh_token' => '54321',
'expires_in' => '3600',
'id_token' => (
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl9oYXNoIjoidGdoRD' +
'lKN244VjBOMnZjdzZlTWlqZyIsImlkIjoiMTIzNDUiLCJpYXQiOjEzMjA2NzA5N' +
'zgsImV4cCI6MTMyMDY3NDg3OCwiY2lkIjoiY2xpZW50LTEyMzQ1IiwiaXNzIjoi' +
'ZXhhbXBsZS5jb20ifQ.7qj85CKbQyVdDe5y2ScdJAZNkEeKMPW9LIonLxG1vu8'
)
})
end
end
connection = Faraday.new(:url => 'https://www.google.com') do |builder|
builder.adapter(:test, stubs)
end
@client.fetch_access_token!(
:connection => connection
)
expect(@client.access_token).to eq '12345'
expect(@client.refresh_token).to eq '54321'
expect(@client.expires_in).to eq 3600
expect(lambda do
@client.decoded_id_token(nil, :verify_expiration => false)
end).to raise_error(Signet::UnsafeOperationError)
stubs.verify_stubbed_calls
end
it 'should raise an error if the id token cannot be verified' do
pending "Need to set test data"
@client.client_id = 'client-12345'
@client.client_secret = 'secret-12345'
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.post('/token') do
build_json_response({
'access_token' => '12345',
'refresh_token' => '54321',
'expires_in' => '3600',
'id_token' => (
'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwiY' +
'XVkIjoiMTA2MDM1Nzg5MTY4OC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSI' +
'sImNpZCI6IjEwNjAzNTc4OTE2ODguYXBwcy5nb29nbGV1c2VyY29udGVudC5jb' +
'20iLCJpZCI6IjExNjQ1MjgyNDMwOTg1Njc4MjE2MyIsInRva2VuX2hhc2giOiJ' +
'0Z2hEOUo4bjhWME4ydmN3NmVNaWpnIiwiaWF0IjoxMzIwNjcwOTc4LCJleHAiO' +
'jEzMjA2NzQ4Nzh9.D8x_wirkxDElqKdJBcsIws3Ogesk38okz6MN7zqC7nEAA7' +
'wcy1PxsROY1fmBvXSer0IQesAqOW-rPOCNReSn-eY8d53ph1x2HAF-AzEi3GOl' +
'6hFycH8wj7Su6JqqyEbIVLxE7q7DkAZGaMPkxbTHs1EhSd5_oaKQ6O4xO3ZnnT4'
)
})
end
end
connection = Faraday.new(:url => 'https://www.google.com') do |builder|
builder.adapter(:test, stubs)
end
@client.fetch_access_token!(
:connection => connection
)
expect(@client.access_token).to eq '12345'
expect(@client.refresh_token).to eq '54321'
expect(@client.expires_in).to eq 3600
expect(lambda do
pubkey = OpenSSL::PKey::RSA.new(<<-PUBKEY)
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCaY7425h964bjaoLeUm
SlZ8sK7VtVk9zHbGmZh2ygGYwfuUf2bmMye2Ofv99yDE/rd4loVIAcu7RVvDRgHq
3/CZTnIrSvHsiJQsHBNa3d+F1ihPfzURzf1M5k7CFReBj2SBXhDXd57oRfBQj12w
CVhhwP6kGTAWuoppbIIIBfNF2lE/Nvm7lVVYQqL9xOrP/AQ4xRbpQlB8Ll9sO9Or
SvbWhCDa/LMOWxHdmrcJi6XoSg1vnOyCoKbyAoauTt/XqdkHbkDdQ6HFbJieu9il
LDZZNliPhfENuKeC2MCGVXTEu8Cqhy1w6e4axavLlXoYf4laJIZ/e7au8SqDbY0B
xwIDAQAB
-----END PUBLIC KEY-----
PUBKEY
@client.decoded_id_token(pubkey)
end).to raise_error(JWT::ExpiredSignature)
stubs.verify_stubbed_calls
end
end
describe Signet::OAuth2::Client, 'authorization_uri' do
before do
@client = Signet::OAuth2::Client.new(
:client_id => 's6BhdRkqt3',
:redirect_uri => 'https://example.client.com/callback',
:authorization_uri => 'https://example.com/authorize'
)
end
it 'should set access_type to offline by default' do
expect(@client.authorization_uri.query_values['access_type']).to eq 'offline'
end
it 'should set response_type to code by default' do
expect(@client.authorization_uri.query_values['response_type']).to eq 'code'
end
it 'should raise an error when setting both prompt and approval_prompt' do
expect(lambda do
@client.authorization_uri(:approval_prompt => 'force', :prompt => 'consent')
end).to raise_error(ArgumentError)
end
end
describe Signet::OAuth2::Client, 'configured with custom parameters' do
before do
@client = Signet::OAuth2::Client.new(
:client_id => 's6BhdRkqt3',
:redirect_uri => 'https://example.client.com/callback',
:authorization_uri => 'https://example.com/authorize',
:token_credential_uri => 'https://example.com/token',
:additional_parameters =>{'type' => 'web_server'}
)
end
# Normalizing to symbols - good test case example here for changes to normalized input.
# Also tests Addressable's output.
# Note: The only changes made here are to testing the **INTERNAL** representation of options.
it 'should allow custom parameters to be set on init' do
expect(@client.additional_parameters).to eq({:type => 'web_server'})
end
it 'should allow custom parameters to be updated' do
@client.update!(:additional_parameters => {:type => 'new_type'})
expect(@client.additional_parameters).to eq ({ :type => 'new_type'})
end
it 'should use custom parameters when generating authorization_uri' do
expect(@client.authorization_uri().query_values).to eq ({
"access_type"=>"offline",
"client_id"=>"s6BhdRkqt3",
"redirect_uri"=>"https://example.client.com/callback",
"response_type"=>"code",
"type"=>"web_server"})
end
it 'should merge new authorization_uri custom parameters' do
expect(@client.authorization_uri(:additional_parameters => {'type' => 'new_type', 'new_param' => 'new_val'}).query_values).to eql({"access_type"=>"offline", "client_id"=>"s6BhdRkqt3", "new_param"=>"new_val", "response_type"=>"code","redirect_uri"=>"https://example.client.com/callback", "type"=>"new_type"})
end
it 'should merge new generate_access_token_request custom parameters' do
@client.update!(:code=>'12345')
params = @client.generate_access_token_request(:additional_parameters => {'type' => 'new_type', 'new_param' => 'new_val'})
expect(params).to include('type' => 'new_type')
expect(params).to include('new_param' => 'new_val')
end
end
describe Signet::OAuth2::Client, 'configured with custom parameters' do
before do
@client = Signet::OAuth2::Client.new(
"client_id" => 's6BhdRkqt3',
"redirect_uri" => 'https://example.client.com/callback',
"authorization_uri" => 'https://example.com/authorize',
"token_credential_uri" => 'https://example.com/token',
"additional_parameters" => {'type' => 'web_server'}
)
end
# Normalizing to symbols - good test case example here for changes to normalized input.
# Also tests Addressable's output.
# Note: The only changes made here are to testing the **INTERNAL** representation of options.
it 'should allow custom parameters to be set on init' do
expect(@client.additional_parameters).to eq ({ :type => 'web_server'})
end
it 'should allow custom parameters to be updated' do
@client.update!(:additional_parameters => {'type' => 'new_type'})
expect(@client.additional_parameters).to eql ({ :type => 'new_type'})
end
it 'should use custom parameters when generating authorization_uri' do
expect(@client.authorization_uri().query_values).to eq ({"access_type"=>"offline", "client_id"=>"s6BhdRkqt3", "redirect_uri"=>"https://example.client.com/callback", "response_type"=>"code", "type"=>"web_server"})
end
it 'should have the correct authorization_uri' do
expect(@client.authorization_uri.host).to eq 'example.com'
expect(@client.authorization_uri.path).to eq '/authorize'
end
it 'should merge new authorization_uri custom parameters' do
expect(@client.authorization_uri(:additional_parameters => {'type' => 'new_type', 'new_param' => 'new_val'}).query_values).to eq ({"access_type"=>"offline", "client_id"=>"s6BhdRkqt3", "new_param"=>"new_val", "response_type"=>"code","redirect_uri"=>"https://example.client.com/callback", "type"=>"new_type"})
end
it 'should not have access_type parameter in authorization_uri when we set it to nil in client' do
@client.update!(:access_type=>nil)
expect(@client.authorization_uri().query_values).to eq ({"client_id"=>"s6BhdRkqt3", "response_type"=>"code", "redirect_uri"=>"https://example.client.com/callback"})
end
it 'should use new access_type parameter as default for authorization_uri' do
@client.update!(:access_type=>:online)
expect(@client.authorization_uri().query_values).to eq ({"access_type"=>"online", "client_id"=>"s6BhdRkqt3", "response_type"=>"code", "redirect_uri"=>"https://example.client.com/callback"})
end
it 'should merge new generate_access_token_request custom parameters' do
@client.update!(:code=>'12345')
params = @client.generate_access_token_request(:additional_parameters => {'type' => 'new_type', 'new_param' => 'new_val'})
expect(params).to include("type" => "new_type")
expect(params).to include("new_param" => "new_val")
end
end
describe Signet::OAuth2::Client, 'configured with custom parameters a la JSON.load(credentials_file)' do
before do
@client = Signet::OAuth2::Client.new(
"client_id" => 's6BhdRkqt3',
"redirect_uri" => 'https://example.client.com/callback',
"authorization_uri" => {"scheme"=>"https", "user"=>nil, "password"=>nil, "host"=>"accounts.google.com", "port"=>nil, "path"=>"/o/oauth2/auth", "query"=>nil, "fragment"=>nil},
"token_credential_uri" => 'https://example.com/token',
"additional_parameters" => {'type' => 'web_server'}
)
end
it 'should allow custom parameters to be set on init' do
expect(@client.additional_parameters).to eq ({:type => 'web_server'})
end
it 'should allow custom parameters to be updated' do
@client.update!(:additional_parameters => {'type' => 'new_type'})
expect(@client.additional_parameters).to eql ({:type => 'new_type'})
end
it 'should have correct authorization_uri hash options' do
expect(@client.authorization_uri.host).to eq "accounts.google.com"
expect(@client.authorization_uri.path).to eq "/o/oauth2/auth"
end
it 'should use custom parameters when generating authorization_uri' do
expect(@client.authorization_uri().query_values).to eq ({"access_type"=>"offline", "client_id"=>"s6BhdRkqt3", "redirect_uri"=>"https://example.client.com/callback", "response_type"=>"code", "type"=>"web_server"})
end
# , "path" => "/o/oauth2/oauth", "host" => "accounts.google.com"
it 'should merge new authorization_uri custom parameters' do
expect(@client.authorization_uri(:additional_parameters => {'type' => 'new_type', 'new_param' => 'new_val'}).query_values).to eq ({
"access_type"=>"offline",
"client_id"=>"s6BhdRkqt3",
"new_param"=>"new_val",
"response_type"=>"code",
"redirect_uri"=>"https://example.client.com/callback",
"type"=>"new_type"})
end
it 'should merge new generate_access_token_request custom parameters' do
@client.update!(:code=>'12345')
params = @client.generate_access_token_request(:additional_parameters => {'type' => 'new_type', 'new_param' => 'new_val'})
expect(params).to include("type" => "new_type")
expect(params).to include("new_param" => "new_val")
end
end
signet-0.11.0/spec/signet/oauth_1_spec.rb 0000644 0000041 0000041 00000112671 13410324340 020236 0 ustar www-data www-data # Copyright (C) 2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'
require 'signet/oauth_1'
require 'signet/oauth_1/client'
require 'signet/oauth_1/credential'
describe Signet::OAuth1 do
it 'should correctly normalize parameters' do
parameters = [
["a", "1"],
["c", "hi there"],
["f", "25"],
["f", "50"],
["f", "a"],
["z", "p"],
["z", "t"]
]
expect(Signet::OAuth1.normalize_parameters(parameters)).to eq(
'a=1&c=hi%20there&f=25&f=50&f=a&z=p&z=t')
end
it 'should correctly normalize parameters' do
parameters = [
["b5", "=%3D"],
["a3", "a"],
["c@", ""],
["a2", "r b"],
["oauth_consumer_key", "9djdj82h48djs9d2"],
["oauth_token", "kkk9d7dh3k39sjv7"],
["oauth_signature_method", "HMAC-SHA1"],
["oauth_timestamp", "137131201"],
["oauth_nonce", "7d8f3e4a"],
["c2", ""],
["a3", "2 q"]
]
expect(Signet::OAuth1.normalize_parameters(parameters)).to eq(
'a2=r%20b&a3=2%20q&a3=a&b5=%3D%253D&c%40=&c2=&oauth_consumer_key=9dj' +
'dj82h48djs9d2&oauth_nonce=7d8f3e4a&oauth_signature_method=HMAC-SHA1' +
'&oauth_timestamp=137131201&oauth_token=kkk9d7dh3k39sjv7')
end
it 'should exclude the "oauth_signature" parameter when normalizing' do
parameters = [
["a", "1"],
["b", "2"],
["c", "3"],
["oauth_signature", "dpf43f3p2l4k3l03"]
]
expect(Signet::OAuth1.normalize_parameters(parameters)).to eq(
"a=1&b=2&c=3")
end
it 'should raise an error if normalizing parameters with bogus values' do
expect(lambda do
Signet::OAuth1.normalize_parameters(42)
end).to raise_error(TypeError)
end
it 'should raise an error if generating a base string with bogus values' do
expect(lambda do
Signet::OAuth1.generate_base_string(
"GET", "http://photos.example.net/photos", 42
)
end).to raise_error(TypeError)
end
it 'should correctly generate a base string' do
method = "GET"
uri = "http://photos.example.net/photos"
parameters = {
"oauth_consumer_key" => "dpf43f3p2l4k3l03",
"oauth_token" => "nnch734d00sl2jdk",
"oauth_signature_method" => "HMAC-SHA1",
"oauth_timestamp" => "1191242096",
"oauth_nonce" => "kllo9940pd9333jh",
"oauth_version" => "1.0",
"file" => "vacation.jpg",
"size" => "original"
}
expect(Signet::OAuth1.generate_base_string(method, uri, parameters)).to eq(
"GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26" +
"oauth_consumer_key%3Ddpf43f3p2l4k3l03%26" +
"oauth_nonce%3Dkllo9940pd9333jh%26" +
"oauth_signature_method%3DHMAC-SHA1%26" +
"oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26" +
"oauth_version%3D1.0%26size%3Doriginal"
)
end
it "should correctly generate a base string with an already encoded URI" do
method = "GET"
uri = "http://photos.example.net/https%3A%2F%2Fwww.example.com"
parameters = {
"oauth_consumer_key" => "dpf43f3p2l4k3l03",
"oauth_token" => "nnch734d00sl2jdk",
"oauth_signature_method" => "HMAC-SHA1",
"oauth_timestamp" => "1191242096",
"oauth_nonce" => "kllo9940pd9333jh",
"oauth_version" => "1.0",
"file" => "vacation.jpg",
"size" => "original"
}
expect(Signet::OAuth1.generate_base_string(method, uri, parameters)).to eq(
"GET&http%3A%2F%2Fphotos.example.net%2F" +
"https%253A%252F%252Fwww.example.com&file%3Dvacation.jpg%26" +
"oauth_consumer_key%3Ddpf43f3p2l4k3l03%26" +
"oauth_nonce%3Dkllo9940pd9333jh%26" +
"oauth_signature_method%3DHMAC-SHA1%26" +
"oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26" +
"oauth_version%3D1.0%26size%3Doriginal"
)
end
it "should correctly generate a base string with an already encoded URI" do
method = "GET"
uri = "http://example.com/r%20v/X?id=123"
parameters = {
"oauth_consumer_key" => "dpf43f3p2l4k3l03",
"oauth_token" => "nnch734d00sl2jdk",
"oauth_signature_method" => "HMAC-SHA1",
"oauth_timestamp" => "1191242096",
"oauth_nonce" => "kllo9940pd9333jh",
"oauth_version" => "1.0"
}
expect(Signet::OAuth1.generate_base_string(method, uri, parameters)).to eq(
"GET&http%3A%2F%2Fexample.com%2Fr%2520v%2FX&" +
"id%3D123%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26" +
"oauth_nonce%3Dkllo9940pd9333jh%26" +
"oauth_signature_method%3DHMAC-SHA1%26" +
"oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26" +
"oauth_version%3D1.0"
)
end
it 'should correctly generate a base string when port 8080 is specified' do
method = "GET"
uri = "http://www.example.net:8080/?q=1"
parameters = {
"oauth_consumer_key" => "dpf43f3p2l4k3l03",
"oauth_token" => "nnch734d00sl2jdk",
"oauth_signature_method" => "HMAC-SHA1",
"oauth_timestamp" => "1191242096",
"oauth_nonce" => "kllo9940pd9333jh",
"oauth_version" => "1.0"
}
expect(Signet::OAuth1.generate_base_string(method, uri, parameters)).to eq(
"GET&http%3A%2F%2Fwww.example.net%3A8080%2F&" +
"oauth_consumer_key%3Ddpf43f3p2l4k3l03%26" +
"oauth_nonce%3Dkllo9940pd9333jh%26" +
"oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26" +
"oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26q%3D1"
)
end
it 'should correctly generate a base string when port 80 is specified' do
method = "GET"
uri = "http://photos.example.net:80/photos"
parameters = {
"oauth_consumer_key" => "dpf43f3p2l4k3l03",
"oauth_token" => "nnch734d00sl2jdk",
"oauth_signature_method" => "HMAC-SHA1",
"oauth_timestamp" => "1191242096",
"oauth_nonce" => "kllo9940pd9333jh",
"oauth_version" => "1.0",
"file" => "vacation.jpg",
"size" => "original"
}
expect(Signet::OAuth1.generate_base_string(method, uri, parameters)).to eq(
"GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26" +
"oauth_consumer_key%3Ddpf43f3p2l4k3l03%26" +
"oauth_nonce%3Dkllo9940pd9333jh%26" +
"oauth_signature_method%3DHMAC-SHA1%26" +
"oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26" +
"oauth_version%3D1.0%26size%3Doriginal"
)
end
it 'should correctly generate a base string when port 443 is specified' do
method = "GET"
uri = "https://photos.example.net:443/photos"
parameters = {
"oauth_consumer_key" => "dpf43f3p2l4k3l03",
"oauth_token" => "nnch734d00sl2jdk",
"oauth_signature_method" => "HMAC-SHA1",
"oauth_timestamp" => "1191242096",
"oauth_nonce" => "kllo9940pd9333jh",
"oauth_version" => "1.0",
"file" => "vacation.jpg",
"size" => "original"
}
expect(Signet::OAuth1.generate_base_string(method, uri, parameters)).to eq(
"GET&https%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26" +
"oauth_consumer_key%3Ddpf43f3p2l4k3l03%26" +
"oauth_nonce%3Dkllo9940pd9333jh%26" +
"oauth_signature_method%3DHMAC-SHA1%26" +
"oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26" +
"oauth_version%3D1.0%26size%3Doriginal"
)
end
it 'should correctly generate a base signature with uppercase scheme' do
method = 'GET'
uri =
"HTTP://photos.example.net/photos?file=vacation.jpg"
parameters = {
"oauth_consumer_key" => "dpf43f3p2l4k3l03",
"oauth_token" => "nnch734d00sl2jdk",
"oauth_signature_method" => "HMAC-SHA1",
"oauth_timestamp" => "1191242096",
"oauth_nonce" => "kllo9940pd9333jh",
"oauth_version" => "1.0",
"size" => "original"
}
expect(Signet::OAuth1.generate_base_string(method, uri, parameters)).to eq(
"GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26" +
"oauth_consumer_key%3Ddpf43f3p2l4k3l03%26" +
"oauth_nonce%3Dkllo9940pd9333jh%26" +
"oauth_signature_method%3DHMAC-SHA1%26" +
"oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26" +
"oauth_version%3D1.0%26size%3Doriginal"
)
end
it 'should correctly generate a base signature with mixedcase authority' do
method = 'GET'
uri =
"http://photos.eXaMpLe.NET/photos?file=vacation.jpg"
parameters = {
"oauth_consumer_key" => "dpf43f3p2l4k3l03",
"oauth_token" => "nnch734d00sl2jdk",
"oauth_signature_method" => "HMAC-SHA1",
"oauth_timestamp" => "1191242096",
"oauth_nonce" => "kllo9940pd9333jh",
"oauth_version" => "1.0",
"size" => "original"
}
expect(Signet::OAuth1.generate_base_string(method, uri, parameters)).to eq(
"GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26" +
"oauth_consumer_key%3Ddpf43f3p2l4k3l03%26" +
"oauth_nonce%3Dkllo9940pd9333jh%26" +
"oauth_signature_method%3DHMAC-SHA1%26" +
"oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26" +
"oauth_version%3D1.0%26size%3Doriginal"
)
end
it 'should correctly generate a base signature with a method symbol' do
method = :get
uri =
"http://photos.example.net/photos?file=vacation.jpg"
parameters = {
"oauth_consumer_key" => "dpf43f3p2l4k3l03",
"oauth_token" => "nnch734d00sl2jdk",
"oauth_signature_method" => "HMAC-SHA1",
"oauth_timestamp" => "1191242096",
"oauth_nonce" => "kllo9940pd9333jh",
"oauth_version" => "1.0",
"size" => "original"
}
expect(Signet::OAuth1.generate_base_string(method, uri, parameters)).to eq(
"GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26" +
"oauth_consumer_key%3Ddpf43f3p2l4k3l03%26" +
"oauth_nonce%3Dkllo9940pd9333jh%26" +
"oauth_signature_method%3DHMAC-SHA1%26" +
"oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26" +
"oauth_version%3D1.0%26size%3Doriginal"
)
end
it 'should correctly generate an authorization header' do
parameters = [
["oauth_consumer_key", "0685bd9184jfhq22"],
["oauth_token", "ad180jjd733klru7"],
["oauth_signature_method", "HMAC-SHA1"],
["oauth_signature", "wOJIO9A2W5mFwDgiDvZbTSMK/PY="],
["oauth_timestamp", "137131200"],
["oauth_nonce", "4572616e48616d6d65724c61686176"],
["oauth_version", "1.0"]
]
expect(Signet::OAuth1.generate_authorization_header(
parameters, "http://sp.example.com/"
)).to eq (
'OAuth realm="http://sp.example.com/", ' +
'oauth_consumer_key="0685bd9184jfhq22", ' +
'oauth_token="ad180jjd733klru7", ' +
'oauth_signature_method="HMAC-SHA1", ' +
'oauth_signature="wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D", ' +
'oauth_timestamp="137131200", ' +
'oauth_nonce="4572616e48616d6d65724c61686176", ' +
'oauth_version="1.0"'
)
end
it 'should raise an error if generating an authorization header ' +
'with bogus values' do
expect(lambda do
Signet::OAuth1.generate_authorization_header(42)
end).to raise_error(TypeError)
end
it 'should raise an error if generating an authorization header ' +
'with the "realm" parameter specified the wrong way' do
parameters = [
["realm", "http://sp.example.com/"],
["oauth_consumer_key", "0685bd9184jfhq22"],
["oauth_token", "ad180jjd733klru7"],
["oauth_signature_method", "HMAC-SHA1"],
["oauth_signature", "wOJIO9A2W5mFwDgiDvZbTSMK/PY="],
["oauth_timestamp", "137131200"],
["oauth_nonce", "4572616e48616d6d65724c61686176"],
["oauth_version", "1.0"]
]
expect(lambda do
Signet::OAuth1.generate_authorization_header(parameters)
end).to raise_error(ArgumentError)
end
it 'should correctly parse an authorization header' do
parameters = Signet::OAuth1.parse_authorization_header(
'OAuth realm="http://sp.example.com/", ' +
'oauth_consumer_key="0685bd9184jfhq22", ' +
'oauth_token="ad180jjd733klru7", ' +
'oauth_signature_method="HMAC-SHA1", ' +
'oauth_signature="wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D", ' +
'oauth_timestamp="137131200", ' +
'oauth_nonce="4572616e48616d6d65724c61686176", ' +
'oauth_version="1.0"'
).inject({}) { |h,(k,v)| h[k]=v; h }
expect(parameters['realm']).to eq 'http://sp.example.com/'
expect(parameters['oauth_consumer_key']).to eq '0685bd9184jfhq22'
expect(parameters['oauth_token']).to eq 'ad180jjd733klru7'
expect(parameters['oauth_signature_method']).to eq 'HMAC-SHA1'
expect(parameters['oauth_signature']).to eq 'wOJIO9A2W5mFwDgiDvZbTSMK/PY='
expect(parameters['oauth_timestamp']).to eq '137131200'
expect(parameters['oauth_nonce']).to eq '4572616e48616d6d65724c61686176'
expect(parameters['oauth_version']).to eq '1.0'
end
it 'should not unescape a realm in an authorization header' do
parameters = Signet::OAuth1.parse_authorization_header(
'OAuth realm="http%3A%2F%2Fsp.example.com%2F", ' +
'domain="http%3A%2F%2Fsp.example.com%2F", ' +
'oauth_consumer_key="0685bd9184jfhq22", ' +
'oauth_token="ad180jjd733klru7", ' +
'oauth_signature_method="HMAC-SHA1", ' +
'oauth_signature="wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D", ' +
'oauth_timestamp="137131200", ' +
'oauth_nonce="4572616e48616d6d65724c61686176", ' +
'oauth_version="1.0"'
).inject({}) { |h,(k,v)| h[k]=v; h }
expect(parameters['realm']).to eq 'http%3A%2F%2Fsp.example.com%2F'
expect(parameters['domain']).to eq 'http://sp.example.com/'
expect(parameters['oauth_consumer_key']).to eq '0685bd9184jfhq22'
expect(parameters['oauth_token']).to eq 'ad180jjd733klru7'
expect(parameters['oauth_signature_method']).to eq 'HMAC-SHA1'
expect(parameters['oauth_signature']).to eq 'wOJIO9A2W5mFwDgiDvZbTSMK/PY='
expect(parameters['oauth_timestamp']).to eq '137131200'
expect(parameters['oauth_nonce']).to eq '4572616e48616d6d65724c61686176'
expect(parameters['oauth_version']).to eq '1.0'
end
it 'should raise an error if parsing an authorization header ' +
'with bogus values' do
expect(lambda do
Signet::OAuth1.parse_authorization_header(42)
end).to raise_error(TypeError)
end
it 'should raise an error if parsing a non-OAuth authorization header' do
expect(lambda do
Signet::OAuth1.parse_authorization_header(
'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='
)
end).to raise_error(Signet::ParseError)
end
it 'should correctly parse a form encoded credential' do
credential = Signet::OAuth1.parse_form_encoded_credentials(
'oauth_token=hh5s93j4hdidpola&oauth_token_secret=hdhd0244k9j7ao03'
)
expect(credential.key).to eq 'hh5s93j4hdidpola'
expect(credential.secret).to eq 'hdhd0244k9j7ao03'
end
it 'should correctly parse a form encoded credential' do
credential = Signet::OAuth1.parse_form_encoded_credentials(
'oauth_token=hdk48Djdsa&oauth_token_secret=xyz4992k83j47x0b&' +
'oauth_callback_confirmed=true'
)
expect(credential.key).to eq 'hdk48Djdsa'
expect(credential.secret).to eq 'xyz4992k83j47x0b'
end
it 'should raise an error if parsing a form encoded credential ' +
'with bogus values' do
expect(lambda do
Signet::OAuth1.parse_form_encoded_credentials(42)
end).to raise_error(TypeError)
end
it 'should correctly generate a signature for a set of parameters' do
method = :get
uri = "http://photos.example.net/photos"
client_credential_secret = 'kd94hf93k423kf44'
token_credential_secret = 'pfkkdhi9sl3r4s00'
parameters = {
"oauth_consumer_key" => "dpf43f3p2l4k3l03",
"oauth_token" => "nnch734d00sl2jdk",
"oauth_signature_method" => "HMAC-SHA1",
"oauth_timestamp" => "1191242096",
"oauth_nonce" => "kllo9940pd9333jh",
"oauth_version" => "1.0",
"file" => "vacation.jpg",
"size" => "original"
}
expect(Signet::OAuth1.sign_parameters(
method,
uri,
parameters,
client_credential_secret,
token_credential_secret
)).to eq "tR3+Ty81lMeYAr/Fid0kMTYa/WM="
end
it 'should raise an error when trying to sign with with unknown method' do
method = :get
uri = "http://photos.example.net/photos"
client_credential_secret = 'kd94hf93k423kf44'
token_credential_secret = 'pfkkdhi9sl3r4s00'
parameters = {
"oauth_consumer_key" => "dpf43f3p2l4k3l03",
"oauth_token" => "nnch734d00sl2jdk",
"oauth_signature_method" => "HMAC-BOGUS", # Unknown signature method
"oauth_timestamp" => "1191242096",
"oauth_nonce" => "kllo9940pd9333jh",
"oauth_version" => "1.0",
"file" => "vacation.jpg",
"size" => "original"
}
expect(lambda do
Signet::OAuth1.sign_parameters(
method,
uri,
parameters,
client_credential_secret,
token_credential_secret
)
end).to raise_error(NotImplementedError)
end
it 'should correctly generate authorization URIs' do
authorization_uri = 'http://photos.example.net/authorize'
temporary_credential_key = 'hh5s93j4hdidpola'
callback = 'http://printer.example.com/request_token_ready'
parsed_uri = Addressable::URI.parse(
Signet::OAuth1.generate_authorization_uri(
authorization_uri,
:temporary_credential_key => temporary_credential_key,
:callback => callback
)
)
expect(parsed_uri.query_values).to have_key('oauth_token')
expect(parsed_uri.query_values['oauth_token']).to eq temporary_credential_key
expect(parsed_uri.query_values).to have_key('oauth_callback')
expect(parsed_uri.query_values['oauth_callback']).to eq callback
end
end
describe Signet::OAuth1, 'when generating temporary credentials parameters' do
before do
@client_credential_key = 'dpf43f3p2l4k3l03'
@callback = 'http://printer.example.com/request_token_ready'
@signature_method = 'HMAC-SHA1'
@scope = 'http://photos.example.com/full_access'
@additional_parameters = [['scope', @scope]]
@unsigned_parameters =
Signet::OAuth1.unsigned_temporary_credential_parameters(
:client_credential_key => @client_credential_key,
:callback => @callback,
:signature_method => @signature_method,
:additional_parameters => @additional_parameters
).inject({}) { |h,(k,v)| h[k]=v; h }
end
it 'should raise an error if the client credential key is missing' do
expect(lambda do
Signet::OAuth1.unsigned_temporary_credential_parameters(
:client_credential_key => nil,
:callback => @callback,
:signature_method => @signature_method,
:additional_parameters => @additional_parameters
)
end).to raise_error(ArgumentError)
end
it 'should have the correct client credential key' do
expect(@unsigned_parameters).to have_key('oauth_consumer_key')
expect(@unsigned_parameters['oauth_consumer_key']).to eq @client_credential_key
end
it 'should have the correct signature method' do
expect(@unsigned_parameters).to have_key('oauth_signature_method')
expect(@unsigned_parameters['oauth_signature_method']).to eq @signature_method
end
it 'should have a valid timestamp' do
# Verify that we have a timestamp, it's in the correct format and within
# a reasonable range of the current time.
expect(@unsigned_parameters).to have_key('oauth_timestamp')
expect(@unsigned_parameters['oauth_timestamp']).to match(/^[0-9]+$/)
expect(@unsigned_parameters['oauth_timestamp'].to_i).to be <= Time.now.to_i
expect(@unsigned_parameters['oauth_timestamp'].to_i).to be >= Time.now.to_i - 1
end
it 'should have a valid nonce' do
# Verify that we have a nonce and that it has sufficient length for
# uniqueness.
expect(@unsigned_parameters).to have_key('oauth_nonce')
expect(@unsigned_parameters['oauth_nonce']).to match(/^[0-9a-zA-Z]{16,100}$/)
end
it 'should have the correct callback' do
expect(@unsigned_parameters).to have_key('oauth_callback')
expect(@unsigned_parameters['oauth_callback']).to eq @callback
end
it 'should have the correct scope parameter' do
expect(@unsigned_parameters).to have_key('scope')
expect(@unsigned_parameters['scope']).to eq @scope
end
it 'should have the correct OAuth version' do
expect(@unsigned_parameters).to have_key('oauth_version')
expect(@unsigned_parameters['oauth_version']).to eq '1.0'
end
end
describe Signet::OAuth1, 'when generating token credential parameters' do
before do
@client_credential_key = 'dpf43f3p2l4k3l03'
@temporary_credential_key = 'hh5s93j4hdidpola'
@verifier = '473f82d3'
@signature_method = 'HMAC-SHA1'
@unsigned_parameters =
Signet::OAuth1.unsigned_token_credential_parameters(
:client_credential_key => @client_credential_key,
:temporary_credential_key => @temporary_credential_key,
:signature_method => @signature_method,
:verifier => @verifier
).inject({}) { |h,(k,v)| h[k]=v; h }
end
it 'should raise an error if the client credential key is missing' do
expect(lambda do
Signet::OAuth1.unsigned_token_credential_parameters(
:client_credential_key => nil,
:temporary_credential_key => @temporary_credential_key,
:signature_method => @signature_method,
:verifier => @verifier
)
end).to raise_error(ArgumentError)
end
it 'should raise an error if the temporary credential key is missing' do
expect(lambda do
Signet::OAuth1.unsigned_token_credential_parameters(
:client_credential_key => @client_credential_key,
:temporary_credential_key => nil,
:signature_method => @signature_method,
:verifier => @verifier
)
end).to raise_error(ArgumentError)
end
it 'should raise an error if the verifier is missing' do
expect(lambda do
Signet::OAuth1.unsigned_token_credential_parameters(
:client_credential_key => @client_credential_key,
:temporary_credential_key => @temporary_credential_key,
:signature_method => @signature_method,
:verifier => nil
)
end).to raise_error(ArgumentError)
end
it 'should have the correct client credential key' do
expect(@unsigned_parameters).to have_key('oauth_consumer_key')
expect(@unsigned_parameters['oauth_consumer_key']).to eq @client_credential_key
end
it 'should have the correct temporary credentials key' do
expect(@unsigned_parameters).to have_key('oauth_token')
expect(@unsigned_parameters['oauth_token']).to eq @temporary_credential_key
end
it 'should have the correct signature method' do
expect(@unsigned_parameters).to have_key('oauth_signature_method')
expect(@unsigned_parameters['oauth_signature_method']).to eq @signature_method
end
it 'should have a valid timestamp' do
# Verify that we have a timestamp, it's in the correct format and within
# a reasonable range of the current time.
expect(@unsigned_parameters).to have_key('oauth_timestamp')
expect(@unsigned_parameters['oauth_timestamp']).to match(/^[0-9]+$/)
expect(@unsigned_parameters['oauth_timestamp'].to_i).to be <= Time.now.to_i
expect(@unsigned_parameters['oauth_timestamp'].to_i).to be >= Time.now.to_i - 1
end
it 'should have a valid nonce' do
# Verify that we have a nonce and that it has sufficient length for
# uniqueness.
expect(@unsigned_parameters).to have_key('oauth_nonce')
expect(@unsigned_parameters['oauth_nonce']).to match(/^[0-9a-zA-Z]{16,100}$/)
end
it 'should have the verifier' do
expect(@unsigned_parameters).to have_key('oauth_verifier')
expect(@unsigned_parameters['oauth_verifier']).to eq @verifier
end
it 'should have the correct OAuth version' do
expect(@unsigned_parameters).to have_key('oauth_version')
expect(@unsigned_parameters['oauth_version']).to eq '1.0'
end
end
describe Signet::OAuth1, 'when generating protected resource parameters' do
before do
@client_credential_key = 'dpf43f3p2l4k3l03'
@token_credential_key = 'nnch734d00sl2jdk'
@signature_method = 'HMAC-SHA1'
@unsigned_parameters =
Signet::OAuth1.unsigned_resource_parameters(
:client_credential_key => @client_credential_key,
:token_credential_key => @token_credential_key,
:signature_method => @signature_method
).inject({}) { |h,(k,v)| h[k]=v; h }
end
it 'should raise an error if the client credential key is missing' do
expect(lambda do
Signet::OAuth1.unsigned_resource_parameters(
:client_credential_key => nil,
:token_credential_key => @token_credential_key,
:signature_method => @signature_method
)
end).to raise_error(ArgumentError)
end
it 'should raise an error if the token credential key is missing' do
expect(lambda do
Signet::OAuth1.unsigned_resource_parameters(
:client_credential_key => @client_credential_key,
:token_credential_key => nil,
:signature_method => @signature_method
)
end).to raise_error(ArgumentError)
end
it 'should have the correct client credential key' do
expect(@unsigned_parameters).to have_key('oauth_consumer_key')
expect(@unsigned_parameters['oauth_consumer_key']).to eq @client_credential_key
end
it 'should have the correct token credentials key' do
expect(@unsigned_parameters).to have_key('oauth_token')
expect(@unsigned_parameters['oauth_token']).to eq @token_credential_key
end
it 'should have the correct signature method' do
expect(@unsigned_parameters).to have_key('oauth_signature_method')
expect(@unsigned_parameters['oauth_signature_method']).to eq @signature_method
end
it 'should have a valid timestamp' do
# Verify that we have a timestamp, it's in the correct format and within
# a reasonable range of the current time.
expect(@unsigned_parameters).to have_key('oauth_timestamp')
expect(@unsigned_parameters['oauth_timestamp']).to match(/^[0-9]+$/)
expect(@unsigned_parameters['oauth_timestamp'].to_i).to be <= Time.now.to_i
expect(@unsigned_parameters['oauth_timestamp'].to_i).to be >= Time.now.to_i - 1
end
it 'should have a valid nonce' do
# Verify that we have a nonce and that it has sufficient length for
# uniqueness.
expect(@unsigned_parameters).to have_key('oauth_nonce')
expect(@unsigned_parameters['oauth_nonce']).to match(/^[0-9a-zA-Z]{16,100}$/)
end
it 'should have the correct OAuth version' do
expect(@unsigned_parameters).to have_key('oauth_version')
expect(@unsigned_parameters['oauth_version']).to eq '1.0'
end
end
describe Signet::OAuth1, 'when generating token credential parameters ' +
'with Signet::OAuth1::Credential objects' do
before do
@client_credential = Signet::OAuth1::Credential.new(
'dpf43f3p2l4k3l03', 'kd94hf93k423kf44'
)
@temporary_credential = Signet::OAuth1::Credential.new(
'hh5s93j4hdidpola', 'hdhd0244k9j7ao03'
)
@verifier = '473f82d3'
@signature_method = 'HMAC-SHA1'
@unsigned_parameters =
Signet::OAuth1.unsigned_token_credential_parameters(
:client_credential => @client_credential,
:temporary_credential => @temporary_credential,
:signature_method => @signature_method,
:verifier => @verifier
).inject({}) { |h,(k,v)| h[k]=v; h }
end
it 'should have the correct client credential key' do
expect(@unsigned_parameters).to have_key('oauth_consumer_key')
expect(@unsigned_parameters['oauth_consumer_key']).to eq @client_credential.key
end
it 'should have the correct temporary credentials key' do
expect(@unsigned_parameters).to have_key('oauth_token')
expect(@unsigned_parameters['oauth_token']).to eq @temporary_credential.key
end
it 'should have the correct signature method' do
expect(@unsigned_parameters).to have_key('oauth_signature_method')
expect(@unsigned_parameters['oauth_signature_method']).to eq @signature_method
end
it 'should have a valid timestamp' do
# Verify that we have a timestamp, it's in the correct format and within
# a reasonable range of the current time.
expect(@unsigned_parameters).to have_key('oauth_timestamp')
expect(@unsigned_parameters['oauth_timestamp']).to match(/^[0-9]+$/)
expect(@unsigned_parameters['oauth_timestamp'].to_i).to be <= Time.now.to_i
expect(@unsigned_parameters['oauth_timestamp'].to_i).to be >= Time.now.to_i - 1
end
it 'should have a valid nonce' do
# Verify that we have a nonce and that it has sufficient length for
# uniqueness.
expect(@unsigned_parameters).to have_key('oauth_nonce')
expect(@unsigned_parameters['oauth_nonce']).to match(/^[0-9a-zA-Z]{16,100}$/)
end
it 'should have the correct OAuth version' do
expect(@unsigned_parameters).to have_key('oauth_version')
expect(@unsigned_parameters['oauth_version']).to eq '1.0'
end
end
describe Signet::OAuth1, 'when generating token credential parameters ' +
'with a Signet::OAuth1::Client object' do
before do
@client = Signet::OAuth1::Client.new
@client.client_credential = Signet::OAuth1::Credential.new(
'dpf43f3p2l4k3l03', 'kd94hf93k423kf44'
)
@client.temporary_credential = Signet::OAuth1::Credential.new(
'hh5s93j4hdidpola', 'hdhd0244k9j7ao03'
)
@verifier = '473f82d3'
@signature_method = 'HMAC-SHA1'
@unsigned_parameters =
Signet::OAuth1.unsigned_token_credential_parameters(
:client => @client,
:signature_method => @signature_method,
:verifier => @verifier
).inject({}) { |h,(k,v)| h[k]=v; h }
end
it 'should have the correct client credential key' do
expect(@unsigned_parameters).to have_key('oauth_consumer_key')
expect(@unsigned_parameters['oauth_consumer_key']).to eq @client.client_credential_key
end
it 'should have the correct temporary credentials key' do
expect(@unsigned_parameters).to have_key('oauth_token')
expect(@unsigned_parameters['oauth_token']).to eq @client.temporary_credential_key
end
it 'should have the correct signature method' do
expect(@unsigned_parameters).to have_key('oauth_signature_method')
expect(@unsigned_parameters['oauth_signature_method']).to eq @signature_method
end
it 'should have a valid timestamp' do
# Verify that we have a timestamp, it's in the correct format and within
# a reasonable range of the current time.
expect(@unsigned_parameters).to have_key('oauth_timestamp')
expect(@unsigned_parameters['oauth_timestamp']).to match(/^[0-9]+$/)
expect(@unsigned_parameters['oauth_timestamp'].to_i).to be <= Time.now.to_i
expect(@unsigned_parameters['oauth_timestamp'].to_i).to be >= Time.now.to_i - 1
end
it 'should have a valid nonce' do
# Verify that we have a nonce and that it has sufficient length for
# uniqueness.
expect(@unsigned_parameters).to have_key('oauth_nonce')
expect(@unsigned_parameters['oauth_nonce']).to match(/^[0-9a-zA-Z]{16,100}$/)
end
it 'should have the correct OAuth version' do
expect(@unsigned_parameters).to have_key('oauth_version')
expect(@unsigned_parameters['oauth_version']).to eq '1.0'
end
end
describe Signet::OAuth1, 'when generating token credential parameters ' +
'with Signet::OAuth1::Credential objects' do
before do
@client_credential = Signet::OAuth1::Credential.new(
'dpf43f3p2l4k3l03', 'kd94hf93k423kf44'
)
@temporary_credential = Signet::OAuth1::Credential.new(
'hh5s93j4hdidpola', 'hdhd0244k9j7ao03'
)
@verifier = '473f82d3'
@signature_method = 'HMAC-SHA1'
@unsigned_parameters =
Signet::OAuth1.unsigned_token_credential_parameters(
:client_credential => @client_credential,
:temporary_credential => @temporary_credential,
:signature_method => @signature_method,
:verifier => @verifier
).inject({}) { |h,(k,v)| h[k]=v; h }
end
it 'should have the correct client credential key' do
expect(@unsigned_parameters).to have_key('oauth_consumer_key')
expect(@unsigned_parameters['oauth_consumer_key']).to eq @client_credential.key
end
it 'should have the correct temporary credentials key' do
expect(@unsigned_parameters).to have_key('oauth_token')
expect(@unsigned_parameters['oauth_token']).to eq @temporary_credential.key
end
it 'should have the correct signature method' do
expect(@unsigned_parameters).to have_key('oauth_signature_method')
expect(@unsigned_parameters['oauth_signature_method']).to eq @signature_method
end
it 'should have a valid timestamp' do
# Verify that we have a timestamp, it's in the correct format and within
# a reasonable range of the current time.
expect(@unsigned_parameters).to have_key('oauth_timestamp')
expect(@unsigned_parameters['oauth_timestamp']).to match(/^[0-9]+$/)
expect(@unsigned_parameters['oauth_timestamp'].to_i).to be <= Time.now.to_i
expect(@unsigned_parameters['oauth_timestamp'].to_i).to be >= Time.now.to_i - 1
end
it 'should have a valid nonce' do
# Verify that we have a nonce and that it has sufficient length for
# uniqueness.
expect(@unsigned_parameters).to have_key('oauth_nonce')
expect(@unsigned_parameters['oauth_nonce']).to match(/^[0-9a-zA-Z]{16,100}$/)
end
it 'should have the correct OAuth version' do
expect(@unsigned_parameters).to have_key('oauth_version')
expect(@unsigned_parameters['oauth_version']).to eq '1.0'
end
end
describe Signet::OAuth1, 'extracting credential keys from options' do
it 'should raise an error for bogus credentials' do
expect(lambda do
Signet::OAuth1.extract_credential_key_option(
:client, {:client_credential_key => true}
)
end).to raise_error(TypeError)
end
it 'should raise an error for bogus credentials' do
expect(lambda do
Signet::OAuth1.extract_credential_key_option(
:client, {:client_credential => 42}
)
end).to raise_error(TypeError)
end
it 'should raise an error for bogus credentials' do
expect(lambda do
Signet::OAuth1.extract_credential_key_option(
:client, {:client => 42}
)
end).to raise_error(TypeError)
end
it 'should return nil for missing credential key' do
expect(Signet::OAuth1.extract_credential_key_option(:client, {})).to eq nil
end
it 'should find the correct credential key' do
expect(Signet::OAuth1.extract_credential_key_option(
:client, {:client_credential_key => 'dpf43f3p2l4k3l03'}
)).to eq 'dpf43f3p2l4k3l03'
end
it 'should find the correct credential key' do
expect(Signet::OAuth1.extract_credential_key_option(
:client, {:client_credential => Signet::OAuth1::Credential.new(
'dpf43f3p2l4k3l03', 'kd94hf93k423kf44'
)}
)).to eq 'dpf43f3p2l4k3l03'
end
it 'should find the correct credential key' do
client = Signet::OAuth1::Client.new
client.client_credential = Signet::OAuth1::Credential.new(
'dpf43f3p2l4k3l03', 'kd94hf93k423kf44'
)
expect(Signet::OAuth1.extract_credential_key_option(
:client, {:client => client}
)).to eq 'dpf43f3p2l4k3l03'
end
it 'should find the correct credential key' do
client = Signet::OAuth1::Client.new
client.temporary_credential = Signet::OAuth1::Credential.new(
'hh5s93j4hdidpola', 'hdhd0244k9j7ao03'
)
expect(Signet::OAuth1.extract_credential_key_option(
:temporary, {:client => client}
)).to eq 'hh5s93j4hdidpola'
end
end
describe Signet::OAuth1, 'extracting credential secrets from options' do
it 'should raise an error for bogus credentials' do
expect(lambda do
Signet::OAuth1.extract_credential_secret_option(
:client, {:client_credential_secret => true}
)
end).to raise_error(TypeError)
end
it 'should raise an error for bogus credentials' do
expect(lambda do
Signet::OAuth1.extract_credential_secret_option(
:client, {:client_credential => 42}
)
end).to raise_error(TypeError)
end
it 'should raise an error for bogus credentials' do
expect(lambda do
Signet::OAuth1.extract_credential_secret_option(
:client, {:client => 42}
)
end).to raise_error(TypeError)
end
it 'should raise an error for missing credential secret' do
expect(Signet::OAuth1.extract_credential_secret_option(:client, {})).to eq nil
end
it 'should find the correct credential secret' do
expect(Signet::OAuth1.extract_credential_secret_option(
:client, {:client_credential_secret => 'kd94hf93k423kf44'}
)).to eq 'kd94hf93k423kf44'
end
it 'should find the correct credential secret' do
expect(Signet::OAuth1.extract_credential_secret_option(
:client, {:client_credential => Signet::OAuth1::Credential.new(
'dpf43f3p2l4k3l03', 'kd94hf93k423kf44'
)}
)).to eq 'kd94hf93k423kf44'
end
it 'should find the correct credential secret' do
client = Signet::OAuth1::Client.new
client.client_credential = Signet::OAuth1::Credential.new(
'dpf43f3p2l4k3l03', 'kd94hf93k423kf44'
)
expect(Signet::OAuth1.extract_credential_secret_option(
:client, {:client => client}
)).to eq 'kd94hf93k423kf44'
end
it 'should find the correct credential secret' do
client = Signet::OAuth1::Client.new
client.temporary_credential = Signet::OAuth1::Credential.new(
'hh5s93j4hdidpola', 'hdhd0244k9j7ao03'
)
expect(Signet::OAuth1.extract_credential_secret_option(
:temporary, {:client => client}
)).to eq 'hdhd0244k9j7ao03'
end
end
signet-0.11.0/spec/signet/oauth_2_spec.rb 0000644 0000041 0000041 00000017370 13410324340 020237 0 ustar www-data www-data # Copyright (C) 2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'
require 'signet/errors'
require 'signet/oauth_2'
describe Signet::OAuth2 do
# This behavior will almost certainly change in subsequent updates.
describe 'when parsing an Authorization header' do
it 'should correctly handle HTTP Basic auth-scheme' do
parameters = Signet::OAuth2.parse_authorization_header(
'Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW'
).inject({}) { |h,(k,v)| h[k]=v; h }
expect(parameters['client_id']).to eq 's6BhdRkqt3'
expect(parameters['client_secret']).to eq 'gX1fBat3bV'
end
it 'should correctly handle OAuth auth-scheme' do
parameters = Signet::OAuth2.parse_authorization_header(
'OAuth vF9dft4qmT'
).inject({}) { |h,(k,v)| h[k]=v; h }
expect(parameters['access_token']).to eq 'vF9dft4qmT'
end
it 'should correctly handle OAuth auth-scheme with realm' do
parameters = Signet::OAuth2.parse_authorization_header(
'OAuth vF9dft4qmT, realm="http://sp.example.com/"'
).inject({}) { |h,(k,v)| h[k]=v; h }
expect(parameters['access_token']).to eq 'vF9dft4qmT'
expect(parameters['realm']).to eq 'http://sp.example.com/'
end
it 'should correctly handle OAuth auth-scheme with multiple auth-params' do
parameters = Signet::OAuth2.parse_authorization_header(
'OAuth vF9dft4qmT, first="one", second="two"'
).inject({}) { |h,(k,v)| h[k]=v; h }
expect(parameters['access_token']).to eq 'vF9dft4qmT'
expect(parameters['first']).to eq 'one'
expect(parameters['second']).to eq 'two'
end
it 'should liberally handle auth-params with single-quoted strings' do
parameters = Signet::OAuth2.parse_authorization_header(
'OAuth vF9dft4qmT, first=\'one\', second=\'two\''
).inject({}) { |h,(k,v)| h[k]=v; h }
expect(parameters['access_token']).to eq 'vF9dft4qmT'
expect(parameters['first']).to eq 'one'
expect(parameters['second']).to eq 'two'
end
it 'should liberally handle auth-params with unquoted strings' do
parameters = Signet::OAuth2.parse_authorization_header(
'OAuth vF9dft4qmT, first=one, second=two'
).inject({}) { |h,(k,v)| h[k]=v; h }
expect(parameters['access_token']).to eq 'vF9dft4qmT'
expect(parameters['first']).to eq 'one'
expect(parameters['second']).to eq 'two'
end
it 'should not allow unquoted strings that do not match tchar' do
expect(lambda do
parameters = Signet::OAuth2.parse_authorization_header(
'OAuth vF9dft4qmT, first=one:1'
)
end).to raise_error(Signet::ParseError)
end
it 'should not parse non-OAuth auth-schemes' do
expect(lambda do
Signet::OAuth2.parse_authorization_header(
'AuthSub token="GD32CMCL25aZ-v____8B"'
)
end).to raise_error(Signet::ParseError)
end
end
# This behavior will almost certainly change in subsequent updates.
describe 'when parsing a WWW-Authenticate header' do
it 'should correctly handle OAuth challenge with auth-params' do
parameters = Signet::OAuth2.parse_www_authenticate_header(
'OAuth realm="http://sp.example.com/", error="expired_token", ' +
'error_description="The access token has expired."'
).inject({}) { |h,(k,v)| h[k]=v; h }
expect(parameters['realm']).to eq 'http://sp.example.com/'
expect(parameters['error']).to eq 'expired_token'
expect(parameters['error_description']).to eq 'The access token has expired.'
end
it 'should liberally handle auth-params with single-quoted strings' do
parameters = Signet::OAuth2.parse_www_authenticate_header(
'OAuth realm=\'http://sp.example.com/\', error=\'expired_token\', ' +
'error_description=\'The access token has expired.\''
).inject({}) { |h,(k,v)| h[k]=v; h }
expect(parameters['realm']).to eq 'http://sp.example.com/'
expect(parameters['error']).to eq 'expired_token'
expect(parameters['error_description']).to eq 'The access token has expired.'
end
it 'should liberally handle auth-params with token strings' do
parameters = Signet::OAuth2.parse_www_authenticate_header(
'OAuth realm="http://sp.example.com/", error=expired_token, ' +
'error_description="The access token has expired."'
).inject({}) { |h,(k,v)| h[k]=v; h }
expect(parameters['realm']).to eq 'http://sp.example.com/'
expect(parameters['error']).to eq 'expired_token'
expect(parameters['error_description']).to eq 'The access token has expired.'
end
it 'should liberally handle out-of-order auth-params' do
parameters = Signet::OAuth2.parse_www_authenticate_header(
'OAuth error_description=\'The access token has expired.\', ' +
'error=\'expired_token\', realm=\'http://sp.example.com/\''
).inject({}) { |h,(k,v)| h[k]=v; h }
expect(parameters['realm']).to eq 'http://sp.example.com/'
expect(parameters['error']).to eq 'expired_token'
expect(parameters['error_description']).to eq 'The access token has expired.'
end
it 'should not allow unquoted strings that do not match tchar' do
expect(lambda do
Signet::OAuth2.parse_www_authenticate_header(
'OAuth realm=http://sp.example.com/, error=expired_token, ' +
'error_description="The access token has expired."'
)
end).to raise_error(Signet::ParseError)
end
it 'should not parse non-OAuth challenges' do
expect(lambda do
Signet::OAuth2.parse_www_authenticate_header(
'AuthSub realm="https://www.google.com/accounts/AuthSubRequest"'
)
end).to raise_error(Signet::ParseError)
end
end
describe 'when generating a Basic Authorization header' do
it 'should correctly handle client ID and password pairs' do
# Example from OAuth 2 spec
expect(Signet::OAuth2.generate_basic_authorization_header(
's6BhdRkqt3', 'gX1fBat3bV'
)).to eq 'Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW'
end
it 'should correctly encode using the alogrithm given in RFC 2617' do
# Example from RFC 2617
expect(Signet::OAuth2.generate_basic_authorization_header(
'Aladdin', 'open sesame'
)).to eq 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='
end
end
describe 'when parsing a token response body' do
it 'should correctly handle just an access token' do
expect(Signet::OAuth2.parse_credentials(
'{"access_token": "12345"}',
'application/json; charset=utf-8'
)).to eq ({"access_token" => "12345"})
end
it 'should handle form encoded responses' do
expect(Signet::OAuth2.parse_credentials(
'access_token=12345&expires=1000',
'application/x-www-form-urlencoded; charset=utf-8'
)).to eq({"access_token" => "12345", "expires" => "1000" })
end
it 'should raise an error for an invalid body' do
expect(lambda do
Signet::OAuth2.parse_credentials(
'This is not JSON.',
'application/json'
)
end).to raise_error(MultiJson::DecodeError)
end
it 'should raise an error for a bogus body' do
expect(lambda do
Signet::OAuth2.parse_credentials(:bogus, 'application/json')
end).to raise_error(TypeError)
end
end
end
signet-0.11.0/spec/spec_helper_spec.rb 0000644 0000041 0000041 00000000731 13410324340 017667 0 ustar www-data www-data RSpec.describe 'spec_helper.rb' do
let(:spec_dir) { File.expand_path(File.dirname(__FILE__)) }
let(:root_dir) { File.expand_path(File.join(spec_dir, '..')) }
let(:lib_dir) { File.expand_path(File.join(root_dir, 'lib')) }
describe 'spec_dir' do
it 'is already in $LOAD_PATH' do
expect($LOAD_PATH).to include spec_dir
end
end
describe 'lib_dir' do
it 'is already in $LOAD_PATH' do
expect($LOAD_PATH).to include lib_dir
end
end
end
signet-0.11.0/spec/spec_helper.rb 0000644 0000041 0000041 00000000260 13410324340 016652 0 ustar www-data www-data $:.uniq!
require 'rubygems'
require 'signet'
require 'rspec'
require 'simplecov'
require 'faraday'
SimpleCov.start if ENV["COVERAGE"]
Faraday::Adapter.load_middleware(:test)
signet-0.11.0/CHANGELOG.md 0000644 0000041 0000041 00000010556 13410324340 014724 0 ustar www-data www-data ## 0.11.0 (2018-10-08)
* Add constant time comparison for oauth signatures.
## 0.10.0 (2018-09-21)
* Add UnexpectedStatusError class for http status errors that are not handled.
## 0.9.2 (2018-09-12)
* Update issued_at correctly when it is set simultaneously with expires_in.
## 0.9.1 (2018-08-29)
* Warn on EOL ruby versions.
* Fix DateTime normalization.
## 0.9.0 (2018-08-20)
* Add RemoteServerError class for 5xx level errors.
* Allow to_json to be called with arguments
* Expires_in now sets and reflects current expires_at value
* Expires_within(0) now returns false when expires_at is nil.
## 0.8.1 (2017-10-13)
* Restore support for Ruby 1.9.3
## 0.8.0 (2017-10-12)
* Ensure the "expires_at" attribute is recalculated on refresh (chutzimir)
* Fix warnings on Ruby 2.4 (koic)
* Allow DateTime objects to be passed into attributes (foxtacles)
* Provide signature verification algorithm for compatibility with ruby-jwt 2.0 (jurriaan)
* Signet::OAuth2::Client#decoded_id_token can take a keyfinder block (mvastola)
## 0.7.3 (2016-06-20)
* Fix timestamp parsing on 32-bit systems
* Fix expiration check when issue/expiry times are nil
## 0.7.2 (2015-12-21)
* Don't assume Faraday form encoding middleware is present
## 0.7.1 (2015-12-17)
* Fix an issue with date parsing
## 0.7 (2015-12-06)
* No longer overwrite SSL environment variables.
* Tighten up date & URL (de)serialization for OAuth2 client
* Allow Hurley as a connection
* Allow scope as an option in `fetch_access_token!` to request downscoped access tokens
* Add expires_within(sec) method to oauth2 client to facilitate proactive
refreshes
## 0.6.1 (2015-06-08)
* Fix language warnings for unused & shadowed variables ((@blowmage)[])
* Update SSL cert path for OSX ((@gambaroff)[])
* Update JWT library and fix broken tests
* Fix incorrect parameter name in OAuth2 client docs ((@samuelreh)[])
* Fix symbolization of URL parameter keys ((@swifthand)[])
## 0.6.0 (2014-12-05)
* Drop support for ruby versions < 1.9.3
* Update gem dependencies and lock down versions tighter
* Allow form encoded responses when exchanging OAuth 2 authorization codes
* Normalize options keys for indifferent access
## 0.5.1 (2014-06-08)
* Allow Hash objects to be used to initialize authorization URI
* Added PLAINTEXT and RSA-SHA1 signature methods to OAuth 1 support
* Added client object serialization
* The `approval_prompt` option no longer defaults to `:force`
* The `approval_prompt` and `prompt` are now mutually exclusive.
## 0.5.0 (2013-05-31)
* Switched to faraday 0.9.0
* Added `expires_at` option
## 0.4.5
* Minor documentation fixes
* Allow postmessage as a valid redirect_uri in OAuth 2
## 0.4.4
* Add support for assertion profile
## 0.4.3
* Added method to clear credentials
## 0.4.2
* Backwards compatibility for MultiJson
## 0.4.1
* Updated Launchy dependency
## 0.4.0
* Added OAuth 1 server implementation
* Updated Faraday dependency
## 0.3.4
* Attempts to auto-detect CA cert location
## 0.3.3
* Request objects no longer recreated during processing
* Faraday middleware now supported
* Streamed requests now supported
* Fixed assertion profiles; client ID/secret omission no longer an error
## 0.3.2
* Added audience security check for ID tokens
## 0.3.1
* Fixed a warning while determining grant type
* Removed requirement that a connection be supplied when authorizing requests
* Updated addressable dependency to avoid minor bug
* Fixed some documentation stuff around markdown formatting
* Added support for Google Code wiki format output when generating docs
## 0.3.0
* Replaced httpadapter gem dependency with faraday
* Replaced json gem dependency with multi_json
* Updated to OAuth 2.0 draft 22
* Complete test coverage
## 0.2.4
* Updated to incorporate changes to the Google OAuth endpoints
## 0.2.3
* Added support for JWT-formatted ID tokens.
* Added :issued_at option to #update_token! method.
## 0.2.2
* Lowered requirements for json gem
## 0.2.1
* Updated to keep in sync with the new httpadapter changes
## 0.2.0
* Added support for OAuth 2.0 draft 10
## 0.1.4
* Added support for a two-legged authorization flow
## 0.1.3
* Fixed issue with headers passed in as a Hash
* Fixed incompatibilities with Ruby 1.8.6
## 0.1.2
* Fixed bug with overzealous normalization
## 0.1.1
* Fixed bug with missing StringIO require
* Fixed issue with dependency on unreleased features of addressable
## 0.1.0
* Initial release
signet-0.11.0/signet.gemspec 0000644 0000041 0000041 00000003463 13410324340 015750 0 ustar www-data www-data # -*- encoding: utf-8 -*-
# stub: signet 0.5.1 ruby lib
require File.join(File.dirname(__FILE__), 'lib/signet', 'version')
Gem::Specification.new do |s|
s.name = "signet"
s.version = Signet::VERSION::STRING
s.required_rubygems_version = ">= 1.3.5"
s.require_paths = ["lib"]
s.authors = ["Bob Aman", "Steven Bazyl"]
s.license = "Apache-2.0"
s.description = "Signet is an OAuth 1.0 / OAuth 2.0 implementation.\n"
s.email = "sbazyl@google.com"
s.extra_rdoc_files = ["README.md"]
s.files = %w(signet.gemspec Rakefile LICENSE CHANGELOG.md README.md Gemfile)
s.files += Dir.glob("lib/**/*.rb")
s.files += Dir.glob("spec/**/*.{rb,opts}")
s.files += Dir.glob("vendor/**/*.rb")
s.files += Dir.glob("tasks/**/*")
s.files += Dir.glob("website/**/*")
s.homepage = "https://github.com/google/signet/"
s.rdoc_options = ["--main", "README.md"]
s.summary = "Signet is an OAuth 1.0 / OAuth 2.0 implementation."
s.required_ruby_version = ">= 1.9.3"
s.add_runtime_dependency 'addressable', '~> 2.3'
s.add_runtime_dependency 'faraday', '~> 0.9'
s.add_runtime_dependency 'multi_json', '~> 1.10'
s.add_runtime_dependency 'jwt', '>= 1.5', '< 3.0'
s.add_development_dependency 'rake', '~> 12.0'
s.add_development_dependency 'yard', '~> 0.9', '>= 0.9.12'
s.add_development_dependency 'rspec', '~> 3.1'
s.add_development_dependency 'launchy', '~> 2.4'
s.add_development_dependency 'kramdown', '~> 1.5'
s.add_development_dependency 'simplecov', '~> 0.9'
s.post_install_message = Signet::VERSION::warn_on_old_ruby_version
if s.respond_to?(:metadata)
s.metadata['changelog_uri'] = 'https://github.com/google/signet/blob/master/CHANGELOG.md'
s.metadata['source_code_uri'] = 'https://github.com/google/signet'
s.metadata['bug_tracker_uri'] = 'https://github.com/google/signet/issues'
end
end
signet-0.11.0/LICENSE 0000644 0000041 0000041 00000025143 13410324340 014116 0 ustar www-data www-data
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
signet-0.11.0/Rakefile 0000644 0000041 0000041 00000002334 13410324340 014553 0 ustar www-data www-data lib_dir = File.expand_path(File.join(File.dirname(__FILE__), "lib"))
$:.unshift(lib_dir)
$:.uniq!
require 'rubygems'
require 'rake'
require "bundler/gem_tasks"
require File.join(File.dirname(__FILE__), 'lib/signet', 'version')
PKG_DISPLAY_NAME = 'Signet'
PKG_NAME = PKG_DISPLAY_NAME.downcase
PKG_VERSION = Signet::VERSION::STRING
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
RELEASE_NAME = "REL #{PKG_VERSION}"
PKG_AUTHOR = "Bob Aman"
PKG_AUTHOR_EMAIL = "bobaman@google.com"
PKG_HOMEPAGE = "http://code.google.com/p/oauth-signet/"
PKG_DESCRIPTION = <<-TEXT
Signet is an OAuth 1.0 / OAuth 2.0 implementation.
TEXT
PKG_SUMMARY = PKG_DESCRIPTION
PKG_FILES = FileList[
"lib/**/*", "spec/**/*", "vendor/**/*",
"tasks/**/*", "website/**/*",
"[A-Z]*", "Rakefile"
].exclude(/database\.yml/).exclude(/[_\.]git$/).exclude(/Gemfile\.lock/)
RCOV_ENABLED = !!(RUBY_PLATFORM != 'java' && RUBY_VERSION =~ /^1\.8/)
if RCOV_ENABLED
task :default => 'spec:rcov'
else
task :default => 'spec:normal'
end
WINDOWS = (RUBY_PLATFORM =~ /mswin|win32|mingw|bccwin|cygwin/) rescue false
SUDO = WINDOWS ? '' : ('sudo' unless ENV['SUDOLESS'])
Dir['tasks/**/*.rake'].each { |rake| load rake }
signet-0.11.0/lib/ 0000755 0000041 0000041 00000000000 13410324340 013652 5 ustar www-data www-data signet-0.11.0/lib/signet.rb 0000644 0000041 0000041 00000005454 13410324340 015500 0 ustar www-data www-data # Copyright (C) 2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'signet/version'
module Signet #:nodoc:
def self.parse_auth_param_list(auth_param_string)
# Production rules from:
# http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-12
token = /[-!#$\%&'*+.^_`|~0-9a-zA-Z]+/
d_qdtext = /[\s\x21\x23-\x5B\x5D-\x7E\x80-\xFF]/n
d_quoted_pair = /\\[\s\x21-\x7E\x80-\xFF]/n
d_qs = /"(?:#{d_qdtext}|#{d_quoted_pair})*"/
# Production rules that allow for more liberal parsing, i.e. single quotes
s_qdtext = /[\s\x21-\x26\x28-\x5B\x5D-\x7E\x80-\xFF]/n
s_quoted_pair = /\\[\s\x21-\x7E\x80-\xFF]/n
s_qs = /'(?:#{s_qdtext}|#{s_quoted_pair})*'/
# Combine the above production rules to find valid auth-param pairs.
auth_param = /((?:#{token})\s*=\s*(?:#{d_qs}|#{s_qs}|#{token}))/
auth_param_pairs = []
last_match = nil
remainder = auth_param_string
# Iterate over the string, consuming pair matches as we go. Verify that
# pre-matches and post-matches contain only allowable characters.
#
# This would be way easier in Ruby 1.9, but we want backwards
# compatibility.
while (match = remainder.match(auth_param))
if match.pre_match && match.pre_match !~ /^[\s,]*$/
raise ParseError,
"Unexpected auth param format: '#{auth_param_string}'."
end
auth_param_pairs << match.captures[0] # Appending pair
remainder = match.post_match
last_match = match
end
if last_match.post_match && last_match.post_match !~ /^[\s,]*$/
raise ParseError,
"Unexpected auth param format: '#{auth_param_string}'."
end
# Now parse the auth-param pair strings & turn them into key-value pairs.
return (auth_param_pairs.inject([]) do |accu, pair|
name, value = pair.split('=', 2)
if value =~ /^".*"$/
value = value.gsub(/^"(.*)"$/, '\1').gsub(/\\(.)/, '\1')
elsif value =~ /^'.*'$/
value = value.gsub(/^'(.*)'$/, '\1').gsub(/\\(.)/, '\1')
elsif value =~ /[\(\)<>@,;:\\\"\/\[\]?={}]/
# Certain special characters are not allowed
raise ParseError, (
"Unexpected characters in auth param " +
"list: '#{auth_param_string}'."
)
end
accu << [name, value]
accu
end)
end
end
signet-0.11.0/lib/signet/ 0000755 0000041 0000041 00000000000 13410324340 015143 5 ustar www-data www-data signet-0.11.0/lib/signet/oauth_1/ 0000755 0000041 0000041 00000000000 13410324340 016503 5 ustar www-data www-data signet-0.11.0/lib/signet/oauth_1/signature_methods/ 0000755 0000041 0000041 00000000000 13410324340 022227 5 ustar www-data www-data signet-0.11.0/lib/signet/oauth_1/signature_methods/rsa_sha1.rb 0000644 0000041 0000041 00000001102 13410324340 024247 0 ustar www-data www-data require 'digest/sha1'
require 'base64'
require 'openssl'
require 'signet'
module Signet #:nodoc:
module OAuth1
module RSASHA1
def self.generate_signature(
base_string, client_credential_secret, token_credential_secret)
private_key = OpenSSL::PKey::RSA.new(client_credential_secret)
signature = private_key.sign(OpenSSL::Digest::SHA1.new, base_string)
#using strict_encode64 because the encode64 method adds newline characters after ever 60 chars
return Base64.strict_encode64(signature).strip
end
end
end
end
signet-0.11.0/lib/signet/oauth_1/signature_methods/hmac_sha1.rb 0000644 0000041 0000041 00000001556 13410324340 024407 0 ustar www-data www-data require 'openssl'
require 'signet'
module Signet #:nodoc:
module OAuth1
module HMACSHA1
def self.generate_signature(
base_string, client_credential_secret, token_credential_secret)
# Both the client secret and token secret must be escaped
client_credential_secret =
Signet::OAuth1.encode(client_credential_secret)
token_credential_secret =
Signet::OAuth1.encode(token_credential_secret)
# The key for the signature is just the client secret and token
# secret joined by the '&' character. If the token secret is omitted,
# the '&' must still be present.
key = [client_credential_secret, token_credential_secret].join("&")
return Base64.encode64(OpenSSL::HMAC.digest(
OpenSSL::Digest.new('sha1'), key, base_string
)).strip
end
end
end
end
signet-0.11.0/lib/signet/oauth_1/signature_methods/plaintext.rb 0000644 0000041 0000041 00000001421 13410324340 024562 0 ustar www-data www-data require 'signet'
module Signet #:nodoc:
module OAuth1
module PLAINTEXT
def self.generate_signature(
base_string, client_credential_secret, token_credential_secret)
# Both the client secret and token secret must be escaped
client_credential_secret =
Signet::OAuth1.encode(client_credential_secret)
token_credential_secret =
Signet::OAuth1.encode(token_credential_secret)
# The key for the signature is just the client secret and token
# secret joined by the '&' character. If the token secret is omitted,
# the '&' must still be present.
key = [client_credential_secret, token_credential_secret].join("&")
return Signet::OAuth1.encode(key).strip
end
end
end
end
signet-0.11.0/lib/signet/oauth_1/client.rb 0000644 0000041 0000041 00000116333 13410324340 020315 0 ustar www-data www-data # Copyright (C) 2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'faraday'
#require 'faraday/utils'
require 'stringio'
require 'addressable/uri'
require 'signet'
require 'signet/errors'
require 'signet/oauth_1'
require 'signet/oauth_1/credential'
module Signet
module OAuth1
class Client
##
# Creates an OAuth 1.0 client.
#
# @param [Hash] options
# The configuration parameters for the client.
# - :temporary_credential_uri
-
# The OAuth temporary credentials URI.
# - :authorization_uri
-
# The OAuth authorization URI.
# - :token_credential_uri
-
# The OAuth token credentials URI.
# - :client_credential_key
-
# The OAuth client credential key.
# - :client_credential_secret
-
# The OAuth client credential secret.
# - :callback
- The OAuth callback. Defaults to 'oob'.
#
# @example
# client = Signet::OAuth1::Client.new(
# :temporary_credential_uri =>
# 'https://www.google.com/accounts/OAuthGetRequestToken',
# :authorization_uri =>
# 'https://www.google.com/accounts/OAuthAuthorizeToken',
# :token_credential_uri =>
# 'https://www.google.com/accounts/OAuthGetAccessToken',
# :client_credential_key => 'anonymous',
# :client_credential_secret => 'anonymous'
# )
def initialize(options={})
self.update!(options)
end
##
# Updates an OAuth 1.0 client.
#
# @param [Hash] options
# The configuration parameters for the client.
# - :temporary_credential_uri
-
# The OAuth temporary credentials URI.
# - :authorization_uri
-
# The OAuth authorization URI.
# - :token_credential_uri
-
# The OAuth token credentials URI.
# - :client_credential_key
-
# The OAuth client credential key.
# - :client_credential_secret
-
# The OAuth client credential secret.
# - :callback
- The OAuth callback. Defaults to 'oob'.
#
# @example
# client.update!(
# :temporary_credential_uri =>
# 'https://www.google.com/accounts/OAuthGetRequestToken',
# :authorization_uri =>
# 'https://www.google.com/accounts/OAuthAuthorizeToken',
# :token_credential_uri =>
# 'https://www.google.com/accounts/OAuthGetAccessToken',
# :client_credential_key => 'anonymous',
# :client_credential_secret => 'anonymous'
# )
#
# @see Signet::OAuth1::Client#initialize
def update!(options={})
# Normalize key to String to allow indifferent access.
options = options.inject({}) { |accu, (k, v)| accu[k.to_s] = v; accu }
self.temporary_credential_uri = options["temporary_credential_uri"]
self.authorization_uri = options["authorization_uri"]
self.token_credential_uri = options["token_credential_uri"]
# Technically... this would allow you to pass in a :client key...
# But that would be weird. Don't do that.
self.client_credential_key =
Signet::OAuth1.extract_credential_key_option("client", options)
self.client_credential_secret =
Signet::OAuth1.extract_credential_secret_option("client", options)
self.temporary_credential_key =
Signet::OAuth1.extract_credential_key_option("temporary", options)
self.temporary_credential_secret =
Signet::OAuth1.extract_credential_secret_option("temporary", options)
self.token_credential_key =
Signet::OAuth1.extract_credential_key_option("token", options)
self.token_credential_secret =
Signet::OAuth1.extract_credential_secret_option("token", options)
self.callback = options["callback"]
self.two_legged = options["two_legged"] || false
return self
end
##
# Returns the temporary credentials URI for this client.
#
# @return [Addressable::URI] The temporary credentials URI.
def temporary_credential_uri
return @temporary_credential_uri
end
alias_method :request_token_uri, :temporary_credential_uri
##
# Sets the temporary credentials URI for this client.
#
# @param [Addressable::URI, String, #to_str]
# new_temporary_credential_uri
# The temporary credentials URI.
def temporary_credential_uri=(new_temporary_credential_uri)
if new_temporary_credential_uri != nil
new_temporary_credential_uri =
Addressable::URI.parse(new_temporary_credential_uri)
@temporary_credential_uri = new_temporary_credential_uri
else
@temporary_credential_uri = nil
end
end
alias_method :request_token_uri=, :temporary_credential_uri=
##
# Returns the authorization URI that the user should be redirected to.
#
# @return [Addressable::URI] The authorization URI.
#
# @see Signet::OAuth1.generate_authorization_uri
def authorization_uri(options={})
options = options.merge(
:temporary_credential_key => self.temporary_credential_key,
:callback => self.callback
)
return nil if @authorization_uri == nil
return Addressable::URI.parse(
::Signet::OAuth1.generate_authorization_uri(
@authorization_uri, options
)
)
end
##
# Sets the authorization URI for this client.
#
# @param [Addressable::URI, String, #to_str] new_authorization_uri
# The authorization URI.
def authorization_uri=(new_authorization_uri)
if new_authorization_uri != nil
new_authorization_uri = Addressable::URI.send(
new_authorization_uri.kind_of?(Hash) ? :new : :parse,
new_authorization_uri
)
@authorization_uri = new_authorization_uri
else
@authorization_uri = nil
end
end
##
# Returns the token credential URI for this client.
#
# @return [Addressable::URI] The token credential URI.
def token_credential_uri
return @token_credential_uri
end
alias_method :access_token_uri, :token_credential_uri
##
# Sets the token credential URI for this client.
#
# @param [Addressable::URI, Hash, String, #to_str] new_token_credential_uri
# The token credential URI.
def token_credential_uri=(new_token_credential_uri)
if new_token_credential_uri != nil
new_token_credential_uri = Addressable::URI.send(
new_token_credential_uri.kind_of?(Hash) ? :new : :parse,
new_token_credential_uri
)
@token_credential_uri = new_token_credential_uri
else
@token_credential_uri = nil
end
end
alias_method :access_token_uri=, :token_credential_uri=
# Lots of duplicated code here, but for the sake of auto-generating
# documentation, we're going to let it slide. Oh well.
##
# Returns the client credential for this client.
#
# @return [Signet::OAuth1::Credential] The client credentials.
def client_credential
if self.client_credential_key && self.client_credential_secret
return ::Signet::OAuth1::Credential.new(
self.client_credential_key,
self.client_credential_secret
)
elsif !self.client_credential_key && !self.client_credential_secret
return nil
else
raise ArgumentError,
"The client credential key and secret must be set."
end
end
alias_method :consumer_token, :client_credential
##
# Sets the client credential for this client.
#
# @param [Signet::OAuth1::Credential] new_client_credential
# The client credentials.
def client_credential=(new_client_credential)
if new_client_credential != nil
if !new_client_credential.kind_of?(::Signet::OAuth1::Credential)
raise TypeError,
"Expected Signet::OAuth1::Credential, " +
"got #{new_client_credential.class}."
end
@client_credential_key = new_client_credential.key
@client_credential_secret = new_client_credential.secret
else
@client_credential_key = nil
@client_credential_secret = nil
end
end
alias_method :consumer_token=, :client_credential=
##
# Returns the client credential key for this client.
#
# @return [String] The client credential key.
def client_credential_key
return @client_credential_key
end
alias_method :consumer_key, :client_credential_key
##
# Sets the client credential key for this client.
#
# @param [String, #to_str] new_client_credential_key
# The client credential key.
def client_credential_key=(new_client_credential_key)
if new_client_credential_key != nil
if !new_client_credential_key.respond_to?(:to_str)
raise TypeError,
"Can't convert #{new_client_credential_key.class} into String."
end
new_client_credential_key = new_client_credential_key.to_str
@client_credential_key = new_client_credential_key
else
@client_credential_key = nil
end
end
alias_method :consumer_key=, :client_credential_key=
##
# Returns the client credential secret for this client.
#
# @return [String] The client credential secret.
def client_credential_secret
return @client_credential_secret
end
alias_method :consumer_secret, :client_credential_secret
##
# Sets the client credential secret for this client.
#
# @param [String, #to_str] new_client_credential_secret
# The client credential secret.
def client_credential_secret=(new_client_credential_secret)
if new_client_credential_secret != nil
if !new_client_credential_secret.respond_to?(:to_str)
raise TypeError,
"Can't convert #{new_client_credential_secret.class} " +
"into String."
end
new_client_credential_secret = new_client_credential_secret.to_str
@client_credential_secret = new_client_credential_secret
else
@client_credential_secret = nil
end
end
alias_method :consumer_secret=, :client_credential_secret=
##
# Returns the temporary credential for this client.
#
# @return [Signet::OAuth1::Credential] The temporary credentials.
def temporary_credential
if self.temporary_credential_key && self.temporary_credential_secret
return ::Signet::OAuth1::Credential.new(
self.temporary_credential_key,
self.temporary_credential_secret
)
elsif !self.temporary_credential_key &&
!self.temporary_credential_secret
return nil
else
raise ArgumentError,
"The temporary credential key and secret must be set."
end
end
alias_method :request_token, :temporary_credential
##
# Sets the temporary credential for this client.
#
# @param [Signet::OAuth1::Credential] new_temporary_credential
# The temporary credentials.
def temporary_credential=(new_temporary_credential)
if new_temporary_credential != nil
if !new_temporary_credential.kind_of?(::Signet::OAuth1::Credential)
raise TypeError,
"Expected Signet::OAuth1::Credential, " +
"got #{new_temporary_credential.class}."
end
@temporary_credential_key = new_temporary_credential.key
@temporary_credential_secret = new_temporary_credential.secret
else
@temporary_credential_key = nil
@temporary_credential_secret = nil
end
end
alias_method :request_token=, :temporary_credential=
##
# Returns the temporary credential key for this client.
#
# @return [String] The temporary credential key.
def temporary_credential_key
return @temporary_credential_key
end
alias_method :request_token_key, :temporary_credential_key
##
# Sets the temporary credential key for this client.
#
# @param [String, #to_str] new_temporary_credential_key
# The temporary credential key.
def temporary_credential_key=(new_temporary_credential_key)
if new_temporary_credential_key != nil
if !new_temporary_credential_key.respond_to?(:to_str)
raise TypeError,
"Can't convert #{new_temporary_credential_key.class} " +
"into String."
end
new_temporary_credential_key = new_temporary_credential_key.to_str
@temporary_credential_key = new_temporary_credential_key
else
@temporary_credential_key = nil
end
end
alias_method :request_token_key=, :temporary_credential_key=
##
# Returns the temporary credential secret for this client.
#
# @return [String] The temporary credential secret.
def temporary_credential_secret
return @temporary_credential_secret
end
alias_method :request_token_secret, :temporary_credential_secret
##
# Sets the temporary credential secret for this client.
#
# @param [String, #to_str] new_temporary_credential_secret
# The temporary credential secret.
def temporary_credential_secret=(new_temporary_credential_secret)
if new_temporary_credential_secret != nil
if !new_temporary_credential_secret.respond_to?(:to_str)
raise TypeError,
"Can't convert #{new_temporary_credential_secret.class} " +
"into String."
end
new_temporary_credential_secret =
new_temporary_credential_secret.to_str
@temporary_credential_secret = new_temporary_credential_secret
else
@temporary_credential_secret = nil
end
end
alias_method :request_token_secret=, :temporary_credential_secret=
##
# Returns the token credential for this client.
#
# @return [Signet::OAuth1::Credential] The token credentials.
def token_credential
if self.token_credential_key && self.token_credential_secret
return ::Signet::OAuth1::Credential.new(
self.token_credential_key,
self.token_credential_secret
)
elsif !self.token_credential_key &&
!self.token_credential_secret
return nil
else
raise ArgumentError,
"The token credential key and secret must be set."
end
end
alias_method :access_token, :token_credential
##
# Sets the token credential for this client.
#
# @param [Signet::OAuth1::Credential] new_token_credential
# The token credentials.
def token_credential=(new_token_credential)
if new_token_credential != nil
if !new_token_credential.kind_of?(::Signet::OAuth1::Credential)
raise TypeError,
"Expected Signet::OAuth1::Credential, " +
"got #{new_token_credential.class}."
end
@token_credential_key = new_token_credential.key
@token_credential_secret = new_token_credential.secret
else
@token_credential_key = nil
@token_credential_secret = nil
end
end
alias_method :access_token=, :token_credential=
##
# Returns the token credential key for this client.
#
# @return [String] The token credential key.
def token_credential_key
return @token_credential_key
end
alias_method :access_token_key, :token_credential_key
##
# Sets the token credential key for this client.
#
# @param [String, #to_str] new_token_credential_key
# The token credential key.
def token_credential_key=(new_token_credential_key)
if new_token_credential_key != nil
if !new_token_credential_key.respond_to?(:to_str)
raise TypeError,
"Can't convert #{new_token_credential_key.class} " +
"into String."
end
new_token_credential_key = new_token_credential_key.to_str
@token_credential_key = new_token_credential_key
else
@token_credential_key = nil
end
end
alias_method :access_token_key=, :token_credential_key=
##
# Returns the token credential secret for this client.
#
# @return [String] The token credential secret.
def token_credential_secret
return @token_credential_secret
end
alias_method :access_token_secret, :token_credential_secret
##
# Sets the token credential secret for this client.
#
# @param [String, #to_str] new_token_credential_secret
# The token credential secret.
def token_credential_secret=(new_token_credential_secret)
if new_token_credential_secret != nil
if !new_token_credential_secret.respond_to?(:to_str)
raise TypeError,
"Can't convert #{new_token_credential_secret.class} " +
"into String."
end
new_token_credential_secret =
new_token_credential_secret.to_str
@token_credential_secret = new_token_credential_secret
else
@token_credential_secret = nil
end
end
alias_method :access_token_secret=, :token_credential_secret=
##
# Returns the callback for this client.
#
# @return [String] The OAuth callback.
def callback
return @callback || ::Signet::OAuth1::OUT_OF_BAND
end
##
# Sets the callback for this client.
#
# @param [String, #to_str] new_callback
# The OAuth callback.
def callback=(new_callback)
if new_callback != nil
if !new_callback.respond_to?(:to_str)
raise TypeError,
"Can't convert #{new_callback.class} into String."
end
new_callback = new_callback.to_str
@callback = new_callback
else
@callback = nil
end
end
##
# Returns whether the client is in two-legged mode.
#
# @return [TrueClass, FalseClass]
# true
for two-legged mode, false
otherwise.
def two_legged
return @two_legged ||= false
end
##
# Sets the client for two-legged mode.
#
# @param [TrueClass, FalseClass] new_two_legged
# true
for two-legged mode, false
otherwise.
def two_legged=(new_two_legged)
if new_two_legged != true && new_two_legged != false
raise TypeError,
"Expected true or false, got #{new_two_legged.class}."
else
@two_legged = new_two_legged
end
end
##
# Serialize the client object to JSON.
#
# @note A serialized client contains sensitive information. Persist or transmit with care.
#
# @return [String] A serialized JSON representation of the client.
def to_json
return MultiJson.dump({
'temporary_credential_uri' => self.temporary_credential_uri,
'authorization_uri' => self.authorization_uri,
'token_credential_uri' => self.token_credential_uri,
'callback' => self.callback,
'two_legged' => self.two_legged,
'client_credential_key' => self.client_credential_key,
'client_credential_secret' => self.client_credential_secret,
'temporary_credential_key' => self.temporary_credential_key,
'temporary_credential_secret' => self.temporary_credential_secret,
'token_credential_key' => self.token_credential_key,
'token_credential_secret' => self.token_credential_secret
})
end
##
# Generates a request for temporary credentials.
#
# @param [Hash] options
# The configuration parameters for the request.
# - :signature_method
-
# The signature method. Defaults to 'HMAC-SHA1'
.
# - :additional_parameters
-
# Non-standard additional parameters.
# - :realm
-
# The Authorization realm. See RFC 2617.
#
# @return [Array] The request object.
def generate_temporary_credential_request(options={})
verifications = {
:temporary_credential_uri => 'Temporary credentials URI',
:client_credential_key => 'Client credential key',
:client_credential_secret => 'Client credential secret'
}
# Make sure all required state is set
verifications.each do |(key, _value)|
unless self.send(key)
raise ArgumentError, "#{key} was not set."
end
end
options = {
:signature_method => 'HMAC-SHA1',
:additional_parameters => [],
:realm => nil,
:connection => Faraday.default_connection
}.merge(options)
method = :post
parameters = ::Signet::OAuth1.unsigned_temporary_credential_parameters(
:client_credential_key => self.client_credential_key,
:callback => self.callback,
:signature_method => options[:signature_method],
:additional_parameters => options[:additional_parameters]
)
signature = ::Signet::OAuth1.sign_parameters(
method,
self.temporary_credential_uri,
parameters,
self.client_credential_secret
)
parameters << ['oauth_signature', signature]
authorization_header = [
'Authorization',
::Signet::OAuth1.generate_authorization_header(
parameters, options[:realm]
)
]
headers = [authorization_header]
if method == :post
headers << ['Content-Type', 'application/x-www-form-urlencoded']
headers << ['Content-Length', '0']
end
return options[:connection].build_request(method.to_s.downcase.to_sym) do |req|
req.url(Addressable::URI.parse(
self.temporary_credential_uri.to_str
).normalize.to_s)
req.headers = Faraday::Utils::Headers.new(headers)
end
end
alias_method(
:generate_request_token_request,
:generate_temporary_credential_request
)
##
# Transmits a request for a temporary credential. This method does not
# have side-effects within the client.
#
# @param [Hash] options
# The configuration parameters for the request.
# - :signature_method
-
# The signature method. Defaults to 'HMAC-SHA1'
.
# - :additional_parameters
-
# Non-standard additional parameters.
# - :realm
-
# The Authorization realm. See RFC 2617.
# - :connection
-
# The HTTP connection to use.
# Must be of type Faraday::Connection
.
#
# @return [Signet::OAuth1::Credential] The temporary credential.
#
# @example
# temporary_credential = client.fetch_temporary_credential(
# :additional_parameters => {
# :scope => 'https://mail.google.com/mail/feed/atom'
# }
# )
def fetch_temporary_credential(options={})
options[:connection] ||= Faraday.default_connection
request = self.generate_temporary_credential_request(options)
request_env = request.to_env(options[:connection])
request_env[:request] ||= request
response = options[:connection].app.call(request_env)
if response.status.to_i == 200
return ::Signet::OAuth1.parse_form_encoded_credentials(response.body)
elsif [400, 401, 403].include?(response.status.to_i)
message = 'Authorization failed.'
if response.body.to_s.strip.length > 0
message += " Server message:\n#{response.body.to_s.strip}"
end
raise ::Signet::AuthorizationError.new(
message, :request => request, :response => response
)
else
message = "Unexpected status code: #{response.status}."
if response.body.to_s.strip.length > 0
message += " Server message:\n#{response.body.to_s.strip}"
end
raise ::Signet::AuthorizationError.new(
message, :request => request, :response => response
)
end
end
alias_method(
:fetch_request_token,
:fetch_temporary_credential
)
##
# Transmits a request for a temporary credential. This method updates
# the client with the new temporary credential.
#
# @param [Hash] options
# The configuration parameters for the request.
# - :signature_method
-
# The signature method. Defaults to 'HMAC-SHA1'
.
# - :additional_parameters
-
# Non-standard additional parameters.
# - :realm
-
# The Authorization realm. See RFC 2617.
# - :connection
-
# The HTTP connection to use.
# Must be of type Faraday::Connection
.
#
# @return [Signet::OAuth1::Credential] The temporary credential.
#
# @example
# client.fetch_temporary_credential!(:additional_parameters => {
# :scope => 'https://mail.google.com/mail/feed/atom'
# })
def fetch_temporary_credential!(options={})
credential = self.fetch_temporary_credential(options)
self.temporary_credential = credential
end
alias_method(
:fetch_request_token!,
:fetch_temporary_credential!
)
##
# Generates a request for token credentials.
#
# @param [Hash] options
# The configuration parameters for the request.
# - :verifier
-
# The OAuth verifier provided by the server. Required.
# - :signature_method
-
# The signature method. Defaults to 'HMAC-SHA1'
.
# - :realm
-
# The Authorization realm. See RFC 2617.
#
# @return [Array] The request object.
def generate_token_credential_request(options={})
verifications = {
:token_credential_uri => 'Token credentials URI',
:client_credential_key => 'Client credential key',
:client_credential_secret => 'Client credential secret',
:temporary_credential_key => 'Temporary credential key',
:temporary_credential_secret => 'Temporary credential secret'
}
# Make sure all required state is set
verifications.each do |(key, _value)|
unless self.send(key)
raise ArgumentError, "#{key} was not set."
end
end
options = {
:signature_method => 'HMAC-SHA1',
:realm => nil,
:connection => Faraday.default_connection
}.merge(options)
method = :post
parameters = ::Signet::OAuth1.unsigned_token_credential_parameters(
:client_credential_key => self.client_credential_key,
:temporary_credential_key => self.temporary_credential_key,
:signature_method => options[:signature_method],
:verifier => options[:verifier]
)
signature = ::Signet::OAuth1.sign_parameters(
method,
self.token_credential_uri,
parameters,
self.client_credential_secret,
self.temporary_credential_secret
)
parameters << ['oauth_signature', signature]
authorization_header = [
'Authorization',
::Signet::OAuth1.generate_authorization_header(
parameters, options[:realm]
)
]
headers = [authorization_header]
headers << ['Cache-Control', 'no-store']
if method == :post
headers << ['Content-Type', 'application/x-www-form-urlencoded']
headers << ['Content-Length', '0']
end
return options[:connection].build_request(method.to_s.downcase.to_sym) do |req|
req.url(Addressable::URI.parse(
self.token_credential_uri.to_str
).normalize.to_s)
req.headers = Faraday::Utils::Headers.new(headers)
end
end
alias_method(
:generate_access_token_request,
:generate_token_credential_request
)
##
# Transmits a request for a token credential. This method does not
# have side-effects within the client.
#
# @param [Hash] options
# The configuration parameters for the request.
# - :verifier
-
# The OAuth verifier provided by the server. Required.
# - :signature_method
-
# The signature method. Defaults to 'HMAC-SHA1'
.
# - :realm
-
# The Authorization realm. See RFC 2617.
# - :connection
-
# The HTTP connection to use.
# Must be of type Faraday::Connection
.
#
# @return [Signet::OAuth1::Credential] The token credential.
#
# @example
# token_credential = client.fetch_token_credential(
# :verifier => '12345'
# )
def fetch_token_credential(options={})
options[:connection] ||= Faraday.default_connection
request = self.generate_token_credential_request(options)
request_env = request.to_env(options[:connection])
request_env[:request] ||= request
response = options[:connection].app.call(request_env)
if response.status.to_i == 200
return ::Signet::OAuth1.parse_form_encoded_credentials(response.body)
elsif [400, 401, 403].include?(response.status.to_i)
message = 'Authorization failed.'
if response.body.to_s.strip.length > 0
message += " Server message:\n#{response.body.to_s.strip}"
end
raise ::Signet::AuthorizationError.new(
message, :request => request, :response => response
)
else
message = "Unexpected status code: #{response.status}."
if response.body.to_s.strip.length > 0
message += " Server message:\n#{response.body.to_s.strip}"
end
raise ::Signet::AuthorizationError.new(
message, :request => request, :response => response
)
end
end
alias_method(
:fetch_access_token,
:fetch_token_credential
)
##
# Transmits a request for a token credential. This method updates
# the client with the new token credential.
#
# @param [Hash] options
# The configuration parameters for the request.
# - :signature_method
-
# The signature method. Defaults to 'HMAC-SHA1'
.
# - :additional_parameters
-
# Non-standard additional parameters.
# - :realm
-
# The Authorization realm. See RFC 2617.
# - :connection
-
# The HTTP connection to use.
# Must be of type Faraday::Connection
.
#
# @return [Signet::OAuth1::Credential] The token credential.
#
# @example
# client.fetch_token_credential!(:verifier => '12345')
def fetch_token_credential!(options={})
credential = self.fetch_token_credential(options)
self.token_credential = credential
end
alias_method(
:fetch_access_token!,
:fetch_token_credential!
)
##
# Generates an authenticated request for protected resources.
#
# @param [Hash] options
# The configuration parameters for the request.
# - :request
-
# A pre-constructed request to sign.
# - :method
-
# The HTTP method for the request. Defaults to :get.
# - :uri
-
# The URI for the request.
# - :headers
-
# The HTTP headers for the request.
# - :body
-
# The HTTP body for the request.
# - :signature_method
-
# The signature method. Defaults to 'HMAC-SHA1'
.
# - :realm
-
# The Authorization realm. See RFC 2617.
#
# @return [Array] The request object.
def generate_authenticated_request(options={})
verifications = {
:client_credential_key => 'Client credential key',
:client_credential_secret => 'Client credential secret'
}
unless self.two_legged
verifications.update(
:token_credential_key => 'Token credential key',
:token_credential_secret => 'Token credential secret'
)
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
options = {
:signature_method => 'HMAC-SHA1',
:realm => nil,
:connection => Faraday.default_connection
}.merge(options)
if options[:request].kind_of?(Faraday::Request)
request = options[:request]
else
if options[:request].kind_of?(Array)
method, uri, headers, body = options[:request]
else
method = options[:method] || :get
uri = options[:uri]
headers = options[:headers] || []
body = options[:body] || ''
end
headers = headers.to_a if headers.kind_of?(Hash)
request_components = {
:method => method,
:uri => uri,
:headers => headers,
:body => body
}
# Verify that we have all pieces required to return an HTTP request
request_components.each do |(key, value)|
unless value
raise ArgumentError, "Missing :#{key} parameter."
end
end
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
method = method.to_s.downcase.to_sym
request = options[:connection].build_request(method) do |req|
req.url(Addressable::URI.parse(uri).normalize.to_s)
req.headers = Faraday::Utils::Headers.new(headers)
req.body = body
end
end
parameters = ::Signet::OAuth1.unsigned_resource_parameters(
:client_credential_key => self.client_credential_key,
:token_credential_key => self.token_credential_key,
:signature_method => options[:signature_method],
:two_legged => self.two_legged
)
env = request.to_env(options[:connection])
content_type = request['Content-Type'].to_s
content_type = content_type.split(';', 2).first if content_type.index(';')
if request.method == :post && content_type == 'application/x-www-form-urlencoded'
# Serializes the body in case a hash/array was passed. Noop if already string like
encoder = Faraday::Request::UrlEncoded.new(lambda { |_env| })
encoder.call(env)
request.body = env[:body]
post_parameters = Addressable::URI.form_unencode(env[:body])
parameters = parameters.concat(post_parameters)
end
# No need to attach URI query parameters, the .sign_parameters
# method takes care of that automatically.
signature = ::Signet::OAuth1.sign_parameters(
env[:method],
env[:url],
parameters,
self.client_credential_secret,
self.token_credential_secret
)
parameters << ['oauth_signature', signature]
request['Authorization'] = ::Signet::OAuth1.generate_authorization_header(
parameters, options[:realm])
request['Cache-Control'] = 'no-store'
return request
end
##
# Transmits a request for a protected resource.
#
# @param [Hash] options
# The configuration parameters for the request.
# - :request
-
# A pre-constructed request to sign.
# - :method
-
# The HTTP method for the request. Defaults to :get.
# - :uri
-
# The URI for the request.
# - :headers
-
# The HTTP headers for the request.
# - :body
-
# The HTTP body for the request.
# - :signature_method
-
# The signature method. Defaults to 'HMAC-SHA1'
.
# - :realm
-
# The Authorization realm. See RFC 2617.
# - :connection
-
# The HTTP connection to use.
# Must be of type Faraday::Connection
.
#
# @example
# # Using Net::HTTP
# response = client.fetch_protected_resource(
# :uri => 'http://www.example.com/protected/resource'
# )
#
# @example
# # Using Typhoeus
# response = client.fetch_protected_resource(
# :request => Typhoeus::Request.new(
# 'http://www.example.com/protected/resource'
# ),
# :connection => connection
# )
#
# @return [Array] The response object.
def fetch_protected_resource(options={})
options[:connection] ||= Faraday.default_connection
request = self.generate_authenticated_request(options)
request_env = request.to_env(options[:connection])
request_env[:request] ||= request
response = options[:connection].app.call(request_env)
if response.status.to_i == 401
# When accessing a protected resource, we only want to raise an
# error for 401 responses.
message = 'Authorization failed.'
if response.body.to_s.strip.length > 0
message += " Server message:\n#{response.body.to_s.strip}"
end
raise ::Signet::AuthorizationError.new(
message, :request => request, :response => response
)
else
return response
end
end
end
end
end
signet-0.11.0/lib/signet/oauth_1/server.rb 0000644 0000041 0000041 00000046556 13410324340 020356 0 ustar www-data www-data # Copyright (C) 2011 The Yakima Herald-Republic.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require 'faraday'
require 'stringio'
require 'addressable/uri'
require 'signet'
require 'signet/errors'
require 'signet/oauth_1'
require 'signet/oauth_1/credential'
module Signet
module OAuth1
class Server
# @return [Proc] lookup the value from this Proc.
attr_accessor :nonce_timestamp, :client_credential, :token_credential,
:temporary_credential, :verifier
##
# 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? }
# )
def initialize(options={})
[:nonce_timestamp, :client_credential, :token_credential,
:temporary_credential, :verifier].each do |attr|
instance_variable_set("@#{attr}", options[attr])
end
end
# Constant time string comparison.
def safe_equals?(a, b)
check = a.bytesize ^ b.bytesize
a.bytes.zip(b.bytes) { |x, y| check |= x ^ y.to_i }
check == 0
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.
def validate_nonce_timestamp(nonce, timestamp)
nonce =
@nonce_timestamp.call(nonce, timestamp) if
@nonce_timestamp.respond_to?(:call)
nonce ? true : false
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.
def find_client_credential(key)
call_credential_lookup(@client_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.
def find_token_credential(key)
call_credential_lookup(@token_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.
def find_temporary_credential(key)
call_credential_lookup(@temporary_credential, key)
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
# credential
(if any).
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
##
# 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
# nil
or false
.
def find_verifier(verifier)
verified = @verifier.call(verifier) if @verifier.respond_to?(:call)
verified ? true : false
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 GET
# @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
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
##
# Validate and normalize the HTTP Authorization header.
#
# @param [Array] headers from HTTP request.
# @return [Hash] Hash of Authorization header.
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
##
# @overload request_realm(options)
# @param [Hash] request A pre-constructed request to verify.
# @param [String] method the HTTP method , defaults to GET
# @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.
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
##
# 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 GET
# @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.
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 safe_equals?(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 GET
# @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 nil
if not valid.
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 safe_equals?(computed_signature, auth_hash['oauth_signature'])
{:client_credential=>client_credential,
:temporary_credential=>temporary_credential,
:realm=>auth_hash['realm']
}
else
nil
end
end
##
# 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 GET
# @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 nil
if not valid.
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 safe_equals?(computed_signature, auth_hash['oauth_signature'])
{:client_credential=>client_credential,
:token_credential=>token_credential,
:realm=>auth_hash['realm']
}
else
nil
end
end
end
end
end
signet-0.11.0/lib/signet/oauth_1/credential.rb 0000644 0000041 0000041 00000007731 13410324340 021152 0 ustar www-data www-data # Copyright (C) 2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
module Signet #:nodoc:
module OAuth1
class Credential
##
# Creates a token object from a key and secret.
#
# @example
# Signet::OAuth1::Credential.new(
# :key => "dpf43f3p2l4k3l03",
# :secret => "kd94hf93k423kf44"
# )
#
# @example
# Signet::OAuth1::Credential.new([
# ["oauth_token", "dpf43f3p2l4k3l03"],
# ["oauth_token_secret", "kd94hf93k423kf44"]
# ])
#
# @example
# Signet::OAuth1::Credential.new(
# "dpf43f3p2l4k3l03", "kd94hf93k423kf44"
# )
def initialize(*args)
# We want to be particularly flexible in how we initialize a token
# object for maximum interoperability. However, this flexibility
# means we need to be careful about returning an unexpected value for
# key or secret to avoid difficult-to-debug situations. Thus lots
# of type-checking.
# This is cheaper than coercing to some kind of Hash with
# indifferent access. Also uglier.
key_from_hash = lambda do |parameters|
parameters["oauth_token"] ||
parameters[:oauth_token] ||
parameters["key"] ||
parameters[:key]
end
secret_from_hash = lambda do |parameters|
parameters["oauth_token_secret"] ||
parameters[:oauth_token_secret] ||
parameters["secret"] ||
parameters[:secret]
end
if args.first.respond_to?(:to_hash)
parameters = args.first.to_hash
@key = key_from_hash.call(parameters)
@secret = secret_from_hash.call(parameters)
unless @key && @secret
raise ArgumentError,
"Could not find both key and secret in #{hash.inspect}."
end
else
# Normalize to an Array
if !args.first.kind_of?(String) &&
!args.first.respond_to?(:to_str) &&
args.first.kind_of?(Enumerable)
# We need to special-case strings since they're technically
# Enumerable objects.
args = args.first.to_a
elsif args.first.respond_to?(:to_ary)
args = args.first.to_ary
end
if args.all? { |value| value.kind_of?(Array) }
parameters = args.inject({}) { |h,(k,v)| h[k]=v; h }
@key = key_from_hash.call(parameters)
@secret = secret_from_hash.call(parameters)
elsif args.size == 2
@key, @secret = args
else
raise ArgumentError,
"wrong number of arguments (#{args.size} for 2)"
end
end
if @key.respond_to?(:to_str)
@key = @key.to_str
else
raise TypeError, "Expected String, got #{@key.class}."
end
if @secret.respond_to?(:to_str)
@secret = @secret.to_str
else
raise TypeError, "Expected String, got #{@secret.class}."
end
end
attr_accessor :key, :secret
def to_hash
return {
"oauth_token" => self.key,
"oauth_token_secret" => self.secret
}
end
alias_method :to_h, :to_hash
def ==(other)
if other.respond_to?(:key) && other.respond_to?(:secret)
return self.key == other.key && self.secret == other.secret
else
return false
end
end
end
end
end
signet-0.11.0/lib/signet/oauth_2.rb 0000644 0000041 0000041 00000012307 13410324340 017034 0 ustar www-data www-data # Copyright (C) 2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'base64'
require 'signet'
require 'multi_json'
module Signet #:nodoc:
##
# An implementation of http://tools.ietf.org/html/draft-ietf-oauth-v2-10
#
# This module will be updated periodically to support newer drafts of the
# specification, as they become widely deployed.
module OAuth2
def self.parse_authorization_header(field_value)
auth_scheme = field_value[/^([-._0-9a-zA-Z]+)/, 1]
case auth_scheme
when /^Basic$/i
# HTTP Basic is allowed in OAuth 2
return self.parse_basic_credentials(field_value[/^Basic\s+(.*)$/i, 1])
when /^OAuth$/i
# Other token types may be supported eventually
return self.parse_bearer_credentials(field_value[/^OAuth\s+(.*)$/i, 1])
else
raise ParseError,
'Parsing non-OAuth Authorization headers is out of scope.'
end
end
def self.parse_www_authenticate_header(field_value)
auth_scheme = field_value[/^([-._0-9a-zA-Z]+)/, 1]
case auth_scheme
when /^OAuth$/i
# Other token types may be supported eventually
return self.parse_oauth_challenge(field_value[/^OAuth\s+(.*)$/i, 1])
else
raise ParseError,
'Parsing non-OAuth WWW-Authenticate headers is out of scope.'
end
end
def self.parse_basic_credentials(credential_string)
decoded = Base64.decode64(credential_string)
client_id, client_secret = decoded.split(':', 2)
return [['client_id', client_id], ['client_secret', client_secret]]
end
def self.parse_bearer_credentials(credential_string)
access_token = credential_string[/^([^,\s]+)(?:\s|,|$)/i, 1]
parameters = []
parameters << ['access_token', access_token]
auth_param_string = credential_string[/^(?:[^,\s]+)\s*,\s*(.*)$/i, 1]
if auth_param_string
# This code will rarely get called, but is included for completeness
parameters.concat(Signet.parse_auth_param_list(auth_param_string))
end
return parameters
end
def self.parse_oauth_challenge(challenge_string)
return Signet.parse_auth_param_list(challenge_string)
end
def self.parse_credentials(body, content_type)
if !body.kind_of?(String)
raise TypeError, "Expected String, got #{body.class}."
end
case content_type
when /^application\/json.*/
return MultiJson.load(body)
when /^application\/x-www-form-urlencoded.*/
return Hash[Addressable::URI.form_unencode(body)]
else
raise ArgumentError, "Invalid content type '#{content_type}'"
end
end
##
# Generates a Basic Authorization header from a client identifier and a
# client password.
#
# @param [String] client_id
# The client identifier.
# @param [String] client_password
# The client password.
#
# @return [String]
# The value for the HTTP Basic Authorization header.
def self.generate_basic_authorization_header(client_id, client_password)
if client_id =~ /:/
raise ArgumentError,
"A client identifier may not contain a ':' character."
end
return 'Basic ' + Base64.encode64(
client_id + ':' + client_password
).gsub(/\n/, '')
end
##
# Generates an authorization header for an access token
#
# @param [String] access_token
# The access token.
# @param [Hash] auth_params
# Additional parameters to be encoded in the header
#
# @return [String]
# The value for the HTTP Basic Authorization header.
def self.generate_bearer_authorization_header(
access_token, auth_params=nil)
# TODO: escaping?
header = "Bearer #{access_token}"
if auth_params && !auth_params.empty?
header += (", " +
(auth_params.inject([]) do |accu, (key, value)|
accu << "#{key}=\"#{value}\""
accu
end).join(", ")
)
end
return header
end
##
# Appends the necessary OAuth parameters to
# the base authorization endpoint URI.
#
# @param [Addressable::URI, String, #to_str] authorization_uri
# The base authorization endpoint URI.
#
# @return [String] The authorization URI to redirect the user to.
def self.generate_authorization_uri(authorization_uri, parameters={})
for key, value in parameters
parameters.delete(key) if value.nil?
end
parsed_uri = Addressable::URI.parse(authorization_uri).dup
query_values = parsed_uri.query_values || {}
query_values = query_values.merge(parameters)
parsed_uri.query_values = query_values
return parsed_uri.normalize.to_s
end
end
end
signet-0.11.0/lib/signet/version.rb 0000644 0000041 0000041 00000006727 13410324340 017171 0 ustar www-data www-data # Copyright (C) 2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Used to prevent the class/module from being loaded more than once
unless defined? Signet::VERSION
module Signet
module VERSION
MAJOR = 0
MINOR = 11
TINY = 0
PRE = nil
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
# On March 31, 2019, set supported version to 2.4 and recommended to 2.6.
# Thereafter, follow the MRI support schedule: supported means non-EOL,
# and recommended means in normal (rather than security) maintenance.
# See https://www.ruby-lang.org/en/downloads/branches/
##
# Minimum "supported" Ruby version (non-EOL)
# @private
#
SUPPORTED_VERSION_THRESHOLD = '1.9'.freeze
##
# Minimum "recommended" Ruby version (normal maintenance)
# @private
#
RECOMMENDED_VERSION_THRESHOLD = '2.4'.freeze
##
# Check Ruby version and emit a warning if it is old
# @private
#
def self.warn_on_old_ruby_version
return if ENV['GOOGLE_CLOUD_SUPPRESS_RUBY_WARNINGS']
cur_version = Gem::Version.new RUBY_VERSION
if cur_version < Gem::Version.new(SUPPORTED_VERSION_THRESHOLD)
warn_unsupported_ruby cur_version, RECOMMENDED_VERSION_THRESHOLD
elsif cur_version < Gem::Version.new(RECOMMENDED_VERSION_THRESHOLD)
warn_nonrecommended_ruby cur_version, RECOMMENDED_VERSION_THRESHOLD
end
rescue ArgumentError
'Unable to determine current Ruby version.'
end
##
# Print a warning for an EOL version of Ruby
# @private
#
def self.warn_unsupported_ruby cur_version, recommended_version
"WARNING: You are running Ruby #{cur_version}, which has reached" \
" end-of-life and is no longer supported by Ruby Core.\n" \
'Signet works best on supported versions of' \
' Ruby. It is strongly recommended that you upgrade to Ruby' \
" #{recommended_version} or later. \n" \
'See https://www.ruby-lang.org/en/downloads/branches/ for more' \
" info on the Ruby maintenance schedule.\n" \
'To suppress this message, set the' \
' GOOGLE_CLOUD_SUPPRESS_RUBY_WARNINGS environment variable.'
end
##
# Print a warning for a supported but nearing EOL version of Ruby
# @private
#
def self.warn_nonrecommended_ruby cur_version, recommended_version
"WARNING: You are running Ruby #{cur_version}, which is nearing" \
" end-of-life.\n" \
'Signet works best on supported versions of' \
" Ruby. Consider upgrading to Ruby #{recommended_version} or later.\n" \
'See https://www.ruby-lang.org/en/downloads/branches/ for more' \
" info on the Ruby maintenance schedule.\n" \
'To suppress this message, set the' \
' GOOGLE_CLOUD_SUPPRESS_RUBY_WARNINGS environment variable.'
end
end
end
end
signet-0.11.0/lib/signet/oauth_2/ 0000755 0000041 0000041 00000000000 13410324340 016504 5 ustar www-data www-data signet-0.11.0/lib/signet/oauth_2/client.rb 0000644 0000041 0000041 00000122410 13410324340 020307 0 ustar www-data www-data # Copyright (C) 2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'faraday'
require 'stringio'
require 'addressable/uri'
require 'signet'
require 'signet/errors'
require 'signet/oauth_2'
require 'jwt'
require 'date'
module Signet
module OAuth2
class Client
OOB_MODES = %w(urn:ietf:wg:oauth:2.0:oob:auto urn:ietf:wg:oauth:2.0:oob oob)
##
# Creates an OAuth 2.0 client.
#
# @param [Hash] options
# The configuration parameters for the client.
# - :authorization_uri
-
# The authorization server's HTTP endpoint capable of
# authenticating the end-user and obtaining authorization.
# - :token_credential_uri
-
# The authorization server's HTTP endpoint capable of issuing
# tokens and refreshing expired tokens.
# - :client_id
-
# A unique identifier issued to the client to identify itself to the
# authorization server.
# - :client_secret
-
# A shared symmetric secret issued by the authorization server,
# which is used to authenticate the client.
# - :scope
-
# The scope of the access request, expressed either as an Array
# or as a space-delimited String.
# - :state
-
# An arbitrary string designed to allow the client to maintain state.
# - :code
-
# The authorization code received from the authorization server.
# - :redirect_uri
-
# The redirection URI used in the initial request.
# - :username
-
# The resource owner's username.
# - :password
-
# The resource owner's password.
# - :issuer
-
# Issuer ID when using assertion profile
# - :person
-
# Target user for assertions
# - :expiry
-
# Number of seconds assertions are valid for
# - :signing_key
-
# Signing key when using assertion profile
# - :refresh_token
-
# The refresh token associated with the access token
# to be refreshed.
# - :access_token
-
# The current access token for this client.
# - :id_token
-
# The current ID token for this client.
# - :extension_parameters
-
# When using an extension grant type, this the set of parameters used
# by that extension.
#
# @example
# client = Signet::OAuth2::Client.new(
# :authorization_uri =>
# 'https://example.server.com/authorization',
# :token_credential_uri =>
# 'https://example.server.com/token',
# :client_id => 'anonymous',
# :client_secret => 'anonymous',
# :scope => 'example',
# :redirect_uri => 'https://example.client.com/oauth'
# )
#
# @see Signet::OAuth2::Client#update!
def initialize options={}
@authorization_uri = nil
@token_credential_uri = nil
@client_id = nil
@client_secret = nil
@code = nil
@expires_at = nil
@issued_at = nil
@issuer = nil
@password = nil
@principal = nil
@redirect_uri = nil
@scope = nil
@state = nil
@username = nil
@access_type = nil
self.update!(options)
end
##
# Updates an OAuth 2.0 client.
#
# @param [Hash] options
# The configuration parameters for the client.
# - :authorization_uri
-
# The authorization server's HTTP endpoint capable of
# authenticating the end-user and obtaining authorization.
# - :token_credential_uri
-
# The authorization server's HTTP endpoint capable of issuing
# tokens and refreshing expired tokens.
# - :client_id
-
# A unique identifier issued to the client to identify itself to the
# authorization server.
# - :client_secret
-
# A shared symmetric secret issued by the authorization server,
# which is used to authenticate the client.
# - :scope
-
# The scope of the access request, expressed either as an Array
# or as a space-delimited String.
# - :state
-
# An arbitrary string designed to allow the client to maintain state.
# - :code
-
# The authorization code received from the authorization server.
# - :redirect_uri
-
# The redirection URI used in the initial request.
# - :username
-
# The resource owner's username.
# - :password
-
# The resource owner's password.
# - :issuer
-
# Issuer ID when using assertion profile
# - :audience
-
# Target audience for assertions
# - :person
-
# Target user for assertions
# - :expiry
-
# Number of seconds assertions are valid for
# - :signing_key
-
# Signing key when using assertion profile
# - :refresh_token
-
# The refresh token associated with the access token
# to be refreshed.
# - :access_token
-
# The current access token for this client.
# - :access_type
-
# The current access type parameter for #authorization_uri.
# - :id_token
-
# The current ID token for this client.
# - :extension_parameters
-
# When using an extension grant type, this is the set of parameters used
# by that extension.
#
# @example
# client.update!(
# :code => 'i1WsRn1uB1',
# :access_token => 'FJQbwq9',
# :expires_in => 3600
# )
#
# @see Signet::OAuth2::Client#initialize
# @see Signet::OAuth2::Client#update_token!
def update!(options={})
# Normalize all keys to symbols to allow indifferent access.
options = deep_hash_normalize(options)
self.authorization_uri = options[:authorization_uri] if options.has_key?(:authorization_uri)
self.token_credential_uri = options[:token_credential_uri] if options.has_key?(:token_credential_uri)
self.client_id = options[:client_id] if options.has_key?(:client_id)
self.client_secret = options[:client_secret] if options.has_key?(:client_secret)
self.scope = options[:scope] if options.has_key?(:scope)
self.state = options[:state] if options.has_key?(:state)
self.code = options[:code] if options.has_key?(:code)
self.redirect_uri = options[:redirect_uri] if options.has_key?(:redirect_uri)
self.username = options[:username] if options.has_key?(:username)
self.password = options[:password] if options.has_key?(:password)
self.issuer = options[:issuer] if options.has_key?(:issuer)
self.person = options[:person] if options.has_key?(:person)
self.sub = options[:sub] if options.has_key?(:sub)
self.expiry = options[:expiry] || 60
self.audience = options[:audience] if options.has_key?(:audience)
self.signing_key = options[:signing_key] if options.has_key?(:signing_key)
self.extension_parameters = options[:extension_parameters] || {}
self.additional_parameters = options[:additional_parameters] || {}
self.access_type = options.fetch(:access_type) { :offline }
self.update_token!(options)
return self
end
##
# Updates an OAuth 2.0 client.
#
# @param [Hash] options
# The configuration parameters related to the token.
# - :refresh_token
-
# The refresh token associated with the access token
# to be refreshed.
# - :access_token
-
# The current access token for this client.
# - :id_token
-
# The current ID token for this client.
# - :expires_in
-
# The time in seconds until access token expiration.
# - :expires_at
-
# The time as an integer number of seconds since the Epoch
# - :issued_at
-
# The timestamp that the token was issued at.
#
# @example
# client.update!(
# :refresh_token => 'n4E9O119d',
# :access_token => 'FJQbwq9',
# :expires_in => 3600
# )
#
# @see Signet::OAuth2::Client#initialize
# @see Signet::OAuth2::Client#update!
def update_token!(options={})
# Normalize all keys to symbols to allow indifferent access internally
options = deep_hash_normalize(options)
self.expires_in = options[:expires] if options.has_key?(:expires)
self.expires_in = options[:expires_in] if options.has_key?(:expires_in)
self.expires_at = options[:expires_at] if options.has_key?(:expires_at)
# By default, the token is issued at `Time.now` when `expires_in` is
# set, but this can be used to supply a more precise time.
self.issued_at = options[:issued_at] if options.has_key?(:issued_at)
# Special case where we want expires_at to be relative to issued_at
if options.has_key?(:issued_at) && options.has_key?(:expires_in)
set_relative_expires_at options[:issued_at], options[:expires_in]
end
self.access_token = options[:access_token] if options.has_key?(:access_token)
self.refresh_token = options[:refresh_token] if options.has_key?(:refresh_token)
self.id_token = options[:id_token] if options.has_key?(:id_token)
return self
end
##
# Returns the authorization URI that the user should be redirected to.
#
# @return [Addressable::URI] The authorization URI.
#
# @see Signet::OAuth2.generate_authorization_uri
def authorization_uri(options={})
# Normalize external input
options = deep_hash_normalize(options)
return nil if @authorization_uri == nil
unless options[:response_type]
options[:response_type] = :code
end
if !options[:access_type] && access_type
options[:access_type] = access_type
end
options[:client_id] ||= self.client_id
options[:redirect_uri] ||= self.redirect_uri
if options[:prompt] && options[:approval_prompt]
raise ArgumentError, "prompt and approval_prompt are mutually exclusive parameters"
end
if !options[:client_id]
raise ArgumentError, "Missing required client identifier."
end
unless options[:redirect_uri]
raise ArgumentError, "Missing required redirect URI."
end
if !options[:scope] && self.scope
options[:scope] = self.scope.join(' ')
end
options[:state] = self.state unless options[:state]
options.merge!(self.additional_parameters.merge(options[:additional_parameters] || {}))
options.delete(:additional_parameters)
options = Hash[options.map do |key, option|
[key.to_s, option]
end]
uri = Addressable::URI.parse(
::Signet::OAuth2.generate_authorization_uri(
@authorization_uri, options
)
)
if uri.normalized_scheme != 'https'
raise Signet::UnsafeOperationError,
'Authorization endpoint must be protected by TLS.'
end
return uri
end
##
# Sets the authorization URI for this client.
#
# @param [Addressable::URI, Hash, String, #to_str] new_authorization_uri
# The authorization URI.
def authorization_uri=(new_authorization_uri)
@authorization_uri = coerce_uri(new_authorization_uri)
end
##
# Returns the token credential URI for this client.
#
# @return [Addressable::URI] The token credential URI.
def token_credential_uri
return @token_credential_uri
end
##
# Sets the token credential URI for this client.
#
# @param [Addressable::URI, Hash, String, #to_str] new_token_credential_uri
# The token credential URI.
def token_credential_uri=(new_token_credential_uri)
@token_credential_uri = coerce_uri(new_token_credential_uri)
end
# Addressable expects URIs formatted as hashes to come in with symbols as keys.
# Returns nil implicitly for the nil case.
def coerce_uri(incoming_uri)
if incoming_uri.is_a? Hash
Addressable::URI.new(deep_hash_normalize(incoming_uri))
elsif incoming_uri
Addressable::URI.parse(incoming_uri)
end
end
##
# Returns the current access type parameter for #authorization_uri.
#
# @return [String, Symbol] The current access type.
def access_type
return @access_type
end
##
# Sets the current access type parameter for #authorization_uri.
#
# @param [String, Symbol] new_access_type
# The current access type.
def access_type=(new_access_type)
@access_type = new_access_type
end
##
# Returns the client identifier for this client.
#
# @return [String] The client identifier.
def client_id
return @client_id
end
##
# Sets the client identifier for this client.
#
# @param [String] new_client_id
# The client identifier.
def client_id=(new_client_id)
@client_id = new_client_id
end
##
# Returns the client secret for this client.
#
# @return [String] The client secret.
def client_secret
return @client_secret
end
##
# Sets the client secret for this client.
#
# @param [String] new_client_secret
# The client secret.
def client_secret=(new_client_secret)
@client_secret = new_client_secret
end
##
# Returns the scope for this client. Scope is a list of access ranges
# defined by the authorization server.
#
# @return [Array] The scope of access the client is requesting.
def scope
return @scope
end
##
# Sets the scope for this client.
#
# @param [Array, String] new_scope
# The scope of access the client is requesting. This may be
# expressed as either an Array of String objects or as a
# space-delimited String.
def scope=(new_scope)
case new_scope
when Array
new_scope.each do |scope|
if scope.include?(' ')
raise ArgumentError,
"Individual scopes cannot contain the space character."
end
end
@scope = new_scope
when String
@scope = new_scope.split(' ')
when nil
@scope = nil
else
raise TypeError, "Expected Array or String, got #{new_scope.class}"
end
end
##
# Returns the client's current state value.
#
# @return [String] The state value.
def state
return @state
end
##
# Sets the client's current state value.
#
# @param [String] new_state
# The state value.
def state=(new_state)
@state = new_state
end
##
# Returns the authorization code issued to this client.
# Used only by the authorization code access grant type.
#
# @return [String] The authorization code.
def code
return @code
end
##
# Sets the authorization code issued to this client.
# Used only by the authorization code access grant type.
#
# @param [String] new_code
# The authorization code.
def code=(new_code)
@code = new_code
end
##
# Returns the redirect URI for this client.
#
# @return [String] The redirect URI.
def redirect_uri
return @redirect_uri
end
##
# Sets the redirect URI for this client.
#
# @param [String] new_redirect_uri
# The redirect URI.
def redirect_uri=(new_redirect_uri)
new_redirect_uri = Addressable::URI.parse(new_redirect_uri)
#TODO - Better solution to allow google postmessage flow. For now, make an exception to the spec.
if new_redirect_uri == nil|| new_redirect_uri.absolute? || uri_is_postmessage?(new_redirect_uri) || uri_is_oob?(new_redirect_uri)
@redirect_uri = new_redirect_uri
else
raise ArgumentError, "Redirect URI must be an absolute URI."
end
end
##
# Returns the username associated with this client.
# Used only by the resource owner password credential access grant type.
#
# @return [String] The username.
def username
return @username
end
##
# Sets the username associated with this client.
# Used only by the resource owner password credential access grant type.
#
# @param [String] new_username
# The username.
def username=(new_username)
@username = new_username
end
##
# Returns the password associated with this client.
# Used only by the resource owner password credential access grant type.
#
# @return [String] The password.
def password
return @password
end
##
# Sets the password associated with this client.
# Used only by the resource owner password credential access grant type.
#
# @param [String] new_password
# The password.
def password=(new_password)
@password = new_password
end
##
# Returns the issuer ID associated with this client.
# Used only by the assertion grant type.
#
# @return [String] Issuer id.
def issuer
return @issuer
end
##
# Sets the issuer ID associated with this client.
# Used only by the assertion grant type.
#
# @param [String] new_issuer
# Issuer ID (typical in email adddress form).
def issuer=(new_issuer)
@issuer = new_issuer
end
##
# Returns the issuer ID associated with this client.
# Used only by the assertion grant type.
#
# @return [String] Target audience ID.
def audience
return @audience
end
##
# Sets the target audience ID when issuing assertions.
# Used only by the assertion grant type.
#
# @param [String] new_audience
# Target audience ID
def audience=(new_audience)
@audience = new_audience
end
##
# Returns the target resource owner for impersonation.
# Used only by the assertion grant type.
#
# @return [String] Target user for impersonation.
def principal
return @principal
end
##
# Sets the target resource owner for impersonation.
# Used only by the assertion grant type.
#
# @param [String] new_person
# Target user for impersonation
def principal=(new_person)
@principal = new_person
end
alias_method :person, :principal
alias_method :person=, :principal=
##
# The target "sub" when issuing assertions.
# Used in some Admin SDK APIs.
#
attr_accessor :sub
##
# Returns the number of seconds assertions are valid for
# Used only by the assertion grant type.
#
# @return [Integer] Assertion expiry, in seconds
def expiry
return @expiry
end
##
# Sets the number of seconds assertions are valid for
# Used only by the assertion grant type.
#
# @param [Integer, String] new_expiry
# Assertion expiry, in seconds
def expiry=(new_expiry)
@expiry = new_expiry ? new_expiry.to_i : nil
end
##
# Returns the signing key associated with this client.
# Used only by the assertion grant type.
#
# @return [String,OpenSSL::PKey] Signing key
def signing_key
return @signing_key
end
##
# Sets the signing key when issuing assertions.
# Used only by the assertion grant type.
#
# @param [String, OpenSSL::Pkey] new_key
# Signing key. Either private key for RSA or string for HMAC algorithm
def signing_key=(new_key)
@signing_key = new_key
end
##
# Algorithm used for signing JWTs
# @return [String] Signing algorithm
def signing_algorithm
self.signing_key.is_a?(String) ? "HS256" : "RS256"
end
##
# Returns the set of extension parameters used by the client.
# Used only by extension access grant types.
#
# @return [Hash] The extension parameters.
def extension_parameters
return @extension_parameters ||= {}
end
##
# Sets extension parameters used by the client.
# Used only by extension access grant types.
#
# @param [Hash] new_extension_parameters
# The parameters.
def extension_parameters=(new_extension_parameters)
if new_extension_parameters.respond_to?(:to_hash)
@extension_parameters = new_extension_parameters.to_hash
else
raise TypeError,
"Expected Hash, got #{new_extension_parameters.class}."
end
end
##
# Returns the set of additional (non standard) parameters to be used by the client.
#
# @return [Hash] The pass through parameters.
def additional_parameters
return @additional_parameters ||= {}
end
##
# Sets additional (non standard) parameters to be used by the client.
#
# @param [Hash] new_additional_parameters
# The parameters.
def additional_parameters=(new_additional_parameters)
if new_additional_parameters.respond_to?(:to_hash)
@additional_parameters = new_additional_parameters.to_hash
else
raise TypeError,
"Expected Hash, got #{new_additional_parameters.class}."
end
end
##
# Returns the refresh token associated with this client.
#
# @return [String] The refresh token.
def refresh_token
return @refresh_token ||= nil
end
##
# Sets the refresh token associated with this client.
#
# @param [String] new_refresh_token
# The refresh token.
def refresh_token=(new_refresh_token)
@refresh_token = new_refresh_token
end
##
# Returns the access token associated with this client.
#
# @return [String] The access token.
def access_token
return @access_token ||= nil
end
##
# Sets the access token associated with this client.
#
# @param [String] new_access_token
# The access token.
def access_token=(new_access_token)
@access_token = new_access_token
end
##
# Returns the ID token associated with this client.
#
# @return [String] The ID token.
def id_token
return @id_token ||= nil
end
##
# Sets the ID token associated with this client.
#
# @param [String] new_id_token
# The ID token.
def id_token=(new_id_token)
@id_token = new_id_token
end
##
# Returns the decoded ID token associated with this client.
#
# @param [OpenSSL::PKey::RSA, Object] public_key
# The public key to use to verify the ID token. Skips verification if
# omitted.
#
# @return [String] The decoded ID token.
def decoded_id_token public_key=nil, options = {}, &keyfinder
options[:algorithm] ||= signing_algorithm
verify = !!(public_key || keyfinder)
payload, _header = JWT.decode(self.id_token, public_key, verify, options, &keyfinder)
if !payload.has_key?('aud')
raise Signet::UnsafeOperationError, 'No ID token audience declared.'
elsif payload['aud'] != self.client_id
raise Signet::UnsafeOperationError,
'ID token audience did not match Client ID.'
end
return payload
end
##
# Returns the lifetime of the access token in seconds.
# Returns nil if the token does not expire.
#
# @return [Integer, nil] The access token lifetime.
def expires_in
if @expires_at.nil? || @issued_at.nil?
nil
else
(@expires_at - @issued_at).to_i
end
end
##
# Sets the lifetime of the access token in seconds. Resets the issued_at
# timestamp. Nil values will be treated as though the token does
# not expire.
#
# @param [String, Integer, nil] new_expires_in
# The access token lifetime.
def expires_in= new_expires_in
if !new_expires_in.nil?
@issued_at = Time.now
@expires_at = @issued_at + new_expires_in.to_i
else
@expires_at = nil
@issued_at = nil
end
end
##
# Returns the timestamp the access token was issued at.
#
# @return [Time, nil] The access token issuance time.
def issued_at
@issued_at
end
##
# Sets the timestamp the access token was issued at.
#
# @param [String,Integer,Time] new_issued_at
# The access token issuance time.
def issued_at=(new_issued_at)
@issued_at = normalize_timestamp(new_issued_at)
end
##
# Returns the timestamp the access token will expire at.
# Returns nil if the token does not expire.
#
# @return [Time, nil] The access token lifetime.
def expires_at
@expires_at
end
##
# Limits the lifetime of the access token as number of seconds since
# the Epoch. Nil values will be treated as though the token does
# not expire.
# @param [String,Integer,Time, nil] new_expires_at
# The access token expiration time.
def expires_at=(new_expires_at)
@expires_at = normalize_timestamp new_expires_at
end
##
# Returns true if the access token has expired.
# Returns false if the token has not expired or has an nil @expires_at.
#
# @return [TrueClass, FalseClass]
# The expiration state of the access token.
def expired?
return self.expires_at != nil && Time.now >= self.expires_at
end
##
# Returns true if the access token has expired or expires within
# the next n seconds. Returns false for tokens with a nil @expires_at.
#
# @param [Integer] sec
# Max number of seconds from now where a token is still considered
# expired.
# @return [TrueClass, FalseClass]
# The expiration state of the access token.
def expires_within?(sec)
return self.expires_at != nil && Time.now >= (self.expires_at - sec)
end
##
# Removes all credentials from the client.
def clear_credentials!
@access_token = nil
@refresh_token = nil
@id_token = nil
@username = nil
@password = nil
@code = nil
@issued_at = nil
@expires_at = nil
end
##
# Returns the inferred grant type, based on the current state of the
# client object. Returns `"none"` if the client has insufficient
# information to make an in-band authorization request.
#
# @return [String]
# The inferred grant type.
def grant_type
@grant_type ||= nil
if @grant_type
return @grant_type
else
if self.code && self.redirect_uri
'authorization_code'
elsif self.refresh_token
'refresh_token'
elsif self.username && self.password
'password'
elsif self.issuer && self.signing_key
'urn:ietf:params:oauth:grant-type:jwt-bearer'
else
# We don't have sufficient auth information, assume an out-of-band
# authorization arrangement between the client and server, or an
# extension grant type.
nil
end
end
end
def grant_type=(new_grant_type)
case new_grant_type
when 'authorization_code', 'refresh_token',
'password', 'client_credentials'
@grant_type = new_grant_type
else
@grant_type = Addressable::URI.parse(new_grant_type)
end
end
def to_jwt(options={})
options = deep_hash_normalize(options)
now = Time.new
skew = options[:skew] || 60
assertion = {
"iss" => self.issuer,
"aud" => self.audience,
"exp" => (now + self.expiry).to_i,
"iat" => (now - skew).to_i
}
assertion['scope'] = self.scope.join(' ') unless self.scope.nil?
assertion['prn'] = self.person unless self.person.nil?
assertion['sub'] = self.sub unless self.sub.nil?
JWT.encode(assertion, self.signing_key, self.signing_algorithm)
end
##
# Serialize the client object to JSON.
#
# @note A serialized client contains sensitive information. Persist or transmit with care.
#
# @return [String] A serialized JSON representation of the client.
def to_json(*)
return MultiJson.dump({
'authorization_uri' => self.authorization_uri ? self.authorization_uri.to_s : nil,
'token_credential_uri' => self.token_credential_uri ? self.token_credential_uri.to_s : nil,
'client_id' => self.client_id,
'client_secret' => self.client_secret,
'scope' => self.scope,
'state' => self.state,
'code' => self.code,
'redirect_uri' => self.redirect_uri ? self.redirect_uri.to_s : nil,
'username' => self.username,
'password' => self.password,
'issuer' => self.issuer,
'audience' => self.audience,
'person' => self.person,
'expiry' => self.expiry,
'expires_at' => self.expires_at ? self.expires_at.to_i : nil,
'signing_key' => self.signing_key,
'refresh_token' => self.refresh_token,
'access_token' => self.access_token,
'id_token' => self.id_token,
'extension_parameters' => self.extension_parameters
})
end
##
# Generates a request for token credentials.
#
# @param [Hash] options
# The configuration parameters for the request.
# - :code
-
# The authorization code.
#
# @private
# @return [Array] The request object.
def generate_access_token_request(options={})
options = deep_hash_normalize(options)
parameters = {"grant_type" => self.grant_type}
case self.grant_type
when 'authorization_code'
parameters['code'] = self.code
parameters['redirect_uri'] = self.redirect_uri
when 'password'
parameters['username'] = self.username
parameters['password'] = self.password
when 'refresh_token'
parameters['refresh_token'] = self.refresh_token
when 'urn:ietf:params:oauth:grant-type:jwt-bearer'
parameters['assertion'] = self.to_jwt(options)
else
if self.redirect_uri
# Grant type was intended to be `authorization_code` because of
# the presence of the redirect URI.
raise ArgumentError, 'Missing authorization code.'
end
parameters.merge!(self.extension_parameters)
end
parameters['client_id'] = self.client_id unless self.client_id.nil?
parameters['client_secret'] = self.client_secret unless self.client_secret.nil?
if options[:scope]
parameters['scope'] = options[:scope]
elsif options[:use_configured_scope] && !self.scope.nil?
parameters['scope'] = self.scope
end
additional = self.additional_parameters.merge(options[:additional_parameters] || {})
additional.each { |k, v| parameters[k.to_s] = v }
parameters
end
def fetch_access_token(options={})
if self.token_credential_uri.nil?
raise ArgumentError, 'Missing token endpoint URI.'
end
options = deep_hash_normalize(options)
client = options[:connection] ||= Faraday.default_connection
url = Addressable::URI.parse(self.token_credential_uri).normalize.to_s
parameters = self.generate_access_token_request(options)
if client.is_a?(Faraday::Connection)
response = client.post url,
Addressable::URI.form_encode(parameters),
{ 'Content-Type' => 'application/x-www-form-urlencoded' }
status = response.status.to_i
body = response.body
content_type = response.headers['Content-type']
else
# Hurley
response = client.post url, parameters
status = response.status_code.to_i
body = response.body
content_type = response.header[:content_type]
end
if status == 200
return ::Signet::OAuth2.parse_credentials(body, content_type)
elsif [400, 401, 403].include?(status)
message = 'Authorization failed.'
if body.to_s.strip.length > 0
message += " Server message:\n#{response.body.to_s.strip}"
end
raise ::Signet::AuthorizationError.new(
message, :response => response
)
elsif status.to_s[0] == "5"
message = 'Remote server error.'
if body.to_s.strip.length > 0
message += " Server message:\n#{response.body.to_s.strip}"
end
raise ::Signet::RemoteServerError.new(message)
else
message = "Unexpected status code: #{response.status}."
if body.to_s.strip.length > 0
message += " Server message:\n#{response.body.to_s.strip}"
end
raise ::Signet::UnexpectedStatusError.new(message)
end
end
def fetch_access_token!(options={})
token_hash = self.fetch_access_token(options)
if token_hash
# No-op for grant types other than `authorization_code`.
# An authorization code is a one-time use token and is immediately
# revoked after usage.
self.code = nil
self.issued_at = Time.now
self.update_token!(token_hash)
end
return token_hash
end
##
# Refresh the access token, if possible
def refresh!(options={})
self.fetch_access_token!(options)
end
##
# Generates an authenticated request for protected resources.
#
# @param [Hash] options
# The configuration parameters for the request.
# - :request
-
# A pre-constructed request. An OAuth 2 Authorization header
# will be added to it, as well as an explicit Cache-Control
# `no-store` directive.
# - :method
-
# The HTTP method for the request. Defaults to 'GET'.
# - :uri
-
# The URI for the request.
# - :headers
-
# The HTTP headers for the request.
# - :body
-
# The HTTP body for the request.
# - :realm
-
# The Authorization realm. See RFC 2617.
# @return [Faraday::Request] The request object.
def generate_authenticated_request(options={})
options = deep_hash_normalize(options)
if self.access_token == nil
raise ArgumentError, 'Missing access token.'
end
options = {
:realm => nil
}.merge(options)
if options[:request].kind_of?(Faraday::Request)
request = options[:request]
else
if options[:request].kind_of?(Array)
method, uri, headers, body = options[:request]
else
method = options[:method] || :get
uri = options[:uri]
headers = options[:headers] || []
body = options[:body] || ''
end
headers = headers.to_a if headers.kind_of?(Hash)
request_components = {
:method => method,
:uri => uri,
:headers => headers,
:body => body
}
# Verify that we have all pieces required to return an HTTP request
request_components.each do |(key, value)|
unless value
raise ArgumentError, "Missing :#{key} parameter."
end
end
method = method.to_s.downcase.to_sym
request = options[:connection].build_request(method.to_s.downcase.to_sym) do |req|
req.url(Addressable::URI.parse(uri).normalize.to_s)
req.headers = Faraday::Utils::Headers.new(headers)
req.body = body
end
end
request['Authorization'] = ::Signet::OAuth2.generate_bearer_authorization_header(
self.access_token,
options[:realm] ? [['realm', options[:realm]]] : nil
)
request['Cache-Control'] = 'no-store'
return request
end
##
# Transmits a request for a protected resource.
#
# @param [Hash] options
# The configuration parameters for the request.
# - :request
-
# A pre-constructed request. An OAuth 2 Authorization header
# will be added to it, as well as an explicit Cache-Control
# `no-store` directive.
# - :method
-
# The HTTP method for the request. Defaults to 'GET'.
# - :uri
-
# The URI for the request.
# - :headers
-
# The HTTP headers for the request.
# - :body
-
# The HTTP body for the request.
# - :realm
-
# The Authorization realm. See RFC 2617.
# - :connection
-
# The HTTP connection to use.
# Must be of type Faraday::Connection
.
#
# @example
# # Using Net::HTTP
# response = client.fetch_protected_resource(
# :uri => 'http://www.example.com/protected/resource'
# )
#
# @return [Array] The response object.
def fetch_protected_resource(options={})
options = deep_hash_normalize(options)
options[:connection] ||= Faraday.default_connection
request = self.generate_authenticated_request(options)
request_env = request.to_env(options[:connection])
request_env[:request] ||= request
response = options[:connection].app.call(request_env)
if response.status.to_i == 401
# When accessing a protected resource, we only want to raise an
# error for 401 responses.
message = 'Authorization failed.'
if response.body.to_s.strip.length > 0
message += " Server message:\n#{response.body.to_s.strip}"
end
raise ::Signet::AuthorizationError.new(
message, :request => request, :response => response
)
else
return response
end
end
private
##
# Check if URI is Google's postmessage flow (not a valid redirect_uri by spec, but allowed)
# @private
def uri_is_postmessage?(uri)
return uri.to_s.casecmp('postmessage') == 0
end
##
# Check if the URI is a out-of-band
# @private
def uri_is_oob?(uri)
return OOB_MODES.include?(uri.to_s)
end
# Convert all keys in this hash (nested) to symbols for uniform retrieval
def recursive_hash_normalize_keys(val)
if val.is_a? Hash
deep_hash_normalize(val)
else
val
end
end
def deep_hash_normalize(old_hash)
sym_hash = {}
old_hash and old_hash.each {|k,v| sym_hash[k.to_sym] = recursive_hash_normalize_keys(v)}
sym_hash
end
def normalize_timestamp(time)
case time
when NilClass
nil
when Time
time
when DateTime
time.to_time
when String
Time.parse(time)
when Integer
Time.at(time)
else
fail "Invalid time value #{time}"
end
end
def set_relative_expires_at(issued_at, expires_in)
self.issued_at = issued_at
# Using local expires_in because if self.expires_in is used, it returns
# the time left before the token expires
self.expires_at = self.issued_at + expires_in.to_i
end
end
end
end
signet-0.11.0/lib/signet/errors.rb 0000644 0000041 0000041 00000005613 13410324340 017011 0 ustar www-data www-data # Copyright (C) 2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'addressable/uri'
module Signet
##
# An error indicating that the client has aborted an operation that
# would have been unsafe to perform.
class UnsafeOperationError < StandardError
end
##
# An error indicating the client failed to parse a value.
class ParseError < StandardError
end
##
# An error indicating that the server considers the Authorization header to
# be malformed(missing/unsupported/invalid parameters), and the request
# should be considered invalid.
class MalformedAuthorizationError < StandardError
end
##
# An error indicating that the server failed at processing the request
# due to a internal error
class RemoteServerError < StandardError
end
##
# An error indicating that the server sent an unexpected http status
class UnexpectedStatusError < StandardError
end
##
# An error indicating the remote server refused to authorize the client.
class AuthorizationError < StandardError
##
# Creates a new authentication error.
#
# @param [String] message
# A message describing the error.
# @param [Hash] options
# The configuration parameters for the request.
# - :request
-
# A Faraday::Request object. Optional.
# - :response
-
# A Faraday::Response object. Optional.
# - :code
-
# An error code.
# - :description
-
# Human-readable text intended to be used to assist in resolving the
# error condition.
# - :uri
-
# A URI identifying a human-readable web page with additional
# information about the error, indended for the resource owner.
def initialize(message, options={})
super(message)
@options = options
@request = options[:request]
@response = options[:response]
@code = options[:code]
@description = options[:description]
@uri = Addressable::URI.parse(options[:uri])
end
##
# The HTTP request that triggered this authentication error.
#
# @return [Array] A tuple of method, uri, headers, and body.
attr_reader :request
##
# The HTTP response that triggered this authentication error.
#
# @return [Array] A tuple of status, headers, and body.
attr_reader :response
end
end
signet-0.11.0/lib/signet/oauth_1.rb 0000644 0000041 0000041 00000042754 13410324340 017044 0 ustar www-data www-data require 'addressable/uri'
require 'signet'
require 'securerandom'
module Signet #:nodoc:
module OAuth1
OUT_OF_BAND = 'oob'
##
# 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.
def self.encode(value)
value = value.to_s if value.kind_of?(Symbol)
return Addressable::URI.encode_component(
value,
Addressable::URI::CharacterClasses::UNRESERVED
)
end
##
# Converts a percent-encoded String to an unencoded value.
#
# @param [#to_str] value
# The percent-encoded String
to be unencoded.
#
# @return [String] The unencoded value.
def self.unencode(value)
return Addressable::URI.unencode_component(value)
end
##
# Returns a timestamp suitable for use as an 'oauth_timestamp'
# value.
#
# @return [String] The current timestamp.
def self.generate_timestamp()
return Time.now.to_i.to_s
end
##
# Returns a nonce suitable for use as an 'oauth_nonce'
# value.
#
# @return [String] A random nonce.
def self.generate_nonce()
return SecureRandom.random_bytes(16).unpack('H*').join('')
end
##
# Processes an options Hash
to find a credential key value.
# Allows for greater flexibility in configuration.
#
# @param [Symbol] credential_type
# One of :client
, :temporary
,
# :token
, :consumer
, :request
,
# or :access
.
#
# @return [String] The credential key value.
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
##
# Processes an options Hash
to find a credential secret value.
# Allows for greater flexibility in configuration.
#
# @param [Symbol] credential_type
# One of :client
, :temporary
,
# :token
, :consumer
, :request
,
# or :access
.
#
# @return [String] The credential secret value.
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
##
# 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.
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
##
# 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.
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
##
# 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 Authorization
realm. See RFC 2617.
#
# @return [String] The Authorization
header.
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
##
# Parses an Authorization
header into its component
# parameters. Parameter keys and values are decoded according to the
# rules given in RFC 5849.
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
##
# 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.
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
##
# 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.
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
##
# Generates an OAuth parameter list to be used when obtaining a set of
# temporary credentials.
#
# @param [Hash] options
# The configuration parameters for the request.
# - :client_credential_key
-
# The client credential key.
# - :callback
-
# The OAuth callback. Defaults to {Signet::OAuth1::OUT_OF_BAND}.
# - :signature_method
-
# The signature method. Defaults to 'HMAC-SHA1'
.
# - :additional_parameters
-
# Non-standard additional parameters.
#
# @return [Array]
# The parameter list as an Array
of key/value pairs.
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
##
# 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.
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
##
# Generates an OAuth parameter list to be used when obtaining a set of
# token credentials.
#
# @param [Hash] options
# The configuration parameters for the request.
# - :client_credential_key
-
# The client credential key.
# - :temporary_credential_key
-
# The temporary credential key.
# - :verifier
-
# The OAuth verifier.
# - :signature_method
-
# The signature method. Defaults to 'HMAC-SHA1'
.
#
# @return [Array]
# The parameter list as an Array
of key/value pairs.
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
##
# Generates an OAuth parameter list to be used when requesting a
# protected resource.
#
# @param [Hash] options
# The configuration parameters for the request.
# - :client_credential_key
-
# The client credential key.
# - :token_credential_key
-
# The token credential key.
# - :signature_method
-
# The signature method. Defaults to 'HMAC-SHA1'
.
# - :two_legged
-
# A switch for two-legged OAuth. Defaults to false
.
#
# @return [Array]
# The parameter list as an Array
of key/value pairs.
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
end
end
signet-0.11.0/Gemfile 0000644 0000041 0000041 00000000170 13410324340 014375 0 ustar www-data www-data source 'https://rubygems.org'
gemspec
gem 'jruby-openssl', :platforms => :jruby
gem 'hurley'
gem 'bundler', '~> 1.15'
signet-0.11.0/website/ 0000755 0000041 0000041 00000000000 13410324340 014546 5 ustar www-data www-data signet-0.11.0/website/index.html 0000644 0000041 0000041 00000003430 13410324340 016543 0 ustar www-data www-data
Signet
Signet
Signet is an OAuth 1.0 / OAuth 2.0 implementation.
You know what to do:
sudo gem install signet