googleauth 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 296c6ddfcc4f4a1150e7bfc14e00fc37d60ba933
4
- data.tar.gz: 4e3921f83b5816e22e8509ce7a1912376322ba95
3
+ metadata.gz: ac8bcce8f139bb5d243108c0ab7273b52db59305
4
+ data.tar.gz: df908e203c1963f8194cd89d26d055cced4b5895
5
5
  SHA512:
6
- metadata.gz: cc0469ce9a49f4b41cf5be4f0682395f9616940e2eae99af294b22ea646a35a8efbd527ce865056d719cd00d5f473c6bb3c07ffe28a77c82ba8f8e1bd2ba7168
7
- data.tar.gz: b5217485091a72f408dce0fd08e6f9a5d45450f3c16783b8c5f2b8dd13fdeaf96eea90f9bc8b80ec9c10d0c1ee5925536b64f4b0cf8dc9abdd187470f825bcb0
6
+ metadata.gz: 7345453339664b2afd90a55695dfd02f075850c93ba6896193c9dccc7f8043da9762edfcf5131f9fe489dd0f6e1108cc2318f5bc2fc6e5363dd9c54cddba7b81
7
+ data.tar.gz: 0c556b84edd90bbfcd70132775acfb46c03f6bbcbb081d066d5e400e8e5f4ff60a01e059e5ad08b027eb3c58664449f5fc6bcd9450af417133c694eede138440
data/.rubocop.yml ADDED
@@ -0,0 +1 @@
1
+ inherit_from: .rubocop_todo.yml
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,15 @@
1
+ # This configuration was generated by `rubocop --auto-gen-config`
2
+ # on 2015-02-25 04:34:33 -0800 using RuboCop version 0.28.0.
3
+ # The point is for the user to remove these configuration records
4
+ # one by one as the offenses are removed from the code base.
5
+ # Note that changes in the inspected code, or installation of new
6
+ # versions of RuboCop, may require this file to be generated again.
7
+
8
+ # Offense count: 1
9
+ Metrics/AbcSize:
10
+ Max: 16
11
+
12
+ # Offense count: 1
13
+ # Configuration parameters: CountComments.
14
+ Metrics/MethodLength:
15
+ Max: 11
data/.travis.yml CHANGED
@@ -6,7 +6,7 @@ rvm:
6
6
  - 1.9.3
7
7
  - rbx-2
8
8
  - jruby
9
- script: "bundle exec rake spec:all"
9
+ script: "bundle exec rake"
10
10
  before_install:
11
11
  - sudo apt-get update
12
12
  - sudo apt-get install idn
data/README.md CHANGED
@@ -36,8 +36,8 @@ $ gem install googleauth
36
36
  require 'googleauth'
37
37
 
38
38
  # Get the environment configured authorization
39
- scope = 'https://www.googleapis.com/auth/userinfo.profile'
40
- authorization = Google::Auth.get_application_default(scope)
39
+ scopes = ['https://www.googleapis.com/auth/cloud-platform', 'https://www.googleapis.com/auth/compute']
40
+ authorization = Google::Auth.get_application_default(scopes)
41
41
 
42
42
  # Add the the access token obtained using the authorization to a hash, e.g
43
43
  # headers.
data/googleauth.gemspec CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
10
10
  s.email = 'temiola@google.com'
11
11
  s.homepage = 'https://github.com/google/google-auth-library-ruby'
12
12
  s.summary = 'Google Auth Library for Ruby'
13
- s.license = "Apache-2.0"
13
+ s.license = 'Apache-2.0'
14
14
  s.description = <<-eos
15
15
  Allows simple authorization for accessing Google APIs.
16
16
  Provide support for Application Default Credentials, as described at
@@ -27,9 +27,9 @@ Gem::Specification.new do |s|
27
27
 
28
28
  s.add_dependency 'faraday', '~> 0.9'
29
29
  s.add_dependency 'logging', '~> 1.8'
30
- s.add_dependency 'jwt', '~> 1.2.1'
30
+ s.add_dependency 'jwt', '~> 1.3.0'
31
31
  s.add_dependency 'memoist', '~> 0.11.0'
32
- s.add_dependency 'multi_json', '1.10.1'
32
+ s.add_dependency 'multi_json', '1.11.0'
33
33
  s.add_dependency 'signet', '~> 0.6.0'
34
34
 
35
35
  s.add_development_dependency 'bundler', '~> 1.7'
data/lib/googleauth.rb CHANGED
@@ -27,8 +27,13 @@
27
27
  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
28
  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
29
 
30
- require 'googleauth/service_account'
30
+ require 'multi_json'
31
+ require 'stringio'
32
+
33
+ require 'googleauth/credentials_loader'
31
34
  require 'googleauth/compute_engine'
35
+ require 'googleauth/service_account'
36
+ require 'googleauth/user_refresh'
32
37
 
33
38
  module Google
34
39
  # Module Auth provides classes that provide Google-specific authorization
@@ -40,6 +45,29 @@ https://developers.google.com/accounts/docs/application-default-credentials
40
45
  for more information
