googleauth 0.5.1 → 0.14.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/.github/CODEOWNERS +7 -0
- 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 +19 -1
- data/CHANGELOG.md +112 -19
- data/CODE_OF_CONDUCT.md +43 -0
- data/Gemfile +19 -13
- data/{COPYING → LICENSE} +0 -0
- data/README.md +58 -18
- data/Rakefile +126 -9
- data/googleauth.gemspec +28 -25
- data/integration/helper.rb +31 -0
- data/integration/id_tokens/key_source_test.rb +74 -0
- data/lib/googleauth.rb +7 -96
- data/lib/googleauth/application_default.rb +81 -0
- data/lib/googleauth/client_id.rb +21 -19
- data/lib/googleauth/compute_engine.rb +70 -43
- data/lib/googleauth/credentials.rb +442 -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/id_tokens.rb +233 -0
- data/lib/googleauth/id_tokens/errors.rb +71 -0
- data/lib/googleauth/id_tokens/key_sources.rb +394 -0
- data/lib/googleauth/id_tokens/verifier.rb +144 -0
- data/lib/googleauth/json_key_reader.rb +50 -0
- data/lib/googleauth/scope_util.rb +12 -12
- data/lib/googleauth/service_account.rb +74 -63
- data/lib/googleauth/signet.rb +55 -13
- 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/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 +74 -50
- data/spec/googleauth/client_id_spec.rb +75 -55
- data/spec/googleauth/compute_engine_spec.rb +98 -46
- data/spec/googleauth/credentials_spec.rb +478 -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 +269 -144
- data/spec/googleauth/signet_spec.rb +101 -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
- data/test/helper.rb +33 -0
- data/test/id_tokens/key_sources_test.rb +240 -0
- data/test/id_tokens/verifier_test.rb +269 -0
- metadata +87 -34
- data/.rubocop_todo.yml +0 -32
- data/.travis.yml +0 -37
data/lib/googleauth/version.rb
CHANGED
@@ -27,11 +27,11 @@
|
|
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 "multi_json"
|
31
|
+
require "googleauth/signet"
|
32
|
+
require "googleauth/user_authorizer"
|
33
|
+
require "googleauth/user_refresh"
|
34
|
+
require "securerandom"
|
35
35
|
|
36
36
|
module Google
|
37
37
|
module Auth
|
@@ -66,20 +66,21 @@ module Google
|
|
66
66
|
# @see {Google::Auth::ControllerHelpers}
|
67
67
|
# @note Requires sessions are enabled
|
68
68
|
class WebUserAuthorizer < Google::Auth::UserAuthorizer
|
69
|
-
STATE_PARAM =
|
70
|
-
AUTH_CODE_KEY =
|
71
|
-
ERROR_CODE_KEY =
|
72
|
-
SESSION_ID_KEY =
|
73
|
-
CALLBACK_STATE_KEY =
|
74
|
-
CURRENT_URI_KEY =
|
75
|
-
XSRF_KEY =
|
76
|
-
SCOPE_KEY =
|
69
|
+
STATE_PARAM = "state".freeze
|
70
|
+
AUTH_CODE_KEY = "code".freeze
|
71
|
+
ERROR_CODE_KEY = "error".freeze
|
72
|
+
SESSION_ID_KEY = "session_id".freeze
|
73
|
+
CALLBACK_STATE_KEY = "g-auth-callback".freeze
|
74
|
+
CURRENT_URI_KEY = "current_uri".freeze
|
75
|
+
XSRF_KEY = "g-xsrf-token".freeze
|
76
|
+
SCOPE_KEY = "scope".freeze
|
77
77
|
|
78
|
-
NIL_REQUEST_ERROR =
|
79
|
-
NIL_SESSION_ERROR =
|
80
|
-
MISSING_AUTH_CODE_ERROR =
|
81
|
-
AUTHORIZATION_ERROR =
|
82
|
-
INVALID_STATE_TOKEN_ERROR =
|
78
|
+
NIL_REQUEST_ERROR = "Request is required.".freeze
|
79
|
+
NIL_SESSION_ERROR = "Sessions must be enabled".freeze
|
80
|
+
MISSING_AUTH_CODE_ERROR = "Missing authorization code in request".freeze
|
81
|
+
AUTHORIZATION_ERROR = "Authorization error: %s".freeze
|
82
|
+
INVALID_STATE_TOKEN_ERROR =
|
83
|
+
"State token does not match expected value".freeze
|
83
84
|
|
84
85
|
class << self
|
85
86
|
attr_accessor :default
|
@@ -96,9 +97,9 @@ module Google
|
|
96
97
|
#
|
97
98
|
# @param [Rack::Request] request
|
98
99
|
# Current request
|
99
|
-
def self.handle_auth_callback_deferred
|
100
|
-
callback_state, redirect_uri = extract_callback_state
|
101
|
-
request.session[CALLBACK_STATE_KEY] = MultiJson.dump
|
100
|
+
def self.handle_auth_callback_deferred request
|
101
|
+
callback_state, redirect_uri = extract_callback_state request
|
102
|
+
request.session[CALLBACK_STATE_KEY] = MultiJson.dump callback_state
|
102
103
|
redirect_uri
|
103
104
|
end
|
104
105
|
|
@@ -113,8 +114,8 @@ module Google
|
|
113
114
|
# @param [String] callback_uri
|
114
115
|
# URL (either absolute or relative) of the auth callback. Defaults
|
115
116
|
# to '/oauth2callback'
|
116
|
-
def initialize
|
117
|
-
super
|
117
|
+
def initialize client_id, scope, token_store, callback_uri = nil
|
118
|
+
super client_id, scope, token_store, callback_uri
|
118
119
|
end
|
119
120
|
|
120
121
|
# Handle the result of the oauth callback. Exchanges the authorization
|
@@ -126,15 +127,17 @@ module Google
|
|
126
127
|
# Current request
|
127
128
|
# @return (Google::Auth::UserRefreshCredentials, String)
|
128
129
|
# credentials & next URL to redirect to
|
129
|
-
def handle_auth_callback
|
130
|
+
def handle_auth_callback user_id, request
|
130
131
|
callback_state, redirect_uri = WebUserAuthorizer.extract_callback_state(
|
131
|
-
request
|
132
|
-
|
132
|
+
request
|
133
|
+
)
|
134
|
+
WebUserAuthorizer.validate_callback_state callback_state, request
|
133
135
|
credentials = get_and_store_credentials_from_code(
|
134
|
-
user_id:
|
135
|
-
code:
|
136
|
-
scope:
|
137
|
-
base_url: request.url
|
136
|
+
user_id: user_id,
|
137
|
+
code: callback_state[AUTH_CODE_KEY],
|
138
|
+
scope: callback_state[SCOPE_KEY],
|
139
|
+
base_url: request.url
|
140
|
+
)
|
138
141
|
[credentials, redirect_uri]
|
139
142
|
end
|
140
143
|
|
@@ -151,29 +154,35 @@ module Google
|
|
151
154
|
# @param [String, Array<String>] scope
|
152
155
|
# Authorization scope to request. Overrides the instance scopes if
|
153
156
|
# not nil.
|
157
|
+
# @param [Hash] state
|
158
|
+
# Optional key-values to be returned to the oauth callback.
|
154
159
|
# @return [String]
|
155
160
|
# Authorization url
|
156
|
-
def get_authorization_url
|
161
|
+
def get_authorization_url options = {}
|
157
162
|
options = options.dup
|
158
163
|
request = options[:request]
|
159
|
-
|
160
|
-
|
164
|
+
raise NIL_REQUEST_ERROR if request.nil?
|
165
|
+
raise NIL_SESSION_ERROR if request.session.nil?
|
166
|
+
|
167
|
+
state = options[:state] || {}
|
161
168
|
|
162
169
|
redirect_to = options[:redirect_to] || request.url
|
163
170
|
request.session[XSRF_KEY] = SecureRandom.base64
|
164
|
-
options[:state] = MultiJson.dump(
|
165
|
-
|
166
|
-
|
171
|
+
options[:state] = MultiJson.dump(state.merge(
|
172
|
+
SESSION_ID_KEY => request.session[XSRF_KEY],
|
173
|
+
CURRENT_URI_KEY => redirect_to
|
174
|
+
))
|
167
175
|
options[:base_url] = request.url
|
168
|
-
super
|
176
|
+
super options
|
169
177
|
end
|
170
178
|
|
171
|
-
# Fetch stored credentials for the user.
|
179
|
+
# Fetch stored credentials for the user from the given request session.
|
172
180
|
#
|
173
181
|
# @param [String] user_id
|
174
182
|
# Unique ID of the user for loading/storing credentials.
|
175
183
|
# @param [Rack::Request] request
|
176
|
-
# Current request
|
184
|
+
# Current request. Optional. If omitted, this will attempt to fall back
|
185
|
+
# on the base class behavior of reading from the token store.
|
177
186
|
# @param [Array<String>, String] scope
|
178
187
|
# If specified, only returns credentials that have all the \
|
179
188
|
# requested scopes
|
@@ -182,31 +191,32 @@ module Google
|
|
182
191
|
# @raise [Signet::AuthorizationError]
|
183
192
|
# May raise an error if an authorization code is present in the session
|
184
193
|
# and exchange of the code fails
|
185
|
-
def get_credentials
|
186
|
-
if request.session.key?(CALLBACK_STATE_KEY)
|
194
|
+
def get_credentials user_id, request = nil, scope = nil
|
195
|
+
if request && request.session.key?(CALLBACK_STATE_KEY)
|
187
196
|
# Note - in theory, no need to check required scope as this is
|
188
197
|
# expected to be called immediately after a return from authorization
|
189
|
-
state_json = request.session.delete
|
190
|
-
callback_state = MultiJson.load
|
191
|
-
WebUserAuthorizer.validate_callback_state
|
198
|
+
state_json = request.session.delete CALLBACK_STATE_KEY
|
199
|
+
callback_state = MultiJson.load state_json
|
200
|
+
WebUserAuthorizer.validate_callback_state callback_state, request
|
192
201
|
get_and_store_credentials_from_code(
|
193
|
-
user_id:
|
194
|
-
code:
|
195
|
-
scope:
|
196
|
-
base_url: request.url
|
202
|
+
user_id: user_id,
|
203
|
+
code: callback_state[AUTH_CODE_KEY],
|
204
|
+
scope: callback_state[SCOPE_KEY],
|
205
|
+
base_url: request.url
|
206
|
+
)
|
197
207
|
else
|
198
|
-
super
|
208
|
+
super user_id, scope
|
199
209
|
end
|
200
210
|
end
|
201
211
|
|
202
|
-
def self.extract_callback_state
|
203
|
-
state = MultiJson.load(request[STATE_PARAM] ||
|
212
|
+
def self.extract_callback_state request
|
213
|
+
state = MultiJson.load(request[STATE_PARAM] || "{}")
|
204
214
|
redirect_uri = state[CURRENT_URI_KEY]
|
205
215
|
callback_state = {
|
206
|
-
AUTH_CODE_KEY
|
207
|
-
ERROR_CODE_KEY =>
|
216
|
+
AUTH_CODE_KEY => request[AUTH_CODE_KEY],
|
217
|
+
ERROR_CODE_KEY => request[ERROR_CODE_KEY],
|
208
218
|
SESSION_ID_KEY => state[SESSION_ID_KEY],
|
209
|
-
SCOPE_KEY
|
219
|
+
SCOPE_KEY => request[SCOPE_KEY]
|
210
220
|
}
|
211
221
|
[callback_state, redirect_uri]
|
212
222
|
end
|
@@ -221,14 +231,13 @@ module Google
|
|
221
231
|
# Error message if failed
|
222
232
|
# @param [Rack::Request] request
|
223
233
|
# Current request
|
224
|
-
def self.validate_callback_state
|
225
|
-
if state[AUTH_CODE_KEY].nil?
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
sprintf(AUTHORIZATION_ERROR, state[ERROR_CODE_KEY])
|
234
|
+
def self.validate_callback_state state, request
|
235
|
+
raise Signet::AuthorizationError, MISSING_AUTH_CODE_ERROR if state[AUTH_CODE_KEY].nil?
|
236
|
+
if state[ERROR_CODE_KEY]
|
237
|
+
raise Signet::AuthorizationError,
|
238
|
+
format(AUTHORIZATION_ERROR, state[ERROR_CODE_KEY])
|
230
239
|
elsif request.session[XSRF_KEY] != state[SESSION_ID_KEY]
|
231
|
-
|
240
|
+
raise Signet::AuthorizationError, INVALID_STATE_TOKEN_ERROR
|
232
241
|
end
|
233
242
|
end
|
234
243
|
|
@@ -254,7 +263,7 @@ module Google
|
|
254
263
|
#
|
255
264
|
# @see {Google::Auth::WebUserAuthorizer}
|
256
265
|
class CallbackApp
|
257
|
-
LOCATION_HEADER =
|
266
|
+
LOCATION_HEADER = "Location".freeze
|
258
267
|
REDIR_STATUS = 302
|
259
268
|
ERROR_STATUS = 500
|
260
269
|
|
@@ -270,18 +279,18 @@ module Google
|
|
270
279
|
# Rack environment
|
271
280
|
# @return [Array]
|
272
281
|
# HTTP response
|
273
|
-
def self.call
|
274
|
-
request = Rack::Request.new
|
275
|
-
return_url = WebUserAuthorizer.handle_auth_callback_deferred
|
282
|
+
def self.call env
|
283
|
+
request = Rack::Request.new env
|
284
|
+
return_url = WebUserAuthorizer.handle_auth_callback_deferred request
|
276
285
|
if return_url
|
277
286
|
[REDIR_STATUS, { LOCATION_HEADER => return_url }, []]
|
278
287
|
else
|
279
|
-
[ERROR_STATUS, {}, [
|
288
|
+
[ERROR_STATUS, {}, ["No return URL is present in the request."]]
|
280
289
|
end
|
281
290
|
end
|
282
291
|
|
283
|
-
def call
|
284
|
-
self.class.call
|
292
|
+
def call env
|
293
|
+
self.class.call env
|
285
294
|
end
|
286
295
|
end
|
287
296
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
require_relative "repo_metadata.rb"
|
4
|
+
|
5
|
+
class DevsiteBuilder
|
6
|
+
def initialize master_dir = "."
|
7
|
+
@master_dir = Pathname.new master_dir
|
8
|
+
@output_dir = "doc"
|
9
|
+
@metadata = RepoMetadata.from_source "#{master_dir}/.repo-metadata.json"
|
10
|
+
end
|
11
|
+
|
12
|
+
def build
|
13
|
+
FileUtils.remove_dir @output_dir if Dir.exist? @output_dir
|
14
|
+
markup = "--markup markdown"
|
15
|
+
|
16
|
+
Dir.chdir @master_dir do
|
17
|
+
cmds = ["-o #{@output_dir}", markup]
|
18
|
+
cmd "yard --verbose #{cmds.join ' '}"
|
19
|
+
end
|
20
|
+
@metadata.build @master_dir + @output_dir
|
21
|
+
end
|
22
|
+
|
23
|
+
def upload
|
24
|
+
Dir.chdir @output_dir do
|
25
|
+
opts = [
|
26
|
+
"--credentials=#{ENV['KOKORO_KEYSTORE_DIR']}/73713_docuploader_service_account",
|
27
|
+
"--staging-bucket=#{ENV.fetch 'STAGING_BUCKET', 'docs-staging'}",
|
28
|
+
"--metadata-file=./docs.metadata"
|
29
|
+
]
|
30
|
+
cmd "python3 -m docuploader upload . #{opts.join ' '}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def publish
|
35
|
+
build
|
36
|
+
upload
|
37
|
+
end
|
38
|
+
|
39
|
+
def cmd line
|
40
|
+
puts line
|
41
|
+
output = `#{line}`
|
42
|
+
puts output
|
43
|
+
output
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require "open3"
|
2
|
+
|
3
|
+
class LinkChecker
|
4
|
+
def initialize
|
5
|
+
@failed = false
|
6
|
+
end
|
7
|
+
|
8
|
+
def run
|
9
|
+
job_info
|
10
|
+
git_commit = ENV.fetch "KOKORO_GITHUB_COMMIT", "master"
|
11
|
+
|
12
|
+
markdown_files = Dir.glob "**/*.md"
|
13
|
+
broken_markdown_links = check_links markdown_files,
|
14
|
+
"https://github.com/googleapis/google-auth-library-ruby/tree/#{git_commit}",
|
15
|
+
" --skip '^(?!(\\Wruby.*google|.*google.*\\Wruby|.*cloud\\.google\\.com))'"
|
16
|
+
|
17
|
+
broken_devsite_links = check_links ["googleauth"],
|
18
|
+
"https://googleapis.dev/ruby",
|
19
|
+
"/latest/ --recurse --skip https:.*github.*"
|
20
|
+
|
21
|
+
puts_broken_links broken_markdown_links
|
22
|
+
puts_broken_links broken_devsite_links
|
23
|
+
end
|
24
|
+
|
25
|
+
def check_links location_list, base, tail
|
26
|
+
broken_links = Hash.new { |h, k| h[k] = [] }
|
27
|
+
location_list.each do |location|
|
28
|
+
out, err, st = Open3.capture3 "npx linkinator #{base}/#{location}#{tail}"
|
29
|
+
puts out
|
30
|
+
unless st.to_i.zero?
|
31
|
+
@failed = true
|
32
|
+
puts err
|
33
|
+
end
|
34
|
+
checked_links = out.split "\n"
|
35
|
+
checked_links.select! { |link| link =~ /\[\d+\]/ && !link.include?("[200]") }
|
36
|
+
unless checked_links.empty?
|
37
|
+
@failed = true
|
38
|
+
broken_links[location] += checked_links
|
39
|
+
end
|
40
|
+
end
|
41
|
+
broken_links
|
42
|
+
end
|
43
|
+
|
44
|
+
def puts_broken_links link_hash
|
45
|
+
link_hash.each do |location, links|
|
46
|
+
puts "#{location} contains the following broken links:"
|
47
|
+
links.each { |link| puts " #{link}" }
|
48
|
+
puts ""
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def job_info
|
53
|
+
line_length = "Using Ruby - #{RUBY_VERSION}".length + 8
|
54
|
+
puts ""
|
55
|
+
puts "#" * line_length
|
56
|
+
puts "### Using Ruby - #{RUBY_VERSION} ###"
|
57
|
+
puts "#" * line_length
|
58
|
+
puts ""
|
59
|
+
end
|
60
|
+
|
61
|
+
def exit_status
|
62
|
+
@failed ? 1 : 0
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
class RepoMetadata
|
4
|
+
attr_reader :data
|
5
|
+
|
6
|
+
def initialize data
|
7
|
+
@data = data
|
8
|
+
normalize_data!
|
9
|
+
end
|
10
|
+
|
11
|
+
def allowed_fields
|
12
|
+
[
|
13
|
+
"name", "version", "language", "distribution-name",
|
14
|
+
"product-page", "github-repository", "issue-tracker"
|
15
|
+
]
|
16
|
+
end
|
17
|
+
|
18
|
+
def build output_directory
|
19
|
+
fields = @data.to_a.map { |kv| "--#{kv[0]} #{kv[1]}" }
|
20
|
+
Dir.chdir output_directory do
|
21
|
+
cmd "python3 -m docuploader create-metadata #{fields.join ' '}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def normalize_data!
|
26
|
+
require_relative "../lib/googleauth/version.rb"
|
27
|
+
|
28
|
+
@data.delete_if { |k, _| !allowed_fields.include?(k) }
|
29
|
+
@data["version"] = "v#{Google::Auth::VERSION}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def [] key
|
33
|
+
data[key]
|
34
|
+
end
|
35
|
+
|
36
|
+
def []= key, value
|
37
|
+
@data[key] = value
|
38
|
+
end
|
39
|
+
|
40
|
+
def cmd line
|
41
|
+
puts line
|
42
|
+
output = `#{line}`
|
43
|
+
puts output
|
44
|
+
output
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.from_source source
|
48
|
+
if source.is_a? RepoMetadata
|
49
|
+
data = source.data
|
50
|
+
elsif source.is_a? Hash
|
51
|
+
data = source
|
52
|
+
elsif File.file? source
|
53
|
+
data = JSON.parse File.read(source)
|
54
|
+
else
|
55
|
+
raise "Source must be a path, hash, or RepoMetadata instance"
|
56
|
+
end
|
57
|
+
RepoMetadata.new data
|
58
|
+
end
|
59
|
+
end
|
@@ -27,14 +27,14 @@
|
|
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
|
-
spec_dir = File.expand_path
|
31
|
-
$LOAD_PATH.unshift
|
30
|
+
spec_dir = File.expand_path File.join(File.dirname(__FILE__))
|
31
|
+
$LOAD_PATH.unshift spec_dir
|
32
32
|
$LOAD_PATH.uniq!
|
33
33
|
|
34
|
-
require
|
35
|
-
require
|
34
|
+
require "faraday"
|
35
|
+
require "spec_helper"
|
36
36
|
|
37
|
-
shared_examples
|
37
|
+
shared_examples "apply/apply! are OK" do
|
38
38
|
let(:auth_key) { :authorization }
|
39
39
|
|
40
40
|
# tests that use these examples need to define
|
@@ -43,102 +43,126 @@ shared_examples 'apply/apply! are OK' do
|
|
43
43
|
#
|
44
44
|
# @make_auth_stubs, which should stub out the expected http behaviour of the
|
45
45
|
# auth client
|
46
|
-
describe
|
47
|
-
let(:token) {
|
48
|
-
let
|
46
|
+
describe "#fetch_access_token" do
|
47
|
+
let(:token) { "1/abcdef1234567890" }
|
48
|
+
let :access_stub do
|
49
49
|
make_auth_stubs access_token: token
|
50
50
|
end
|
51
|
+
let :id_stub do
|
52
|
+
make_auth_stubs id_token: token
|
53
|
+
end
|
51
54
|
|
52
|
-
it
|
53
|
-
|
55
|
+
it "should set access_token to the fetched value" do
|
56
|
+
access_stub
|
54
57
|
@client.fetch_access_token!
|
55
58
|
expect(@client.access_token).to eq(token)
|
56
|
-
expect(
|
59
|
+
expect(access_stub).to have_been_requested
|
57
60
|
end
|
58
61
|
|
59
|
-
it
|
60
|
-
|
62
|
+
it "should set id_token to the fetched value" do
|
63
|
+
skip unless @id_client
|
64
|
+
id_stub
|
65
|
+
@id_client.fetch_access_token!
|
66
|
+
expect(@id_client.id_token).to eq(token)
|
67
|
+
expect(id_stub).to have_been_requested
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should notify refresh listeners after updating" do
|
71
|
+
access_stub
|
61
72
|
expect do |b|
|
62
73
|
@client.on_refresh(&b)
|
63
74
|
@client.fetch_access_token!
|
64
75
|
end.to yield_with_args(have_attributes(
|
65
|
-
access_token:
|
66
|
-
|
76
|
+
access_token: "1/abcdef1234567890"
|
77
|
+
))
|
78
|
+
expect(access_stub).to have_been_requested
|
67
79
|
end
|
68
80
|
end
|
69
81
|
|
70
|
-
describe
|
71
|
-
it
|
72
|
-
token =
|
82
|
+
describe "#apply!" do
|
83
|
+
it "should update the target hash with fetched access token" do
|
84
|
+
token = "1/abcdef1234567890"
|
73
85
|
stub = make_auth_stubs access_token: token
|
74
86
|
|
75
|
-
md = { foo:
|
76
|
-
@client.apply!
|
77
|
-
want = { :foo =>
|
87
|
+
md = { foo: "bar" }
|
88
|
+
@client.apply! md
|
89
|
+
want = { :foo => "bar", auth_key => "Bearer #{token}" }
|
90
|
+
expect(md).to eq(want)
|
91
|
+
expect(stub).to have_been_requested
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should update the target hash with fetched ID token" do
|
95
|
+
skip unless @id_client
|
96
|
+
token = "1/abcdef1234567890"
|
97
|
+
stub = make_auth_stubs id_token: token
|
98
|
+
|
99
|
+
md = { foo: "bar" }
|
100
|
+
@id_client.apply! md
|
101
|
+
want = { :foo => "bar", auth_key => "Bearer #{token}" }
|
78
102
|
expect(md).to eq(want)
|
79
103
|
expect(stub).to have_been_requested
|
80
104
|
end
|
81
105
|
end
|
82
106
|
|
83
|
-
describe
|
84
|
-
it
|
85
|
-
token =
|
107
|
+
describe "updater_proc" do
|
108
|
+
it "should provide a proc that updates a hash with the access token" do
|
109
|
+
token = "1/abcdef1234567890"
|
86
110
|
stub = make_auth_stubs access_token: token
|
87
|
-
md = { foo:
|
111
|
+
md = { foo: "bar" }
|
88
112
|
the_proc = @client.updater_proc
|
89
|
-
got = the_proc.call
|
90
|
-
want = { :foo =>
|
113
|
+
got = the_proc.call md
|
114
|
+
want = { :foo => "bar", auth_key => "Bearer #{token}" }
|
91
115
|
expect(got).to eq(want)
|
92
116
|
expect(stub).to have_been_requested
|
93
117
|
end
|
94
118
|
end
|
95
119
|
|
96
|
-
describe
|
97
|
-
it
|
98
|
-
token =
|
120
|
+
describe "#apply" do
|
121
|
+
it "should not update the original hash with the access token" do
|
122
|
+
token = "1/abcdef1234567890"
|
99
123
|
stub = make_auth_stubs access_token: token
|
100
124
|
|
101
|
-
md = { foo:
|
102
|
-
@client.apply
|
103
|
-
want = { foo:
|
125
|
+
md = { foo: "bar" }
|
126
|
+
@client.apply md
|
127
|
+
want = { foo: "bar" }
|
104
128
|
expect(md).to eq(want)
|
105
129
|
expect(stub).to have_been_requested
|
106
130
|
end
|
107
131
|
|
108
|
-
it
|
109
|
-
token =
|
132
|
+
it "should add the token to the returned hash" do
|
133
|
+
token = "1/abcdef1234567890"
|
110
134
|
stub = make_auth_stubs access_token: token
|
111
135
|
|
112
|
-
md = { foo:
|
113
|
-
got = @client.apply
|
114
|
-
want = { :foo =>
|
136
|
+
md = { foo: "bar" }
|
137
|
+
got = @client.apply md
|
138
|
+
want = { :foo => "bar", auth_key => "Bearer #{token}" }
|
115
139
|
expect(got).to eq(want)
|
116
140
|
expect(stub).to have_been_requested
|
117
141
|
end
|
118
142
|
|
119
|
-
it
|
120
|
-
token =
|
143
|
+
it "should not fetch a new token if the current is not expired" do
|
144
|
+
token = "1/abcdef1234567890"
|
121
145
|
stub = make_auth_stubs access_token: token
|
122
146
|
|
123
147
|
n = 5 # arbitrary
|
124
148
|
n.times do |_t|
|
125
|
-
md = { foo:
|
126
|
-
got = @client.apply
|
127
|
-
want = { :foo =>
|
149
|
+
md = { foo: "bar" }
|
150
|
+
got = @client.apply md
|
151
|
+
want = { :foo => "bar", auth_key => "Bearer #{token}" }
|
128
152
|
expect(got).to eq(want)
|
129
153
|
end
|
130
154
|
expect(stub).to have_been_requested
|
131
155
|
end
|
132
156
|
|
133
|
-
it
|
134
|
-
|
135
|
-
|
157
|
+
it "should fetch a new token if the current one is expired" do
|
158
|
+
token1 = "1/abcdef1234567890"
|
159
|
+
token2 = "2/abcdef1234567891"
|
136
160
|
|
137
|
-
[
|
161
|
+
[token1, token2].each do |t|
|
138
162
|
make_auth_stubs access_token: t
|
139
|
-
md = { foo:
|
140
|
-
got = @client.apply
|
141
|
-
want = { :foo =>
|
163
|
+
md = { foo: "bar" }
|
164
|
+
got = @client.apply md
|
165
|
+
want = { :foo => "bar", auth_key => "Bearer #{t}" }
|
142
166
|
expect(got).to eq(want)
|
143
167
|
@client.expires_at -= 3601 # default is to expire in 1hr
|
144
168
|
end
|