googleauth 0.5.1 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +5 -5
  2. data/.github/CODEOWNERS +7 -0
  3. data/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +5 -4
  4. data/.github/ISSUE_TEMPLATE/bug_report.md +36 -0
  5. data/.github/ISSUE_TEMPLATE/feature_request.md +21 -0
  6. data/.github/ISSUE_TEMPLATE/support_request.md +7 -0
  7. data/.kokoro/build.bat +16 -0
  8. data/.kokoro/build.sh +4 -0
  9. data/.kokoro/continuous/common.cfg +24 -0
  10. data/.kokoro/continuous/linux.cfg +25 -0
  11. data/.kokoro/continuous/osx.cfg +8 -0
  12. data/.kokoro/continuous/post.cfg +30 -0
  13. data/.kokoro/continuous/windows.cfg +29 -0
  14. data/.kokoro/osx.sh +4 -0
  15. data/.kokoro/presubmit/common.cfg +24 -0
  16. data/.kokoro/presubmit/linux.cfg +24 -0
  17. data/.kokoro/presubmit/osx.cfg +8 -0
  18. data/.kokoro/presubmit/windows.cfg +29 -0
  19. data/.kokoro/release.cfg +94 -0
  20. data/.kokoro/trampoline.bat +10 -0
  21. data/.kokoro/trampoline.sh +4 -0
  22. data/.repo-metadata.json +5 -0
  23. data/.rubocop.yml +19 -1
  24. data/CHANGELOG.md +112 -19
  25. data/CODE_OF_CONDUCT.md +43 -0
  26. data/Gemfile +19 -13
  27. data/{COPYING → LICENSE} +0 -0
  28. data/README.md +58 -18
  29. data/Rakefile +126 -9
  30. data/googleauth.gemspec +28 -25
  31. data/integration/helper.rb +31 -0
  32. data/integration/id_tokens/key_source_test.rb +74 -0
  33. data/lib/googleauth.rb +7 -96
  34. data/lib/googleauth/application_default.rb +81 -0
  35. data/lib/googleauth/client_id.rb +21 -19
  36. data/lib/googleauth/compute_engine.rb +70 -43
  37. data/lib/googleauth/credentials.rb +442 -0
  38. data/lib/googleauth/credentials_loader.rb +117 -43
  39. data/lib/googleauth/default_credentials.rb +93 -0
  40. data/lib/googleauth/iam.rb +11 -11
  41. data/lib/googleauth/id_tokens.rb +233 -0
  42. data/lib/googleauth/id_tokens/errors.rb +71 -0
  43. data/lib/googleauth/id_tokens/key_sources.rb +394 -0
  44. data/lib/googleauth/id_tokens/verifier.rb +144 -0
  45. data/lib/googleauth/json_key_reader.rb +50 -0
  46. data/lib/googleauth/scope_util.rb +12 -12
  47. data/lib/googleauth/service_account.rb +74 -63
  48. data/lib/googleauth/signet.rb +55 -13
  49. data/lib/googleauth/stores/file_token_store.rb +8 -8
  50. data/lib/googleauth/stores/redis_token_store.rb +22 -22
  51. data/lib/googleauth/token_store.rb +6 -6
  52. data/lib/googleauth/user_authorizer.rb +80 -68
  53. data/lib/googleauth/user_refresh.rb +44 -35
  54. data/lib/googleauth/version.rb +1 -1
  55. data/lib/googleauth/web_user_authorizer.rb +77 -68
  56. data/rakelib/devsite_builder.rb +45 -0
  57. data/rakelib/link_checker.rb +64 -0
  58. data/rakelib/repo_metadata.rb +59 -0
  59. data/spec/googleauth/apply_auth_examples.rb +74 -50
  60. data/spec/googleauth/client_id_spec.rb +75 -55
  61. data/spec/googleauth/compute_engine_spec.rb +98 -46
  62. data/spec/googleauth/credentials_spec.rb +478 -0
  63. data/spec/googleauth/get_application_default_spec.rb +149 -111
  64. data/spec/googleauth/iam_spec.rb +25 -25
  65. data/spec/googleauth/scope_util_spec.rb +26 -24
  66. data/spec/googleauth/service_account_spec.rb +269 -144
  67. data/spec/googleauth/signet_spec.rb +101 -30
  68. data/spec/googleauth/stores/file_token_store_spec.rb +12 -13
  69. data/spec/googleauth/stores/redis_token_store_spec.rb +11 -11
  70. data/spec/googleauth/stores/store_examples.rb +16 -16
  71. data/spec/googleauth/user_authorizer_spec.rb +153 -124
  72. data/spec/googleauth/user_refresh_spec.rb +186 -121
  73. data/spec/googleauth/web_user_authorizer_spec.rb +82 -69
  74. data/spec/spec_helper.rb +21 -19
  75. data/test/helper.rb +33 -0
  76. data/test/id_tokens/key_sources_test.rb +240 -0
  77. data/test/id_tokens/verifier_test.rb +269 -0
  78. metadata +87 -34
  79. data/.rubocop_todo.yml +0 -32
  80. data/.travis.yml +0 -37