41
46
  END
42
47
 
48
+ # DefaultCredentials is used to preload the credentials file, to determine
49
+ # which type of credentials should be loaded.
50
+ class DefaultCredentials
51
+ extend CredentialsLoader
52
+
53
+ # override CredentialsLoader#make_creds to use the class determined by
54
+ # loading the json.
55
+ def self.make_creds(scope, json_key_io)
56
+ json_key, clz = determine_creds_class(json_key_io)
57
+ clz.new(scope, StringIO.new(MultiJson.dump(json_key)))
58
+ end
59
+
60
+ # Reads the input json and determines which creds class to use.
61
+ def self.determine_creds_class(json_key_io)
62
+ json_key = MultiJson.load(json_key_io.read)
63
+ fail "the json is missing the #{key} field" unless json_key.key?('type')
64
+ type = json_key['type']
65
+ return json_key, ServiceAccountCredentials if type == 'service_account'
66
+ return [json_key, UserRefreshCredentials] if type == 'authorized_user'
67
+ fail "credentials type '#{type}' is not supported"
68
+ end
69
+ end
70
+
43
71
  # Obtains the default credentials implementation to use in this
44
72
  # environment.
45
73
  #
@@ -53,9 +81,9 @@ END
53
81
  # @param scope [string|array] the scope(s) to access
54
82
  # @param options [hash] allows override of the connection being used
55
83
  def get_application_default(scope, options = {})
56
- creds = ServiceAccountCredentials.from_env(scope)
84
+ creds = DefaultCredentials.from_env(scope)
57
85
  return creds unless creds.nil?
58
- creds = ServiceAccountCredentials.from_well_known_path(scope)
86
+ creds = DefaultCredentials.from_well_known_path(scope)
59
87
  return creds unless creds.nil?
60
88
  fail NOT_FOUND_ERROR unless GCECredentials.on_gce?(options)
61
89
  GCECredentials.new
@@ -0,0 +1,93 @@
1
+ # Copyright 2015, Google Inc.
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are
6
+ # met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright
9
+ # notice, this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above
11
+ # copyright notice, this list of conditions and the following disclaimer
12
+ # in the documentation and/or other materials provided with the
13
+ # distribution.
14
+ # * Neither the name of Google Inc. nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+ #
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ require 'memoist'
31
+ require 'rbconfig'
32
+
33
+ module Google
34
+ # Module Auth provides classes that provide Google-specific authorization
35
+ # used to access Google APIs.
36
+ module Auth
37
+ # CredentialsLoader contains the behaviour used to locate and find default
38
+ # credentials files on the file system.
39
+ module CredentialsLoader
40
+ extend Memoist
41
+ ENV_VAR = 'GOOGLE_APPLICATION_CREDENTIALS'
42
+ NOT_FOUND_ERROR =
43
+ "Unable to read the credential file specified by #{ENV_VAR}"
44
+ WELL_KNOWN_PATH = 'gcloud/application_default_credentials.json'
45
+ WELL_KNOWN_ERROR = 'Unable to read the default credential file'
46
+
47
+ # determines if the current OS is windows
48
+ def windows?
49
+ RbConfig::CONFIG['host_os'] =~ /Windows|mswin/
50
+ end
51
+ memoize :windows?
52
+
53
+ # make_creds proxies the construction of a credentials instance
54
+ #
55
+ # By default, it calls #new on the current class, but this behaviour can
56
+ # be modified, allowing different instances to be created.
57
+ def make_creds(*args)
58
+ new(*args)
59
+ end
60
+
61
+ # Creates an instance from the path specified in an environment
62
+ # variable.
63
+ #
64
+ # @param scope [string|array] the scope(s) to access
65
+ def from_env(scope)
66
+ return nil unless ENV.key?(ENV_VAR)
67
+ path = ENV[ENV_VAR]
68
+ fail 'file #{path} does not exist' unless File.exist?(path)
69
+ File.open(path) do |f|
70
+ return make_creds(scope, f)
71
+ end
72
+ rescue StandardError => e
73
+ raise "#{NOT_FOUND_ERROR}: #{e}"
74
+ end
75
+
76
+ # Creates an instance from a well known path.
77
+ #
78
+ # @param scope [string|array] the scope(s) to access
79
+ def from_well_known_path(scope)
80
+ home_var, base = windows? ? 'APPDATA' : 'HOME', WELL_KNOWN_PATH
81
+ root = ENV[home_var].nil? ? '' : ENV[home_var]
82
+ base = File.join('.config', base) unless windows?
83
+ path = File.join(root, base)
84
+ return nil unless File.exist?(path)
85
+ File.open(path) do |f|
86
+ return make_creds(scope, f)
87
+ end
88
+ rescue StandardError => e
89
+ raise "#{WELL_KNOWN_ERROR}: #{e}"
90
+ end
91
+ end
92
+ end
93
+ end
@@ -28,18 +28,8 @@
28
28
  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
29
 
30
30
  require 'googleauth/signet'
