googleauth-0.8.0/ 0000755 0000041 0000041 00000000000 13437515753 013722 5 ustar www-data www-data googleauth-0.8.0/COPYING 0000644 0000041 0000041 00000026116 13437515753 014763 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 2015 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.
googleauth-0.8.0/.travis.yml 0000644 0000041 0000041 00000001320 13437515753 016027 0 ustar www-data www-data sudo: false
language: ruby
rvm:
- 2.5.1
- 2.4.4
- 2.3.7
- 2.2.10
- 2.1.10
- 2.0.0
- 1.9.3
- rbx-2
- jruby-9.1.9.0
matrix:
allow_failures:
- rvm: rbx-2 # See rubinius/rubinius#3485 - rubocop segfaults
script: "bundle exec rake"
addons:
apt:
packages:
- idn
- build-essential # this and below attempt allow rubinius to be setup ok
- bison
- ruby-dev
- rake zlib1g-dev
- libyaml-dev
- libssl-dev
- libreadline-dev
- libncurses5-dev
- llvm
- llvm-dev
- libeditline-dev
- libedit-dev
before_install:
- gem update bundler
notifications:
email:
recipients:
- ruby-cloud-eng@google.com
on_success: change
on_failure: change
googleauth-0.8.0/.rspec 0000644 0000041 0000041 00000000040 13437515753 015031 0 ustar www-data www-data --colour
--format documentation
googleauth-0.8.0/README.md 0000644 0000041 0000041 00000015002 13437515753 015177 0 ustar www-data www-data # Google Auth Library for Ruby
- Homepage
- http://www.github.com/google/google-auth-library-ruby
- Authors
- Tim Emiola
- Copyright
- Copyright © 2015 Google, Inc.
- License
- Apache 2.0
[](http://badge.fury.io/rb/googleauth)
[](http://travis-ci.org/google/google-auth-library-ruby)
[](https://coveralls.io/r/google/google-auth-library-ruby)
## Description
This is Google's officially supported ruby client library for using OAuth 2.0
authorization and authentication with Google APIs.
## Alpha
This library is in Alpha. We will make an effort to support the library, but
we reserve the right to make incompatible changes when necessary.
## Install
Be sure `https://rubygems.org/` is in your gem sources.
For normal client usage, this is sufficient:
```bash
$ gem install googleauth
```
## Example Usage
```ruby
require 'googleauth'
# Get the environment configured authorization
scopes = ['https://www.googleapis.com/auth/cloud-platform',
'https://www.googleapis.com/auth/compute']
authorization = Google::Auth.get_application_default(scopes)
# Add the the access token obtained using the authorization to a hash, e.g
# headers.
some_headers = {}
authorization.apply(some_headers)
```
## Application Default Credentials
This library provides an implementation of
[application default credentials][application default credentials] for Ruby.
The Application Default Credentials provide a simple way to get authorization
credentials for use in calling Google APIs.
They are best suited for cases when the call needs to have the same identity
and authorization level for the application independent of the user. This is
the recommended approach to authorize calls to Cloud APIs, particularly when
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 Identity 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(login_hint: 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'
OOB_URI = 'urn:ietf:wg:oauth:2.0:oob'
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: OOB_URI )
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
```
### Example (Service Account)
```ruby
scope = 'https://www.googleapis.com/auth/androidpublisher'
authorizer = Google::Auth::ServiceAccountCredentials.make_creds(
json_key_io: File.open('/path/to/service_account_json_key.json'),
scope: scope)
authorizer.fetch_access_token!
```
### Example (Environment Variables)
```bash
export GOOGLE_ACCOUNT_TYPE=service_account
export GOOGLE_CLIENT_ID=000000000000000000000
export GOOGLE_CLIENT_EMAIL=xxxx@xxxx.iam.gserviceaccount.com
export GOOGLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
```
### 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.
## 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.
## License
This library is licensed under Apache 2.0. Full license text is
available in [COPYING][copying].
## Contributing
See [CONTRIBUTING][contributing].
## Support
Please
[report bugs at the project on Github](https://github.com/google/google-auth-library-ruby/issues). Don't
hesitate to
[ask questions](http://stackoverflow.com/questions/tagged/google-auth-library-ruby)
about the client or APIs on [StackOverflow](http://stackoverflow.com).
[google-apis-ruby-client]: (https://github.com/google/google-api-ruby-client)
[application default credentials]: (https://developers.google.com/accounts/docs/application-default-credentials)
[contributing]: https://github.com/google/google-auth-library-ruby/tree/master/CONTRIBUTING.md
[copying]: https://github.com/google/google-auth-library-ruby/tree/master/COPYING
googleauth-0.8.0/spec/ 0000755 0000041 0000041 00000000000 13437515753 014654 5 ustar www-data www-data googleauth-0.8.0/spec/googleauth/ 0000755 0000041 0000041 00000000000 13437515753 017012 5 ustar www-data www-data googleauth-0.8.0/spec/googleauth/iam_spec.rb 0000644 0000041 0000041 00000006177 13437515753 021132 0 ustar www-data www-data # Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq!
require 'googleauth/iam'
describe Google::Auth::IAMCredentials do
IAMCredentials = Google::Auth::IAMCredentials
let(:test_selector) { 'the-test-selector' }
let(:test_token) { 'the-test-token' }
let(:test_creds) { IAMCredentials.new(test_selector, test_token) }
describe '#apply!' do
it 'should update the target hash with the iam values' do
md = { foo: 'bar' }
test_creds.apply!(md)
expect(md[IAMCredentials::SELECTOR_KEY]).to eq test_selector
expect(md[IAMCredentials::TOKEN_KEY]).to eq test_token
expect(md[:foo]).to eq 'bar'
end
end
describe 'updater_proc' do
it 'should provide a proc that updates a hash with the iam values' do
md = { foo: 'bar' }
the_proc = test_creds.updater_proc
got = the_proc.call(md)
expect(got[IAMCredentials::SELECTOR_KEY]).to eq test_selector
expect(got[IAMCredentials::TOKEN_KEY]).to eq test_token
expect(got[:foo]).to eq 'bar'
end
end
describe '#apply' do
it 'should not update the original hash with the iam values' do
md = { foo: 'bar' }
test_creds.apply(md)
expect(md[IAMCredentials::SELECTOR_KEY]).to be_nil
expect(md[IAMCredentials::TOKEN_KEY]).to be_nil
expect(md[:foo]).to eq 'bar'
end
it 'should return a with the iam values' do
md = { foo: 'bar' }
got = test_creds.apply(md)
expect(got[IAMCredentials::SELECTOR_KEY]).to eq test_selector
expect(got[IAMCredentials::TOKEN_KEY]).to eq test_token
expect(got[:foo]).to eq 'bar'
end
end
end
googleauth-0.8.0/spec/googleauth/user_refresh_spec.rb 0000644 0000041 0000041 00000027337 13437515753 023061 0 ustar www-data www-data # Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq!
require 'apply_auth_examples'
require 'fakefs/safe'
require 'fileutils'
require 'googleauth/user_refresh'
require 'jwt'
require 'multi_json'
require 'openssl'
require 'spec_helper'
require 'tmpdir'
require 'os'
include Google::Auth::CredentialsLoader
describe Google::Auth::UserRefreshCredentials do
UserRefreshCredentials = Google::Auth::UserRefreshCredentials
let(:cred_json) do
{
client_secret: 'privatekey',
client_id: 'client123',
refresh_token: 'refreshtoken',
type: 'authorized_user'
}
end
before(:example) do
@key = OpenSSL::PKey::RSA.new(2048)
@client = UserRefreshCredentials.make_creds(
json_key_io: StringIO.new(cred_json_text),
scope: 'https://www.googleapis.com/auth/userinfo.profile'
)
end
def make_auth_stubs(opts = {})
access_token = opts[:access_token] || ''
body = MultiJson.dump('access_token' => access_token,
'token_type' => 'Bearer',
'expires_in' => 3600)
stub_request(:post, 'https://oauth2.googleapis.com/token')
.with(body: hash_including('grant_type' => 'refresh_token'))
.to_return(body: body,
status: 200,
headers: { 'Content-Type' => 'application/json' })
end
def cred_json_text(missing = nil)
cred_json.delete(missing.to_sym) unless missing.nil?
MultiJson.dump(cred_json)
end
it_behaves_like 'apply/apply! are OK'
describe '#from_env' do
before(:example) do
@var_name = ENV_VAR
@credential_vars = [
ENV_VAR, CLIENT_ID_VAR, CLIENT_SECRET_VAR, REFRESH_TOKEN_VAR,
ACCOUNT_TYPE_VAR
]
@original_env_vals = {}
@credential_vars.each { |var| @original_env_vals[var] = ENV[var] }
@scope = 'https://www.googleapis.com/auth/userinfo.profile'
@clz = UserRefreshCredentials
@project_id = 'a_project_id'
end
after(:example) do
@credential_vars.each { |var| ENV[var] = @original_env_vals[var] }
end
it 'returns nil if the GOOGLE_APPLICATION_CREDENTIALS is unset' do
ENV.delete(@var_name) unless ENV[@var_name].nil?
expect(UserRefreshCredentials.from_env(@scope)).to be_nil
end
it 'fails if the GOOGLE_APPLICATION_CREDENTIALS path does not exist' do
ENV.delete(@var_name) unless ENV[@var_name].nil?
expect(UserRefreshCredentials.from_env(@scope)).to be_nil
Dir.mktmpdir do |dir|
key_path = File.join(dir, 'does-not-exist')
ENV[@var_name] = key_path
expect { @clz.from_env(@scope) }.to raise_error RuntimeError
end
end
it 'fails if the GOOGLE_APPLICATION_CREDENTIALS path file is invalid' do
needed = %w(client_id client_secret refresh_token)
needed.each do |missing|
Dir.mktmpdir do |dir|
key_path = File.join(dir, 'my_cert_file')
FileUtils.mkdir_p(File.dirname(key_path))
File.write(key_path, cred_json_text(missing))
ENV[@var_name] = key_path
expect { @clz.from_env(@scope) }.to raise_error RuntimeError
end
end
end
it 'succeeds when the GOOGLE_APPLICATION_CREDENTIALS file is valid' do
Dir.mktmpdir do |dir|
key_path = File.join(dir, 'my_cert_file')
FileUtils.mkdir_p(File.dirname(key_path))
File.write(key_path, cred_json_text)
ENV[@var_name] = key_path
expect(@clz.from_env(@scope)).to_not be_nil
end
end
it 'succeeds when GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, and '\
'GOOGLE_REFRESH_TOKEN env vars are valid' do
ENV[ENV_VAR] = nil
ENV[CLIENT_ID_VAR] = cred_json[:client_id]
ENV[CLIENT_SECRET_VAR] = cred_json[:client_secret]
ENV[REFRESH_TOKEN_VAR] = cred_json[:refresh_token]
ENV[ACCOUNT_TYPE_VAR] = cred_json[:type]
creds = @clz.from_env(@scope)
expect(creds).to_not be_nil
expect(creds.client_id).to eq(cred_json[:client_id])
expect(creds.client_secret).to eq(cred_json[:client_secret])
expect(creds.refresh_token).to eq(cred_json[:refresh_token])
end
it 'sets project_id when the PROJECT_ID_VAR env var is set' do
ENV[ENV_VAR] = nil
ENV[CLIENT_ID_VAR] = cred_json[:client_id]
ENV[CLIENT_SECRET_VAR] = cred_json[:client_secret]
ENV[REFRESH_TOKEN_VAR] = cred_json[:refresh_token]
ENV[ACCOUNT_TYPE_VAR] = cred_json[:type]
ENV[PROJECT_ID_VAR] = @project_id
creds = @clz.from_env(@scope)
expect(creds.project_id).to eq(@project_id)
end
end
describe '#from_well_known_path' do
before(:example) do
@home = ENV['HOME']
@app_data = ENV['APPDATA']
@scope = 'https://www.googleapis.com/auth/userinfo.profile'
@known_path = WELL_KNOWN_PATH
@clz = UserRefreshCredentials
end
after(:example) do
ENV['HOME'] = @home unless @home == ENV['HOME']
ENV['APPDATA'] = @app_data unless @app_data == ENV['APPDATA']
end
it 'is nil if no file exists' do
ENV['HOME'] = File.dirname(__FILE__)
expect(UserRefreshCredentials.from_well_known_path(@scope)).to be_nil
end
it 'fails if the file is invalid' do
needed = %w(client_id client_secret refresh_token)
needed.each do |missing|
Dir.mktmpdir do |dir|
key_path = File.join(dir, '.config', @known_path)
key_path = File.join(dir, @known_path) if OS.windows?
FileUtils.mkdir_p(File.dirname(key_path))
File.write(key_path, cred_json_text(missing))
ENV['HOME'] = dir
ENV['APPDATA'] = dir
expect { @clz.from_well_known_path(@scope) }
.to raise_error RuntimeError
end
end
end
it 'successfully loads the file when it is present' do
Dir.mktmpdir do |dir|
key_path = File.join(dir, '.config', @known_path)
key_path = File.join(dir, @known_path) if OS.windows?
FileUtils.mkdir_p(File.dirname(key_path))
File.write(key_path, cred_json_text)
ENV['HOME'] = dir
ENV['APPDATA'] = dir
expect(@clz.from_well_known_path(@scope)).to_not be_nil
end
end
it 'checks gcloud config for project_id if none was provided' do
Dir.mktmpdir do |dir|
key_path = File.join(dir, '.config', @known_path)
key_path = File.join(dir, @known_path) if OS.windows?
FileUtils.mkdir_p(File.dirname(key_path))
File.write(key_path, cred_json_text)
ENV['HOME'] = dir
ENV['APPDATA'] = dir
ENV[PROJECT_ID_VAR] = nil
expect(Google::Auth::CredentialsLoader).to receive(:load_gcloud_project_id).with(no_args)
@clz.from_well_known_path(@scope)
end
end
end
describe '#from_system_default_path' do
before(:example) do
@scope = 'https://www.googleapis.com/auth/userinfo.profile'
@prefix = OS.windows? ? '/etc/Google/Auth/' : '/etc/google/auth/'
@path = File.join(@prefix, CREDENTIALS_FILE_NAME)
@program_data = ENV['ProgramData']
@clz = UserRefreshCredentials
end
after(:example) do
ENV['ProgramData'] = @program_data
end
it 'is nil if no file exists' do
FakeFS do
expect(UserRefreshCredentials.from_system_default_path(@scope))
.to be_nil
end
end
it 'fails if the file is invalid' do
needed = %w(client_id client_secret refresh_token)
needed.each do |missing|
FakeFS do
ENV['ProgramData'] = '/etc'
FileUtils.mkdir_p(File.dirname(@path))
File.write(@path, cred_json_text(missing))
expect { @clz.from_system_default_path(@scope) }
.to raise_error RuntimeError
File.delete(@path)
end
end
end
it 'successfully loads the file when it is present' do
FakeFS do
ENV['ProgramData'] = '/etc'
FileUtils.mkdir_p(File.dirname(@path))
File.write(@path, cred_json_text)
expect(@clz.from_system_default_path(@scope)).to_not be_nil
File.delete(@path)
end
end
end
shared_examples 'revoked token' do
it 'should nil the refresh token' do
expect(@client.refresh_token).to be_nil
end
it 'should nil the access token' do
expect(@client.access_token).to be_nil
end
it 'should mark the token as expired' do
expect(@client.expired?).to be_truthy
end
end
describe 'when revoking a refresh token' do
let(:stub) do
stub_request(:post, 'https://oauth2.googleapis.com/revoke')
.with(body: hash_including('token' => 'refreshtoken'))
.to_return(status: 200,
headers: { 'Content-Type' => 'application/json' })
end
before(:example) do
stub
@client.revoke!
end
it_behaves_like 'revoked token'
end
describe 'when revoking an access token' do
let(:stub) do
stub_request(:post, 'https://oauth2.googleapis.com/revoke')
.with(body: hash_including('token' => 'accesstoken'))
.to_return(status: 200,
headers: { 'Content-Type' => 'application/json' })
end
before(:example) do
stub
@client.refresh_token = nil
@client.access_token = 'accesstoken'
@client.revoke!
end
it_behaves_like 'revoked token'
end
describe 'when revoking an invalid token' do
let(:stub) do
stub_request(:post, 'https://oauth2.googleapis.com/revoke')
.with(body: hash_including('token' => 'refreshtoken'))
.to_return(status: 400,
headers: { 'Content-Type' => 'application/json' })
end
it 'raises an authorization error' do
stub
expect { @client.revoke! }.to raise_error(
Signet::AuthorizationError
)
end
end
describe 'when errors occurred with request' do
it 'should fail with Signet::AuthorizationError if request times out' do
allow_any_instance_of(Faraday::Connection).to receive(:post)
.and_raise(Faraday::TimeoutError)
expect { @client.revoke! }
.to raise_error Signet::AuthorizationError
end
it 'should fail with Signet::AuthorizationError if request fails' do
allow_any_instance_of(Faraday::Connection).to receive(:post)
.and_raise(Faraday::ConnectionFailed, nil)
expect { @client.revoke! }
.to raise_error Signet::AuthorizationError
end
end
end
googleauth-0.8.0/spec/googleauth/apply_auth_examples.rb 0000644 0000041 0000041 00000011604 13437515753 023405 0 ustar www-data www-data # Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq!
require 'faraday'
require 'spec_helper'
shared_examples 'apply/apply! are OK' do
let(:auth_key) { :authorization }
# tests that use these examples need to define
#
# @client which should be an auth client
#
# @make_auth_stubs, which should stub out the expected http behaviour of the
# auth client
describe '#fetch_access_token' do
let(:token) { '1/abcdef1234567890' }
let(:stub) do
make_auth_stubs access_token: token
end
it 'should set access_token to the fetched value' do
stub
@client.fetch_access_token!
expect(@client.access_token).to eq(token)
expect(stub).to have_been_requested
end
it 'should notify refresh listeners after updating' do
stub
expect do |b|
@client.on_refresh(&b)
@client.fetch_access_token!
end.to yield_with_args(have_attributes(
access_token: '1/abcdef1234567890'
))
expect(stub).to have_been_requested
end
end
describe '#apply!' do
it 'should update the target hash with fetched access token' do
token = '1/abcdef1234567890'
stub = make_auth_stubs access_token: token
md = { foo: 'bar' }
@client.apply!(md)
want = { :foo => 'bar', auth_key => "Bearer #{token}" }
expect(md).to eq(want)
expect(stub).to have_been_requested
end
end
describe 'updater_proc' do
it 'should provide a proc that updates a hash with the access token' do
token = '1/abcdef1234567890'
stub = make_auth_stubs access_token: token
md = { foo: 'bar' }
the_proc = @client.updater_proc
got = the_proc.call(md)
want = { :foo => 'bar', auth_key => "Bearer #{token}" }
expect(got).to eq(want)
expect(stub).to have_been_requested
end
end
describe '#apply' do
it 'should not update the original hash with the access token' do
token = '1/abcdef1234567890'
stub = make_auth_stubs access_token: token
md = { foo: 'bar' }
@client.apply(md)
want = { foo: 'bar' }
expect(md).to eq(want)
expect(stub).to have_been_requested
end
it 'should add the token to the returned hash' do
token = '1/abcdef1234567890'
stub = make_auth_stubs access_token: token
md = { foo: 'bar' }
got = @client.apply(md)
want = { :foo => 'bar', auth_key => "Bearer #{token}" }
expect(got).to eq(want)
expect(stub).to have_been_requested
end
it 'should not fetch a new token if the current is not expired' do
token = '1/abcdef1234567890'
stub = make_auth_stubs access_token: token
n = 5 # arbitrary
n.times do |_t|
md = { foo: 'bar' }
got = @client.apply(md)
want = { :foo => 'bar', auth_key => "Bearer #{token}" }
expect(got).to eq(want)
end
expect(stub).to have_been_requested
end
it 'should fetch a new token if the current one is expired' do
token1 = '1/abcdef1234567890'
token2 = '2/abcdef1234567891'
[token1, token2].each do |t|
make_auth_stubs access_token: t
md = { foo: 'bar' }
got = @client.apply(md)
want = { :foo => 'bar', auth_key => "Bearer #{t}" }
expect(got).to eq(want)
@client.expires_at -= 3601 # default is to expire in 1hr
end
end
end
end
googleauth-0.8.0/spec/googleauth/signet_spec.rb 0000644 0000041 0000041 00000010125 13437515753 021641 0 ustar www-data www-data # Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq!
require 'apply_auth_examples'
require 'googleauth/signet'
require 'jwt'
require 'openssl'
require 'spec_helper'
describe Signet::OAuth2::Client do
before(:example) 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
def make_auth_stubs(opts)
access_token = opts[:access_token] || ''
body = MultiJson.dump('access_token' => access_token,
'token_type' => 'Bearer',
'expires_in' => 3600)
blk = proc do |request|
params = Addressable::URI.form_unencode(request.body)
_claim, _header = JWT.decode(params.assoc('assertion').last,
@key.public_key, true,
algorithm: 'RS256')
end
with_params = {body: hash_including(
"grant_type" => "urn:ietf:params:oauth:grant-type:jwt-bearer")}
if opts[:user_agent]
with_params[:headers] = {"User-Agent" => opts[:user_agent]}
end
stub_request(:post, 'https://oauth2.googleapis.com/token')
.with(with_params, &blk)
.to_return(body: body,
status: 200,
headers: { 'Content-Type' => 'application/json' })
end
it_behaves_like 'apply/apply! are OK'
describe "#configure_connection" do
it "honors default_connection" do
token = "1/abcdef1234567890"
stub = make_auth_stubs access_token: token, user_agent: "RubyRocks/1.0"
conn = Faraday.new headers: {"User-Agent" => "RubyRocks/1.0"}
@client.configure_connection(default_connection: conn)
md = { foo: "bar" }
@client.apply!(md)
want = { foo: "bar", authorization: "Bearer #{token}" }
expect(md).to eq(want)
expect(stub).to have_been_requested
end
it "honors connection_builder" do
token = "1/abcdef1234567890"
stub = make_auth_stubs access_token: token, user_agent: "RubyRocks/2.0"
connection_builder = proc do
Faraday.new headers: {"User-Agent" => "RubyRocks/2.0"}
end
@client.configure_connection(connection_builder: connection_builder)
md = { foo: "bar" }
@client.apply!(md)
want = { foo: "bar", authorization: "Bearer #{token}" }
expect(md).to eq(want)
expect(stub).to have_been_requested
end
end
end
googleauth-0.8.0/spec/googleauth/client_id_spec.rb 0000644 0000041 0000041 00000010677 13437515753 022316 0 ustar www-data www-data # Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq!
require 'spec_helper'
require 'fakefs/safe'
require 'googleauth'
describe Google::Auth::ClientId do
shared_examples 'it has a valid config' do
it 'should include a valid id' do
expect(client_id.id).to eql 'abc@example.com'
end
it 'should include a valid secret' do
expect(client_id.secret).to eql 'notasecret'
end
end
shared_examples 'it can successfully load client_id' do
context 'loaded from hash' do
let(:client_id) { Google::Auth::ClientId.from_hash config }
it_behaves_like 'it has a valid config'
end
context 'loaded from file' do
file_path = '/client_secrets.json'
let(:client_id) do
FakeFS do
content = MultiJson.dump(config)
File.write(file_path, content)
Google::Auth::ClientId.from_file(file_path)
end
end
it_behaves_like 'it has a valid config'
end
end
describe 'with web config' do
let(:config) do
{
'web' => {
'client_id' => 'abc@example.com',
'client_secret' => 'notasecret'
}
}
end
it_behaves_like 'it can successfully load client_id'
end
describe 'with installed app config' do
let(:config) do
{
'installed' => {
'client_id' => 'abc@example.com',
'client_secret' => 'notasecret'
}
}
end
it_behaves_like 'it can successfully load client_id'
end
context 'with missing top level property' do
let(:config) do
{
'notvalid' => {
'client_id' => 'abc@example.com',
'client_secret' => 'notasecret'
}
}
end
it 'should raise error' do
expect { Google::Auth::ClientId.from_hash config }.to raise_error(
/Expected top level property/
)
end
end
context 'with missing client id' do
let(:config) do
{
'web' => {
'client_secret' => 'notasecret'
}
}
end
it 'should raise error' do
expect { Google::Auth::ClientId.from_hash config }.to raise_error(
/Client id can not be nil/
)
end
end
context 'with missing client secret' do
let(:config) do
{
'web' => {
'client_id' => 'abc@example.com'
}
}
end
it 'should raise error' do
expect { Google::Auth::ClientId.from_hash config }.to raise_error(
/Client secret can not be nil/
)
end
end
context 'with cloud sdk credentials' do
let(:config) do
{
'web' => {
'client_id' => Google::Auth::CredentialsLoader::CLOUD_SDK_CLIENT_ID,
'client_secret' => 'notasecret'
}
}
end
it 'should raise warning' do
expect { Google::Auth::ClientId.from_hash config }.to output(
Google::Auth::CredentialsLoader::CLOUD_SDK_CREDENTIALS_WARNING + "\n"
).to_stderr
end
end
end
googleauth-0.8.0/spec/googleauth/user_authorizer_spec.rb 0000644 0000041 0000041 00000024076 13437515753 023614 0 ustar www-data www-data # Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq!
require 'googleauth'
require 'googleauth/user_authorizer'
require 'uri'
require 'multi_json'
require 'spec_helper'
describe Google::Auth::UserAuthorizer do
include TestHelpers
let(:client_id) { Google::Auth::ClientId.new('testclient', 'notasecret') }
let(:scope) { %w(email profile) }
let(:token_store) { DummyTokenStore.new }
let(:callback_uri) { 'https://www.example.com/oauth/callback' }
let(:authorizer) do
Google::Auth::UserAuthorizer.new(client_id,
scope,
token_store,
callback_uri)
end
shared_examples 'valid authorization url' do
it 'should have a valid base URI' do
expect(uri).to match %r{https://accounts.google.com/o/oauth2/auth}
end
it 'should request offline access' do
expect(URI(uri).query).to match(/access_type=offline/)
end
it 'should request response type code' do
expect(URI(uri).query).to match(/response_type=code/)
end
it 'should force approval' do
expect(URI(uri).query).to match(/approval_prompt=force/)
end
it 'should include granted scopes' do
expect(URI(uri).query).to match(/include_granted_scopes=true/)
end
it 'should include the correct client id' do
expect(URI(uri).query).to match(/client_id=testclient/)
end
it 'should not include a client secret' do
expect(URI(uri).query).to_not match(/client_secret/)
end
it 'should include the callback uri' do
expect(URI(uri).query).to match(
%r{redirect_uri=https://www.example.com/oauth/callback}
)
end
it 'should include the scope' do
expect(URI(uri).query).to match(/scope=email%20profile/)
end
end
context 'when generating authorization URLs with user ID & state' do
let(:uri) do
authorizer.get_authorization_url(login_hint: 'user1', state: 'mystate')
end
it_behaves_like 'valid authorization url'
it 'includes a login hint' do
expect(URI(uri).query).to match(/login_hint=user1/)
end
it 'includes the app state' do
expect(URI(uri).query).to match(/state=mystate/)
end
end
context 'when generating authorization URLs with user ID and no state' do
let(:uri) { authorizer.get_authorization_url(login_hint: 'user1') }
it_behaves_like 'valid authorization url'
it 'includes a login hint' do
expect(URI(uri).query).to match(/login_hint=user1/)
end
it 'does not include the state parameter' do
expect(URI(uri).query).to_not match(/state/)
end
end
context 'when generating authorization URLs with no user ID and no state' do
let(:uri) { authorizer.get_authorization_url }
it_behaves_like 'valid authorization url'
it 'does not include the login hint parameter' do
expect(URI(uri).query).to_not match(/login_hint/)
end
it 'does not include the state parameter' do
expect(URI(uri).query).to_not match(/state/)
end
end
context 'when retrieving tokens' do
let(:token_json) do
MultiJson.dump(
access_token: 'accesstoken',
refresh_token: 'refreshtoken',
expiration_time_millis: 1_441_234_742_000
)
end
context 'with a valid user id' do
let(:credentials) do
token_store.store('user1', token_json)
authorizer.get_credentials('user1')
end
it 'should return an instance of UserRefreshCredentials' do
expect(credentials).to be_instance_of(
Google::Auth::UserRefreshCredentials
)
end
it 'should return credentials with a valid refresh token' do
expect(credentials.refresh_token).to eq 'refreshtoken'
end
it 'should return credentials with a valid access token' do
expect(credentials.access_token).to eq 'accesstoken'
end
it 'should return credentials with a valid client ID' do
expect(credentials.client_id).to eq 'testclient'
end
it 'should return credentials with a valid client secret' do
expect(credentials.client_secret).to eq 'notasecret'
end
it 'should return credentials with a valid scope' do
expect(credentials.scope).to eq %w(email profile)
end
it 'should return credentials with a valid expiration time' do
expect(credentials.expires_at).to eq Time.at(1_441_234_742)
end
end
context 'with an invalid user id' do
it 'should return nil' do
expect(authorizer.get_credentials('notauser')).to be_nil
end
end
end
context 'when saving tokens' do
let(:expiry) { Time.now.to_i }
let(:credentials) do
Google::Auth::UserRefreshCredentials.new(
client_id: client_id.id,
client_secret: client_id.secret,
scope: scope,
refresh_token: 'refreshtoken',
access_token: 'accesstoken',
expires_at: expiry
)
end
let(:token_json) do
authorizer.store_credentials('user1', credentials)
token_store.load('user1')
end
it 'should persist in the token store' do
expect(token_json).to_not be_nil
end
it 'should persist the refresh token' do
expect(MultiJson.load(token_json)['refresh_token']).to eq 'refreshtoken'
end
it 'should persist the access token' do
expect(MultiJson.load(token_json)['access_token']).to eq 'accesstoken'
end
it 'should persist the client id' do
expect(MultiJson.load(token_json)['client_id']).to eq 'testclient'
end
it 'should persist the scope' do
expect(MultiJson.load(token_json)['scope']).to include('email', 'profile')
end
it 'should persist the expiry as milliseconds' do
expected_expiry = expiry * 1000
expect(MultiJson.load(token_json)['expiration_time_millis']).to eql(
expected_expiry
)
end
end
context 'with valid authorization code' do
let(:token_json) do
MultiJson.dump('access_token' => '1/abc123',
'token_type' => 'Bearer',
'expires_in' => 3600)
end
before(:example) do
stub_request(:post, 'https://oauth2.googleapis.com/token')
.to_return(body: token_json, status: 200, headers: {
'Content-Type' => 'application/json'
})
end
it 'should exchange a code for credentials' do
credentials = authorizer.get_credentials_from_code(
user_id: 'user1', code: 'code'
)
expect(credentials.access_token).to eq '1/abc123'
end
it 'should not store credentials when get only requested' do
authorizer.get_credentials_from_code(user_id: 'user1', code: 'code')
expect(token_store.load('user1')).to be_nil
end
it 'should store credentials when requested' do
authorizer.get_and_store_credentials_from_code(
user_id: 'user1', code: 'code'
)
expect(token_store.load('user1')).to_not be_nil
end
end
context 'with invalid authorization code' do
before(:example) do
stub_request(:post, 'https://oauth2.googleapis.com/token')
.to_return(status: 400)
end
it 'should raise an authorization error' do
expect do
authorizer.get_credentials_from_code(user_id: 'user1', code: 'badcode')
end.to raise_error Signet::AuthorizationError
end
it 'should not store credentials when exchange fails' do
expect do
authorizer.get_credentials_from_code(user_id: 'user1', code: 'badcode')
end.to raise_error Signet::AuthorizationError
expect(token_store.load('user1')).to be_nil
end
end
context 'when reovking authorization' do
let(:token_json) do
MultiJson.dump(
access_token: 'accesstoken',
refresh_token: 'refreshtoken',
expiration_time_millis: 1_441_234_742_000
)
end
before(:example) do
token_store.store('user1', token_json)
stub_request(:post, 'https://oauth2.googleapis.com/revoke')
.with(body: hash_including('token' => 'refreshtoken'))
.to_return(status: 200)
end
it 'should revoke the grant' do
authorizer.revoke_authorization('user1')
expect(a_request(
:post, 'https://oauth2.googleapis.com/revoke'
).with(body: hash_including('token' => 'refreshtoken'))
)
.to have_been_made
end
it 'should remove the token from storage' do
authorizer.revoke_authorization('user1')
expect(token_store.load('user1')).to be_nil
end
end
# TODO: - Test that tokens are monitored
# TODO - Test scope enforcement (auth if upgrade required)
end
googleauth-0.8.0/spec/googleauth/stores/ 0000755 0000041 0000041 00000000000 13437515753 020331 5 ustar www-data www-data googleauth-0.8.0/spec/googleauth/stores/redis_token_store_spec.rb 0000644 0000041 0000041 00000003726 13437515753 025422 0 ustar www-data www-data # Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq!
require 'googleauth'
require 'googleauth/stores/redis_token_store'
require 'spec_helper'
require 'fakeredis/rspec'
require 'googleauth/stores/store_examples'
describe Google::Auth::Stores::RedisTokenStore do
let(:redis) do
Redis.new
end
let(:store) do
Google::Auth::Stores::RedisTokenStore.new(redis: redis)
end
it_behaves_like 'token store'
end
googleauth-0.8.0/spec/googleauth/stores/store_examples.rb 0000644 0000041 0000041 00000004254 13437515753 023715 0 ustar www-data www-data # Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq!
require 'spec_helper'
shared_examples 'token store' do
before(:each) do
store.store('default', 'test')
end
it 'should return a stored value' do
expect(store.load('default')).to eq 'test'
end
it 'should return nil for missing tokens' do
expect(store.load('notavalidkey')).to be_nil
end
it 'should return nil for deleted tokens' do
store.delete('default')
expect(store.load('default')).to be_nil
end
it 'should save overwrite values on store' do
store.store('default', 'test2')
expect(store.load('default')).to eq 'test2'
end
end
googleauth-0.8.0/spec/googleauth/stores/file_token_store_spec.rb 0000644 0000041 0000041 00000004213 13437515753 025223 0 ustar www-data www-data # Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq!
require 'googleauth'
require 'googleauth/stores/file_token_store'
require 'spec_helper'
require 'fakefs/safe'
require 'fakefs/spec_helpers'
require 'googleauth/stores/store_examples'
module FakeFS
class File
# FakeFS doesn't implement. And since we don't need to actually lock,
# just stub out...
def flock(*); end
end
end
describe Google::Auth::Stores::FileTokenStore do
include FakeFS::SpecHelpers
let(:store) do
Google::Auth::Stores::FileTokenStore.new(file: '/tokens.yaml')
end
it_behaves_like 'token store'
end
googleauth-0.8.0/spec/googleauth/compute_engine_spec.rb 0000644 0000041 0000041 00000011574 13437515753 023362 0 ustar www-data www-data # Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq!
require 'apply_auth_examples'
require 'faraday'
require 'googleauth/compute_engine'
require 'spec_helper'
describe Google::Auth::GCECredentials do
MD_URI = 'http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token'.freeze
GCECredentials = Google::Auth::GCECredentials
before(:example) do
@client = GCECredentials.new
end
def make_auth_stubs(opts = {})
access_token = opts[:access_token] || ''
body = MultiJson.dump('access_token' => access_token,
'token_type' => 'Bearer',
'expires_in' => 3600)
stub_request(:get, MD_URI)
.with(headers: { 'Metadata-Flavor' => 'Google' })
.to_return(body: body,
status: 200,
headers: { 'Content-Type' => 'application/json' })
end
it_behaves_like 'apply/apply! are OK'
context 'metadata is unavailable' do
describe '#fetch_access_token' do
it 'should fail if the metadata request returns a 404' do
stub = stub_request(:get, MD_URI)
.to_return(status: 404,
headers: { 'Metadata-Flavor' => 'Google' })
expect { @client.fetch_access_token! }
.to raise_error Signet::AuthorizationError
expect(stub).to have_been_requested
end
it 'should fail if the metadata request returns an unexpected code' do
stub = stub_request(:get, MD_URI)
.to_return(status: 503,
headers: { 'Metadata-Flavor' => 'Google' })
expect { @client.fetch_access_token! }
.to raise_error Signet::AuthorizationError
expect(stub).to have_been_requested
end
it 'should fail with Signet::AuthorizationError if request times out' do
allow_any_instance_of(Faraday::Connection).to receive(:get)
.and_raise(Faraday::TimeoutError)
expect { @client.fetch_access_token! }
.to raise_error Signet::AuthorizationError
end
it 'should fail with Signet::AuthorizationError if request fails' do
allow_any_instance_of(Faraday::Connection).to receive(:get)
.and_raise(Faraday::ConnectionFailed, nil)
expect { @client.fetch_access_token! }
.to raise_error Signet::AuthorizationError
end
end
end
describe '#on_gce?' do
it 'should be true when Metadata-Flavor is Google' do
stub = stub_request(:get, 'http://169.254.169.254')
.to_return(status: 200,
headers: { 'Metadata-Flavor' => 'Google' })
expect(GCECredentials.on_gce?({}, true)).to eq(true)
expect(stub).to have_been_requested
end
it 'should be false when Metadata-Flavor is not Google' do
stub = stub_request(:get, 'http://169.254.169.254')
.to_return(status: 200,
headers: { 'Metadata-Flavor' => 'NotGoogle' })
expect(GCECredentials.on_gce?({}, true)).to eq(false)
expect(stub).to have_been_requested
end
it 'should be false if the response is not 200' do
stub = stub_request(:get, 'http://169.254.169.254')
.to_return(status: 404,
headers: { 'Metadata-Flavor' => 'NotGoogle' })
expect(GCECredentials.on_gce?({}, true)).to eq(false)
expect(stub).to have_been_requested
end
end
end
googleauth-0.8.0/spec/googleauth/get_application_default_spec.rb 0000644 0000041 0000041 00000024745 13437515753 025233 0 ustar www-data www-data # Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq!
require 'faraday'
require 'fakefs/safe'
require 'googleauth'
require 'spec_helper'
require 'os'
describe '#get_application_default' do
# Pass unique options each time to bypass memoization
let(:options) { |example| { dememoize: example } }
before(:example) do
@key = OpenSSL::PKey::RSA.new(2048)
@var_name = ENV_VAR
@credential_vars = [
ENV_VAR, PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR, CLIENT_ID_VAR,
CLIENT_SECRET_VAR, REFRESH_TOKEN_VAR, ACCOUNT_TYPE_VAR
]
@original_env_vals = {}
@credential_vars.each { |var| @original_env_vals[var] = ENV[var] }
@home = ENV['HOME']
@app_data = ENV['APPDATA']
@program_data = ENV['ProgramData']
@scope = 'https://www.googleapis.com/auth/userinfo.profile'
end
after(:example) do
@credential_vars.each { |var| ENV[var] = @original_env_vals[var] }
ENV['HOME'] = @home unless @home == ENV['HOME']
ENV['APPDATA'] = @app_data unless @app_data == ENV['APPDATA']
ENV['ProgramData'] = @program_data unless @program_data == ENV['ProgramData']
end
shared_examples 'it cannot load misconfigured credentials' do
it 'fails if the GOOGLE_APPLICATION_CREDENTIALS path does not exist' do
Dir.mktmpdir do |dir|
key_path = File.join(dir, 'does-not-exist')
ENV[@var_name] = key_path
expect { Google::Auth.get_application_default @scope, options }
.to raise_error RuntimeError
end
end
it 'fails without default file or env if not on compute engine' do
stub = stub_request(:get, 'http://169.254.169.254')
.to_return(status: 404,
headers: { 'Metadata-Flavor' => 'NotGoogle' })
Dir.mktmpdir do |dir|
ENV.delete(@var_name) unless ENV[@var_name].nil? # no env var
ENV['HOME'] = dir # no config present in this tmp dir
expect do
Google::Auth.get_application_default @scope, options
end.to raise_error RuntimeError
end
expect(stub).to have_been_requested
end
end
shared_examples 'it can successfully load credentials' do
it 'succeeds if the GOOGLE_APPLICATION_CREDENTIALS file is valid' do
Dir.mktmpdir do |dir|
key_path = File.join(dir, 'my_cert_file')
FileUtils.mkdir_p(File.dirname(key_path))
File.write(key_path, cred_json_text)
ENV[@var_name] = key_path
expect(Google::Auth.get_application_default(@scope, options))
.to_not be_nil
end
end
it "propagates default_connection option" do
Dir.mktmpdir do |dir|
key_path = File.join(dir, 'my_cert_file')
FileUtils.mkdir_p(File.dirname(key_path))
File.write(key_path, cred_json_text)
ENV[@var_name] = key_path
connection = Faraday.new(headers: {"User-Agent" => "hello"})
opts = options.merge(default_connection: connection)
creds = Google::Auth.get_application_default(@scope, opts)
expect(creds.build_default_connection).to be connection
end
end
it 'succeeds with default file without GOOGLE_APPLICATION_CREDENTIALS' do
ENV.delete(@var_name) unless ENV[@var_name].nil?
Dir.mktmpdir do |dir|
key_path = File.join(dir, '.config', WELL_KNOWN_PATH)
key_path = File.join(dir, WELL_KNOWN_PATH) if OS.windows?
FileUtils.mkdir_p(File.dirname(key_path))
File.write(key_path, cred_json_text)
ENV['HOME'] = dir
ENV['APPDATA'] = dir
expect(Google::Auth.get_application_default(@scope, options))
.to_not be_nil
end
end
it 'succeeds with default file without a scope' do
ENV.delete(@var_name) unless ENV[@var_name].nil?
Dir.mktmpdir do |dir|
key_path = File.join(dir, '.config', WELL_KNOWN_PATH)
key_path = File.join(dir, WELL_KNOWN_PATH) if OS.windows?
FileUtils.mkdir_p(File.dirname(key_path))
File.write(key_path, cred_json_text)
ENV['HOME'] = dir
ENV['APPDATA'] = dir
expect(Google::Auth.get_application_default(nil, options)).to_not be_nil
end
end
it 'succeeds without default file or env if on compute engine' do
stub = stub_request(:get, 'http://169.254.169.254')
.to_return(status: 200,
headers: { 'Metadata-Flavor' => 'Google' })
Dir.mktmpdir do |dir|
ENV.delete(@var_name) unless ENV[@var_name].nil? # no env var
ENV['HOME'] = dir # no config present in this tmp dir
creds = Google::Auth.get_application_default @scope, options
expect(creds).to_not be_nil
end
expect(stub).to have_been_requested
end
it 'succeeds with system default file' do
ENV.delete(@var_name) unless ENV[@var_name].nil?
FakeFS do
ENV['ProgramData'] = '/etc'
prefix = OS.windows? ? '/etc/Google/Auth/' : '/etc/google/auth/'
key_path = File.join(prefix, CREDENTIALS_FILE_NAME)
FileUtils.mkdir_p(File.dirname(key_path))
File.write(key_path, cred_json_text)
expect(Google::Auth.get_application_default(@scope, options))
.to_not be_nil
File.delete(key_path)
end
end
it 'succeeds if environment vars are valid' do
ENV.delete(@var_name) unless ENV[@var_name].nil? # no env var
ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]
ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
ENV[CLIENT_ID_VAR] = cred_json[:client_id]
ENV[CLIENT_SECRET_VAR] = cred_json[:client_secret]
ENV[REFRESH_TOKEN_VAR] = cred_json[:refresh_token]
ENV[ACCOUNT_TYPE_VAR] = cred_json[:type]
expect(Google::Auth.get_application_default(@scope, options))
.to_not be_nil
end
it 'warns when using cloud sdk credentials' do
ENV.delete(@var_name) unless ENV[@var_name].nil? # no env var
ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]
ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
ENV[CLIENT_ID_VAR] = Google::Auth::CredentialsLoader::CLOUD_SDK_CLIENT_ID
ENV[CLIENT_SECRET_VAR] = cred_json[:client_secret]
ENV[REFRESH_TOKEN_VAR] = cred_json[:refresh_token]
ENV[ACCOUNT_TYPE_VAR] = cred_json[:type]
ENV[PROJECT_ID_VAR] = 'a_project_id'
expect { Google::Auth.get_application_default @scope, options }.to output(
Google::Auth::CredentialsLoader::CLOUD_SDK_CREDENTIALS_WARNING + "\n"
).to_stderr
end
end
describe 'when credential type is service account' do
let(:cred_json) do
{
private_key_id: 'a_private_key_id',
private_key: @key.to_pem,
client_email: 'app@developer.gserviceaccount.com',
client_id: 'app.apps.googleusercontent.com',
type: 'service_account'
}
end
def cred_json_text
MultiJson.dump(cred_json)
end
it_behaves_like 'it can successfully load credentials'
it_behaves_like 'it cannot load misconfigured credentials'
end
describe 'when credential type is authorized_user' do
let(:cred_json) do
{
client_secret: 'privatekey',
refresh_token: 'refreshtoken',
client_id: 'app.apps.googleusercontent.com',
type: 'authorized_user'
}
end
def cred_json_text
MultiJson.dump(cred_json)
end
it_behaves_like 'it can successfully load credentials'
it_behaves_like 'it cannot load misconfigured credentials'
end
describe 'when credential type is unknown' do
let(:cred_json) do
{
client_secret: 'privatekey',
refresh_token: 'refreshtoken',
client_id: 'app.apps.googleusercontent.com',
private_key: @key.to_pem,
client_email: 'app@developer.gserviceaccount.com',
type: 'not_known_type'
}
end
def cred_json_text
MultiJson.dump(cred_json)
end
it 'fails if the GOOGLE_APPLICATION_CREDENTIALS file contains the creds' do
Dir.mktmpdir do |dir|
key_path = File.join(dir, 'my_cert_file')
FileUtils.mkdir_p(File.dirname(key_path))
File.write(key_path, cred_json_text)
ENV[@var_name] = key_path
expect do
Google::Auth.get_application_default @scope, options
end.to raise_error RuntimeError
end
end
it 'fails if the well known file contains the creds' do
ENV.delete(@var_name) unless ENV[@var_name].nil?
Dir.mktmpdir do |dir|
key_path = File.join(dir, '.config', WELL_KNOWN_PATH)
key_path = File.join(dir, WELL_KNOWN_PATH) if OS.windows?
FileUtils.mkdir_p(File.dirname(key_path))
File.write(key_path, cred_json_text)
ENV['HOME'] = dir
ENV['APPDATA'] = dir
expect do
Google::Auth.get_application_default @scope, options
end.to raise_error RuntimeError
end
end
it 'fails if env vars are set' do
ENV[ENV_VAR] = nil
ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]
ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
expect do
Google::Auth.get_application_default @scope, options
end.to raise_error RuntimeError
end
end
end
googleauth-0.8.0/spec/googleauth/service_account_spec.rb 0000644 0000041 0000041 00000037525 13437515753 023541 0 ustar www-data www-data # Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq!
require 'apply_auth_examples'
require 'fakefs/safe'
require 'fileutils'
require 'googleauth/service_account'
require 'jwt'
require 'multi_json'
require 'openssl'
require 'spec_helper'
require 'tmpdir'
require 'os'
include Google::Auth::CredentialsLoader
shared_examples 'jwt header auth' do
context 'when jwt_aud_uri is present' do
let(:test_uri) { 'https://www.googleapis.com/myservice' }
let(:auth_prefix) { 'Bearer ' }
let(:auth_key) { ServiceAccountJwtHeaderCredentials::AUTH_METADATA_KEY }
let(:jwt_uri_key) { ServiceAccountJwtHeaderCredentials::JWT_AUD_URI_KEY }
def expect_is_encoded_jwt(hdr)
expect(hdr).to_not be_nil
expect(hdr.start_with?(auth_prefix)).to be true
authorization = hdr[auth_prefix.length..-1]
payload, = JWT.decode(authorization, @key.public_key, true, algorithm: 'RS256')
expect(payload['aud']).to eq(test_uri)
expect(payload['iss']).to eq(client_email)
end
describe '#apply!' do
it 'should update the target hash with a jwt token' do
md = { foo: 'bar' }
md[jwt_uri_key] = test_uri
@client.apply!(md)
auth_header = md[auth_key]
expect_is_encoded_jwt(auth_header)
expect(md[jwt_uri_key]).to be_nil
end
end
describe 'updater_proc' do
it 'should provide a proc that updates a hash with a jwt token' do
md = { foo: 'bar' }
md[jwt_uri_key] = test_uri
the_proc = @client.updater_proc
got = the_proc.call(md)
auth_header = got[auth_key]
expect_is_encoded_jwt(auth_header)
expect(got[jwt_uri_key]).to be_nil
expect(md[jwt_uri_key]).to_not be_nil
end
end
describe '#apply' do
it 'should not update the original hash with a jwt token' do
md = { foo: 'bar' }
md[jwt_uri_key] = test_uri
the_proc = @client.updater_proc
got = the_proc.call(md)
auth_header = md[auth_key]
expect(auth_header).to be_nil
expect(got[jwt_uri_key]).to be_nil
expect(md[jwt_uri_key]).to_not be_nil
end
it 'should add a jwt token to the returned hash' do
md = { foo: 'bar' }
md[jwt_uri_key] = test_uri
got = @client.apply(md)
auth_header = got[auth_key]
expect_is_encoded_jwt(auth_header)
end
end
end
end
describe Google::Auth::ServiceAccountCredentials do
ServiceAccountCredentials = Google::Auth::ServiceAccountCredentials
let(:client_email) { 'app@developer.gserviceaccount.com' }
let(:cred_json) do
{
private_key_id: 'a_private_key_id',
private_key: @key.to_pem,
client_email: client_email,
client_id: 'app.apps.googleusercontent.com',
type: 'service_account',
project_id: 'a_project_id'
}
end
before(:example) do
@key = OpenSSL::PKey::RSA.new(2048)
@client = ServiceAccountCredentials.make_creds(
json_key_io: StringIO.new(cred_json_text),
scope: 'https://www.googleapis.com/auth/userinfo.profile'
)
end
def make_auth_stubs(opts = {})
access_token = opts[:access_token] || ''
body = MultiJson.dump('access_token' => access_token,
'token_type' => 'Bearer',
'expires_in' => 3600)
blk = proc do |request|
params = Addressable::URI.form_unencode(request.body)
_claim, _header = JWT.decode(params.assoc('assertion').last,
@key.public_key, true,
algorithm: 'RS256')
end
stub_request(:post, 'https://www.googleapis.com/oauth2/v4/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
def cred_json_text
MultiJson.dump(cred_json)
end
it_behaves_like 'apply/apply! are OK'
context 'when scope is nil' do
before(:example) do
@client.scope = nil
end
it_behaves_like 'jwt header auth'
end
describe '#from_env' do
before(:example) do
@var_name = ENV_VAR
@credential_vars = [
ENV_VAR, PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR, ACCOUNT_TYPE_VAR
]
@original_env_vals = {}
@credential_vars.each { |var| @original_env_vals[var] = ENV[var] }
ENV[ACCOUNT_TYPE_VAR] = cred_json[:type]
@scope = 'https://www.googleapis.com/auth/userinfo.profile'
@clz = ServiceAccountCredentials
end
after(:example) do
@credential_vars.each { |var| ENV[var] = @original_env_vals[var] }
end
it 'returns nil if the GOOGLE_APPLICATION_CREDENTIALS is unset' do
ENV.delete(@var_name) unless ENV[@var_name].nil?
expect(ServiceAccountCredentials.from_env(@scope)).to be_nil
end
it 'fails if the GOOGLE_APPLICATION_CREDENTIALS path does not exist' do
ENV.delete(@var_name) unless ENV[@var_name].nil?
expect(ServiceAccountCredentials.from_env(@scope)).to be_nil
Dir.mktmpdir do |dir|
key_path = File.join(dir, 'does-not-exist')
ENV[@var_name] = key_path
expect { @clz.from_env(@scope) }.to raise_error RuntimeError
end
end
it 'succeeds when the GOOGLE_APPLICATION_CREDENTIALS file is valid' do
Dir.mktmpdir do |dir|
key_path = File.join(dir, 'my_cert_file')
FileUtils.mkdir_p(File.dirname(key_path))
File.write(key_path, cred_json_text)
ENV[@var_name] = key_path
expect(@clz.from_env(@scope)).to_not be_nil
end
end
it 'succeeds when GOOGLE_PRIVATE_KEY and GOOGLE_CLIENT_EMAIL env vars are'\
' valid' do
ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]
ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
expect(@clz.from_env(@scope)).to_not be_nil
end
it 'sets project_id when the PROJECT_ID_VAR env var is set' do
ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]
ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
ENV[PROJECT_ID_VAR] = cred_json[:project_id]
ENV[ENV_VAR] = nil
credentials = @clz.from_env(@scope)
expect(credentials.project_id).to eq(cred_json[:project_id])
end
it 'succeeds when GOOGLE_PRIVATE_KEY is escaped' do
escaped_key = cred_json[:private_key].gsub "\n", '\n'
ENV[PRIVATE_KEY_VAR] = "\"#{escaped_key}\""
ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
expect(@clz.from_env(@scope)).to_not be_nil
end
it "propagates default_connection option" do
ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]
ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
connection = Faraday.new(headers: {"User-Agent" => "hello"})
creds = @clz.from_env(@scope, default_connection: connection)
expect(creds.build_default_connection).to be connection
end
end
describe '#from_well_known_path' do
before(:example) do
@home = ENV['HOME']
@app_data = ENV['APPDATA']
@scope = 'https://www.googleapis.com/auth/userinfo.profile'
@known_path = WELL_KNOWN_PATH
@clz = ServiceAccountCredentials
end
after(:example) do
ENV['HOME'] = @home unless @home == ENV['HOME']
ENV['APPDATA'] = @app_data unless @app_data == ENV['APPDATA']
end
it 'is nil if no file exists' do
ENV['HOME'] = File.dirname(__FILE__)
expect(ServiceAccountCredentials.from_well_known_path(@scope)).to be_nil
end
it 'successfully loads the file when it is present' do
Dir.mktmpdir do |dir|
key_path = File.join(dir, '.config', @known_path)
key_path = File.join(dir, WELL_KNOWN_PATH) if OS.windows?
FileUtils.mkdir_p(File.dirname(key_path))
File.write(key_path, cred_json_text)
ENV['HOME'] = dir
ENV['APPDATA'] = dir
expect(@clz.from_well_known_path(@scope)).to_not be_nil
end
end
it 'successfully sets project_id when file is present' do
Dir.mktmpdir do |dir|
key_path = File.join(dir, '.config', @known_path)
key_path = File.join(dir, WELL_KNOWN_PATH) if OS.windows?
FileUtils.mkdir_p(File.dirname(key_path))
File.write(key_path, cred_json_text)
ENV['HOME'] = dir
ENV['APPDATA'] = dir
credentials = @clz.from_well_known_path(@scope)
expect(credentials.project_id).to eq(cred_json[:project_id])
end
end
it "propagates default_connection option" do
Dir.mktmpdir do |dir|
key_path = File.join(dir, '.config', @known_path)
key_path = File.join(dir, WELL_KNOWN_PATH) if OS.windows?
FileUtils.mkdir_p(File.dirname(key_path))
File.write(key_path, cred_json_text)
ENV['HOME'] = dir
ENV['APPDATA'] = dir
connection = Faraday.new(headers: {"User-Agent" => "hello"})
creds = @clz.from_well_known_path(@scope, default_connection: connection)
expect(creds.build_default_connection).to be connection
end
end
end
describe '#from_system_default_path' do
before(:example) do
@scope = 'https://www.googleapis.com/auth/userinfo.profile'
@program_data = ENV['ProgramData']
@prefix = OS.windows? ? '/etc/Google/Auth/' : '/etc/google/auth/'
@path = File.join(@prefix, CREDENTIALS_FILE_NAME)
@clz = ServiceAccountCredentials
end
after(:example) do
ENV['ProgramData'] = @program_data
end
it 'is nil if no file exists' do
FakeFS do
expect(ServiceAccountCredentials.from_system_default_path(@scope))
.to be_nil
end
end
it 'successfully loads the file when it is present' do
FakeFS do
ENV['ProgramData'] = '/etc'
FileUtils.mkdir_p(File.dirname(@path))
File.write(@path, cred_json_text)
expect(@clz.from_system_default_path(@scope)).to_not be_nil
File.delete(@path)
end
end
it "propagates default_connection option" do
FakeFS do
ENV['ProgramData'] = '/etc'
FileUtils.mkdir_p(File.dirname(@path))
File.write(@path, cred_json_text)
connection = Faraday.new(headers: {"User-Agent" => "hello"})
creds = @clz.from_system_default_path(@scope, default_connection: connection)
expect(creds.build_default_connection).to be connection
File.delete(@path)
end
end
end
end
describe Google::Auth::ServiceAccountJwtHeaderCredentials do
ServiceAccountJwtHeaderCredentials =
Google::Auth::ServiceAccountJwtHeaderCredentials
let(:client_email) { 'app@developer.gserviceaccount.com' }
let(:clz) { Google::Auth::ServiceAccountJwtHeaderCredentials }
let(:cred_json) do
{
private_key_id: 'a_private_key_id',
private_key: @key.to_pem,
client_email: client_email,
client_id: 'app.apps.googleusercontent.com',
type: 'service_account',
project_id: 'a_project_id'
}
end
before(:example) do
@key = OpenSSL::PKey::RSA.new(2048)
@client = clz.make_creds(json_key_io: StringIO.new(cred_json_text))
end
def cred_json_text
MultiJson.dump(cred_json)
end
it_behaves_like 'jwt header auth'
describe '#from_env' do
before(:example) do
@var_name = ENV_VAR
@credential_vars = [
ENV_VAR, PRIVATE_KEY_VAR, CLIENT_EMAIL_VAR, ACCOUNT_TYPE_VAR
]
@original_env_vals = {}
@credential_vars.each { |var| @original_env_vals[var] = ENV[var] }
ENV[ACCOUNT_TYPE_VAR] = cred_json[:type]
end
after(:example) do
@credential_vars.each { |var| ENV[var] = @original_env_vals[var] }
end
it 'returns nil if the GOOGLE_APPLICATION_CREDENTIALS is unset' do
ENV.delete(@var_name) unless ENV[@var_name].nil?
expect(clz.from_env).to be_nil
end
it 'fails if the GOOGLE_APPLICATION_CREDENTIALS path does not exist' do
ENV.delete(@var_name) unless ENV[@var_name].nil?
expect(clz.from_env).to be_nil
Dir.mktmpdir do |dir|
key_path = File.join(dir, 'does-not-exist')
ENV[@var_name] = key_path
expect { clz.from_env }.to raise_error RuntimeError
end
end
it 'succeeds when the GOOGLE_APPLICATION_CREDENTIALS file is valid' do
Dir.mktmpdir do |dir|
key_path = File.join(dir, 'my_cert_file')
FileUtils.mkdir_p(File.dirname(key_path))
File.write(key_path, cred_json_text)
ENV[@var_name] = key_path
expect(clz.from_env).to_not be_nil
end
end
it 'succeeds when GOOGLE_PRIVATE_KEY and GOOGLE_CLIENT_EMAIL env vars are'\
' valid' do
ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]
ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
expect(clz.from_env(@scope)).to_not be_nil
end
it 'sets project_id when the PROJECT_ID_VAR env var is set' do
ENV[PRIVATE_KEY_VAR] = cred_json[:private_key]
ENV[CLIENT_EMAIL_VAR] = cred_json[:client_email]
ENV[PROJECT_ID_VAR] = cred_json[:project_id]
ENV[ENV_VAR] = nil
credentials = clz.from_env(@scope)
expect(credentials).to_not be_nil
expect(credentials.project_id).to eq(cred_json[:project_id])
end
end
describe '#from_well_known_path' do
before(:example) do
@home = ENV['HOME']
@app_data = ENV['APPDATA']
end
after(:example) do
ENV['HOME'] = @home unless @home == ENV['HOME']
ENV['APPDATA'] = @app_data unless @app_data == ENV['APPDATA']
end
it 'is nil if no file exists' do
ENV['HOME'] = File.dirname(__FILE__)
expect(clz.from_well_known_path).to be_nil
end
it 'successfully loads the file when it is present' do
Dir.mktmpdir do |dir|
key_path = File.join(dir, '.config', WELL_KNOWN_PATH)
key_path = File.join(dir, WELL_KNOWN_PATH) if OS.windows?
FileUtils.mkdir_p(File.dirname(key_path))
File.write(key_path, cred_json_text)
ENV['HOME'] = dir
ENV['APPDATA'] = dir
expect(clz.from_well_known_path).to_not be_nil
end
end
it 'successfully sets project_id when file is present' do
Dir.mktmpdir do |dir|
key_path = File.join(dir, '.config', WELL_KNOWN_PATH)
key_path = File.join(dir, WELL_KNOWN_PATH) if OS.windows?
FileUtils.mkdir_p(File.dirname(key_path))
File.write(key_path, cred_json_text)
ENV['HOME'] = dir
ENV['APPDATA'] = dir
credentials = clz.from_well_known_path(@scope)
expect(credentials.project_id).to eq(cred_json[:project_id])
end
end
end
end
googleauth-0.8.0/spec/googleauth/web_user_authorizer_spec.rb 0000644 0000041 0000041 00000012772 13437515753 024451 0 ustar www-data www-data # Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq!
require 'googleauth'
require 'googleauth/web_user_authorizer'
require 'uri'
require 'multi_json'
require 'spec_helper'
require 'rack'
describe Google::Auth::WebUserAuthorizer do
include TestHelpers
let(:client_id) { Google::Auth::ClientId.new('testclient', 'notasecret') }
let(:scope) { %w(email profile) }
let(:token_store) { DummyTokenStore.new }
let(:authorizer) do
Google::Auth::WebUserAuthorizer.new(client_id, scope, token_store)
end
describe '#get_authorization_url' do
let(:env) do
Rack::MockRequest.env_for(
'http://example.com:8080/test',
'REMOTE_ADDR' => '10.10.10.10'
)
end
let(:request) { Rack::Request.new(env) }
it 'should include current url in state' do
url = authorizer.get_authorization_url(request: request)
expect(url).to match(
%r{%22current_uri%22:%22http://example.com:8080/test%22}
)
end
it 'should include request forgery token in state' do
expect(SecureRandom).to receive(:base64).and_return('aGVsbG8=')
url = authorizer.get_authorization_url(request: request)
expect(url).to match(/%22session_id%22:%22aGVsbG8=%22/)
end
it 'should include request forgery token in session' do
expect(SecureRandom).to receive(:base64).and_return('aGVsbG8=')
authorizer.get_authorization_url(request: request)
expect(request.session['g-xsrf-token']).to eq 'aGVsbG8='
end
it 'should resolve callback against base URL' do
url = authorizer.get_authorization_url(request: request)
expect(url).to match(
%r{redirect_uri=http://example.com:8080/oauth2callback}
)
end
it 'should allow overriding the current URL' do
url = authorizer.get_authorization_url(
request: request,
redirect_to: '/foo'
)
expect(url).to match %r{%22current_uri%22:%22/foo%22}
end
it 'should pass through login hint' do
url = authorizer.get_authorization_url(
request: request,
login_hint: 'user@example.com'
)
expect(url).to match(/login_hint=user@example.com/)
end
end
shared_examples 'handles callback' do
let(:token_json) do
MultiJson.dump('access_token' => '1/abc123',
'token_type' => 'Bearer',
'expires_in' => 3600)
end
before(:example) do
stub_request(:post, 'https://oauth2.googleapis.com/token')
.to_return(body: token_json,
status: 200,
headers: { 'Content-Type' => 'application/json' })
end
let(:env) do
Rack::MockRequest.env_for(
'http://example.com:8080/oauth2callback?code=authcode&'\
'state=%7B%22current_uri%22%3A%22%2Ffoo%22%2C%22'\
'session_id%22%3A%22abc%22%7D',
'REMOTE_ADDR' => '10.10.10.10'
)
end
let(:request) { Rack::Request.new(env) }
before(:example) do
request.session['g-xsrf-token'] = 'abc'
end
it 'should return credentials when valid code present' do
expect(credentials).to be_instance_of(
Google::Auth::UserRefreshCredentials
)
end
it 'should return next URL to redirect to' do
expect(next_url).to eq '/foo'
end
it 'should fail if xrsf token in session and does not match request' do
request.session['g-xsrf-token'] = '123'
expect { credentials }.to raise_error(Signet::AuthorizationError)
end
end
describe '#handle_auth_callback' do
let(:result) { authorizer.handle_auth_callback('user1', request) }
let(:credentials) { result[0] }
let(:next_url) { result[1] }
it_behaves_like 'handles callback'
end
describe '#handle_auth_callback_deferred and #get_credentials' do
let(:next_url) do
Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(request)
end
let(:credentials) do
next_url
authorizer.get_credentials('user1', request)
end
it_behaves_like 'handles callback'
end
end
googleauth-0.8.0/spec/googleauth/scope_util_spec.rb 0000644 0000041 0000041 00000005512 13437515753 022522 0 ustar www-data www-data # Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq!
require 'googleauth/scope_util'
describe Google::Auth::ScopeUtil do
shared_examples 'normalizes scopes' do
let(:normalized) { Google::Auth::ScopeUtil.normalize(source) }
it 'normalizes the email scope' do
expect(normalized).to include(
'https://www.googleapis.com/auth/userinfo.email'
)
expect(normalized).to_not include 'email'
end
it 'normalizes the profile scope' do
expect(normalized).to include(
'https://www.googleapis.com/auth/userinfo.profile'
)
expect(normalized).to_not include 'profile'
end
it 'normalizes the openid scope' do
expect(normalized).to include 'https://www.googleapis.com/auth/plus.me'
expect(normalized).to_not include 'openid'
end
it 'leaves other other scopes as-is' do
expect(normalized).to include 'https://www.googleapis.com/auth/drive'
end
end
context 'with scope as string' do
let(:source) do
'email profile openid https://www.googleapis.com/auth/drive'
end
it_behaves_like 'normalizes scopes'
end
context 'with scope as Array' do
let(:source) do
%w(email profile openid https://www.googleapis.com/auth/drive)
end
it_behaves_like 'normalizes scopes'
end
end
googleauth-0.8.0/spec/googleauth/credentials_spec.rb 0000644 0000041 0000041 00000032312 13437515753 022647 0 ustar www-data www-data # Copyright 2017, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
require 'googleauth'
# This test is testing the private class Google::Auth::Credentials. We want to
# make sure that the passed in scope propogates to the Signet object. This means
# testing the private API, which is generally frowned on.
describe Google::Auth::Credentials, :private do
let(:default_keyfile_hash) do
{
'private_key_id' => 'testabc1234567890xyz',
'private_key' => "-----BEGIN RSA PRIVATE KEY-----\nMIIBOwIBAAJBAOyi0Hy1l4Ym2m2o71Q0TF4O9E81isZEsX0bb+Bqz1SXEaSxLiXM\nUZE8wu0eEXivXuZg6QVCW/5l+f2+9UPrdNUCAwEAAQJAJkqubA/Chj3RSL92guy3\nktzeodarLyw8gF8pOmpuRGSiEo/OLTeRUMKKD1/kX4f9sxf3qDhB4e7dulXR1co/\nIQIhAPx8kMW4XTTL6lJYd2K5GrH8uBMp8qL5ya3/XHrBgw3dAiEA7+3Iw3ULTn2I\n1J34WlJ2D5fbzMzB4FAHUNEV7Ys3f1kCIQDtUahCMChrl7+H5t9QS+xrn77lRGhs\nB50pjvy95WXpgQIhAI2joW6JzTfz8fAapb+kiJ/h9Vcs1ZN3iyoRlNFb61JZAiA8\nNy5NyNrMVwtB/lfJf1dAK/p/Bwd8LZLtgM6PapRfgw==\n-----END RSA PRIVATE KEY-----\n",
'client_email' => 'credz-testabc1234567890xyz@developer.gserviceaccount.com',
'client_id' => 'credz-testabc1234567890xyz.apps.googleusercontent.com',
'type' => 'service_account',
'project_id' => 'a_project_id'
}
end
it 'uses a default scope' do
mocked_signet = double('Signet::OAuth2::Client')
allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
allow(mocked_signet).to receive(:client_id)
allow(Signet::OAuth2::Client).to receive(:new) do |options|
expect(options[:token_credential_uri]).to eq('https://oauth2.googleapis.com/token')
expect(options[:audience]).to eq('https://oauth2.googleapis.com/token')
expect(options[:scope]).to eq([])
expect(options[:issuer]).to eq(default_keyfile_hash['client_email'])
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
mocked_signet
end
Google::Auth::Credentials.new default_keyfile_hash
end
it 'uses a custom scope' do
mocked_signet = double('Signet::OAuth2::Client')
allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
allow(mocked_signet).to receive(:client_id)
allow(Signet::OAuth2::Client).to receive(:new) do |options|
expect(options[:token_credential_uri]).to eq('https://oauth2.googleapis.com/token')
expect(options[:audience]).to eq('https://oauth2.googleapis.com/token')
expect(options[:scope]).to eq(['http://example.com/scope'])
expect(options[:issuer]).to eq(default_keyfile_hash['client_email'])
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
mocked_signet
end
Google::Auth::Credentials.new default_keyfile_hash, scope: 'http://example.com/scope'
end
it 'can be subclassed to pass in other env paths' do
TEST_PATH_ENV_VAR = 'TEST_PATH'.freeze
TEST_PATH_ENV_VAL = '/unknown/path/to/file.txt'.freeze
TEST_JSON_ENV_VAR = 'TEST_JSON_VARS'.freeze
ENV[TEST_PATH_ENV_VAR] = TEST_PATH_ENV_VAL
ENV[TEST_JSON_ENV_VAR] = JSON.generate(default_keyfile_hash)
class TestCredentials < Google::Auth::Credentials
SCOPE = 'http://example.com/scope'.freeze
PATH_ENV_VARS = [TEST_PATH_ENV_VAR].freeze
JSON_ENV_VARS = [TEST_JSON_ENV_VAR].freeze
end
allow(::File).to receive(:file?).with(TEST_PATH_ENV_VAL) { false }
mocked_signet = double('Signet::OAuth2::Client')
allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
allow(mocked_signet).to receive(:client_id)
allow(Signet::OAuth2::Client).to receive(:new) do |options|
expect(options[:token_credential_uri]).to eq('https://oauth2.googleapis.com/token')
expect(options[:audience]).to eq('https://oauth2.googleapis.com/token')
expect(options[:scope]).to eq(['http://example.com/scope'])
expect(options[:issuer]).to eq(default_keyfile_hash['client_email'])
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
mocked_signet
end
creds = TestCredentials.default
expect(creds).to be_a_kind_of(TestCredentials)
expect(creds.client).to eq(mocked_signet)
expect(creds.project_id).to eq(default_keyfile_hash['project_id'])
end
it 'subclasses can use PATH_ENV_VARS to get keyfile path' do
class TestCredentials < Google::Auth::Credentials
SCOPE = 'http://example.com/scope'.freeze
PATH_ENV_VARS = ['PATH_ENV_DUMMY', 'PATH_ENV_TEST'].freeze
JSON_ENV_VARS = ['JSON_ENV_DUMMY'].freeze
DEFAULT_PATHS = ['~/default/path/to/file.txt'].freeze
end
allow(::ENV).to receive(:[]).with('PATH_ENV_DUMMY') { '/fake/path/to/file.txt' }
allow(::File).to receive(:file?).with('/fake/path/to/file.txt') { false }
allow(::ENV).to receive(:[]).with('PATH_ENV_TEST') { '/unknown/path/to/file.txt' }
allow(::File).to receive(:file?).with('/unknown/path/to/file.txt') { true }
allow(::File).to receive(:read).with('/unknown/path/to/file.txt') { JSON.generate(default_keyfile_hash) }
mocked_signet = double('Signet::OAuth2::Client')
allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
allow(mocked_signet).to receive(:client_id)
allow(Signet::OAuth2::Client).to receive(:new) do |options|
expect(options[:token_credential_uri]).to eq('https://oauth2.googleapis.com/token')
expect(options[:audience]).to eq('https://oauth2.googleapis.com/token')
expect(options[:scope]).to eq(['http://example.com/scope'])
expect(options[:issuer]).to eq(default_keyfile_hash['client_email'])
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
mocked_signet
end
creds = TestCredentials.default
expect(creds).to be_a_kind_of(TestCredentials)
expect(creds.client).to eq(mocked_signet)
expect(creds.project_id).to eq(default_keyfile_hash['project_id'])
end
it 'subclasses can use JSON_ENV_VARS to get keyfile contents' do
class TestCredentials < Google::Auth::Credentials
SCOPE = 'http://example.com/scope'.freeze
PATH_ENV_VARS = ['PATH_ENV_DUMMY'].freeze
JSON_ENV_VARS = ['JSON_ENV_DUMMY', 'JSON_ENV_TEST'].freeze
DEFAULT_PATHS = ['~/default/path/to/file.txt'].freeze
end
allow(::ENV).to receive(:[]).with('PATH_ENV_DUMMY') { '/fake/path/to/file.txt' }
allow(::File).to receive(:file?).with('/fake/path/to/file.txt') { false }
allow(::ENV).to receive(:[]).with('JSON_ENV_DUMMY') { nil }
allow(::ENV).to receive(:[]).with('JSON_ENV_TEST') { JSON.generate(default_keyfile_hash) }
mocked_signet = double('Signet::OAuth2::Client')
allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
allow(mocked_signet).to receive(:client_id)
allow(Signet::OAuth2::Client).to receive(:new) do |options|
expect(options[:token_credential_uri]).to eq('https://oauth2.googleapis.com/token')
expect(options[:audience]).to eq('https://oauth2.googleapis.com/token')
expect(options[:scope]).to eq(['http://example.com/scope'])
expect(options[:issuer]).to eq(default_keyfile_hash['client_email'])
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
mocked_signet
end
creds = TestCredentials.default
expect(creds).to be_a_kind_of(TestCredentials)
expect(creds.client).to eq(mocked_signet)
expect(creds.project_id).to eq(default_keyfile_hash['project_id'])
end
it 'subclasses can use DEFAULT_PATHS to get keyfile path' do
class TestCredentials < Google::Auth::Credentials
SCOPE = 'http://example.com/scope'.freeze
PATH_ENV_VARS = ['PATH_ENV_DUMMY'].freeze
JSON_ENV_VARS = ['JSON_ENV_DUMMY'].freeze
DEFAULT_PATHS = ['~/default/path/to/file.txt'].freeze
end
allow(::ENV).to receive(:[]).with('PATH_ENV_DUMMY') { '/fake/path/to/file.txt' }
allow(::File).to receive(:file?).with('/fake/path/to/file.txt') { false }
allow(::ENV).to receive(:[]).with('JSON_ENV_DUMMY') { nil }
allow(::File).to receive(:file?).with('~/default/path/to/file.txt') { true }
allow(::File).to receive(:read).with('~/default/path/to/file.txt') { JSON.generate(default_keyfile_hash) }
mocked_signet = double('Signet::OAuth2::Client')
allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
allow(mocked_signet).to receive(:client_id)
allow(Signet::OAuth2::Client).to receive(:new) do |options|
expect(options[:token_credential_uri]).to eq('https://oauth2.googleapis.com/token')
expect(options[:audience]).to eq('https://oauth2.googleapis.com/token')
expect(options[:scope]).to eq(['http://example.com/scope'])
expect(options[:issuer]).to eq(default_keyfile_hash['client_email'])
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
mocked_signet
end
creds = TestCredentials.default
expect(creds).to be_a_kind_of(TestCredentials)
expect(creds.client).to eq(mocked_signet)
expect(creds.project_id).to eq(default_keyfile_hash['project_id'])
end
it 'subclasses that find no matches default to Google::Auth.get_application_default' do
class TestCredentials < Google::Auth::Credentials
SCOPE = 'http://example.com/scope'.freeze
PATH_ENV_VARS = ['PATH_ENV_DUMMY'].freeze
JSON_ENV_VARS = ['JSON_ENV_DUMMY'].freeze
DEFAULT_PATHS = ['~/default/path/to/file.txt'].freeze
end
allow(::ENV).to receive(:[]).with('PATH_ENV_DUMMY') { '/fake/path/to/file.txt' }
allow(::File).to receive(:file?).with('/fake/path/to/file.txt') { false }
allow(::ENV).to receive(:[]).with('JSON_ENV_DUMMY') { nil }
allow(::File).to receive(:file?).with('~/default/path/to/file.txt') { false }
mocked_signet = double('Signet::OAuth2::Client')
allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
allow(mocked_signet).to receive(:client_id)
allow(Google::Auth).to receive(:get_application_default) do |scope|
expect(scope).to eq(TestCredentials::SCOPE)
# This should really be a Signet::OAuth2::Client object,
# but mocking is making that difficult, so return a valid hash instead.
default_keyfile_hash
end
allow(Signet::OAuth2::Client).to receive(:new) do |options|
expect(options[:token_credential_uri]).to eq('https://oauth2.googleapis.com/token')
expect(options[:audience]).to eq('https://oauth2.googleapis.com/token')
expect(options[:scope]).to eq(['http://example.com/scope'])
expect(options[:issuer]).to eq(default_keyfile_hash['client_email'])
expect(options[:signing_key]).to be_a_kind_of(OpenSSL::PKey::RSA)
mocked_signet
end
creds = TestCredentials.default
expect(creds).to be_a_kind_of(TestCredentials)
expect(creds.client).to eq(mocked_signet)
expect(creds.project_id).to eq(default_keyfile_hash['project_id'])
end
it 'warns when cloud sdk credentials are used' do
mocked_signet = double('Signet::OAuth2::Client')
allow(mocked_signet).to receive(:configure_connection).and_return(mocked_signet)
allow(mocked_signet).to receive(:fetch_access_token!).and_return(true)
allow(Signet::OAuth2::Client).to receive(:new) do |options|
mocked_signet
end
allow(mocked_signet).to receive(:client_id).and_return(Google::Auth::CredentialsLoader::CLOUD_SDK_CLIENT_ID)
expect { Google::Auth::Credentials.new default_keyfile_hash }.to output(
Google::Auth::CredentialsLoader::CLOUD_SDK_CREDENTIALS_WARNING + "\n"
).to_stderr
end
end
googleauth-0.8.0/spec/spec_helper.rb 0000644 0000041 0000041 00000005475 13437515753 017505 0 ustar www-data www-data # Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.dirname(__FILE__))
root_dir = File.expand_path(File.join(spec_dir, '..'))
lib_dir = File.expand_path(File.join(root_dir, 'lib'))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.unshift(lib_dir)
$LOAD_PATH.uniq!
# set up coverage
require 'simplecov'
require 'coveralls'
SimpleCov.formatters = [
Coveralls::SimpleCov::Formatter,
SimpleCov::Formatter::HTMLFormatter
]
SimpleCov.start
require 'faraday'
require 'rspec'
require 'logging'
require 'rspec/logging_helper'
require 'webmock/rspec'
require 'multi_json'
# Preload adapter to work around Rubinius error with FakeFS
MultiJson.use(:json_gem)
# Allow Faraday to support test stubs
Faraday::Adapter.load_middleware(:test)
# Configure RSpec to capture log messages for each test. The output from the
# logs will be stored in the @log_output variable. It is a StringIO instance.
RSpec.configure do |config|
include RSpec::LoggingHelper
config.capture_log_messages
config.include WebMock::API
config.filter_run focus: true
config.run_all_when_everything_filtered = true
end
module TestHelpers
include WebMock::API
include WebMock::Matchers
end
class DummyTokenStore
def initialize
@tokens = {}
end
def load(id)
@tokens[id]
end
def store(id, token)
@tokens[id] = token
end
def delete(id)
@tokens.delete(id)
end
end
googleauth-0.8.0/CHANGELOG.md 0000644 0000041 0000041 00000006402 13437515753 015535 0 ustar www-data www-data ## 0.8.0 (2019/01/02)
* Support connection options :default_connection and :connection_builder when creating credentials that need to refresh OAuth tokens. This lets clients provide connection objects with custom settings, such as proxies, needed for the client environment.
* Removed an unnecessary warning about project IDs.
## 0.7.1 (2018/10/25)
* Make load_gcloud_project_id module function.
## 0.7.0 (2018/10/24)
* Add project_id instance variable to UserRefreshCredentials, ServiceAccountCredentials, and Credentials.
## 0.6.7 (2018/10/16)
* Update memoist dependency to ~> 0.16.
## 0.6.6 (2018/08/22)
* Remove ruby version warnings.
## 0.6.5 (2018/08/16)
* Fix incorrect http verb when revoking credentials.
* Warn on EOL ruby versions.
## 0.6.4 (2018/08/03)
* Resolve issue where DefaultCredentials constant was undefined.
## 0.6.3 (2018/08/02)
* Resolve issue where token_store was being written to twice
## 0.6.2 (2018/08/01)
* Add warning when using cloud sdk credentials
## 0.6.1 (2017/10/18)
* Fix file permissions
## 0.6.0 (2017/10/17)
### Changes
* Support ruby-jwt 2.0
* Add simple credentials class
## 0.5.3 (2017/07/21)
### Changes
* Fix file permissions on the gem's `.rb` files.
## 0.5.2 (2017/07/19)
### Changes
* Add retry mechanism when fetching access tokens in `GCECredentials` and `UserRefreshCredentials` classes.
* Update Google API OAuth2 token credential URI to v4.
## 0.5.1 (2016/01/06)
### Changes
* Change header name emitted by `Client#apply` from "Authorization" to "authorization" ([@murgatroid99][])
* Fix ADC not working on some windows machines ([@vsubramani][])
[#55](https://github.com/google/google-auth-library-ruby/issues/55)
## 0.5.0 (2015/10/12)
### Changes
* Initial support for user credentials ([@sqrrrl][])
* Update Signet to 0.7
## 0.4.2 (2015/08/05)
### Changes
* Updated UserRefreshCredentials hash to use string keys ([@haabaato][])
[#36](https://github.com/google/google-auth-library-ruby/issues/36)
* Add support for a system default credentials file. ([@mr-salty][])
[#33](https://github.com/google/google-auth-library-ruby/issues/33)
* Fix bug when loading credentials from ENV ([@dwilkie][])
[#31](https://github.com/google/google-auth-library-ruby/issues/31)
* Relax the constraint of dependent version of multi_json ([@igrep][])
[#30](https://github.com/google/google-auth-library-ruby/issues/30)
### Changes
* Enables passing credentials via environment variables. ([@haabaato][])
[#27](https://github.com/google/google-auth-library-ruby/issues/27)
## 0.4.1 (2015/04/25)
### Changes
* Improves handling of --no-scopes GCE authorization ([@tbetbetbe][])
* Refactoring and cleanup ([@joneslee85][])
## 0.4.0 (2015/03/25)
### Changes
* Adds an implementation of JWT header auth ([@tbetbetbe][])
## 0.3.0 (2015/03/23)
### Changes
* makes the scope parameter's optional in all APIs. ([@tbetbetbe][])
* changes the scope parameter's position in various constructors. ([@tbetbetbe][])
[@dwilkie]: https://github.com/dwilkie
[@haabaato]: https://github.com/haabaato
[@igrep]: https://github.com/igrep
[@joneslee85]: https://github.com/joneslee85
[@mr-salty]: https://github.com/mr-salty
[@tbetbetbe]: https://github.com/tbetbetbe
[@murgatroid99]: https://github.com/murgatroid99
[@vsubramani]: https://github.com/vsubramani
googleauth-0.8.0/.rubocop.yml 0000644 0000041 0000041 00000001007 13437515753 016172 0 ustar www-data www-data AllCops:
Exclude:
- "spec/**/*"
Metrics/AbcSize:
Max: 25
Metrics/BlockLength:
Exclude:
- "googleauth.gemspec"
Metrics/CyclomaticComplexity:
Max: 8
Metrics/MethodLength:
Max: 20
Metrics/ModuleLength:
Max: 150
Metrics/ClassLength:
Enabled: false
Layout/IndentHeredoc:
Enabled: false
Style/FormatString:
Enabled: false
Style/GuardClause:
Enabled: false
Style/PercentLiteralDelimiters: # Contradicting rule
Enabled: false
Style/SymbolArray: # Undefined syntax in Ruby 1.9.3
Enabled: false
googleauth-0.8.0/.kokoro/ 0000755 0000041 0000041 00000000000 13437515753 015304 5 ustar www-data www-data googleauth-0.8.0/.kokoro/continuous/ 0000755 0000041 0000041 00000000000 13437515753 017512 5 ustar www-data www-data googleauth-0.8.0/.kokoro/continuous/common.cfg 0000644 0000041 0000041 00000000707 13437515753 021467 0 ustar www-data www-data # Format: //devtools/kokoro/config/proto/build.proto
# Build logs will be here
action {
define_artifacts {
regex: "**/*sponge_log.xml"
}
}
# Download trampoline resources.
gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline"
# Download resources for system tests (service account key, etc.)
gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/google-cloud-ruby"
env_vars: {
key: "JOB_TYPE"
value: "continuous"
}
googleauth-0.8.0/.kokoro/continuous/windows.cfg 0000644 0000041 0000041 00000000157 13437515753 021670 0 ustar www-data www-data # Format: //devtools/kokoro/config/proto/build.proto
build_file: "google-auth-library-ruby/.kokoro/build.bat"
googleauth-0.8.0/.kokoro/continuous/linux.cfg 0000644 0000041 0000041 00000001012 13437515753 021324 0 ustar www-data www-data # Format: //devtools/kokoro/config/proto/build.proto
build_file: "google-auth-library-ruby/.kokoro/trampoline.sh"
# Configure the docker image for kokoro-trampoline.
# Dockerfile is maintained at https://github.com/googleapis/google-cloud-ruby/tree/master/.kokoro/docker/ruby-multi
env_vars: {
key: "TRAMPOLINE_IMAGE"
value: "gcr.io/cloud-devrel-kokoro-resources/google-cloud-ruby/ruby-multi-ubuntu"
}
env_vars: {
key: "TRAMPOLINE_BUILD_FILE"
value: "github/google-auth-library-ruby/.kokoro/build.sh"
}
googleauth-0.8.0/.kokoro/continuous/osx.cfg 0000644 0000041 0000041 00000000154 13437515753 021004 0 ustar www-data www-data # Format: //devtools/kokoro/config/proto/build.proto
build_file: "google-auth-library-ruby/.kokoro/osx.sh"
googleauth-0.8.0/.kokoro/trampoline.sh 0000755 0000041 0000041 00000001613 13437515753 020016 0 ustar www-data www-data #!/bin/bash
# Copyright 2017 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.
set -eo pipefail
# Always run the cleanup script, regardless of the success of bouncing into
# the container.
function cleanup() {
chmod +x ${KOKORO_GFILE_DIR}/trampoline_cleanup.sh
${KOKORO_GFILE_DIR}/trampoline_cleanup.sh
echo "cleanup";
}
trap cleanup EXIT
python3 "${KOKORO_GFILE_DIR}/trampoline_v1.py"
googleauth-0.8.0/.kokoro/common.cfg 0000755 0000041 0000041 00000001227 13437515753 017262 0 ustar www-data www-data # Format: //devtools/kokoro/config/proto/build.proto
# Download trampoline resources. These will be in ${KOKORO_GFILE_DIR}
gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline"
# All builds use the trampoline script to run in docker.
build_file: "google-auth-library-ruby/.kokoro/trampoline.sh"
# Download secrets from Cloud Storage.
gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/google-auth-library-ruby"
# Tell the trampoline which build file to use.
env_vars: {
key: "TRAMPOLINE_BUILD_FILE"
value: "github/google-auth-library-ruby/.kokoro/build.sh"
}
action {
define_artifacts {
regex: "**/*sponge_log.xml"
}
}
googleauth-0.8.0/.kokoro/osx.sh 0000755 0000041 0000041 00000001532 13437515753 016455 0 ustar www-data www-data #!/bin/bash
# This file runs tests for merges, PRs, and nightlies.
# There are a few rules for what tests are run:
# * PRs run all non-acceptance tests for every library.
# * Merges run all non-acceptance tests for every library, and acceptance tests for all altered libraries.
# * Nightlies run all acceptance tests for every library.
# * Currently only runs tests on 2.5.0
set -eo pipefail
# Debug: show build environment
env | grep KOKORO
cd github/google-auth-library-ruby/
# Print out Ruby version
ruby --version
# Temporary workaround for a known bundler+docker issue:
# https://github.com/bundler/bundler/issues/6154
export BUNDLE_GEMFILE=
# Capture failures
EXIT_STATUS=0 # everything passed
function set_failed_status {
EXIT_STATUS=1
}
gem install bundle
(bundle update && bundle exec rake) || set_failed_status
exit $EXIT_STATUS
googleauth-0.8.0/.kokoro/build.sh 0000755 0000041 0000041 00000001624 13437515753 016745 0 ustar www-data www-data #!/bin/bash
# This file runs tests for merges, PRs, and nightlies.
# There are a few rules for what tests are run:
# * PRs run all non-acceptance tests for every library.
# * Merges run all non-acceptance tests for every library, and acceptance tests for all altered libraries.
# * Nightlies run all acceptance tests for every library.
set -eo pipefail
# Debug: show build environment
env | grep KOKORO
cd github/google-auth-library-ruby/
# Print out Ruby version
ruby --version
# Temporary workaround for a known bundler+docker issue:
# https://github.com/bundler/bundler/issues/6154
export BUNDLE_GEMFILE=
RUBY_VERSIONS=("2.3.8" "2.4.5" "2.5.3")
# Capture failures
EXIT_STATUS=0 # everything passed
function set_failed_status {
EXIT_STATUS=1
}
for version in "${RUBY_VERSIONS[@]}"; do
rbenv global "$version"
(bundle update && bundle exec rake) || set_failed_status
done
exit $EXIT_STATUS
googleauth-0.8.0/.kokoro/presubmit/ 0000755 0000041 0000041 00000000000 13437515753 017316 5 ustar www-data www-data googleauth-0.8.0/.kokoro/presubmit/common.cfg 0000644 0000041 0000041 00000000706 13437515753 021272 0 ustar www-data www-data # Format: //devtools/kokoro/config/proto/build.proto
# Build logs will be here
action {
define_artifacts {
regex: "**/*sponge_log.xml"
}
}
# Download trampoline resources.
gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline"
# Download resources for system tests (service account key, etc.)
gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/google-cloud-ruby"
env_vars: {
key: "JOB_TYPE"
value: "presubmit"
}
googleauth-0.8.0/.kokoro/presubmit/windows.cfg 0000644 0000041 0000041 00000000157 13437515753 021474 0 ustar www-data www-data # Format: //devtools/kokoro/config/proto/build.proto
build_file: "google-auth-library-ruby/.kokoro/build.bat"
googleauth-0.8.0/.kokoro/presubmit/linux.cfg 0000644 0000041 0000041 00000000626 13437515753 021142 0 ustar www-data www-data # Format: //devtools/kokoro/config/proto/build.proto
build_file: "google-auth-library-ruby/.kokoro/trampoline.sh"
# Configure the docker image for kokoro-trampoline.
env_vars: {
key: "TRAMPOLINE_IMAGE"
value: "gcr.io/cloud-devrel-kokoro-resources/google-cloud-ruby/ruby-multi-ubuntu"
}
env_vars: {
key: "TRAMPOLINE_BUILD_FILE"
value: "github/google-auth-library-ruby/.kokoro/build.sh"
}
googleauth-0.8.0/.kokoro/presubmit/osx.cfg 0000644 0000041 0000041 00000000154 13437515753 020610 0 ustar www-data www-data # Format: //devtools/kokoro/config/proto/build.proto
build_file: "google-auth-library-ruby/.kokoro/osx.sh"
googleauth-0.8.0/.kokoro/windows.sh 0000644 0000041 0000041 00000001505 13437515753 017333 0 ustar www-data www-data #!/bin/bash
# This file runs tests for merges, PRs, and nightlies.
# There are a few rules for what tests are run:
# * PRs run all non-acceptance tests for every library.
# * Merges run all non-acceptance tests for every library, and acceptance tests for all altered libraries.
# * Nightlies run all acceptance tests for every library.
# * Currently only runs tests on 2.5.1
set -eo pipefail
# Debug: show build environment
env | grep KOKORO
cd github/google-auth-library-ruby/
# Print out Ruby version
ruby --version
# Temporary workaround for a known bundler+docker issue:
# https://github.com/bundler/bundler/issues/6154
export BUNDLE_GEMFILE=
# Capture failures
EXIT_STATUS=0 # everything passed
function set_failed_status {
EXIT_STATUS=1
}
(bundle update && bundle exec rake) || set_failed_status
exit $EXIT_STATUS
googleauth-0.8.0/.kokoro/build.bat 0000644 0000041 0000041 00000000720 13437515753 017072 0 ustar www-data www-data REM This file runs tests for merges, PRs, and nightlies.
REM There are a few rules for what tests are run:
REM * PRs run all non-acceptance tests for every library.
REM * Merges run all non-acceptance tests for every library, and acceptance tests for all altered libraries.
REM * Nightlies run all acceptance tests for every library.
REM Currently only runs tests on 2.5.1
"C:\Program Files\Git\bin\bash.exe" github/google-auth-library-ruby/.kokoro/windows.sh
googleauth-0.8.0/.gitignore 0000644 0000041 0000041 00000001115 13437515753 015710 0 ustar www-data www-data *~
Gemfile.lock
*.gem
*.rbc
/.config
/coverage/
/InstalledFiles
/pkg/
/spec/reports/
/test/tmp/
/test/version_tmp/
/tmp/
## Specific to RubyMotion:
.dat*
.repl_history
build/
## Documentation cache and generated files:
/.yardoc/
/_yardoc/
/doc/
/rdoc/
## Environment normalisation:
/.bundle/
/lib/bundler/man/
# for a library or gem, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# Gemfile.lock
# .ruby-version
# .ruby-gemset
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
.rvmrc
googleauth-0.8.0/CODE_OF_CONDUCT.md 0000644 0000041 0000041 00000003675 13437515753 016534 0 ustar www-data www-data # Contributor Code of Conduct
As contributors and maintainers of this project,
and in the interest of fostering an open and welcoming community,
we pledge to respect all people who contribute through reporting issues,
posting feature requests, updating documentation,
submitting pull requests or patches, and other activities.
We are committed to making participation in this project
a harassment-free experience for everyone,
regardless of level of experience, gender, gender identity and expression,
sexual orientation, disability, personal appearance,
body size, race, ethnicity, age, religion, or nationality.
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information,
such as physical or electronic
addresses, without explicit permission
* Other unethical or unprofessional conduct.
Project maintainers have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct.
By adopting this Code of Conduct,
project maintainers commit themselves to fairly and consistently
applying these principles to every aspect of managing this project.
Project maintainers who do not follow or enforce the Code of Conduct
may be permanently removed from the project team.
This code of conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior
may be reported by opening an issue
or contacting one or more of the project maintainers.
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0,
available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/)
googleauth-0.8.0/googleauth.gemspec 0000755 0000041 0000041 00000002324 13437515753 017431 0 ustar www-data www-data # -*- ruby -*-
# encoding: utf-8
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
require 'googleauth/version'
Gem::Specification.new do |s|
s.name = 'googleauth'
s.version = Google::Auth::VERSION
s.authors = ['Tim Emiola']
s.email = 'temiola@google.com'
s.homepage = 'https://github.com/google/google-auth-library-ruby'
s.summary = 'Google Auth Library for Ruby'
s.license = 'Apache-2.0'
s.description = <<-DESCRIPTION
Allows simple authorization for accessing Google APIs.
Provide support for Application Default Credentials, as described at
https://developers.google.com/accounts/docs/application-default-credentials
DESCRIPTION
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- spec/*`.split("\n")
s.executables = `git ls-files -- bin/*.rb`.split("\n").map do |f|
File.basename(f)
end
s.require_paths = ['lib']
s.platform = Gem::Platform::RUBY
s.add_dependency 'faraday', '~> 0.12'
s.add_dependency 'jwt', '>= 1.4', '< 3.0'
s.add_dependency 'memoist', '~> 0.16'
s.add_dependency 'multi_json', '~> 1.11'
s.add_dependency 'os', '>= 0.9', '< 2.0'
s.add_dependency 'signet', '~> 0.7'
end
googleauth-0.8.0/Rakefile 0000755 0000041 0000041 00000000502 13437515753 015367 0 ustar www-data www-data # -*- ruby -*-
require 'rspec/core/rake_task'
require 'rubocop/rake_task'
require 'bundler/gem_tasks'
desc 'Run Rubocop to check for style violations'
RuboCop::RakeTask.new
desc 'Run rake task'
RSpec::Core::RakeTask.new(:spec)
desc 'Does rubocop lint and runs the specs'
task all: [:rubocop, :spec]
task default: :all
googleauth-0.8.0/lib/ 0000755 0000041 0000041 00000000000 13437515753 014470 5 ustar www-data www-data googleauth-0.8.0/lib/googleauth/ 0000755 0000041 0000041 00000000000 13437515753 016626 5 ustar www-data www-data googleauth-0.8.0/lib/googleauth/client_id.rb 0000644 0000041 0000041 00000007470 13437515753 021115 0 ustar www-data www-data # Copyright 2014, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
require 'multi_json'
require 'googleauth/credentials_loader'
module Google
module Auth
# Representation of an application's identity for user authorization
# flows.
class ClientId
INSTALLED_APP = 'installed'.freeze
WEB_APP = 'web'.freeze
CLIENT_ID = 'client_id'.freeze
CLIENT_SECRET = 'client_secret'.freeze
MISSING_TOP_LEVEL_ELEMENT_ERROR =
"Expected top level property 'installed' or 'web' to be present.".freeze
# Text identifier of the client ID
# @return [String]
attr_reader :id
# Secret associated with the client ID
# @return [String]
attr_reader :secret
class << self
attr_accessor :default
end
# Initialize the Client ID
#
# @param [String] id
# Text identifier of the client ID
# @param [String] secret
# Secret associated with the client ID
# @note Direction instantion is discouraged to avoid embedding IDs
# & secrets in source. See {#from_file} to load from
# `client_secrets.json` files.
def initialize(id, secret)
CredentialsLoader.warn_if_cloud_sdk_credentials id
raise 'Client id can not be nil' if id.nil?
raise 'Client secret can not be nil' if secret.nil?
@id = id
@secret = secret
end
# Constructs a Client ID from a JSON file downloaded from the
# Google Developers Console.
#
# @param [String, File] file
# Path of file to read from
# @return [Google::Auth::ClientID]
def self.from_file(file)
raise 'File can not be nil.' if file.nil?
File.open(file.to_s) do |f|
json = f.read
config = MultiJson.load json
from_hash(config)
end
end
# Constructs a Client ID from a previously loaded JSON file. The hash
# structure should
# match the expected JSON format.
#
# @param [hash] config
# Parsed contents of the JSON file
# @return [Google::Auth::ClientID]
def self.from_hash(config)
raise 'Hash can not be nil.' if config.nil?
raw_detail = config[INSTALLED_APP] || config[WEB_APP]
raise MISSING_TOP_LEVEL_ELEMENT_ERROR if raw_detail.nil?
ClientId.new(raw_detail[CLIENT_ID], raw_detail[CLIENT_SECRET])
end
end
end
end
googleauth-0.8.0/lib/googleauth/scope_util.rb 0000644 0000041 0000041 00000004445 13437515753 021330 0 ustar www-data www-data # Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
require 'googleauth/signet'
require 'googleauth/credentials_loader'
require 'multi_json'
module Google
module Auth
# Small utility for normalizing scopes into canonical form
module ScopeUtil
ALIASES = {
'email' => 'https://www.googleapis.com/auth/userinfo.email',
'profile' => 'https://www.googleapis.com/auth/userinfo.profile',
'openid' => 'https://www.googleapis.com/auth/plus.me'
}.freeze
def self.normalize(scope)
list = as_array(scope)
list.map { |item| ALIASES[item] || item }
end
def self.as_array(scope)
case scope
when Array
scope
when String
scope.split(' ')
else
raise 'Invalid scope value. Must be string or array'
end
end
end
end
end
googleauth-0.8.0/lib/googleauth/version.rb 0000644 0000041 0000041 00000003250 13437515753 020640 0 ustar www-data www-data # Copyright 2014, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
module Google
# Module Auth provides classes that provide Google-specific authorization
# used to access Google APIs.
module Auth
VERSION = '0.8.0'.freeze
end
end
googleauth-0.8.0/lib/googleauth/credentials.rb 0000644 0000041 0000041 00000015473 13437515753 021462 0 ustar www-data www-data # Copyright 2017, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity, MethodLength
require 'forwardable'
require 'json'
require 'signet/oauth_2/client'
require 'googleauth/credentials_loader'
module Google
module Auth
# This class is intended to be inherited by API-specific classes
# which overrides the SCOPE constant.
class Credentials
TOKEN_CREDENTIAL_URI = 'https://oauth2.googleapis.com/token'.freeze
AUDIENCE = 'https://oauth2.googleapis.com/token'.freeze
SCOPE = [].freeze
PATH_ENV_VARS = [].freeze
JSON_ENV_VARS = [].freeze
DEFAULT_PATHS = [].freeze
attr_accessor :client
attr_reader :project_id
# Delegate client methods to the client object.
extend Forwardable
def_delegators :@client,
:token_credential_uri, :audience,
:scope, :issuer, :signing_key, :updater_proc
def initialize(keyfile, options = {})
scope = options[:scope]
verify_keyfile_provided! keyfile
@project_id = options['project_id'] || options['project']
if keyfile.is_a? Signet::OAuth2::Client
@client = keyfile
@project_id ||= keyfile.project_id if keyfile.respond_to? :project_id
elsif keyfile.is_a? Hash
hash = stringify_hash_keys keyfile
hash['scope'] ||= scope
@client = init_client hash, options
@project_id ||= (hash['project_id'] || hash['project'])
else
verify_keyfile_exists! keyfile
json = JSON.parse ::File.read(keyfile)
json['scope'] ||= scope
@project_id ||= (json['project_id'] || json['project'])
@client = init_client json, options
end
CredentialsLoader.warn_if_cloud_sdk_credentials @client.client_id
@project_id ||= CredentialsLoader.load_gcloud_project_id
@client.fetch_access_token!
end
# Returns the default credentials checking, in this order, the path env
# evironment variables, json environment variables, default paths. If the
# previously stated locations do not contain keyfile information,
# this method defaults to use the application default.
def self.default(options = {})
# First try to find keyfile file from environment variables.
client = from_path_vars options
# Second try to find keyfile json from environment variables.
client ||= from_json_vars options
# Third try to find keyfile file from known file paths.
client ||= from_default_paths options
# Finally get instantiated client from Google::Auth
client ||= from_application_default options
client
end
def self.from_path_vars(options)
self::PATH_ENV_VARS
.map { |v| ENV[v] }
.compact
.select { |p| ::File.file? p }
.each do |file|
return new file, options
end
nil
end
def self.from_json_vars(options)
json = lambda do |v|
unless ENV[v].nil?
begin
JSON.parse ENV[v]
rescue
nil
end
end
end
self::JSON_ENV_VARS.map(&json).compact.each do |hash|
return new hash, options
end
nil
end
def self.from_default_paths(options)
self::DEFAULT_PATHS
.select { |p| ::File.file? p }
.each do |file|
return new file, options
end
nil
end
def self.from_application_default(options)
scope = options[:scope] || self::SCOPE
client = Google::Auth.get_application_default scope
new client, options
end
private_class_method :from_path_vars,
:from_json_vars,
:from_default_paths,
:from_application_default
protected
# Verify that the keyfile argument is provided.
def verify_keyfile_provided!(keyfile)
return unless keyfile.nil?
raise 'The keyfile passed to Google::Auth::Credentials.new was nil.'
end
# Verify that the keyfile argument is a file.
def verify_keyfile_exists!(keyfile)
exists = ::File.file? keyfile
raise "The keyfile '#{keyfile}' is not a valid file." unless exists
end
# Initializes the Signet client.
def init_client(keyfile, connection_options = {})
client_opts = client_options keyfile
Signet::OAuth2::Client.new(client_opts)
.configure_connection(connection_options)
end
# returns a new Hash with string keys instead of symbol keys.
def stringify_hash_keys(hash)
Hash[hash.map { |k, v| [k.to_s, v] }]
end
def client_options(options)
# Keyfile options have higher priority over constructor defaults
options['token_credential_uri'] ||= self.class::TOKEN_CREDENTIAL_URI
options['audience'] ||= self.class::AUDIENCE
options['scope'] ||= self.class::SCOPE
# client options for initializing signet client
{ token_credential_uri: options['token_credential_uri'],
audience: options['audience'],
scope: Array(options['scope']),
issuer: options['client_email'],
signing_key: OpenSSL::PKey::RSA.new(options['private_key']) }
end
end
end
end
googleauth-0.8.0/lib/googleauth/application_default.rb 0000644 0000041 0000041 00000007260 13437515753 023167 0 ustar www-data www-data # Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
require 'googleauth/compute_engine'
require 'googleauth/default_credentials'
module Google
# Module Auth provides classes that provide Google-specific authorization
# used to access Google APIs.
module Auth
NOT_FOUND_ERROR = <