commit
c75d7398ff
|
@ -9,7 +9,7 @@ rvm:
|
||||||
- jruby
|
- jruby
|
||||||
matrix:
|
matrix:
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- rbx-2 # See rubinius/rubinius#3485 - rubocop segfaults
|
- rvm: rbx-2 # See rubinius/rubinius#3485 - rubocop segfaults
|
||||||
script: "bundle exec rake"
|
script: "bundle exec rake"
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
## 0.5.0 (12/10/2015)
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
* Initial support for user credentials ([@sqrrrl][])
|
||||||
|
* Update Signet to 0.7
|
||||||
|
|
||||||
## 0.4.2 (05/08/2015)
|
## 0.4.2 (05/08/2015)
|
||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
|
|
1
Gemfile
1
Gemfile
|
@ -15,6 +15,7 @@ group :development do
|
||||||
gem 'fakeredis', '~> 0.5'
|
gem 'fakeredis', '~> 0.5'
|
||||||
gem 'webmock', '~> 1.21'
|
gem 'webmock', '~> 1.21'
|
||||||
gem 'rack-test', '~> 0.6'
|
gem 'rack-test', '~> 0.6'
|
||||||
|
gem 'sinatra'
|
||||||
end
|
end
|
||||||
|
|
||||||
platforms :jruby do
|
platforms :jruby do
|
||||||
|
|
81
README.md
81
README.md
|
@ -38,7 +38,8 @@ $ gem install googleauth
|
||||||
require 'googleauth'
|
require 'googleauth'
|
||||||
|
|
||||||
# Get the environment configured authorization
|
# Get the environment configured authorization
|
||||||
scopes = ['https://www.googleapis.com/auth/cloud-platform', 'https://www.googleapis.com/auth/compute']
|
scopes = ['https://www.googleapis.com/auth/cloud-platform',
|
||||||
|
'https://www.googleapis.com/auth/compute']
|
||||||
authorization = Google::Auth.get_application_default(scopes)
|
authorization = Google::Auth.get_application_default(scopes)
|
||||||
|
|
||||||
# Add the the access token obtained using the authorization to a hash, e.g
|
# Add the the access token obtained using the authorization to a hash, e.g
|
||||||
|
@ -61,6 +62,84 @@ and authorization level for the application independent of the user. This is
|
||||||
the recommended approach to authorize calls to Cloud APIs, particularly when
|
the recommended approach to authorize calls to Cloud APIs, particularly when
|
||||||
you're building an application that uses Google Compute Engine.
|
you're building an application that uses Google Compute Engine.
|
||||||
|
|
||||||
|
## User Credentials
|
||||||
|
|
||||||
|
The library also provides support for requesting and storing user
|
||||||
|
credentials (3-Legged OAuth2.) Two implementations are currently available,
|
||||||
|
a generic authorizer useful for command line apps or custom integrations as
|
||||||
|
well as a web variant tailored toward Rack-based applications.
|
||||||
|
|
||||||
|
The authorizers are intended for authorization use cases. For sign-on,
|
||||||
|
see [Google Idenity Platform](https://developers.google.com/identity/)
|
||||||
|
|
||||||
|
### Example (Web)
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
require 'googleauth'
|
||||||
|
require 'googleauth/web_user_authorizer'
|
||||||
|
require 'googleauth/stores/redis_token_store'
|
||||||
|
require 'redis'
|
||||||
|
|
||||||
|
client_id = Google::Auth::ClientId.from_file('/path/to/client_secrets.json')
|
||||||
|
scope = ['https://www.googleapis.com/auth/drive']
|
||||||
|
token_store = Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new)
|
||||||
|
authorizer = Google::Auth::WebUserAuthorizer.new(
|
||||||
|
client_id, scope, token_store, '/oauth2callback')
|
||||||
|
|
||||||
|
|
||||||
|
get('/authorize') do
|
||||||
|
# NOTE: Assumes the user is already authenticated to the app
|
||||||
|
user_id = request.session['user_id']
|
||||||
|
credentials = authorizer.get_credentials(user_id, request)
|
||||||
|
if credentials.nil?
|
||||||
|
redirect authorizer.get_authorization_url(user_id: user_id, request: request)
|
||||||
|
end
|
||||||
|
# Credentials are valid, can call APIs
|
||||||
|
# ...
|
||||||
|
end
|
||||||
|
|
||||||
|
get('/oauth2callback') do
|
||||||
|
target_url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(
|
||||||
|
request)
|
||||||
|
redirect target_url
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example (Command Line)
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
require 'googleauth'
|
||||||
|
require 'googleauth/stores/file_token_store'
|
||||||
|
|
||||||
|
scope = 'https://www.googleapis.com/auth/drive'
|
||||||
|
client_id = Google::Auth::ClientId.from_file('/path/to/client_secrets.json')
|
||||||
|
token_store = Google::Auth::Stores::FileTokenStore.new(
|
||||||
|
:file => '/path/to/tokens.yaml')
|
||||||
|
authorizer = Google::Auth::UserAuthorizer.new(client_id, scope, token_store)
|
||||||
|
|
||||||
|
credentials = authorizer.get_credentials(user_id)
|
||||||
|
if credentials.nil?
|
||||||
|
url = authorizer.get_authorization_url(base_url: 'urn:ietf:wg:oauth:2.0:oob')
|
||||||
|
puts "Open #{url} in your browser and enter the resulting code:"
|
||||||
|
code = gets
|
||||||
|
credentials = authorizer.get_and_store_credentials_from_code(
|
||||||
|
user_id: user_id, code: code, base_url: OOB_URI)
|
||||||
|
end
|
||||||
|
|
||||||
|
# OK to use credentials
|
||||||
|
```
|
||||||
|
|
||||||
|
### Storage
|
||||||
|
|
||||||
|
Authorizers require a storage instance to manage long term persistence of
|
||||||
|
access and refresh tokens. Two storage implementations are included:
|
||||||
|
|
||||||
|
* Google::Auth::Stores::FileTokenStore
|
||||||
|
* Google::Auth::Stores::RedisTokenStore
|
||||||
|
|
||||||
|
Custom storage implementations can also be used. See
|
||||||
|
[token_store.rb](lib/googleauth/token_store.rb) for additional details.
|
||||||
|
|
||||||
## What about auth in google-apis-ruby-client?
|
## What about auth in google-apis-ruby-client?
|
||||||
|
|
||||||
The goal is for all auth done by
|
The goal is for all auth done by
|
||||||
|
|
|
@ -30,5 +30,5 @@ Gem::Specification.new do |s|
|
||||||
s.add_dependency 'jwt', '~> 1.4'
|
s.add_dependency 'jwt', '~> 1.4'
|
||||||
s.add_dependency 'memoist', '~> 0.12'
|
s.add_dependency 'memoist', '~> 0.12'
|
||||||
s.add_dependency 'multi_json', '~> 1.11'
|
s.add_dependency 'multi_json', '~> 1.11'
|
||||||
s.add_dependency 'signet', '~> 0.6'
|
s.add_dependency 'signet', '~> 0.7'
|
||||||
end
|
end
|
||||||
|
|
|
@ -42,7 +42,7 @@ module Signet
|
||||||
def apply!(a_hash, opts = {})
|
def apply!(a_hash, opts = {})
|
||||||
# fetch the access token there is currently not one, or if the client
|
# fetch the access token there is currently not one, or if the client
|
||||||
# has expired
|
# has expired
|
||||||
fetch_access_token!(opts) if access_token.nil? || expired?
|
fetch_access_token!(opts) if access_token.nil? || expires_within?(60)
|
||||||
a_hash[AUTH_METADATA_KEY] = "Bearer #{access_token}"
|
a_hash[AUTH_METADATA_KEY] = "Bearer #{access_token}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ module Signet
|
||||||
end
|
end
|
||||||
|
|
||||||
alias_method :orig_fetch_access_token!, :fetch_access_token!
|
alias_method :orig_fetch_access_token!, :fetch_access_token!
|
||||||
def fetch_access_token!(options)
|
def fetch_access_token!(options = {})
|
||||||
info = orig_fetch_access_token!(options)
|
info = orig_fetch_access_token!(options)
|
||||||
notify_refresh_listeners
|
notify_refresh_listeners
|
||||||
info
|
info
|
||||||
|
|
|
@ -31,6 +31,6 @@ module Google
|
||||||
# Module Auth provides classes that provide Google-specific authorization
|
# Module Auth provides classes that provide Google-specific authorization
|
||||||
# used to access Google APIs.
|
# used to access Google APIs.
|
||||||
module Auth
|
module Auth
|
||||||
VERSION = '0.4.2'
|
VERSION = '0.5.0'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -44,16 +44,17 @@ module Google
|
||||||
# user_id = request.session['user_email']
|
# user_id = request.session['user_email']
|
||||||
# credentials = authorizer.get_credentials(user_id, request)
|
# credentials = authorizer.get_credentials(user_id, request)
|
||||||
# if credentials.nil?
|
# if credentials.nil?
|
||||||
# redirect authorizer.get_redirect_uri(user_id, request)
|
# redirect authorizer.get_authorization_url(user_id: user_id,
|
||||||
|
# request: request)
|
||||||
# end
|
# end
|
||||||
# # Credentials are valid, can call APIs
|
# # Credentials are valid, can call APIs
|
||||||
# ...
|
# ...
|
||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
# get('/oauth2callback') do
|
# get('/oauth2callback') do
|
||||||
# user_id = request.session['user_email']
|
# url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(
|
||||||
# _, return_uri = authorizer.handle_auth_callback(user_id, request)
|
# request)
|
||||||
# redirect return_uri
|
# redirect url
|
||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
# Instead of implementing the callback directly, applications are
|
# Instead of implementing the callback directly, applications are
|
||||||
|
|
|
@ -34,18 +34,6 @@ $LOAD_PATH.uniq!
|
||||||
require 'faraday'
|
require 'faraday'
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
def build_json_response(payload)
|
|
||||||
[200,
|
|
||||||
{ 'Content-Type' => 'application/json; charset=utf-8' },
|
|
||||||
MultiJson.dump(payload)]
|
|
||||||
end
|
|
||||||
|
|
||||||
def build_access_token_json(token)
|
|
||||||
build_json_response('access_token' => token,
|
|
||||||
'token_type' => 'Bearer',
|
|
||||||
'expires_in' => 3600)
|
|
||||||
end
|
|
||||||
|
|
||||||
shared_examples 'apply/apply! are OK' do
|
shared_examples 'apply/apply! are OK' do
|
||||||
let(:auth_key) { :Authorization }
|
let(:auth_key) { :Authorization }
|
||||||
|
|
||||||
|
@ -57,124 +45,101 @@ shared_examples 'apply/apply! are OK' do
|
||||||
# auth client
|
# auth client
|
||||||
describe '#fetch_access_token' do
|
describe '#fetch_access_token' do
|
||||||
let(:token) { '1/abcdef1234567890' }
|
let(:token) { '1/abcdef1234567890' }
|
||||||
let(:stubs) do
|
let(:stub) do
|
||||||
make_auth_stubs access_token: token
|
make_auth_stubs access_token: token
|
||||||
end
|
end
|
||||||
let(:connection) do
|
|
||||||
Faraday.new do |b|
|
|
||||||
b.adapter(:test, stubs)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should set access_token to the fetched value' do
|
it 'should set access_token to the fetched value' do
|
||||||
@client.fetch_access_token!(connection: connection)
|
stub
|
||||||
|
@client.fetch_access_token!
|
||||||
expect(@client.access_token).to eq(token)
|
expect(@client.access_token).to eq(token)
|
||||||
stubs.verify_stubbed_calls
|
expect(stub).to have_been_requested
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should notify refresh listeners after updating' do
|
it 'should notify refresh listeners after updating' do
|
||||||
|
stub
|
||||||
expect do |b|
|
expect do |b|
|
||||||
@client.on_refresh(&b)
|
@client.on_refresh(&b)
|
||||||
@client.fetch_access_token!(connection: connection)
|
@client.fetch_access_token!
|
||||||
end.to yield_with_args(have_attributes(
|
end.to yield_with_args(have_attributes(
|
||||||
access_token: '1/abcdef1234567890'))
|
access_token: '1/abcdef1234567890'))
|
||||||
stubs.verify_stubbed_calls
|
expect(stub).to have_been_requested
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#apply!' do
|
describe '#apply!' do
|
||||||
it 'should update the target hash with fetched access token' do
|
it 'should update the target hash with fetched access token' do
|
||||||
token = '1/abcdef1234567890'
|
token = '1/abcdef1234567890'
|
||||||
stubs = make_auth_stubs access_token: token
|
stub = make_auth_stubs access_token: token
|
||||||
c = Faraday.new do |b|
|
|
||||||
b.adapter(:test, stubs)
|
|
||||||
end
|
|
||||||
|
|
||||||
md = { foo: 'bar' }
|
md = { foo: 'bar' }
|
||||||
@client.apply!(md, connection: c)
|
@client.apply!(md)
|
||||||
want = { :foo => 'bar', auth_key => "Bearer #{token}" }
|
want = { :foo => 'bar', auth_key => "Bearer #{token}" }
|
||||||
expect(md).to eq(want)
|
expect(md).to eq(want)
|
||||||
stubs.verify_stubbed_calls
|
expect(stub).to have_been_requested
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'updater_proc' do
|
describe 'updater_proc' do
|
||||||
it 'should provide a proc that updates a hash with the access token' do
|
it 'should provide a proc that updates a hash with the access token' do
|
||||||
token = '1/abcdef1234567890'
|
token = '1/abcdef1234567890'
|
||||||
stubs = make_auth_stubs access_token: token
|
stub = make_auth_stubs access_token: token
|
||||||
c = Faraday.new do |b|
|
|
||||||
b.adapter(:test, stubs)
|
|
||||||
end
|
|
||||||
|
|
||||||
md = { foo: 'bar' }
|
md = { foo: 'bar' }
|
||||||
the_proc = @client.updater_proc
|
the_proc = @client.updater_proc
|
||||||
got = the_proc.call(md, connection: c)
|
got = the_proc.call(md)
|
||||||
want = { :foo => 'bar', auth_key => "Bearer #{token}" }
|
want = { :foo => 'bar', auth_key => "Bearer #{token}" }
|
||||||
expect(got).to eq(want)
|
expect(got).to eq(want)
|
||||||
stubs.verify_stubbed_calls
|
expect(stub).to have_been_requested
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#apply' do
|
describe '#apply' do
|
||||||
it 'should not update the original hash with the access token' do
|
it 'should not update the original hash with the access token' do
|
||||||
token = '1/abcdef1234567890'
|
token = '1/abcdef1234567890'
|
||||||
stubs = make_auth_stubs access_token: token
|
stub = make_auth_stubs access_token: token
|
||||||
c = Faraday.new do |b|
|
|
||||||
b.adapter(:test, stubs)
|
|
||||||
end
|
|
||||||
|
|
||||||
md = { foo: 'bar' }
|
md = { foo: 'bar' }
|
||||||
@client.apply(md, connection: c)
|
@client.apply(md)
|
||||||
want = { foo: 'bar' }
|
want = { foo: 'bar' }
|
||||||
expect(md).to eq(want)
|
expect(md).to eq(want)
|
||||||
stubs.verify_stubbed_calls
|
expect(stub).to have_been_requested
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should add the token to the returned hash' do
|
it 'should add the token to the returned hash' do
|
||||||
token = '1/abcdef1234567890'
|
token = '1/abcdef1234567890'
|
||||||
stubs = make_auth_stubs access_token: token
|
stub = make_auth_stubs access_token: token
|
||||||
c = Faraday.new do |b|
|
|
||||||
b.adapter(:test, stubs)
|
|
||||||
end
|
|
||||||
|
|
||||||
md = { foo: 'bar' }
|
md = { foo: 'bar' }
|
||||||
got = @client.apply(md, connection: c)
|
got = @client.apply(md)
|
||||||
want = { :foo => 'bar', auth_key => "Bearer #{token}" }
|
want = { :foo => 'bar', auth_key => "Bearer #{token}" }
|
||||||
expect(got).to eq(want)
|
expect(got).to eq(want)
|
||||||
stubs.verify_stubbed_calls
|
expect(stub).to have_been_requested
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should not fetch a new token if the current is not expired' do
|
it 'should not fetch a new token if the current is not expired' do
|
||||||
token = '1/abcdef1234567890'
|
token = '1/abcdef1234567890'
|
||||||
stubs = make_auth_stubs access_token: token
|
stub = make_auth_stubs access_token: token
|
||||||
c = Faraday.new do |b|
|
|
||||||
b.adapter(:test, stubs)
|
|
||||||
end
|
|
||||||
|
|
||||||
n = 5 # arbitrary
|
n = 5 # arbitrary
|
||||||
n.times do |_t|
|
n.times do |_t|
|
||||||
md = { foo: 'bar' }
|
md = { foo: 'bar' }
|
||||||
got = @client.apply(md, connection: c)
|
got = @client.apply(md)
|
||||||
want = { :foo => 'bar', auth_key => "Bearer #{token}" }
|
want = { :foo => 'bar', auth_key => "Bearer #{token}" }
|
||||||
expect(got).to eq(want)
|
expect(got).to eq(want)
|
||||||
end
|
end
|
||||||
stubs.verify_stubbed_calls
|
expect(stub).to have_been_requested
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should fetch a new token if the current one is expired' do
|
it 'should fetch a new token if the current one is expired' do
|
||||||
token_1 = '1/abcdef1234567890'
|
token_1 = '1/abcdef1234567890'
|
||||||
token_2 = '2/abcdef1234567890'
|
token_2 = '2/abcdef1234567891'
|
||||||
|
|
||||||
[token_1, token_2].each do |t|
|
[token_1, token_2].each do |t|
|
||||||
stubs = make_auth_stubs access_token: t
|
make_auth_stubs access_token: t
|
||||||
c = Faraday.new do |b|
|
|
||||||
b.adapter(:test, stubs)
|
|
||||||
end
|
|
||||||
md = { foo: 'bar' }
|
md = { foo: 'bar' }
|
||||||
got = @client.apply(md, connection: c)
|
got = @client.apply(md)
|
||||||
want = { :foo => 'bar', auth_key => "Bearer #{t}" }
|
want = { :foo => 'bar', auth_key => "Bearer #{t}" }
|
||||||
expect(got).to eq(want)
|
expect(got).to eq(want)
|
||||||
stubs.verify_stubbed_calls
|
|
||||||
@client.expires_at -= 3601 # default is to expire in 1hr
|
@client.expires_at -= 3601 # default is to expire in 1hr
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -37,7 +37,7 @@ require 'googleauth/compute_engine'
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe Google::Auth::GCECredentials do
|
describe Google::Auth::GCECredentials do
|
||||||
MD_URI = '/computeMetadata/v1/instance/service-accounts/default/token'
|
MD_URI = 'http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token'
|
||||||
GCECredentials = Google::Auth::GCECredentials
|
GCECredentials = Google::Auth::GCECredentials
|
||||||
|
|
||||||
before(:example) do
|
before(:example) do
|
||||||
|
@ -46,16 +46,14 @@ describe Google::Auth::GCECredentials do
|
||||||
|
|
||||||
def make_auth_stubs(opts = {})
|
def make_auth_stubs(opts = {})
|
||||||
access_token = opts[:access_token] || ''
|
access_token = opts[:access_token] || ''
|
||||||
Faraday::Adapter::Test::Stubs.new do |stub|
|
body = MultiJson.dump('access_token' => access_token,
|
||||||
stub.get(MD_URI) do |env|
|
'token_type' => 'Bearer',
|
||||||
headers = env[:request_headers]
|
'expires_in' => 3600)
|
||||||
expect(headers['Metadata-Flavor']).to eq('Google')
|
stub_request(:get, MD_URI)
|
||||||
build_json_response(
|
.with(headers: { 'Metadata-Flavor' => 'Google' })
|
||||||
'access_token' => access_token,
|
.to_return(body: body,
|
||||||
'token_type' => 'Bearer',
|
status: 200,
|
||||||
'expires_in' => 3600)
|
headers: { 'Content-Type' => 'application/json' })
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'apply/apply! are OK'
|
it_behaves_like 'apply/apply! are OK'
|
||||||
|
@ -63,83 +61,48 @@ describe Google::Auth::GCECredentials do
|
||||||
context 'metadata is unavailable' do
|
context 'metadata is unavailable' do
|
||||||
describe '#fetch_access_token' do
|
describe '#fetch_access_token' do
|
||||||
it 'should fail if the metadata request returns a 404' do
|
it 'should fail if the metadata request returns a 404' do
|
||||||
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
|
stub = stub_request(:get, MD_URI)
|
||||||
stub.get(MD_URI) do |_env|
|
.to_return(status: 404,
|
||||||
[404,
|
headers: { 'Metadata-Flavor' => 'Google' })
|
||||||
{ 'Metadata-Flavor' => 'Google' },
|
blk = proc { @client.fetch_access_token! }
|
||||||
'']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
c = Faraday.new do |b|
|
|
||||||
b.adapter(:test, stubs)
|
|
||||||
end
|
|
||||||
blk = proc { @client.fetch_access_token!(connection: c) }
|
|
||||||
expect(&blk).to raise_error Signet::AuthorizationError
|
expect(&blk).to raise_error Signet::AuthorizationError
|
||||||
stubs.verify_stubbed_calls
|
expect(stub).to have_been_requested
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should fail if the metadata request returns an unexpected code' do
|
it 'should fail if the metadata request returns an unexpected code' do
|
||||||
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
|
stub = stub_request(:get, MD_URI)
|
||||||
stub.get(MD_URI) do |_env|
|
.to_return(status: 503,
|
||||||
[503,
|
headers: { 'Metadata-Flavor' => 'Google' })
|
||||||
{ 'Metadata-Flavor' => 'Google' },
|
blk = proc { @client.fetch_access_token! }
|
||||||
'']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
c = Faraday.new do |b|
|
|
||||||
b.adapter(:test, stubs)
|
|
||||||
end
|
|
||||||
blk = proc { @client.fetch_access_token!(connection: c) }
|
|
||||||
expect(&blk).to raise_error Signet::AuthorizationError
|
expect(&blk).to raise_error Signet::AuthorizationError
|
||||||
stubs.verify_stubbed_calls
|
expect(stub).to have_been_requested
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#on_gce?' do
|
describe '#on_gce?' do
|
||||||
it 'should be true when Metadata-Flavor is Google' do
|
it 'should be true when Metadata-Flavor is Google' do
|
||||||
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
|
stub = stub_request(:get, 'http://169.254.169.254')
|
||||||
stub.get('/') do |_env|
|
.to_return(status: 200,
|
||||||
[200,
|
headers: { 'Metadata-Flavor' => 'Google' })
|
||||||
{ 'Metadata-Flavor' => 'Google' },
|
expect(GCECredentials.on_gce?({}, true)).to eq(true)
|
||||||
'']
|
expect(stub).to have_been_requested
|
||||||
end
|
|
||||||
end
|
|
||||||
c = Faraday.new do |b|
|
|
||||||
b.adapter(:test, stubs)
|
|
||||||
end
|
|
||||||
expect(GCECredentials.on_gce?(connection: c)).to eq(true)
|
|
||||||
stubs.verify_stubbed_calls
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should be false when Metadata-Flavor is not Google' do
|
it 'should be false when Metadata-Flavor is not Google' do
|
||||||
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
|
stub = stub_request(:get, 'http://169.254.169.254')
|
||||||
stub.get('/') do |_env|
|
.to_return(status: 200,
|
||||||
[200,
|
headers: { 'Metadata-Flavor' => 'NotGoogle' })
|
||||||
{ 'Metadata-Flavor' => 'NotGoogle' },
|
expect(GCECredentials.on_gce?({}, true)).to eq(false)
|
||||||
'']
|
expect(stub).to have_been_requested
|
||||||
end
|
|
||||||
end
|
|
||||||
c = Faraday.new do |b|
|
|
||||||
b.adapter(:test, stubs)
|
|
||||||
end
|
|
||||||
expect(GCECredentials.on_gce?(connection: c)).to eq(false)
|
|
||||||
stubs.verify_stubbed_calls
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should be false if the response is not 200' do
|
it 'should be false if the response is not 200' do
|
||||||
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
|
stub = stub_request(:get, 'http://169.254.169.254')
|
||||||
stub.get('/') do |_env|
|
.to_return(status: 404,
|
||||||
[404,
|
headers: { 'Metadata-Flavor' => 'NotGoogle' })
|
||||||
{ 'Metadata-Flavor' => 'Google' },
|
expect(GCECredentials.on_gce?({}, true)).to eq(false)
|
||||||
'']
|
expect(stub).to have_been_requested
|
||||||
end
|
|
||||||
end
|
|
||||||
c = Faraday.new do |b|
|
|
||||||
b.adapter(:test, stubs)
|
|
||||||
end
|
|
||||||
expect(GCECredentials.on_gce?(connection: c)).to eq(false)
|
|
||||||
stubs.verify_stubbed_calls
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -37,6 +37,9 @@ require 'googleauth'
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe '#get_application_default' do
|
describe '#get_application_default' do
|
||||||
|
# Pass unique options each time to bypass memoization
|
||||||
|
let(:options) { |example| { dememoize: example } }
|
||||||
|
|
||||||
before(:example) do
|
before(:example) do
|
||||||
@key = OpenSSL::PKey::RSA.new(2048)
|
@key = OpenSSL::PKey::RSA.new(2048)
|
||||||
@var_name = ENV_VAR
|
@var_name = ENV_VAR
|
||||||
|
@ -59,31 +62,24 @@ describe '#get_application_default' do
|
||||||
Dir.mktmpdir do |dir|
|
Dir.mktmpdir do |dir|
|
||||||
key_path = File.join(dir, 'does-not-exist')
|
key_path = File.join(dir, 'does-not-exist')
|
||||||
ENV[@var_name] = key_path
|
ENV[@var_name] = key_path
|
||||||
expect { Google::Auth.get_application_default(@scope) }
|
expect { Google::Auth.get_application_default(@scope, options) }
|
||||||
.to raise_error RuntimeError
|
.to raise_error RuntimeError
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'fails without default file or env if not on compute engine' do
|
it 'fails without default file or env if not on compute engine' do
|
||||||
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
|
stub = stub_request(:get, 'http://169.254.169.254')
|
||||||
stub.get('/') do |_env|
|
.to_return(status: 404,
|
||||||
[404,
|
headers: { 'Metadata-Flavor' => 'NotGoogle' })
|
||||||
{ 'Metadata-Flavor' => 'Google' },
|
|
||||||
'']
|
|
||||||
end
|
|
||||||
end # GCE not detected
|
|
||||||
Dir.mktmpdir do |dir|
|
Dir.mktmpdir do |dir|
|
||||||
ENV.delete(@var_name) unless ENV[@var_name].nil? # no env var
|
ENV.delete(@var_name) unless ENV[@var_name].nil? # no env var
|
||||||
ENV['HOME'] = dir # no config present in this tmp dir
|
ENV['HOME'] = dir # no config present in this tmp dir
|
||||||
c = Faraday.new do |b|
|
|
||||||
b.adapter(:test, stubs)
|
|
||||||
end
|
|
||||||
blk = proc do
|
blk = proc do
|
||||||
Google::Auth.get_application_default(@scope, connection: c)
|
Google::Auth.get_application_default(@scope, options)
|
||||||
end
|
end
|
||||||
expect(&blk).to raise_error RuntimeError
|
expect(&blk).to raise_error RuntimeError
|
||||||
end
|
end
|
||||||
stubs.verify_stubbed_calls
|
expect(stub).to have_been_requested
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -94,7 +90,8 @@ describe '#get_application_default' do
|
||||||
FileUtils.mkdir_p(File.dirname(key_path))
|
FileUtils.mkdir_p(File.dirname(key_path))
|
||||||
File.write(key_path, cred_json_text)
|
File.write(key_path, cred_json_text)
|
||||||
ENV[@var_name] = key_path
|
ENV[@var_name] = key_path
|
||||||
expect(Google::Auth.get_application_default(@scope)).to_not be_nil
|
expect(Google::Auth.get_application_default(@scope, options))
|
||||||
|
.to_not be_nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -105,7 +102,8 @@ describe '#get_application_default' do
|
||||||
FileUtils.mkdir_p(File.dirname(key_path))
|
FileUtils.mkdir_p(File.dirname(key_path))
|
||||||
File.write(key_path, cred_json_text)
|
File.write(key_path, cred_json_text)
|
||||||
ENV['HOME'] = dir
|
ENV['HOME'] = dir
|
||||||
expect(Google::Auth.get_application_default(@scope)).to_not be_nil
|
expect(Google::Auth.get_application_default(@scope, options))
|
||||||
|
.to_not be_nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -116,30 +114,21 @@ describe '#get_application_default' do
|
||||||
FileUtils.mkdir_p(File.dirname(key_path))
|
FileUtils.mkdir_p(File.dirname(key_path))
|
||||||
File.write(key_path, cred_json_text)
|
File.write(key_path, cred_json_text)
|
||||||
ENV['HOME'] = dir
|
ENV['HOME'] = dir
|
||||||
expect(Google::Auth.get_application_default).to_not be_nil
|
expect(Google::Auth.get_application_default(nil, options)).to_not be_nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'succeeds without default file or env if on compute engine' do
|
it 'succeeds without default file or env if on compute engine' do
|
||||||
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
|
stub = stub_request(:get, 'http://169.254.169.254')
|
||||||
stub.get('/') do |_env|
|
.to_return(status: 200,
|
||||||
[200,
|
headers: { 'Metadata-Flavor' => 'Google' })
|
||||||
{ 'Metadata-Flavor' => 'Google' },
|
|
||||||
'']
|
|
||||||
end
|
|
||||||
end # GCE detected
|
|
||||||
Dir.mktmpdir do |dir|
|
Dir.mktmpdir do |dir|
|
||||||
ENV.delete(@var_name) unless ENV[@var_name].nil? # no env var
|
ENV.delete(@var_name) unless ENV[@var_name].nil? # no env var
|
||||||
ENV['HOME'] = dir # no config present in this tmp dir
|
ENV['HOME'] = dir # no config present in this tmp dir
|
||||||
c = Faraday.new do |b|
|
creds = Google::Auth.get_application_default(@scope, options)
|
||||||
b.adapter(:test, stubs)
|
|
||||||
end
|
|
||||||
creds = Google::Auth.get_application_default(
|
|
||||||
@scope,
|
|
||||||
connection: c)
|
|
||||||
expect(creds).to_not be_nil
|
expect(creds).to_not be_nil
|
||||||
end
|
end
|
||||||
stubs.verify_stubbed_calls
|
expect(stub).to have_been_requested
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'succeeds with system default file' do
|
it 'succeeds with system default file' do
|
||||||
|
@ -148,7 +137,8 @@ describe '#get_application_default' do
|
||||||
key_path = File.join('/etc/google/auth/', CREDENTIALS_FILE_NAME)
|
key_path = File.join('/etc/google/auth/', CREDENTIALS_FILE_NAME)
|
||||||
FileUtils.mkdir_p(File.dirname(key_path))
|
FileUtils.mkdir_p(File.dirname(key_path))
|
||||||
File.write(key_path, cred_json_text)
|
File.write(key_path, cred_json_text)
|
||||||
expect(Google::Auth.get_application_default(@scope)).to_not be_nil
|
expect(Google::Auth.get_application_default(@scope, options))
|
||||||
|
.to_not be_nil
|
||||||
File.delete(key_path)
|
File.delete(key_path)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -161,7 +151,8 @@ describe '#get_application_default' do
|
||||||
ENV[CLIENT_SECRET_VAR] = cred_json[:client_secret]
|
ENV[CLIENT_SECRET_VAR] = cred_json[:client_secret]
|
||||||
ENV[REFRESH_TOKEN_VAR] = cred_json[:refresh_token]
|
ENV[REFRESH_TOKEN_VAR] = cred_json[:refresh_token]
|
||||||
ENV[ACCOUNT_TYPE_VAR] = cred_json[:type]
|
ENV[ACCOUNT_TYPE_VAR] = cred_json[:type]
|
||||||
expect(Google::Auth.get_application_default(@scope)).to_not be_nil
|
expect(Google::Auth.get_application_default(@scope, options))
|
||||||
|
.to_not be_nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -225,7 +216,7 @@ describe '#get_application_default' do
|
||||||
File.write(key_path, cred_json_text)
|
File.write(key_path, cred_json_text)
|
||||||
ENV[@var_name] = key_path
|
ENV[@var_name] = key_path
|
||||||
blk = proc do
|
blk = proc do
|
||||||
Google::Auth.get_application_default(@scope)
|
Google::Auth.get_application_default(@scope, options)
|
||||||
end
|
end
|
||||||
expect(&blk).to raise_error RuntimeError
|
expect(&blk).to raise_error RuntimeError
|
||||||
end
|
end
|
||||||
|
@ -239,7 +230,7 @@ describe '#get_application_default' do
|
||||||
File.write(key_path, cred_json_text)
|
File.write(key_path, cred_json_text)
|
||||||
ENV['HOME'] = dir
|
ENV['HOME'] = dir
|
||||||
blk = proc do
|
blk = proc do
|
||||||
Google::Auth.get_application_default(@scope)
|
Google::Auth.get_application_default(@scope, options)
|
||||||
end
|
end
|
||||||
expect(&blk).to raise_error RuntimeError
|
expect(&blk).to raise_error RuntimeError
|
||||||
end
|
end
|
||||||
|
@ -249,7 +240,7 @@ describe '#get_application_default' do
|
||||||
ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]
|
ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]
|
||||||
ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
|
ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
|
||||||
blk = proc do
|
blk = proc do
|
||||||
Google::Auth.get_application_default(@scope)
|
Google::Auth.get_application_default(@scope, options)
|
||||||
end
|
end
|
||||||
expect(&blk).to raise_error RuntimeError
|
expect(&blk).to raise_error RuntimeError
|
||||||
end
|
end
|
||||||
|
|
|
@ -129,16 +129,21 @@ describe Google::Auth::ServiceAccountCredentials do
|
||||||
|
|
||||||
def make_auth_stubs(opts = {})
|
def make_auth_stubs(opts = {})
|
||||||
access_token = opts[:access_token] || ''
|
access_token = opts[:access_token] || ''
|
||||||
Faraday::Adapter::Test::Stubs.new do |stub|
|
body = MultiJson.dump('access_token' => access_token,
|
||||||
stub.post('/oauth2/v3/token') do |env|
|
'token_type' => 'Bearer',
|
||||||
params = Addressable::URI.form_unencode(env[:body])
|
'expires_in' => 3600)
|
||||||
_claim, _header = JWT.decode(params.assoc('assertion').last,
|
blk = proc do |request|
|
||||||
@key.public_key)
|
params = Addressable::URI.form_unencode(request.body)
|
||||||
want = ['grant_type', 'urn:ietf:params:oauth:grant-type:jwt-bearer']
|
_claim, _header = JWT.decode(params.assoc('assertion').last,
|
||||||
expect(params.assoc('grant_type')).to eq(want)
|
@key.public_key)
|
||||||
build_access_token_json(access_token)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
stub_request(:post, 'https://www.googleapis.com/oauth2/v3/token')
|
||||||
|
.with(body: hash_including(
|
||||||
|
'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer'),
|
||||||
|
&blk)
|
||||||
|
.to_return(body: body,
|
||||||
|
status: 200,
|
||||||
|
headers: { 'Content-Type' => 'application/json' })
|
||||||
end
|
end
|
||||||
|
|
||||||
def cred_json_text
|
def cred_json_text
|
||||||
|
|
|
@ -50,16 +50,21 @@ describe Signet::OAuth2::Client do
|
||||||
|
|
||||||
def make_auth_stubs(opts)
|
def make_auth_stubs(opts)
|
||||||
access_token = opts[:access_token] || ''
|
access_token = opts[:access_token] || ''
|
||||||
Faraday::Adapter::Test::Stubs.new do |stub|
|
body = MultiJson.dump('access_token' => access_token,
|
||||||
stub.post('/o/oauth2/token') do |env|
|
'token_type' => 'Bearer',
|
||||||
params = Addressable::URI.form_unencode(env[:body])
|
'expires_in' => 3600)
|
||||||
_claim, _header = JWT.decode(params.assoc('assertion').last,
|
blk = proc do |request|
|
||||||
@key.public_key)
|
params = Addressable::URI.form_unencode(request.body)
|
||||||
want = ['grant_type', 'urn:ietf:params:oauth:grant-type:jwt-bearer']
|
_claim, _header = JWT.decode(params.assoc('assertion').last,
|
||||||
expect(params.assoc('grant_type')).to eq(want)
|
@key.public_key)
|
||||||
build_access_token_json(access_token)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
stub_request(:post, 'https://accounts.google.com/o/oauth2/token')
|
||||||
|
.with(body: hash_including(
|
||||||
|
'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer'),
|
||||||
|
&blk)
|
||||||
|
.to_return(body: body,
|
||||||
|
status: 200,
|
||||||
|
headers: { 'Content-Type' => 'application/json' })
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'apply/apply! are OK'
|
it_behaves_like 'apply/apply! are OK'
|
||||||
|
|
|
@ -65,14 +65,14 @@ describe Google::Auth::UserRefreshCredentials do
|
||||||
|
|
||||||
def make_auth_stubs(opts = {})
|
def make_auth_stubs(opts = {})
|
||||||
access_token = opts[:access_token] || ''
|
access_token = opts[:access_token] || ''
|
||||||
Faraday::Adapter::Test::Stubs.new do |stub|
|
body = MultiJson.dump('access_token' => access_token,
|
||||||
stub.post('/oauth2/v3/token') do |env|
|
'token_type' => 'Bearer',
|
||||||
params = Addressable::URI.form_unencode(env[:body])
|
'expires_in' => 3600)
|
||||||
want = %w(grant_type refresh_token)
|
stub_request(:post, 'https://www.googleapis.com/oauth2/v3/token')
|
||||||
expect(params.assoc('grant_type')).to eq(want)
|
.with(body: hash_including('grant_type' => 'refresh_token'))
|
||||||
build_access_token_json(access_token)
|
.to_return(body: body,
|
||||||
end
|
status: 200,
|
||||||
end
|
headers: { 'Content-Type' => 'application/json' })
|
||||||
end
|
end
|
||||||
|
|
||||||
def cred_json_text(missing = nil)
|
def cred_json_text(missing = nil)
|
||||||
|
@ -244,70 +244,50 @@ describe Google::Auth::UserRefreshCredentials do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when revoking a refresh token' do
|
describe 'when revoking a refresh token' do
|
||||||
let(:stubs) do
|
let(:stub) do
|
||||||
Faraday::Adapter::Test::Stubs.new do |stub|
|
stub_request(:get, 'https://accounts.google.com/o/oauth2/revoke' \
|
||||||
stub.get('/o/oauth2/revoke') do |env|
|
'?token=refreshtoken')
|
||||||
expect(env.params['token']).to eql 'refreshtoken'
|
.to_return(status: 200,
|
||||||
[200]
|
headers: { 'Content-Type' => 'application/json' })
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:connection) do
|
|
||||||
Faraday.new do |c|
|
|
||||||
c.adapter(:test, stubs)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
before(:example) do
|
before(:example) do
|
||||||
@client.revoke!(connection: connection)
|
stub
|
||||||
|
@client.revoke!
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'revoked token'
|
it_behaves_like 'revoked token'
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when revoking an access token' do
|
describe 'when revoking an access token' do
|
||||||
let(:stubs) do
|
let(:stub) do
|
||||||
Faraday::Adapter::Test::Stubs.new do |stub|
|
stub_request(:get, 'https://accounts.google.com/o/oauth2/revoke' \
|
||||||
stub.get('/o/oauth2/revoke') do |env|
|
'?token=accesstoken')
|
||||||
expect(env.params['token']).to eql 'accesstoken'
|
.to_return(status: 200,
|
||||||
[200]
|
headers: { 'Content-Type' => 'application/json' })
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:connection) do
|
|
||||||
Faraday.new do |c|
|
|
||||||
c.adapter(:test, stubs)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
before(:example) do
|
before(:example) do
|
||||||
|
stub
|
||||||
@client.refresh_token = nil
|
@client.refresh_token = nil
|
||||||
@client.access_token = 'accesstoken'
|
@client.access_token = 'accesstoken'
|
||||||
@client.revoke!(connection: connection)
|
@client.revoke!
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'revoked token'
|
it_behaves_like 'revoked token'
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when revoking an invalid token' do
|
describe 'when revoking an invalid token' do
|
||||||
let(:stubs) do
|
let(:stub) do
|
||||||
Faraday::Adapter::Test::Stubs.new do |stub|
|
stub_request(:get, 'https://accounts.google.com/o/oauth2/revoke' \
|
||||||
stub.get('/o/oauth2/revoke') do |_env|
|
'?token=refreshtoken')
|
||||||
[400]
|
.to_return(status: 400,
|
||||||
end
|
headers: { 'Content-Type' => 'application/json' })
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:connection) do
|
|
||||||
Faraday.new do |c|
|
|
||||||
c.adapter(:test, stubs)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'raises an authorization error' do
|
it 'raises an authorization error' do
|
||||||
expect { @client.revoke!(connection: connection) }.to raise_error(
|
stub
|
||||||
|
expect { @client.revoke! }.to raise_error(
|
||||||
Signet::AuthorizationError)
|
Signet::AuthorizationError)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -39,7 +39,10 @@ $LOAD_PATH.uniq!
|
||||||
require 'simplecov'
|
require 'simplecov'
|
||||||
require 'coveralls'
|
require 'coveralls'
|
||||||
|
|
||||||
SimpleCov.formatter = Coveralls::SimpleCov::Formatter
|
SimpleCov.formatters = [
|
||||||
|
Coveralls::SimpleCov::Formatter,
|
||||||
|
SimpleCov::Formatter::HTMLFormatter
|
||||||
|
]
|
||||||
SimpleCov.start
|
SimpleCov.start
|
||||||
|
|
||||||
require 'faraday'
|
require 'faraday'
|
||||||
|
|
Loading…
Reference in New Issue