@@ -0,0 +1,50 @@
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
+ module Google
31
+ # Module Auth provides classes that provide Google-specific authorization
32
+ # used to access Google APIs.
33
+ module Auth
34
+ # JsonKeyReader contains the behaviour used to read private key and
35
+ # client email fields from the service account
36
+ module JsonKeyReader
37
+ def read_json_key json_key_io
38
+ json_key = MultiJson.load json_key_io.read
39
+ raise "missing client_email" unless json_key.key? "client_email"
40
+ raise "missing private_key" unless json_key.key? "private_key"
41
+ [
42
+ json_key["private_key"],
43
+ json_key["client_email"],
44
+ json_key["project_id"],
45
+ json_key["quota_project_id"]
46
+ ]
47
+ end
48
+ end
49
+ end
50
+ end
@@ -27,33 +27,33 @@
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/signet'
31
- require 'googleauth/credentials_loader'
32
- require 'multi_json'
30
+ require "googleauth/signet"
31
+ require "googleauth/credentials_loader"
32
+ require "multi_json"
33
33
 
34
34
  module Google
35
35
  module Auth
36
36
  # Small utility for normalizing scopes into canonical form
37
37
  module ScopeUtil
38
38
  ALIASES = {
39
- 'email' => 'https://www.googleapis.com/auth/userinfo.email',
40
- 'profile' => 'https://www.googleapis.com/auth/userinfo.profile',
41
- 'openid' => 'https://www.googleapis.com/auth/plus.me'
42
- }
39
+ "email" => "https://www.googleapis.com/auth/userinfo.email",
40
+ "profile" => "https://www.googleapis.com/auth/userinfo.profile",
41
+ "openid" => "https://www.googleapis.com/auth/plus.me"
42
+ }.freeze
43
43
 
44
- def self.normalize(scope)
45
- list = as_array(scope)
44
+ def self.normalize scope
45
+ list = as_array scope
46
46
  list.map { |item| ALIASES[item] || item }
47
47
  end
48
48
 
49
- def self.as_array(scope)
49
+ def self.as_array scope
50
50
  case scope
51
51
  when Array
52
52
  scope
53
53
  when String
54
- scope.split(' ')
54
+ scope.split " "
55
55
  else
56
- fail 'Invalid scope value. Must be string or array'
56
+ raise "Invalid scope value. Must be string or array"
57
57
  end
58
58
  end
59
59
  end
@@ -27,11 +27,12 @@
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/signet'
31
- require 'googleauth/credentials_loader'
32
- require 'jwt'
33
- require 'multi_json'
34
- require 'stringio'
30
+ require "googleauth/signet"
31
+ require "googleauth/credentials_loader"
32
+ require "googleauth/json_key_reader"
33
+ require "jwt"
34
+ require "multi_json"
35
+ require "stringio"
35
36
 