31
- require 'memoist'
31
+ require 'googleauth/credentials_loader'
32
32
  require 'multi_json'
33
- require 'openssl'
34
- require 'rbconfig'
35
-
36
- # Reads the private key and client email fields from service account JSON key.
37
- def read_json_key(json_key_io)
38
- json_key = MultiJson.load(json_key_io.read)
39
- fail 'missing client_email' unless json_key.key?('client_email')
40
- fail 'missing private_key' unless json_key.key?('private_key')
41
- [json_key['private_key'], json_key['client_email']]
42
- end
43
33
 
44
34
  module Google
45
35
  # Module Auth provides classes that provide Google-specific authorization
@@ -53,52 +43,16 @@ module Google
53
43
  #
54
44
  # cf [Application Default Credentials](http://goo.gl/mkAHpZ)
55
45
  class ServiceAccountCredentials < Signet::OAuth2::Client
56
- ENV_VAR = 'GOOGLE_APPLICATION_CREDENTIALS'
57
- NOT_FOUND_ERROR =
58
- "Unable to read the credential file specified by #{ENV_VAR}"
59
46
  TOKEN_CRED_URI = 'https://www.googleapis.com/oauth2/v3/token'
60
- WELL_KNOWN_PATH = 'gcloud/application_default_credentials.json'
61
- WELL_KNOWN_ERROR = 'Unable to read the default credential file'
62
-
63
- class << self
64
- extend Memoist
65
-
66
- # determines if the current OS is windows
67
- def windows?
68
- RbConfig::CONFIG['host_os'] =~ /Windows|mswin/
69
- end
70
- memoize :windows?
71
-
72
- # Creates an instance from the path specified in an environment
73
- # variable.
74
- #
75
- # @param scope [string|array] the scope(s) to access
76
- def from_env(scope)
77
- return nil unless ENV.key?(ENV_VAR)
78
- path = ENV[ENV_VAR]
79
- fail 'file #{path} does not exist' unless File.exist?(path)
80
- File.open(path) do |f|
81
- return new(scope, f)
82
- end
83
- rescue StandardError => e
84
- raise "#{NOT_FOUND_ERROR}: #{e}"
85
- end
47
+ extend CredentialsLoader
86
48
 
87
- # Creates an instance from a well known path.
88
- #
89
- # @param scope [string|array] the scope(s) to access
90
- def from_well_known_path(scope)
91
- home_var, base = windows? ? 'APPDATA' : 'HOME', WELL_KNOWN_PATH
92
- root = ENV[home_var].nil? ? '' : ENV[home_var]
93
- base = File.join('.config', base) unless windows?
94
- path = File.join(root, base)
95
- return nil unless File.exist?(path)
96
- File.open(path) do |f|
97
- return new(scope, f)
98
- end
99
- rescue StandardError => e
100
- raise "#{WELL_KNOWN_ERROR}: #{e}"
101
- end
49
+ # Reads the private key and client email fields from the service account
50
+ # JSON key.
51
+ def self.read_json_key(json_key_io)
52
+ json_key = MultiJson.load(json_key_io.read)
53
+ fail 'missing client_email' unless json_key.key?('client_email')
54
+ fail 'missing private_key' unless json_key.key?('private_key')
55
+ [json_key['private_key'], json_key['client_email']]
102
56
  end
103
57
 
104
58
  # Initializes a ServiceAccountCredentials.
@@ -106,9 +60,9 @@ module Google
106
60
  # @param scope [string|array] the scope(s) to access
107
61
  # @param json_key_io [IO] an IO from which the JSON key can be read
108
62
  def initialize(scope, json_key_io)
109
- private_key, client_email = read_json_key(json_key_io)
63
+ private_key, client_email = self.class.read_json_key(json_key_io)
110
64
  super(token_credential_uri: TOKEN_CRED_URI,
111
- audience: TOKEN_CRED_URI, # TODO: confirm this
65
+ audience: TOKEN_CRED_URI,
112
66
  scope: scope,
113
67
  issuer: client_email,
114
68
  signing_key: OpenSSL::PKey::RSA.new(private_key))
