googleauth 0.5.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +5 -4
- data/.github/ISSUE_TEMPLATE/bug_report.md +36 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +21 -0
- data/.github/ISSUE_TEMPLATE/support_request.md +7 -0
- data/.kokoro/build.bat +16 -0
- data/.kokoro/build.sh +4 -0
- data/.kokoro/continuous/common.cfg +24 -0
- data/.kokoro/continuous/linux.cfg +25 -0
- data/.kokoro/continuous/osx.cfg +8 -0
- data/.kokoro/continuous/post.cfg +30 -0
- data/.kokoro/continuous/windows.cfg +29 -0
- data/.kokoro/osx.sh +4 -0
- data/.kokoro/presubmit/common.cfg +24 -0
- data/.kokoro/presubmit/linux.cfg +24 -0
- data/.kokoro/presubmit/osx.cfg +8 -0
- data/.kokoro/presubmit/windows.cfg +29 -0
- data/.kokoro/release.cfg +94 -0
- data/.kokoro/trampoline.bat +10 -0
- data/.kokoro/trampoline.sh +4 -0
- data/.repo-metadata.json +5 -0
- data/.rubocop.yml +17 -1
- data/CHANGELOG.md +90 -19
- data/CODE_OF_CONDUCT.md +43 -0
- data/Gemfile +16 -13
- data/README.md +58 -18
- data/Rakefile +106 -10
- data/googleauth.gemspec +27 -25
- data/lib/googleauth/application_default.rb +81 -0
- data/lib/googleauth/client_id.rb +21 -19
- data/lib/googleauth/compute_engine.rb +40 -43
- data/lib/googleauth/credentials.rb +375 -0
- data/lib/googleauth/credentials_loader.rb +117 -43
- data/lib/googleauth/default_credentials.rb +93 -0
- data/lib/googleauth/iam.rb +11 -11
- data/lib/googleauth/json_key_reader.rb +46 -0
- data/lib/googleauth/scope_util.rb +12 -12
- data/lib/googleauth/service_account.rb +64 -62
- data/lib/googleauth/signet.rb +53 -12
- data/lib/googleauth/stores/file_token_store.rb +8 -8
- data/lib/googleauth/stores/redis_token_store.rb +22 -22
- data/lib/googleauth/token_store.rb +6 -6
- data/lib/googleauth/user_authorizer.rb +80 -68
- data/lib/googleauth/user_refresh.rb +44 -35
- data/lib/googleauth/version.rb +1 -1
- data/lib/googleauth/web_user_authorizer.rb +77 -68
- data/lib/googleauth.rb +6 -96
- data/rakelib/devsite_builder.rb +45 -0
- data/rakelib/link_checker.rb +64 -0
- data/rakelib/repo_metadata.rb +59 -0
- data/spec/googleauth/apply_auth_examples.rb +47 -46
- data/spec/googleauth/client_id_spec.rb +75 -55
- data/spec/googleauth/compute_engine_spec.rb +60 -43
- data/spec/googleauth/credentials_spec.rb +467 -0
- data/spec/googleauth/get_application_default_spec.rb +149 -111
- data/spec/googleauth/iam_spec.rb +25 -25
- data/spec/googleauth/scope_util_spec.rb +26 -24
- data/spec/googleauth/service_account_spec.rb +261 -143
- data/spec/googleauth/signet_spec.rb +93 -30
- data/spec/googleauth/stores/file_token_store_spec.rb +12 -13
- data/spec/googleauth/stores/redis_token_store_spec.rb +11 -11
- data/spec/googleauth/stores/store_examples.rb +16 -16
- data/spec/googleauth/user_authorizer_spec.rb +153 -124
- data/spec/googleauth/user_refresh_spec.rb +186 -121
- data/spec/googleauth/web_user_authorizer_spec.rb +82 -69
- data/spec/spec_helper.rb +21 -19
- metadata +75 -32
- data/.rubocop_todo.yml +0 -32
- data/.travis.yml +0 -37
data/lib/googleauth/iam.rb
CHANGED
@@ -27,9 +27,9 @@
|
|
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
|
31
|
-
require
|
32
|
-
require
|
30
|
+
require "googleauth/signet"
|
31
|
+
require "googleauth/credentials_loader"
|
32
|
+
require "multi_json"
|
33
33
|
|
34
34
|
module Google
|
35
35
|
# Module Auth provides classes that provide Google-specific authorization
|
@@ -37,31 +37,31 @@ module Google
|
|
37
37
|
module Auth
|
38
38
|
# Authenticates requests using IAM credentials.
|
39
39
|
class IAMCredentials
|
40
|
-
SELECTOR_KEY =
|
41
|
-
TOKEN_KEY =
|
40
|
+
SELECTOR_KEY = "x-goog-iam-authority-selector".freeze
|
41
|
+
TOKEN_KEY = "x-goog-iam-authorization-token".freeze
|
42
42
|
|
43
43
|
# Initializes an IAMCredentials.
|
44
44
|
#
|
45
45
|
# @param selector the IAM selector.
|
46
46
|
# @param token the IAM token.
|
47
|
-
def initialize
|
48
|
-
|
49
|
-
|
47
|
+
def initialize selector, token
|
48
|
+
raise TypeError unless selector.is_a? String
|
49
|
+
raise TypeError unless token.is_a? String
|
50
50
|
@selector = selector
|
51
51
|
@token = token
|
52
52
|
end
|
53
53
|
|
54
54
|
# Adds the credential fields to the hash.
|
55
|
-
def apply!
|
55
|
+
def apply! a_hash
|
56
56
|
a_hash[SELECTOR_KEY] = @selector
|
57
57
|
a_hash[TOKEN_KEY] = @token
|
58
58
|
a_hash
|
59
59
|
end
|
60
60
|
|
61
61
|
# Returns a clone of a_hash updated with the authoriation header
|
62
|
-
def apply
|
62
|
+
def apply a_hash
|
63
63
|
a_copy = a_hash.clone
|
64
|
-
apply!
|
64
|
+
apply! a_copy
|
65
65
|
a_copy
|
66
66
|
end
|
67
67
|
|
@@ -0,0 +1,46 @@
|
|
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
|
+
project_id = json_key["project_id"]
|
42
|
+
[json_key["private_key"], json_key["client_email"], project_id]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
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
|
31
|
-
require
|
32
|
-
require
|
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
|
-
|
40
|
-
|
41
|
-
|
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
|
45
|
-
list = as_array
|
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
|
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
|
-
|
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
|
31
|
-
require
|
32
|
-
require
|
33
|
-
require
|
34
|
-
require
|
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,49 @@ 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](
|
48
|
+
# cf [Application Default Credentials](https://cloud.google.com/docs/authentication/production)
|
48
49
|
class ServiceAccountCredentials < Signet::OAuth2::Client
|
49
|
-
TOKEN_CRED_URI =
|
50
|
+
TOKEN_CRED_URI = "https://www.googleapis.com/oauth2/v4/token".freeze
|
50
51
|
extend CredentialsLoader
|
52
|
+
extend JsonKeyReader
|
53
|
+
attr_reader :project_id
|
51
54
|
|
52
55
|
# Creates a ServiceAccountCredentials.
|
53
56
|
#
|
54
57
|
# @param json_key_io [IO] an IO from which the JSON key can be read
|
55
58
|
# @param scope [string|array|nil] the scope(s) to access
|
56
|
-
def self.make_creds
|
57
|
-
json_key_io, scope = options.values_at
|
59
|
+
def self.make_creds options = {}
|
60
|
+
json_key_io, scope = options.values_at :json_key_io, :scope
|
58
61
|
if json_key_io
|
59
|
-
private_key, client_email = read_json_key
|
62
|
+
private_key, client_email, project_id = read_json_key json_key_io
|
60
63
|
else
|
61
|
-
private_key = ENV[CredentialsLoader::PRIVATE_KEY_VAR]
|
64
|
+
private_key = unescape ENV[CredentialsLoader::PRIVATE_KEY_VAR]
|
62
65
|
client_email = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
|
66
|
+
project_id = ENV[CredentialsLoader::PROJECT_ID_VAR]
|
63
67
|
end
|
68
|
+
project_id ||= CredentialsLoader.load_gcloud_project_id
|
64
69
|
|
65
70
|
new(token_credential_uri: TOKEN_CRED_URI,
|
66
|
-
audience:
|
67
|
-
scope:
|
68
|
-
issuer:
|
69
|
-
signing_key:
|
71
|
+
audience: TOKEN_CRED_URI,
|
72
|
+
scope: scope,
|
73
|
+
issuer: client_email,
|
74
|
+
signing_key: OpenSSL::PKey::RSA.new(private_key),
|
75
|
+
project_id: project_id)
|
76
|
+
.configure_connection(options)
|
70
77
|
end
|
71
78
|
|
72
|
-
#
|
73
|
-
#
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
+
# Handles certain escape sequences that sometimes appear in input.
|
80
|
+
# Specifically, interprets the "\n" sequence for newline, and removes
|
81
|
+
# enclosing quotes.
|
82
|
+
def self.unescape str
|
83
|
+
str = str.gsub '\n', "\n"
|
84
|
+
str = str[1..-2] if str.start_with?('"') && str.end_with?('"')
|
85
|
+
str
|
79
86
|
end
|
80
87
|
|
81
|
-
def initialize
|
82
|
-
|
88
|
+
def initialize options = {}
|
89
|
+
@project_id = options[:project_id]
|
90
|
+
super options
|
83
91
|
end
|
84
92
|
|
85
93
|
# Extends the base class.
|
@@ -87,7 +95,7 @@ module Google
|
|
87
95
|
# If scope(s) is not set, it creates a transient
|
88
96
|
# ServiceAccountJwtHeaderCredentials instance and uses that to
|
89
97
|
# authenticate instead.
|
90
|
-
def apply!
|
98
|
+
def apply! a_hash, opts = {}
|
91
99
|
# Use the base implementation if scopes are set
|
92
100
|
unless scope.nil?
|
93
101
|
super
|
@@ -97,13 +105,13 @@ module Google
|
|
97
105
|
# Use the ServiceAccountJwtHeaderCredentials using the same cred values
|
98
106
|
# if no scopes are set.
|
99
107
|
cred_json = {
|
100
|
-
private_key:
|
108
|
+
private_key: @signing_key.to_s,
|
101
109
|
client_email: @issuer
|
102
110
|
}
|
103
111
|
alt_clz = ServiceAccountJwtHeaderCredentials
|
104
|
-
key_io = StringIO.new
|
105
|
-
alt = alt_clz.make_creds
|
106
|
-
alt.apply!
|
112
|
+
key_io = StringIO.new MultiJson.dump(cred_json)
|
113
|
+
alt = alt_clz.make_creds json_key_io: key_io
|
114
|
+
alt.apply! a_hash
|
107
115
|
end
|
108
116
|
end
|
109
117
|
|
@@ -115,14 +123,16 @@ module Google
|
|
115
123
|
# console (via 'Generate new Json Key'). It is not part of any OAuth2
|
116
124
|
# flow, rather it creates a JWT and sends that as a credential.
|
117
125
|
#
|
118
|
-
# cf [Application Default Credentials](
|
126
|
+
# cf [Application Default Credentials](https://cloud.google.com/docs/authentication/production)
|
119
127
|
class ServiceAccountJwtHeaderCredentials
|
120
128
|
JWT_AUD_URI_KEY = :jwt_aud_uri
|
121
129
|
AUTH_METADATA_KEY = Signet::OAuth2::AUTH_METADATA_KEY
|
122
|
-
TOKEN_CRED_URI =
|
123
|
-
SIGNING_ALGORITHM =
|
130
|
+
TOKEN_CRED_URI = "https://www.googleapis.com/oauth2/v4/token".freeze
|
131
|
+
SIGNING_ALGORITHM = "RS256".freeze
|
124
132
|
EXPIRY = 60
|
125
133
|
extend CredentialsLoader
|
134
|
+
extend JsonKeyReader
|
135
|
+
attr_reader :project_id
|
126
136
|
|
127
137
|
# make_creds proxies the construction of a credentials instance
|
128
138
|
#
|
@@ -131,51 +141,43 @@ module Google
|
|
131
141
|
# By default, it calls #new with 2 args, the second one being an
|
132
142
|
# optional scope. Here's the constructor only has one param, so
|
133
143
|
# we modify make_creds to reflect this.
|
134
|
-
def self.make_creds
|
135
|
-
new
|
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']]
|
144
|
+
def self.make_creds *args
|
145
|
+
new json_key_io: args[0][:json_key_io]
|
145
146
|
end
|
146
147
|
|
147
148
|
# Initializes a ServiceAccountJwtHeaderCredentials.
|
148
149
|
#
|
149
150
|
# @param json_key_io [IO] an IO from which the JSON key can be read
|
150
|
-
def initialize
|
151
|
+
def initialize options = {}
|
151
152
|
json_key_io = options[:json_key_io]
|
152
153
|
if json_key_io
|
153
|
-
private_key,
|
154
|
+
@private_key, @issuer, @project_id =
|
155
|
+
self.class.read_json_key json_key_io
|
154
156
|
else
|
155
|
-
private_key = ENV[CredentialsLoader::PRIVATE_KEY_VAR]
|
156
|
-
|
157
|
+
@private_key = ENV[CredentialsLoader::PRIVATE_KEY_VAR]
|
158
|
+
@issuer = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
|
159
|
+
@project_id = ENV[CredentialsLoader::PROJECT_ID_VAR]
|
157
160
|
end
|
158
|
-
@
|
159
|
-
@
|
160
|
-
@signing_key = OpenSSL::PKey::RSA.new(private_key)
|
161
|
+
@project_id ||= CredentialsLoader.load_gcloud_project_id
|
162
|
+
@signing_key = OpenSSL::PKey::RSA.new @private_key
|
161
163
|
end
|
162
164
|
|
163
165
|
# Construct a jwt token if the JWT_AUD_URI key is present in the input
|
164
166
|
# hash.
|
165
167
|
#
|
166
168
|
# The jwt token is used as the value of a 'Bearer '.
|
167
|
-
def apply!
|
168
|
-
jwt_aud_uri = a_hash.delete
|
169
|
+
def apply! a_hash, opts = {}
|
170
|
+
jwt_aud_uri = a_hash.delete JWT_AUD_URI_KEY
|
169
171
|
return a_hash if jwt_aud_uri.nil?
|
170
|
-
jwt_token = new_jwt_token
|
172
|
+
jwt_token = new_jwt_token jwt_aud_uri, opts
|
171
173
|
a_hash[AUTH_METADATA_KEY] = "Bearer #{jwt_token}"
|
172
174
|
a_hash
|
173
175
|
end
|
174
176
|
|
175
177
|
# Returns a clone of a_hash updated with the authoriation header
|
176
|
-
def apply
|
178
|
+
def apply a_hash, opts = {}
|
177
179
|
a_copy = a_hash.clone
|
178
|
-
apply!
|
180
|
+
apply! a_copy, opts
|
179
181
|
a_copy
|
180
182
|
end
|
181
183
|
|
@@ -188,17 +190,17 @@ module Google
|
|
188
190
|
protected
|
189
191
|
|
190
192
|
# Creates a jwt uri token.
|
191
|
-
def new_jwt_token
|
193
|
+
def new_jwt_token jwt_aud_uri, options = {}
|
192
194
|
now = Time.new
|
193
195
|
skew = options[:skew] || 60
|
194
196
|
assertion = {
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
197
|
+
"iss" => @issuer,
|
198
|
+
"sub" => @issuer,
|
199
|
+
"aud" => jwt_aud_uri,
|
200
|
+
"exp" => (now + EXPIRY).to_i,
|
201
|
+
"iat" => (now - skew).to_i
|
200
202
|
}
|
201
|
-
JWT.encode
|
203
|
+
JWT.encode assertion, @signing_key, SIGNING_ALGORITHM
|
202
204
|
end
|
203
205
|
end
|
204
206
|
end
|
data/lib/googleauth/signet.rb
CHANGED
@@ -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
|
30
|
+
require "signet/oauth_2/client"
|
31
31
|
|
32
32
|
module Signet
|
33
33
|
# OAuth2 supports OAuth2 authentication.
|
@@ -38,18 +38,24 @@ 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!
|
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!
|
51
|
+
fetch_access_token! opts if access_token.nil? || expires_within?(60)
|
46
52
|
a_hash[AUTH_METADATA_KEY] = "Bearer #{access_token}"
|
47
53
|
end
|
48
54
|
|
49
55
|
# Returns a clone of a_hash updated with the authentication token
|
50
|
-
def apply
|
56
|
+
def apply a_hash, opts = {}
|
51
57
|
a_copy = a_hash.clone
|
52
|
-
apply!
|
58
|
+
apply! a_copy, opts
|
53
59
|
a_copy
|
54
60
|
end
|
55
61
|
|
@@ -59,22 +65,57 @@ module Signet
|
|
59
65
|
lambda(&method(:apply))
|
60
66
|
end
|
61
67
|
|
62
|
-
def on_refresh
|
63
|
-
@refresh_listeners
|
68
|
+
def on_refresh &block
|
69
|
+
@refresh_listeners = [] unless defined? @refresh_listeners
|
64
70
|
@refresh_listeners << block
|
65
71
|
end
|
66
72
|
|
67
|
-
|
68
|
-
def fetch_access_token!
|
69
|
-
|
73
|
+
alias orig_fetch_access_token! fetch_access_token!
|
74
|
+
def fetch_access_token! options = {}
|
75
|
+
unless options[:connection]
|
76
|
+
connection = build_default_connection
|
77
|
+
options = options.merge connection: connection if connection
|
78
|
+
end
|
79
|
+
info = retry_with_error do
|
80
|
+
orig_fetch_access_token! options
|
81
|
+
end
|
70
82
|
notify_refresh_listeners
|
71
83
|
info
|
72
84
|
end
|
73
85
|
|
74
86
|
def notify_refresh_listeners
|
75
|
-
listeners = @refresh_listeners
|
87
|
+
listeners = defined?(@refresh_listeners) ? @refresh_listeners : []
|
76
88
|
listeners.each do |block|
|
77
|
-
block.call
|
89
|
+
block.call self
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def build_default_connection
|
94
|
+
if !defined?(@connection_info)
|
95
|
+
nil
|
96
|
+
elsif @connection_info.respond_to? :call
|
97
|
+
@connection_info.call
|
98
|
+
else
|
99
|
+
@connection_info
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def retry_with_error max_retry_count = 5
|
104
|
+
retry_count = 0
|
105
|
+
|
106
|
+
begin
|
107
|
+
yield
|
108
|
+
rescue StandardError => e
|
109
|
+
raise e if e.is_a?(Signet::AuthorizationError) || e.is_a?(Signet::ParseError)
|
110
|
+
|
111
|
+
if retry_count < max_retry_count
|
112
|
+
retry_count += 1
|
113
|
+
sleep retry_count * 0.3
|
114
|
+
retry
|
115
|
+
else
|
116
|
+
msg = "Unexpected error: #{e.inspect}"
|
117
|
+
raise Signet::AuthorizationError, msg
|
118
|
+
end
|
78
119
|
end
|
79
120
|
end
|
80
121
|
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
|
31
|
-
require
|
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
|
42
|
+
def initialize options = {}
|
43
43
|
path = options[:file]
|
44
|
-
@store = YAML::Store.new
|
44
|
+
@store = YAML::Store.new path
|
45
45
|
end
|
46
46
|
|
47
47
|
# (see Google::Auth::Stores::TokenStore#load)
|
48
|
-
def load
|
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
|
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
|
59
|
-
@store.transaction { @store.delete
|
58
|
+
def delete id
|
59
|
+
@store.transaction { @store.delete id }
|
60
60
|
end
|
61
61
|
end
|
62
62
|
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
|
31
|
-
require
|
30
|
+
require "redis"
|
31
|
+
require "googleauth/token_store"
|
32
32
|
|
33
33
|
module Google
|
34
34
|
module Auth
|
@@ -37,7 +37,7 @@ module Google
|
|
37
37
|
# are stored as JSON using the supplied key, prefixed with
|
38
38
|
# `g-user-token:`
|
39
39
|
class RedisTokenStore < Google::Auth::TokenStore
|
40
|
-
DEFAULT_KEY_PREFIX =
|
40
|
+
DEFAULT_KEY_PREFIX = "g-user-token:".freeze
|
41
41
|
|
42
42
|
# Create a new store with the supplied redis client.
|
43
43
|
#
|
@@ -48,34 +48,34 @@ module Google
|
|
48
48
|
# @note If no redis instance is provided, a new one is created and
|
49
49
|
# the options passed through. You may include any other keys accepted
|
50
50
|
# by `Redis.new`
|
51
|
-
def initialize
|
52
|
-
redis = options.delete
|
53
|
-
prefix = options.delete
|
54
|
-
case redis
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
51
|
+
def initialize options = {}
|
52
|
+
redis = options.delete :redis
|
53
|
+
prefix = options.delete :prefix
|
54
|
+
@redis = case redis
|
55
|
+
when Redis
|
56
|
+
redis
|
57
|
+
else
|
58
|
+
Redis.new options
|
59
|
+
end
|
60
60
|
@prefix = prefix || DEFAULT_KEY_PREFIX
|
61
61
|
end
|
62
62
|
|
63
63
|
# (see Google::Auth::Stores::TokenStore#load)
|
64
|
-
def load
|
65
|
-
key = key_for
|
66
|
-
@redis.get
|
64
|
+
def load id
|
65
|
+
key = key_for id
|
66
|
+
@redis.get key
|
67
67
|
end
|
68
68
|
|
69
69
|
# (see Google::Auth::Stores::TokenStore#store)
|
70
|
-
def store
|
71
|
-
key = key_for
|
72
|
-
@redis.set
|
70
|
+
def store id, token
|
71
|
+
key = key_for id
|
72
|
+
@redis.set key, token
|
73
73
|
end
|
74
74
|
|
75
75
|
# (see Google::Auth::Stores::TokenStore#delete)
|
76
|
-
def delete
|
77
|
-
key = key_for
|
78
|
-
@redis.del
|
76
|
+
def delete id
|
77
|
+
key = key_for id
|
78
|
+
@redis.del key
|
79
79
|
end
|
80
80
|
|
81
81
|
private
|
@@ -86,7 +86,7 @@ module Google
|
|
86
86
|
# ID of the token
|
87
87
|
# @return [String]
|
88
88
|
# Redis key
|
89
|
-
def key_for
|
89
|
+
def key_for id
|
90
90
|
@prefix + id
|
91
91
|
end
|
92
92
|
end
|
@@ -43,8 +43,8 @@ module Google
|
|
43
43
|
# ID of token data to load.
|
44
44
|
# @return [String]
|
45
45
|
# The loaded token data.
|
46
|
-
def load
|
47
|
-
|
46
|
+
def load _id
|
47
|
+
raise "Not implemented"
|
48
48
|
end
|
49
49
|
|
50
50
|
# Put the token data into storage for the given ID.
|
@@ -53,16 +53,16 @@ module Google
|
|
53
53
|
# ID of token data to store.
|
54
54
|
# @param [String] token
|
55
55
|
# The token data to store.
|
56
|
-
def store
|
57
|
-
|
56
|
+
def store _id, _token
|
57
|
+
raise "Not implemented"
|
58
58
|
end
|
59
59
|
|
60
60
|
# Remove the token data from storage for the given ID.
|
61
61
|
#
|
62
62
|
# @param [String] id
|
63
63
|
# ID of the token data to delete
|
64
|
-
def delete
|
65
|
-
|
64
|
+
def delete _id
|
65
|
+
raise "Not implemented"
|
66
66
|
end
|
67
67
|
end
|
68
68
|
end
|