36
37
  module Google
37
38
  # Module Auth provides classes that provide Google-specific authorization
@@ -44,42 +45,56 @@ module Google
44
45
  # from credentials from a json key file downloaded from the developer
45
46
  # console (via 'Generate new Json Key').
46
47
  #
47
- # cf [Application Default Credentials](http://goo.gl/mkAHpZ)
48
+ # cf [Application Default Credentials](https://cloud.google.com/docs/authentication/production)
48
49
  class ServiceAccountCredentials < Signet::OAuth2::Client
49
- TOKEN_CRED_URI = 'https://www.googleapis.com/oauth2/v3/token'
50
+ TOKEN_CRED_URI = "https://www.googleapis.com/oauth2/v4/token".freeze
50
51
  extend CredentialsLoader
52
+ extend JsonKeyReader
53
+ attr_reader :project_id
54
+ attr_reader :quota_project_id
51
55
 
52
56
  # Creates a ServiceAccountCredentials.
53
57
  #
54
58
  # @param json_key_io [IO] an IO from which the JSON key can be read
55
59
  # @param scope [string|array|nil] the scope(s) to access
56
- def self.make_creds(options = {})
57
- json_key_io, scope = options.values_at(:json_key_io, :scope)
60
+ def self.make_creds options = {}
61
+ json_key_io, scope, target_audience = options.values_at :json_key_io, :scope, :target_audience
62
+ raise ArgumentError, "Cannot specify both scope and target_audience" if scope && target_audience
63
+
58
64
  if json_key_io
59
- private_key, client_email = read_json_key(json_key_io)
65
+ private_key, client_email, project_id, quota_project_id = read_json_key json_key_io
60
66
  else
61
- private_key = ENV[CredentialsLoader::PRIVATE_KEY_VAR]
67
+ private_key = unescape ENV[CredentialsLoader::PRIVATE_KEY_VAR]
62
68
  client_email = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
69
+ project_id = ENV[CredentialsLoader::PROJECT_ID_VAR]
70
+ quota_project_id = nil
63
71
  end
72
+ project_id ||= CredentialsLoader.load_gcloud_project_id
64
73
 
65
74
  new(token_credential_uri: TOKEN_CRED_URI,
66
- audience: TOKEN_CRED_URI,
67
- scope: scope,
68
- issuer: client_email,
69
- signing_key: OpenSSL::PKey::RSA.new(private_key))
75
+ audience: TOKEN_CRED_URI,
76
+ scope: scope,
77
+ target_audience: target_audience,
78
+ issuer: client_email,
79
+ signing_key: OpenSSL::PKey::RSA.new(private_key),
80
+ project_id: project_id,
81
+ quota_project_id: quota_project_id)
82
+ .configure_connection(options)
70
83
  end
71
84
 
72
- # Reads the private key and client email fields from the service account
73
- # JSON key.
74
- def self.read_json_key(json_key_io)
75
- json_key = MultiJson.load(json_key_io.read)
76
- fail 'missing client_email' unless json_key.key?('client_email')
77
- fail 'missing private_key' unless json_key.key?('private_key')
78
- [json_key['private_key'], json_key['client_email']]
85
+ # Handles certain escape sequences that sometimes appear in input.
86
+ # Specifically, interprets the "\n" sequence for newline, and removes
87
+ # enclosing quotes.
88
+ def self.unescape str
89
+ str = str.gsub '\n', "\n"
90
+ str = str[1..-2] if str.start_with?('"') && str.end_with?('"')
91
+ str
79
92
  end
80
93
 
81
- def initialize(options = {})
82
- super(options)
94
+ def initialize options = {}
95
+ @project_id = options[:project_id]
96
+ @quota_project_id = options[:quota_project_id]
97
+ super options
83
98
  end
84
99
 
85
100
  # Extends the base class.
@@ -87,9 +102,9 @@ module Google
87
102
  # If scope(s) is not set, it creates a transient