@@ -0,0 +1,76 @@
1
+ # Copyright 2015, Google Inc.
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are
6
+ # met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright
9
+ # notice, this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above
11
+ # copyright notice, this list of conditions and the following disclaimer
12
+ # in the documentation and/or other materials provided with the
13
+ # distribution.
14
+ # * Neither the name of Google Inc. nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+ #
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ require 'googleauth/signet'
31
+ require 'googleauth/credentials_loader'
32
+ require 'multi_json'
33
+
34
+ module Google
35
+ # Module Auth provides classes that provide Google-specific authorization
36
+ # used to access Google APIs.
37
+ module Auth
38
+ # Authenticates requests using User Refresh credentials.
39
+ #
40
+ # This class allows authorizing requests from user refresh tokens.
41
+ #
42
+ # This the end of the result of a 3LO flow. E.g, the end result of
43
+ # 'gcloud auth login' saves a file with these contents in well known
44
+ # location
45
+ #
46
+ # cf [Application Default Credentials](http://goo.gl/mkAHpZ)
47
+ class UserRefreshCredentials < Signet::OAuth2::Client
48
+ TOKEN_CRED_URI = 'https://www.googleapis.com/oauth2/v3/token'
49
+ extend CredentialsLoader
50
+
51
+ # Reads the client_id, client_secret and refresh_token fields from the
52
+ # JSON key.
53
+ def self.read_json_key(json_key_io)
54
+ json_key = MultiJson.load(json_key_io.read)
55
+ wanted = %w(client_id client_secret refresh_token)
56
+ wanted.each do |key|
57
+ fail "the json is missing the #{key} field" unless json_key.key?(key)
58
+ end
59
+ json_key
60
+ end
61
+
62
+ # Initializes a UserRefreshCredentials.
63
+ #
64
+ # @param scope [string|array] the scope(s) to access
65
+ # @param json_key_io [IO] an IO from which the JSON key can be read
66
+ def initialize(scope, json_key_io)
67
+ user_creds = self.class.read_json_key(json_key_io)
68
+ super(token_credential_uri: TOKEN_CRED_URI,
69
+ client_id: user_creds['client_id'],
70
+ client_secret: user_creds['client_secret'],
71
+ refresh_token: user_creds['refresh_token'],
72
+ scope: scope)
73
+ end
74
+ end
75
+ end
76
+ end
@@ -31,6 +31,6 @@ module Google
31
31
  # Module Auth provides classes that provide Google-specific authorization
32
32
  # used to access Google APIs.
33
33
  module Auth
34
- VERSION = '0.1.0'
34
+ VERSION = '0.2.0'
35
35
  end
36
36
  end
@@ -44,7 +44,8 @@ describe Google::Auth::GCECredentials do
44
44
  @client = GCECredentials.new
45
45
  end
46
46
 
47
- def make_auth_stubs(access_token: '')
47
+ def make_auth_stubs(opts = {})
48
+ access_token = opts[:access_token] || ''
48
49
  Faraday::Adapter::Test::Stubs.new do |stub|
49
50
  stub.get(MD_URI) do |env|
50
51
  headers = env[:request_headers]
@@ -38,7 +38,7 @@ require 'spec_helper'
38
38
  describe '#get_application_default' do
39
39
  before(:example) do
40
40
  @key = OpenSSL::PKey::RSA.new(2048)
41
- @var_name = ServiceAccountCredentials::ENV_VAR
41
+ @var_name = CredentialsLoader::ENV_VAR
42
42
  @orig = ENV[@var_name]
43
43
  @home = ENV['HOME']
44
44
  @scope = 'https://www.googleapis.com/auth/userinfo.profile'
@@ -49,86 +49,152 @@ describe '#get_application_default' do
49
49
  ENV['HOME'] = @home unless @home == ENV['HOME']
50
50
  end
51
51
 
52
- def cred_json_text
53
- cred_json = {
54
- private_key_id: 'a_private_key_id',
55
- private_key: @key.to_pem,
56
- client_email: 'app@developer.gserviceaccount.com',
57
- client_id: 'app.apps.googleusercontent.com',
58
- type: 'service_account'
59
- }
60
- MultiJson.dump(cred_json)
61
- end
52
+ shared_examples 'it cannot load misconfigured credentials' do
53
+ it 'fails if the GOOGLE_APPLICATION_CREDENTIALS path does not exist' do
54
+ Dir.mktmpdir do |dir|
55
+ key_path = File.join(dir, 'does-not-exist')
56
+ ENV[@var_name] = key_path
57
+ expect { Google::Auth.get_application_default(@scope) }.to raise_error
58
+ end
59
+ end
62
60
 
63
- it 'fails if the GOOGLE_APPLICATION_CREDENTIALS path does not exist' do
64
- Dir.mktmpdir do |dir|
65
- key_path = File.join(dir, 'does-not-exist')
66
- ENV[@var_name] = key_path
67
- expect { Google::Auth.get_application_default(@scope) }.to raise_error
61
+ it 'fails without default file or env if not on compute engine' do
62
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
63
+ stub.get('/') do |_env|
64
+ [404,
65
+ { 'Metadata-Flavor' => 'Google' },
66
+ '']
67
+ end
68
+ end # GCE not detected
69
+ Dir.mktmpdir do |dir|
70
+ ENV.delete(@var_name) unless ENV[@var_name].nil? # no env var
71
+ ENV['HOME'] = dir # no config present in this tmp dir
72
+ c = Faraday.new do |b|
73
+ b.adapter(:test, stubs)
74
+ end
75
+ blk = proc do
76
+ Google::Auth.get_application_default(@scope, connection: c)
77
+ end
78
+ expect(&blk).to raise_error
79
+ end
80
+ stubs.verify_stubbed_calls
68
81
  end
69
82
  end
70
83
 