88
103
  # ServiceAccountJwtHeaderCredentials instance and uses that to
89
104
  # authenticate instead.
90
- def apply!(a_hash, opts = {})
105
+ def apply! a_hash, opts = {}
91
106
  # Use the base implementation if scopes are set
92
- unless scope.nil?
107
+ unless scope.nil? && target_audience.nil?
93
108
  super
94
109
  return
95
110
  end
@@ -97,13 +112,13 @@ module Google
97
112
  # Use the ServiceAccountJwtHeaderCredentials using the same cred values
98
113
  # if no scopes are set.
99
114
  cred_json = {
100
- private_key: @signing_key.to_s,
115
+ private_key: @signing_key.to_s,
101
116
  client_email: @issuer
102
117
  }
103
118
  alt_clz = ServiceAccountJwtHeaderCredentials
104
- key_io = StringIO.new(MultiJson.dump(cred_json))
105
- alt = alt_clz.make_creds(json_key_io: key_io)
106
- alt.apply!(a_hash)
119
+ key_io = StringIO.new MultiJson.dump(cred_json)
120
+ alt = alt_clz.make_creds json_key_io: key_io
121
+ alt.apply! a_hash
107
122
  end
108
123
  end
109
124
 
@@ -115,14 +130,17 @@ module Google
115
130
  # console (via 'Generate new Json Key'). It is not part of any OAuth2
116
131
  # flow, rather it creates a JWT and sends that as a credential.
117
132
  #
118
- # cf [Application Default Credentials](http://goo.gl/mkAHpZ)
133
+ # cf [Application Default Credentials](https://cloud.google.com/docs/authentication/production)
119
134
  class ServiceAccountJwtHeaderCredentials
120
135
  JWT_AUD_URI_KEY = :jwt_aud_uri
121
136
  AUTH_METADATA_KEY = Signet::OAuth2::AUTH_METADATA_KEY
122
- TOKEN_CRED_URI = 'https://www.googleapis.com/oauth2/v3/token'
123
- SIGNING_ALGORITHM = 'RS256'
137
+ TOKEN_CRED_URI = "https://www.googleapis.com/oauth2/v4/token".freeze
138
+ SIGNING_ALGORITHM = "RS256".freeze
124
139
  EXPIRY = 60
125
140
  extend CredentialsLoader
141
+ extend JsonKeyReader
142
+ attr_reader :project_id
143
+ attr_reader :quota_project_id
126
144
 
127
145
  # make_creds proxies the construction of a credentials instance
128
146
  #
@@ -131,51 +149,44 @@ module Google
131
149
  # By default, it calls #new with 2 args, the second one being an
132
150
  # optional scope. Here's the constructor only has one param, so
133
151
  # we modify make_creds to reflect this.
134
- def self.make_creds(*args)
135
- new(json_key_io: args[0][:json_key_io])
136
- end
137
-
138
- # Reads the private key and client email fields from the service account
139
- # JSON key.
140
- def self.read_json_key(json_key_io)
141
- json_key = MultiJson.load(json_key_io.read)
142
- fail 'missing client_email' unless json_key.key?('client_email')
143
- fail 'missing private_key' unless json_key.key?('private_key')
144
- [json_key['private_key'], json_key['client_email']]
152
+ def self.make_creds *args
153
+ new json_key_io: args[0][:json_key_io]
145
154
  end
146
155
 
147
156
  # Initializes a ServiceAccountJwtHeaderCredentials.
148
157
  #
149
158
  # @param json_key_io [IO] an IO from which the JSON key can be read
150
- def initialize(options = {})
159
+ def initialize options = {}
151
160
  json_key_io = options[:json_key_io]
152
161
  if json_key_io
153
- private_key, client_email = self.class.read_json_key(json_key_io)
162
+ @private_key, @issuer, @project_id, @quota_project_id =
163
+ self.class.read_json_key json_key_io
154
164
  else
155
- private_key = ENV[CredentialsLoader::PRIVATE_KEY_VAR]
156
- client_email = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
165
+ @private_key = ENV[CredentialsLoader::PRIVATE_KEY_VAR]
166
+ @issuer = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
167
+ @project_id = ENV[CredentialsLoader::PROJECT_ID_VAR]
168
+ @quota_project_id = nil
157
169
  end
158
- @private_key = private_key
159
- @issuer = client_email
160
- @signing_key = OpenSSL::PKey::RSA.new(private_key)
170
+ @project_id ||= CredentialsLoader.load_gcloud_project_id
171
+ @signing_key = OpenSSL::PKey::RSA.new @private_key
161
172
  end
162
173
 
163
174
  # Construct a jwt token if the JWT_AUD_URI key is present in the input
164
175
  # hash.
165
176
  #
166
177
  # The jwt token is used as the value of a 'Bearer '.
167
- def apply!(a_hash, opts = {})
168
- jwt_aud_uri = a_hash.delete(JWT_AUD_URI_KEY)
178
+ def apply! a_hash, opts = {}
179
+ jwt_aud_uri = a_hash.delete JWT_AUD_URI_KEY
169
180
  return a_hash if jwt_aud_uri.nil?
170
- jwt_token = new_jwt_token(jwt_aud_uri, opts)
181
+ jwt_token = new_jwt_token jwt_aud_uri, opts
171
182
  a_hash[AUTH_METADATA_KEY] = "Bearer #{jwt_token}"
172
183
  a_hash
173
184
  end
174
185
 
175
186
  # Returns a clone of a_hash updated with the authoriation header
176
- def apply(a_hash, opts = {})
187
+ def apply a_hash, opts = {}
177
188
  a_copy = a_hash.clone
178
- apply!(a_copy, opts)
189
+ apply! a_copy, opts
179
190
  a_copy
180
191
  end
181
192
 
@@ -188,17 +199,17 @@ module Google
188
199
  protected
189
200
 
190
201
  # Creates a jwt uri token.
191
- def new_jwt_token(jwt_aud_uri, options = {})
202
+ def new_jwt_token jwt_aud_uri, options = {}
192
203
  now = Time.new
193
204
  skew = options[:skew] || 60
194
205
  assertion = {
195
- 'iss' => @issuer,
196
- 'sub' => @issuer,
197
- 'aud' => jwt_aud_uri,
198
- 'exp' => (now + EXPIRY).to_i,
199
- 'iat' => (now - skew).to_i
206
+ "iss" => @issuer,
207
+ "sub" => @issuer,
208
+ "aud" => jwt_aud_uri,
209
+ "exp" => (now + EXPIRY).to_i,
210
+ "iat" => (now - skew).to_i
200
211
  }
201
- JWT.encode(assertion, @signing_key, SIGNING_ALGORITHM)
212
+ JWT.encode assertion, @signing_key, SIGNING_ALGORITHM
202
213
  end
203
214
  end
204
215
  end
@@ -27,7 +27,7 @@
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 'signet/oauth_2/client'
30
+ require "signet/oauth_2/client"
31
31
 
32
32
  module Signet
33
33
  # OAuth2 supports OAuth2 authentication.
@@ -38,18 +38,25 @@ module Signet
38
38
  # This reopens Client to add #apply and #apply! methods which update a
39
39
  # hash with the fetched authentication token.
40
40
  class Client
41
+ def configure_connection options
42
+ @connection_info =
43
+ options[:connection_builder] || options[:default_connection]
44
+ self
45
+ end
46
+
41
47
  # Updates a_hash updated with the authentication token
42
- def apply!(a_hash, opts = {})
48
+ def apply! a_hash, opts = {}
43
49
  # fetch the access token there is currently not one, or if the client
44
50
  # has expired
45
- fetch_access_token!(opts) if access_token.nil? || expires_within?(60)
46
- a_hash[AUTH_METADATA_KEY] = "Bearer #{access_token}"
51
+ token_type = target_audience ? :id_token : :access_token
52
+ fetch_access_token! opts if send(token_type).nil? || expires_within?(60)
53
+ a_hash[AUTH_METADATA_KEY] = "Bearer #{send token_type}"
47
54
  end