71
- it 'succeeds if the GOOGLE_APPLICATION_CREDENTIALS file is valid' do
72
- Dir.mktmpdir do |dir|
73
- key_path = File.join(dir, 'my_cert_file')
74
- FileUtils.mkdir_p(File.dirname(key_path))
75
- File.write(key_path, cred_json_text)
76
- ENV[@var_name] = key_path
77
- expect(Google::Auth.get_application_default(@scope)).to_not be_nil
84
+ shared_examples 'it can successfully load credentials' do
85
+ it 'succeeds if the GOOGLE_APPLICATION_CREDENTIALS file is valid' do
86
+ Dir.mktmpdir do |dir|
87
+ key_path = File.join(dir, 'my_cert_file')
88
+ FileUtils.mkdir_p(File.dirname(key_path))
89
+ File.write(key_path, cred_json_text)
90
+ ENV[@var_name] = key_path
91
+ expect(Google::Auth.get_application_default(@scope)).to_not be_nil
92
+ end
93
+ end
94
+
95
+ it 'succeeds with default file without GOOGLE_APPLICATION_CREDENTIALS' do
96
+ ENV.delete(@var_name) unless ENV[@var_name].nil?
97
+ Dir.mktmpdir do |dir|
98
+ key_path = File.join(dir, '.config',
99
+ CredentialsLoader::WELL_KNOWN_PATH)
100
+ FileUtils.mkdir_p(File.dirname(key_path))
101
+ File.write(key_path, cred_json_text)
102
+ ENV['HOME'] = dir
103
+ expect(Google::Auth.get_application_default(@scope)).to_not be_nil
104
+ end
105
+ end
106
+
107
+ it 'succeeds without default file or env if on compute engine' do
108
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
109
+ stub.get('/') do |_env|
110
+ [200,
111
+ { 'Metadata-Flavor' => 'Google' },
112
+ '']
113
+ end
114
+ end # GCE detected
115
+ Dir.mktmpdir do |dir|
116
+ ENV.delete(@var_name) unless ENV[@var_name].nil? # no env var
117
+ ENV['HOME'] = dir # no config present in this tmp dir
118
+ c = Faraday.new do |b|
119
+ b.adapter(:test, stubs)
120
+ end
121
+ creds = Google::Auth.get_application_default(
122
+ @scope,
123
+ connection: c)
124
+ expect(creds).to_not be_nil
125
+ end
126
+ stubs.verify_stubbed_calls
78
127
  end
79
128
  end
80
129
 
81
- it 'succeeds with default file if GOOGLE_APPLICATION_CREDENTIALS is unset' do
82
- ENV.delete(@var_name) unless ENV[@var_name].nil?
83
- Dir.mktmpdir do |dir|
84
- key_path = File.join(dir, '.config',
85
- ServiceAccountCredentials::WELL_KNOWN_PATH)
86
- FileUtils.mkdir_p(File.dirname(key_path))
87
- File.write(key_path, cred_json_text)
88
- ENV['HOME'] = dir
89
- expect(Google::Auth.get_application_default(@scope)).to_not be_nil
130
+ describe 'when credential type is service account' do
131
+ def cred_json_text
132
+ cred_json = {
133
+ private_key_id: 'a_private_key_id',
134
+ private_key: @key.to_pem,
135
+ client_email: 'app@developer.gserviceaccount.com',
136
+ client_id: 'app.apps.googleusercontent.com',
137
+ type: 'service_account'
138
+ }
139
+ MultiJson.dump(cred_json)
90
140
  end
141
+
142
+ it_behaves_like 'it can successfully load credentials'
143
+ it_behaves_like 'it cannot load misconfigured credentials'
91
144
  end
92
145
 
93
- it 'fails without default file or env if not on compute engine' do
94
- stubs = Faraday::Adapter::Test::Stubs.new do |stub|
95
- stub.get('/') do |_env|
96
- [404,
97
- { 'Metadata-Flavor' => 'Google' },
98
- '']
99
- end
100
- end # GCE not detected
101
- Dir.mktmpdir do |dir|
102
- ENV.delete(@var_name) unless ENV[@var_name].nil? # no env var
103
- ENV['HOME'] = dir # no config present in this tmp dir
104
- c = Faraday.new do |b|
105
- b.adapter(:test, stubs)
106
- end
107
- blk = proc do
108
- Google::Auth.get_application_default(@scope, connection: c)
109
- end
110
- expect(&blk).to raise_error
146
+ describe 'when credential type is authorized_user' do
147
+ def cred_json_text
148
+ cred_json = {
149
+ client_secret: 'privatekey',
150
+ refresh_token: 'refreshtoken',
151
+ client_id: 'app.apps.googleusercontent.com',
152
+ type: 'authorized_user'
153
+ }
154
+ MultiJson.dump(cred_json)
111
155
  end
112
- stubs.verify_stubbed_calls
156
+
157
+ it_behaves_like 'it can successfully load credentials'
158
+ it_behaves_like 'it cannot load misconfigured credentials'
113
159
  end
114
160
 