48
55
 
49
56
  # Returns a clone of a_hash updated with the authentication token
50
- def apply(a_hash, opts = {})
57
+ def apply a_hash, opts = {}
51
58
  a_copy = a_hash.clone
52
- apply!(a_copy, opts)
59
+ apply! a_copy, opts
53
60
  a_copy
54
61
  end
55
62
 
@@ -59,22 +66,57 @@ module Signet
59
66
  lambda(&method(:apply))
60
67
  end
61
68
 
62
- def on_refresh(&block)
63
- @refresh_listeners ||= []
69
+ def on_refresh &block
70
+ @refresh_listeners = [] unless defined? @refresh_listeners
64
71
  @refresh_listeners << block
65
72
  end
66
73
 
67
- alias_method :orig_fetch_access_token!, :fetch_access_token!
68
- def fetch_access_token!(options = {})
69
- info = orig_fetch_access_token!(options)
74
+ alias orig_fetch_access_token! fetch_access_token!
75
+ def fetch_access_token! options = {}
76
+ unless options[:connection]
77
+ connection = build_default_connection
78
+ options = options.merge connection: connection if connection
79
+ end
80
+ info = retry_with_error do
81
+ orig_fetch_access_token! options
82
+ end
70
83
  notify_refresh_listeners
71
84
  info
72
85
  end
73
86
 
74
87
  def notify_refresh_listeners
75
- listeners = @refresh_listeners || []
88
+ listeners = defined?(@refresh_listeners) ? @refresh_listeners : []
76
89
  listeners.each do |block|
77
- block.call(self)
90
+ block.call self
91
+ end
92
+ end
93
+
94
+ def build_default_connection
95
+ if !defined?(@connection_info)
96
+ nil
97
+ elsif @connection_info.respond_to? :call
98
+ @connection_info.call
99
+ else
100
+ @connection_info
101
+ end
102
+ end
103
+
104
+ def retry_with_error max_retry_count = 5
105
+ retry_count = 0
106
+
107
+ begin
108
+ yield
109
+ rescue StandardError => e
110
+ raise e if e.is_a?(Signet::AuthorizationError) || e.is_a?(Signet::ParseError)
111
+
112
+ if retry_count < max_retry_count
113
+ retry_count += 1
114
+ sleep retry_count * 0.3
115
+ retry
116
+ else
117
+ msg = "Unexpected error: #{e.inspect}"
118
+ raise Signet::AuthorizationError, msg
119
+ end
78
120
  end
79
121
  end
80
122
  end
@@ -27,8 +27,8 @@
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 'yaml/store'
31
- require 'googleauth/token_store'
30
+ require "yaml/store"
31
+ require "googleauth/token_store"
32
32
 
33
33
  module Google
34
34
  module Auth
@@ -39,24 +39,24 @@ module Google
39
39
  #
40
40
  # @param [String, File] file
41
41
  # Path to storage file
42
- def initialize(options = {})
42
+ def initialize options = {}
43
43
  path = options[:file]
44
- @store = YAML::Store.new(path)
44
+ @store = YAML::Store.new path
45
45
  end
46
46
 
47
47
  # (see Google::Auth::Stores::TokenStore#load)
48
- def load(id)
48
+ def load id
49
49
  @store.transaction { @store[id] }
50
50
  end
51
51
 
52
52
  # (see Google::Auth::Stores::TokenStore#store)
53
- def store(id, token)
53
+ def store id, token
54
54
  @store.transaction { @store[id] = token }
55
55
  end
56
56
 
57
57
  # (see Google::Auth::Stores::TokenStore#delete)
58
- def delete(id)
59
- @store.transaction { @store.delete(id) }
58
+ def delete id
59
+ @store.transaction { @store.delete id }
60
60
  end
61
61
  end
62
62
  end