115
- it 'succeeds without default file or env if on compute engine' do
116
- stubs = Faraday::Adapter::Test::Stubs.new do |stub|
117
- stub.get('/') do |_env|
118
- [200,
119
- { 'Metadata-Flavor' => 'Google' },
120
- '']
161
+ describe 'when credential type is unknown' do
162
+ def cred_json_text
163
+ cred_json = {
164
+ client_secret: 'privatekey',
165
+ refresh_token: 'refreshtoken',
166
+ client_id: 'app.apps.googleusercontent.com',
167
+ type: 'not_known_type'
168
+ }
169
+ MultiJson.dump(cred_json)
170
+ end
171
+
172
+ it 'fails if the GOOGLE_APPLICATION_CREDENTIALS file contains the creds' do
173
+ Dir.mktmpdir do |dir|
174
+ key_path = File.join(dir, 'my_cert_file')
175
+ FileUtils.mkdir_p(File.dirname(key_path))
176
+ File.write(key_path, cred_json_text)
177
+ ENV[@var_name] = key_path
178
+ blk = proc do
179
+ Google::Auth.get_application_default(@scope)
180
+ end
181
+ expect(&blk).to raise_error RuntimeError
121
182
  end
122
- end # GCE detected
123
- Dir.mktmpdir do |dir|
124
- ENV.delete(@var_name) unless ENV[@var_name].nil? # no env var
125
- ENV['HOME'] = dir # no config present in this tmp dir
126
- c = Faraday.new do |b|
127
- b.adapter(:test, stubs)
183
+ end
184
+
185
+ it 'fails if the well known file contains the creds' do
186
+ ENV.delete(@var_name) unless ENV[@var_name].nil?
187
+ Dir.mktmpdir do |dir|
188
+ key_path = File.join(dir, '.config',
189
+ CredentialsLoader::WELL_KNOWN_PATH)
190
+ FileUtils.mkdir_p(File.dirname(key_path))
191
+ File.write(key_path, cred_json_text)
192
+ ENV['HOME'] = dir
193
+ blk = proc do
194
+ Google::Auth.get_application_default(@scope)
195
+ end
196
+ expect(&blk).to raise_error RuntimeError
128
197
  end
129
- expect(Google::Auth.get_application_default(@scope,
130
- connection: c)).to_not be_nil
131
198
  end
132
- stubs.verify_stubbed_calls
133
199
  end
134
200
  end
@@ -42,6 +42,7 @@ require 'tmpdir'
42
42
 
43
43
  describe Google::Auth::ServiceAccountCredentials do
44
44
  ServiceAccountCredentials = Google::Auth::ServiceAccountCredentials
45
+ CredentialsLoader = Google::Auth::CredentialsLoader
45
46
 
46
47
  before(:example) do
47
48
  @key = OpenSSL::PKey::RSA.new(2048)
@@ -50,7 +51,8 @@ describe Google::Auth::ServiceAccountCredentials do
50
51
  StringIO.new(cred_json_text))
51
52
  end
52
53
 
53
- def make_auth_stubs(access_token: '')
54
+ def make_auth_stubs(opts = {})
55
+ access_token = opts[:access_token] || ''
54
56
  Faraday::Adapter::Test::Stubs.new do |stub|
55
57
  stub.post('/oauth2/v3/token') do |env|
56
58
  params = Addressable::URI.form_unencode(env[:body])
@@ -78,9 +80,10 @@ describe Google::Auth::ServiceAccountCredentials do
78
80
 
79
81
  describe '#from_env' do
80
82
  before(:example) do
81
- @var_name = ServiceAccountCredentials::ENV_VAR
83
+ @var_name = CredentialsLoader::ENV_VAR
82
84
  @orig = ENV[@var_name]
83
85
  @scope = 'https://www.googleapis.com/auth/userinfo.profile'
86
+ @clz = ServiceAccountCredentials
84
87
  end
85
88
 
86
89
  after(:example) do
@@ -98,18 +101,17 @@ describe Google::Auth::ServiceAccountCredentials do
98
101
  Dir.mktmpdir do |dir|
99
102
  key_path = File.join(dir, 'does-not-exist')
100
103
  ENV[@var_name] = key_path
101
- expect { sac.from_env(@scope) }.to raise_error
104
+ expect { @clz.from_env(@scope) }.to raise_error
102
105
  end
103
106
  end
104
107
 
105
108
  it 'succeeds when the GOOGLE_APPLICATION_CREDENTIALS file is valid' do
106
- sac = ServiceAccountCredentials # shortens name
107
109
  Dir.mktmpdir do |dir|
108
110
  key_path = File.join(dir, 'my_cert_file')
109
111
  FileUtils.mkdir_p(File.dirname(key_path))
110
112
  File.write(key_path, cred_json_text)
111
113
  ENV[@var_name] = key_path
112
- expect(sac.from_env(@scope)).to_not be_nil
114
+ expect(@clz.from_env(@scope)).to_not be_nil
113
115
  end
114
116
  end
115
117
  end
@@ -118,6 +120,8 @@ describe Google::Auth::ServiceAccountCredentials do
118
120
  before(:example) do
119
121
  @home = ENV['HOME']
120
122
  @scope = 'https://www.googleapis.com/auth/userinfo.profile'
123
+ @known_path = CredentialsLoader::WELL_KNOWN_PATH
124
+ @clz = ServiceAccountCredentials
121
125
  end
122
126
 
123
127
  after(:example) do
@@ -130,13 +134,12 @@ describe Google::Auth::ServiceAccountCredentials do
130
134
  end
131
135
 
132
136
  it 'successfully loads the file when it is present' do
133
- sac = ServiceAccountCredentials # shortens name
134
137
  Dir.mktmpdir do |dir|
135
- key_path = File.join(dir, '.config', sac::WELL_KNOWN_PATH)
138
+ key_path = File.join(dir, '.config', @known_path)
136
139
  FileUtils.mkdir_p(File.dirname(key_path))
137
140
  File.write(key_path, cred_json_text)
138
141
  ENV['HOME'] = dir
139
- expect(sac.from_well_known_path(@scope)).to_not be_nil
142
+ expect(@clz.from_well_known_path(@scope)).to_not be_nil
140
143
  end
141
144
  end
142
145
  end
@@ -49,7 +49,8 @@ describe Signet::OAuth2::Client do
49
49
  )
50
50
  end
51
51
 
52
- def make_auth_stubs(access_token: '')
52
+ def make_auth_stubs(opts)
53
+ access_token = opts[:access_token] || ''
53
54
  Faraday::Adapter::Test::Stubs.new do |stub|
54
55
  stub.post('/o/oauth2/token') do |env|
55
56
  params = Addressable::URI.form_unencode(env[:body])
@@ -0,0 +1,170 @@
1
+ # Copyright 2015, Google Inc.
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are
6
+ # met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright
9
+ # notice, this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above
11
+ # copyright notice, this list of conditions and the following disclaimer
12
+ # in the documentation and/or other materials provided with the
13
+ # distribution.
14
+ # * Neither the name of Google Inc. nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+ #
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
31
+ $LOAD_PATH.unshift(spec_dir)
32
+ $LOAD_PATH.uniq!
33
+
34
+ require 'apply_auth_examples'
35
+ require 'fileutils'
36
+ require 'googleauth/user_refresh'
37
+ require 'jwt'
38
+ require 'multi_json'
39
+ require 'openssl'
40
+ require 'spec_helper'
41
+ require 'tmpdir'
42
+
43
+ describe Google::Auth::UserRefreshCredentials do
44
+ UserRefreshCredentials = Google::Auth::UserRefreshCredentials
45
+ CredentialsLoader = Google::Auth::CredentialsLoader
46
+
47
+ before(:example) do
48
+ @key = OpenSSL::PKey::RSA.new(2048)
49
+ @client = UserRefreshCredentials.new(
50
+ 'https://www.googleapis.com/auth/userinfo.profile',
51
+ StringIO.new(cred_json_text))
52
+ end
53
+
54
+ def make_auth_stubs(opts = {})
55
+ access_token = opts[:access_token] || ''
56
+ Faraday::Adapter::Test::Stubs.new do |stub|
57
+ stub.post('/oauth2/v3/token') do |env|
58
+ params = Addressable::URI.form_unencode(env[:body])
59
+ want = %w(grant_type refresh_token)
60
+ expect(params.assoc('grant_type')).to eq(want)
61
+ build_access_token_json(access_token)
62
+ end
63
+ end
64
+ end
65
+
66
+ def cred_json_text(missing = nil)
67
+ cred_json = {
68
+ client_secret: 'privatekey',
69
+ client_id: 'client123',
70
+ refresh_token: 'refreshtoken',
71
+ type: 'authorized_user'
72
+ }
73
+ cred_json.delete(missing.to_sym) unless missing.nil?
74
+ MultiJson.dump(cred_json)
75
+ end
76
+
77
+ it_behaves_like 'apply/apply! are OK'
78
+
79
+ describe '#from_env' do
80
+ before(:example) do
81
+ @var_name = CredentialsLoader::ENV_VAR
82
+ @orig = ENV[@var_name]
83
+ @scope = 'https://www.googleapis.com/auth/userinfo.profile'
84
+ @clz = UserRefreshCredentials
85
+ end
86
+
87
+ after(:example) do
88
+ ENV[@var_name] = @orig unless @orig.nil?
89
+ end
90
+
91
+ it 'returns nil if the GOOGLE_APPLICATION_CREDENTIALS is unset' do
92
+ ENV.delete(@var_name) unless ENV[@var_name].nil?
93
+ expect(UserRefreshCredentials.from_env(@scope)).to be_nil
94
+ end
95
+
96
+ it 'fails if the GOOGLE_APPLICATION_CREDENTIALS path does not exist' do
97
+ ENV.delete(@var_name) unless ENV[@var_name].nil?
98
+ expect(UserRefreshCredentials.from_env(@scope)).to be_nil
99
+ Dir.mktmpdir do |dir|
100
+ key_path = File.join(dir, 'does-not-exist')
101
+ ENV[@var_name] = key_path
102
+ expect { @clz.from_env(@scope) }.to raise_error
103
+ end
104
+ end
105
+
106
+ it 'fails if the GOOGLE_APPLICATION_CREDENTIALS path file is invalid' do
107
+ needed = %w(client_id client_secret refresh_token)
108
+ needed.each do |missing|
109
+ Dir.mktmpdir do |dir|
110
+ key_path = File.join(dir, 'my_cert_file')
111
+ FileUtils.mkdir_p(File.dirname(key_path))
112
+ File.write(key_path, cred_json_text(missing))
113
+ ENV[@var_name] = key_path
114
+ expect { @clz.from_env(@scope) }.to raise_error
115
+ end
116
+ end
117
+ end
118
+
119
+ it 'succeeds when the GOOGLE_APPLICATION_CREDENTIALS file is valid' do
120
+ Dir.mktmpdir do |dir|
121
+ key_path = File.join(dir, 'my_cert_file')
122
+ FileUtils.mkdir_p(File.dirname(key_path))
123
+ File.write(key_path, cred_json_text)
124
+ ENV[@var_name] = key_path
125
+ expect(@clz.from_env(@scope)).to_not be_nil
126
+ end
127
+ end
128
+ end
129
+
130
+ describe '#from_well_known_path' do
131
+ before(:example) do
132
+ @home = ENV['HOME']
133
+ @scope = 'https://www.googleapis.com/auth/userinfo.profile'
134
+ @known_path = CredentialsLoader::WELL_KNOWN_PATH
135
+ @clz = UserRefreshCredentials
136
+ end
137
+
138
+ after(:example) do
139
+ ENV['HOME'] = @home unless @home == ENV['HOME']
140
+ end
141
+
142
+ it 'is nil if no file exists' do
143
+ ENV['HOME'] = File.dirname(__FILE__)
144
+ expect(UserRefreshCredentials.from_well_known_path(@scope)).to be_nil
145
+ end
146
+
147
+ it 'fails if the file is invalid' do
148
+ needed = %w(client_id client_secret refresh_token)
149
+ needed.each do |missing|
150
+ Dir.mktmpdir do |dir|
151
+ key_path = File.join(dir, '.config', @known_path)
152
+ FileUtils.mkdir_p(File.dirname(key_path))
153
+ File.write(key_path, cred_json_text(missing))
154
+ ENV['HOME'] = dir
155
+ expect { @clz.from_env(@scope) }.to raise_error
156
+ end
157
+ end
158
+ end
159
+
160
+ it 'successfully loads the file when it is present' do
161
+ Dir.mktmpdir do |dir|
162
+ key_path = File.join(dir, '.config', @known_path)
163
+ FileUtils.mkdir_p(File.dirname(key_path))
164
+ File.write(key_path, cred_json_text)
165
+ ENV['HOME'] = dir
166
+ expect(@clz.from_well_known_path(@scope)).to_not be_nil
167
+ end
168
+ end
169
+ end
170
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: googleauth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Emiola
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-25 00:00:00.000000000 Z
11
+ date: 2015-03-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 1.2.1
47
+ version: 1.3.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 1.2.1
54
+ version: 1.3.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: memoist
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - '='
74
74
  - !ruby/object:Gem::Version
75
- version: 1.10.1
75
+ version: 1.11.0
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - '='
81
81
  - !ruby/object:Gem::Version
82
- version: 1.10.1
82
+ version: 1.11.0
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: signet
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -161,6 +161,8 @@ extra_rdoc_files: []
161
161
  files:
162
162
  - ".gitignore"
163
163
  - ".rspec"
164
+ - ".rubocop.yml"
165
+ - ".rubocop_todo.yml"
164
166
  - ".travis.yml"
165
167
  - CONTRIBUTING.md
166
168
  - COPYING
@@ -170,14 +172,17 @@ files:
170
172
  - googleauth.gemspec
171
173
  - lib/googleauth.rb
172
174
  - lib/googleauth/compute_engine.rb
175
+ - lib/googleauth/credentials_loader.rb
173
176
  - lib/googleauth/service_account.rb
174
177
  - lib/googleauth/signet.rb
178
+ - lib/googleauth/user_refresh.rb
175
179
  - lib/googleauth/version.rb
176
180
  - spec/googleauth/apply_auth_examples.rb
177
181
  - spec/googleauth/compute_engine_spec.rb
178
182
  - spec/googleauth/get_application_default_spec.rb
179
183
  - spec/googleauth/service_account_spec.rb
180
184
  - spec/googleauth/signet_spec.rb
185
+ - spec/googleauth/user_refresh_spec.rb
181
186
  - spec/spec_helper.rb
182
187
  homepage: https://github.com/google/google-auth-library-ruby
183
188
  licenses:
@@ -209,4 +214,5 @@ test_files:
209
214
  - spec/googleauth/get_application_default_spec.rb
210
215
  - spec/googleauth/service_account_spec.rb
211
216
  - spec/googleauth/signet_spec.rb
217
+ - spec/googleauth/user_refresh_spec.rb
212
218
  - spec/spec_helper.rb