ginjo-omniauth-slack 2.4.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 +7 -0
- data/.gitignore +21 -0
- data/CHANGELOG.md +56 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +316 -0
- data/Rakefile +8 -0
- data/lib/omniauth/strategies/slack.rb +394 -0
- data/lib/omniauth-slack/version.rb +5 -0
- data/lib/omniauth-slack.rb +2 -0
- data/omniauth-slack.gemspec +25 -0
- data/test/helper.rb +57 -0
- data/test/support/shared_examples.rb +85 -0
- data/test/test.rb +246 -0
- metadata +138 -0
@@ -0,0 +1,394 @@
|
|
1
|
+
require 'omniauth/strategies/oauth2'
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
module OmniAuth
|
5
|
+
module Strategies
|
6
|
+
|
7
|
+
class Slack < OmniAuth::Strategies::OAuth2
|
8
|
+
option :name, 'slack'
|
9
|
+
|
10
|
+
option :authorize_options, [:scope, :team, :team_domain, :redirect_uri]
|
11
|
+
|
12
|
+
option :client_options, {
|
13
|
+
site: 'https://slack.com',
|
14
|
+
token_url: '/api/oauth.access'
|
15
|
+
}
|
16
|
+
|
17
|
+
# Add team_domain to site subdomain if provided in auth url or provider options.
|
18
|
+
option :setup, lambda{|env|
|
19
|
+
strategy = env['omniauth.strategy']
|
20
|
+
team_domain = strategy.request.params['team_domain'] || strategy.options[:team_domain]
|
21
|
+
site = strategy.options[:client_options]['site']
|
22
|
+
strategy.options[:client_options].site = (
|
23
|
+
!team_domain.to_s.empty? ? site.sub(/\:\\\\/, "://#{team_domain}.") : site
|
24
|
+
)
|
25
|
+
}
|
26
|
+
|
27
|
+
option :auth_token_params, {
|
28
|
+
mode: :query,
|
29
|
+
param_name: 'token'
|
30
|
+
}
|
31
|
+
|
32
|
+
option :preload_data_with_threads, 0
|
33
|
+
|
34
|
+
option :include_data, []
|
35
|
+
|
36
|
+
option :exclude_data, []
|
37
|
+
|
38
|
+
option :additional_data, {}
|
39
|
+
|
40
|
+
# User ID is not guaranteed to be globally unique across all Slack users.
|
41
|
+
# The combination of user ID and team ID, on the other hand, is guaranteed
|
42
|
+
# to be globally unique.
|
43
|
+
uid { "#{user_id}-#{team_id}" }
|
44
|
+
|
45
|
+
credentials do
|
46
|
+
{
|
47
|
+
token: auth['token'],
|
48
|
+
scope: (is_app_token? ? all_scopes : auth['scope']),
|
49
|
+
expires: false
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
info do
|
54
|
+
define_additional_data
|
55
|
+
semaphore
|
56
|
+
|
57
|
+
num_threads = options.preload_data_with_threads.to_i
|
58
|
+
if num_threads > 0 && !skip_info?
|
59
|
+
preload_data_with_threads(num_threads)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Start with only what we can glean from the authorization response.
|
63
|
+
hash = {
|
64
|
+
name: auth['user'].to_h['name'],
|
65
|
+
email: auth['user'].to_h['email'],
|
66
|
+
user_id: user_id,
|
67
|
+
team_name: auth['team_name'] || auth['team'].to_h['name'],
|
68
|
+
team_id: team_id,
|
69
|
+
image: auth['team'].to_h['image_48']
|
70
|
+
}
|
71
|
+
|
72
|
+
# Now add everything else, using further calls to the api, if necessary.
|
73
|
+
unless skip_info?
|
74
|
+
%w(first_name last_name phone skype avatar_hash real_name real_name_normalized).each do |key|
|
75
|
+
hash[key.to_sym] = (
|
76
|
+
user_info['user'].to_h['profile'] ||
|
77
|
+
user_profile['profile']
|
78
|
+
).to_h[key]
|
79
|
+
end
|
80
|
+
|
81
|
+
%w(deleted status color tz tz_label tz_offset is_admin is_owner is_primary_owner is_restricted is_ultra_restricted is_bot has_2fa).each do |key|
|
82
|
+
hash[key.to_sym] = user_info['user'].to_h[key]
|
83
|
+
end
|
84
|
+
|
85
|
+
more_info = {
|
86
|
+
image: (
|
87
|
+
hash[:image] ||
|
88
|
+
user_identity.to_h['image_48'] ||
|
89
|
+
user_info['user'].to_h['profile'].to_h['image_48'] ||
|
90
|
+
user_profile['profile'].to_h['image_48']
|
91
|
+
),
|
92
|
+
name:(
|
93
|
+
hash[:name] ||
|
94
|
+
user_identity['name'] ||
|
95
|
+
user_info['user'].to_h['real_name'] ||
|
96
|
+
user_profile['profile'].to_h['real_name']
|
97
|
+
),
|
98
|
+
email:(
|
99
|
+
hash[:email] ||
|
100
|
+
user_identity.to_h['email'] ||
|
101
|
+
user_info['user'].to_h['profile'].to_h['email'] ||
|
102
|
+
user_profile['profile'].to_h['email']
|
103
|
+
),
|
104
|
+
team_name:(
|
105
|
+
hash[:team_name] ||
|
106
|
+
team_identity.to_h['name'] ||
|
107
|
+
team_info['team'].to_h['name']
|
108
|
+
),
|
109
|
+
team_domain:(
|
110
|
+
auth['team'].to_h['domain'] ||
|
111
|
+
team_identity.to_h['domain'] ||
|
112
|
+
team_info['team'].to_h['domain']
|
113
|
+
),
|
114
|
+
team_image:(
|
115
|
+
auth['team'].to_h['image_44'] ||
|
116
|
+
team_identity.to_h['image_44'] ||
|
117
|
+
team_info['team'].to_h['icon'].to_h['image_44']
|
118
|
+
),
|
119
|
+
team_email_domain:(
|
120
|
+
team_info['team'].to_h['email_domain']
|
121
|
+
),
|
122
|
+
nickname:(
|
123
|
+
user_info.to_h['user'].to_h['name'] ||
|
124
|
+
auth['user'].to_h['name'] ||
|
125
|
+
user_identity.to_h['name']
|
126
|
+
),
|
127
|
+
}
|
128
|
+
|
129
|
+
hash.merge!(more_info)
|
130
|
+
end
|
131
|
+
hash
|
132
|
+
end
|
133
|
+
|
134
|
+
extra do
|
135
|
+
{
|
136
|
+
scopes_requested: (env['omniauth.params'] && env['omniauth.params']['scope']) || \
|
137
|
+
(env['omniauth.strategy'] && env['omniauth.strategy'].options && env['omniauth.strategy'].options.scope),
|
138
|
+
web_hook_info: web_hook_info,
|
139
|
+
bot_info: auth['bot'] || bot_info['bot'],
|
140
|
+
auth: auth,
|
141
|
+
identity: identity,
|
142
|
+
user_info: user_info,
|
143
|
+
user_profile: user_profile,
|
144
|
+
team_info: team_info,
|
145
|
+
apps_permissions_users_list: apps_permissions_users_list,
|
146
|
+
additional_data: get_additional_data,
|
147
|
+
raw_info: @raw_info
|
148
|
+
}
|
149
|
+
end
|
150
|
+
|
151
|
+
# Pass on certain authorize_params to the Slack authorization GET request.
|
152
|
+
# See https://github.com/omniauth/omniauth/issues/390
|
153
|
+
def authorize_params
|
154
|
+
super.tap do |params|
|
155
|
+
%w(scope team redirect_uri).each do |v|
|
156
|
+
if !request.params[v].to_s.empty?
|
157
|
+
params[v.to_sym] = request.params[v]
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# Get a new OAuth2::Client and define custom capabilities.
|
164
|
+
# * verrides super :client method.
|
165
|
+
#
|
166
|
+
# * Log API requests with OmniAuth.logger
|
167
|
+
# * Add API responses to @raw_info hash
|
168
|
+
#
|
169
|
+
def client
|
170
|
+
st_raw_info = raw_info
|
171
|
+
new_client = super
|
172
|
+
log(:debug, "New client #{new_client}.")
|
173
|
+
|
174
|
+
new_client.instance_eval do
|
175
|
+
define_singleton_method(:request) do |*args|
|
176
|
+
OmniAuth.logger.send(:debug, "(slack) API request #{args[0..1]}; in thread #{Thread.current.object_id}.")
|
177
|
+
request_output = super(*args)
|
178
|
+
uri = args[1].to_s.gsub(/^.*\/([^\/]+)/, '\1') # use single-quote or double-back-slash for replacement.
|
179
|
+
st_raw_info[uri.to_s]= request_output
|
180
|
+
request_output
|
181
|
+
end
|
182
|
+
end
|
183
|
+
new_client
|
184
|
+
end
|
185
|
+
|
186
|
+
# Dropping query_string from callback_url prevents some errors in call to /api/oauth.access.
|
187
|
+
def callback_url
|
188
|
+
full_host + script_name + callback_path
|
189
|
+
end
|
190
|
+
|
191
|
+
def identity
|
192
|
+
return {} unless !skip_info? && has_scope?(identity: ['identity.basic','identity:read:user']) && is_not_excluded?
|
193
|
+
semaphore.synchronize {
|
194
|
+
@identity_raw ||= access_token.get('/api/users.identity', headers: {'X-Slack-User' => user_id})
|
195
|
+
@identity ||= @identity_raw.parsed
|
196
|
+
}
|
197
|
+
end
|
198
|
+
|
199
|
+
|
200
|
+
private
|
201
|
+
|
202
|
+
def initialize(*args)
|
203
|
+
super
|
204
|
+
@main_semaphore = Mutex.new
|
205
|
+
@semaphores = {}
|
206
|
+
end
|
207
|
+
|
208
|
+
# Get a mutex specific to the calling method.
|
209
|
+
# This operation is synchronized with its own mutex.
|
210
|
+
def semaphore(method_name = caller[0][/`([^']*)'/, 1])
|
211
|
+
@main_semaphore.synchronize {
|
212
|
+
@semaphores[method_name] ||= Mutex.new
|
213
|
+
}
|
214
|
+
end
|
215
|
+
|
216
|
+
def active_methods
|
217
|
+
@active_methods ||= (
|
218
|
+
includes = [options.include_data].flatten.compact
|
219
|
+
excludes = [options.exclude_data].flatten.compact unless includes.size > 0
|
220
|
+
method_list = %w(apps_permissions_users_list identity user_info user_profile team_info bot_info) #.concat(options[:additional_data].keys)
|
221
|
+
if includes.size > 0
|
222
|
+
method_list.keep_if {|m| includes.include?(m.to_s) || includes.include?(m.to_s.to_sym)}
|
223
|
+
elsif excludes.size > 0
|
224
|
+
method_list.delete_if {|m| excludes.include?(m.to_s) || excludes.include?(m.to_s.to_sym)}
|
225
|
+
end
|
226
|
+
log :debug, "Activated API calls: #{method_list}."
|
227
|
+
log :debug, "Activated additional_data calls: #{options.additional_data.keys}."
|
228
|
+
method_list
|
229
|
+
)
|
230
|
+
end
|
231
|
+
|
232
|
+
def is_not_excluded?(method_name = caller[0][/`([^']*)'/, 1])
|
233
|
+
active_methods.include?(method_name.to_s) || active_methods.include?(method_name.to_s.to_sym)
|
234
|
+
end
|
235
|
+
|
236
|
+
# Preload additional api calls with a pool of threads.
|
237
|
+
def preload_data_with_threads(num_threads)
|
238
|
+
return unless num_threads > 0
|
239
|
+
preload_methods = active_methods.concat(options[:additional_data].keys)
|
240
|
+
log :info, "Preloading (#{preload_methods.size}) data requests using (#{num_threads}) threads."
|
241
|
+
work_q = Queue.new
|
242
|
+
preload_methods.each{|x| work_q.push x }
|
243
|
+
workers = num_threads.to_i.times.map do
|
244
|
+
Thread.new do
|
245
|
+
begin
|
246
|
+
while x = work_q.pop(true)
|
247
|
+
log :debug, "Preloading #{x}."
|
248
|
+
send x
|
249
|
+
end
|
250
|
+
rescue ThreadError
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
workers.map(&:join); "ok"
|
255
|
+
end
|
256
|
+
|
257
|
+
# Define methods for addional data from :additional_data option
|
258
|
+
def define_additional_data
|
259
|
+
hash = options[:additional_data]
|
260
|
+
if !hash.to_h.empty?
|
261
|
+
hash.each do |k,v|
|
262
|
+
define_singleton_method(k) do
|
263
|
+
instance_variable_get(:"@#{k}") ||
|
264
|
+
instance_variable_set(:"@#{k}", v.respond_to?(:call) ? v.call(env) : v)
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def get_additional_data
|
271
|
+
if skip_info?
|
272
|
+
{}
|
273
|
+
else
|
274
|
+
options[:additional_data].inject({}) do |hash,tupple|
|
275
|
+
hash[tupple[0].to_s] = send(tupple[0].to_s)
|
276
|
+
hash
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
# Parsed data returned from /slack/oauth.access api call.
|
282
|
+
def auth
|
283
|
+
@auth ||= access_token.params.to_h.merge({'token' => access_token.token})
|
284
|
+
end
|
285
|
+
|
286
|
+
def user_identity
|
287
|
+
@user_identity ||= identity['user'].to_h
|
288
|
+
end
|
289
|
+
|
290
|
+
def team_identity
|
291
|
+
@team_identity ||= identity['team'].to_h
|
292
|
+
end
|
293
|
+
|
294
|
+
def user_info
|
295
|
+
return {} unless !skip_info? && has_scope?(identity: 'users:read', team: 'users:read') && is_not_excluded?
|
296
|
+
semaphore.synchronize {
|
297
|
+
@user_info_raw ||= access_token.get('/api/users.info', params: {user: user_id}, headers: {'X-Slack-User' => user_id})
|
298
|
+
@user_info ||= @user_info_raw.parsed
|
299
|
+
}
|
300
|
+
end
|
301
|
+
|
302
|
+
def user_profile
|
303
|
+
return {} unless !skip_info? && has_scope?(identity: 'users.profile:read', team: 'users.profile:read') && is_not_excluded?
|
304
|
+
semaphore.synchronize {
|
305
|
+
@user_profile_raw ||= access_token.get('/api/users.profile.get', params: {user: user_id}, headers: {'X-Slack-User' => user_id})
|
306
|
+
@user_profile ||= @user_profile_raw.parsed
|
307
|
+
}
|
308
|
+
end
|
309
|
+
|
310
|
+
def team_info
|
311
|
+
return {} unless !skip_info? && has_scope?(identity: 'team:read', team: 'team:read') && is_not_excluded?
|
312
|
+
semaphore.synchronize {
|
313
|
+
@team_info_raw ||= access_token.get('/api/team.info')
|
314
|
+
@team_info ||= @team_info_raw.parsed
|
315
|
+
}
|
316
|
+
end
|
317
|
+
|
318
|
+
def web_hook_info
|
319
|
+
return {} unless auth.key? 'incoming_webhook'
|
320
|
+
auth['incoming_webhook']
|
321
|
+
end
|
322
|
+
|
323
|
+
def bot_info
|
324
|
+
return {} unless !skip_info? && has_scope?(identity: 'users:read') && is_not_excluded?
|
325
|
+
semaphore.synchronize {
|
326
|
+
@bot_info_raw ||= access_token.get('/api/bots.info')
|
327
|
+
@bot_info ||= @bot_info_raw.parsed
|
328
|
+
}
|
329
|
+
end
|
330
|
+
|
331
|
+
def user_id
|
332
|
+
auth['user_id'] || auth['user'].to_h['id'] || auth['authorizing_user'].to_h['user_id']
|
333
|
+
end
|
334
|
+
|
335
|
+
def team_id
|
336
|
+
auth['team_id'] || auth['team'].to_h['id']
|
337
|
+
end
|
338
|
+
|
339
|
+
# API call to get user permissions for workspace token.
|
340
|
+
# This is needed because workspace token 'sign-in-with-slack' is missing scopes
|
341
|
+
# in the :scope field (acknowledged issue in developer preview).
|
342
|
+
#
|
343
|
+
# Returns [<id>: <resource>]
|
344
|
+
def apps_permissions_users_list
|
345
|
+
return {} unless !skip_info? && is_app_token?
|
346
|
+
semaphore.synchronize {
|
347
|
+
@apps_permissions_users_list_raw ||= access_token.get('/api/apps.permissions.users.list')
|
348
|
+
@apps_permissions_users_list ||= @apps_permissions_users_list_raw.parsed['resources'].inject({}){|h,i| h[i['id']] = i; h}
|
349
|
+
}
|
350
|
+
end
|
351
|
+
|
352
|
+
def raw_info
|
353
|
+
@raw_info ||= {}
|
354
|
+
end
|
355
|
+
|
356
|
+
# Is this a workspace app token?
|
357
|
+
def is_app_token?
|
358
|
+
auth['token_type'].to_s == 'app'
|
359
|
+
end
|
360
|
+
|
361
|
+
# Scopes come from at least 3 different places now.
|
362
|
+
# * The classic :scope field (string)
|
363
|
+
# * New workshop token :scopes field (hash)
|
364
|
+
# * Separate call to apps.permissions.users.list (array)
|
365
|
+
#
|
366
|
+
# This returns hash of workspace scopes, with classic & new identity scopes in :identity.
|
367
|
+
# Lists of scopes are in array form.
|
368
|
+
def all_scopes
|
369
|
+
@all_scopes ||=
|
370
|
+
{'identity' => (auth['scope'] || apps_permissions_users_list[user_id].to_h['scopes'].to_a.join(',')).to_s.split(',')}
|
371
|
+
.merge(auth['scopes'].to_h)
|
372
|
+
end
|
373
|
+
|
374
|
+
# Determine if given scopes exist in current authorization.
|
375
|
+
# Scopes is hash where
|
376
|
+
# key == scope type <identity|app_hope|team|channel|group|mpim|im>
|
377
|
+
# val == array or string of individual scopes.
|
378
|
+
def has_scope?(**scopes_hash)
|
379
|
+
scopes_hash.detect do |section, scopes|
|
380
|
+
test_scopes = case
|
381
|
+
when scopes.is_a?(String); scopes.split(',')
|
382
|
+
when scopes.is_a?(Array); scopes
|
383
|
+
else raise "Scope must be a string or array"
|
384
|
+
end
|
385
|
+
test_scopes.detect do |scope|
|
386
|
+
all_scopes[section.to_s].to_a.include?(scope.to_s)
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
end
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require File.expand_path('../lib/omniauth-slack/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |spec|
|
5
|
+
spec.name = 'ginjo-omniauth-slack'
|
6
|
+
spec.version = OmniAuth::Slack::VERSION
|
7
|
+
spec.authors = ['kimura', 'ginjo']
|
8
|
+
spec.email = ['kimura@enigmo.co.jp', 'wbr@mac.com']
|
9
|
+
spec.description = %q{OmniAuth strategy for Slack}
|
10
|
+
spec.summary = %q{OmniAuth strategy for Slack, based on the oauth2 and omniauth-oauth2 gems}
|
11
|
+
spec.homepage = 'https://github.com/kmrshntr/omniauth-slack.git'
|
12
|
+
spec.license = 'MIT'
|
13
|
+
|
14
|
+
spec.files = `git ls-files`.split($/)
|
15
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
16
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
17
|
+
spec.require_paths = ['lib']
|
18
|
+
|
19
|
+
spec.add_runtime_dependency 'omniauth-oauth2', ['~> 1.4', '~> 1.5']
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '>= 1.11.2'
|
22
|
+
spec.add_development_dependency 'rake'
|
23
|
+
spec.add_development_dependency 'minitest'
|
24
|
+
spec.add_development_dependency 'mocha'
|
25
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require "bundler/setup"
|
2
|
+
require "minitest/autorun"
|
3
|
+
require "mocha/setup"
|
4
|
+
require "omniauth/strategies/slack"
|
5
|
+
|
6
|
+
OmniAuth.config.test_mode = true
|
7
|
+
|
8
|
+
module BlockTestHelper
|
9
|
+
def test(name, &blk)
|
10
|
+
method_name = "test_#{name.gsub(/\s+/, "_")}"
|
11
|
+
raise "Method already defined: #{method_name}" if instance_methods.include?(method_name.to_sym)
|
12
|
+
define_method method_name, &blk
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module CustomAssertions
|
17
|
+
def assert_has_key(key, hash, msg = nil)
|
18
|
+
msg = message(msg) { "Expected #{hash.inspect} to have key #{key.inspect}" }
|
19
|
+
assert hash.has_key?(key), msg
|
20
|
+
end
|
21
|
+
|
22
|
+
def refute_has_key(key, hash, msg = nil)
|
23
|
+
msg = message(msg) { "Expected #{hash.inspect} not to have key #{key.inspect}" }
|
24
|
+
refute hash.has_key?(key), msg
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class TestCase < Minitest::Test
|
29
|
+
extend BlockTestHelper
|
30
|
+
include CustomAssertions
|
31
|
+
end
|
32
|
+
|
33
|
+
class StrategyTestCase < TestCase
|
34
|
+
def setup
|
35
|
+
@request = stub("Request")
|
36
|
+
@request.stubs(:params).returns({})
|
37
|
+
@request.stubs(:cookies).returns({})
|
38
|
+
@request.stubs(:env).returns({})
|
39
|
+
@request.stubs(:scheme).returns({})
|
40
|
+
@request.stubs(:ssl?).returns(false)
|
41
|
+
|
42
|
+
@client_id = "123"
|
43
|
+
@client_secret = "53cr3tz"
|
44
|
+
@options = {}
|
45
|
+
end
|
46
|
+
|
47
|
+
def strategy
|
48
|
+
@strategy ||= begin
|
49
|
+
args = [@client_id, @client_secret, @options].compact
|
50
|
+
OmniAuth::Strategies::Slack.new(nil, *args).tap do |strategy|
|
51
|
+
strategy.stubs(:request).returns(@request)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
Dir[File.expand_path("../support/**/*", __FILE__)].each(&method(:require))
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# NOTE it would be useful if this lived in omniauth-oauth2 eventually
|
2
|
+
module OAuth2StrategyTests
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval do
|
5
|
+
include ClientTests
|
6
|
+
include AuthorizeParamsTests
|
7
|
+
include CSRFAuthorizeParamsTests
|
8
|
+
include TokenParamsTests
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClientTests
|
13
|
+
extend BlockTestHelper
|
14
|
+
|
15
|
+
test "should be initialized with symbolized client_options" do
|
16
|
+
@options = { :client_options => { "authorize_url" => "https://example.com" } }
|
17
|
+
assert_equal "https://example.com", strategy.client.options[:authorize_url]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module AuthorizeParamsTests
|
22
|
+
extend BlockTestHelper
|
23
|
+
|
24
|
+
test "should include any authorize params passed in the :authorize_params option" do
|
25
|
+
@options = { :authorize_params => { :foo => "bar", :baz => "zip" } }
|
26
|
+
assert_equal "bar", strategy.authorize_params["foo"]
|
27
|
+
assert_equal "zip", strategy.authorize_params["baz"]
|
28
|
+
end
|
29
|
+
|
30
|
+
test "should include top-level options that are marked as :authorize_options" do
|
31
|
+
@options = { :authorize_options => [:scope, :foo], :scope => "bar", :foo => "baz" }
|
32
|
+
assert_equal "bar", strategy.authorize_params["scope"]
|
33
|
+
assert_equal "baz", strategy.authorize_params["foo"]
|
34
|
+
end
|
35
|
+
|
36
|
+
test "should exclude top-level options that are not passed" do
|
37
|
+
@options = { :authorize_options => [:bar] }
|
38
|
+
refute_has_key :bar, strategy.authorize_params
|
39
|
+
refute_has_key "bar", strategy.authorize_params
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
module CSRFAuthorizeParamsTests
|
44
|
+
extend BlockTestHelper
|
45
|
+
|
46
|
+
test "should store random state in the session when none is present in authorize or request params" do
|
47
|
+
assert_includes strategy.authorize_params.keys, "state"
|
48
|
+
refute_empty strategy.authorize_params["state"]
|
49
|
+
refute_empty strategy.session["omniauth.state"]
|
50
|
+
assert_equal strategy.authorize_params["state"], strategy.session["omniauth.state"]
|
51
|
+
end
|
52
|
+
|
53
|
+
test "should not store state in the session when present in authorize params vs. a random one" do
|
54
|
+
@options = { :authorize_params => { :state => "bar" } }
|
55
|
+
refute_empty strategy.authorize_params["state"]
|
56
|
+
refute_equal "bar", strategy.authorize_params[:state]
|
57
|
+
refute_empty strategy.session["omniauth.state"]
|
58
|
+
refute_equal "bar", strategy.session["omniauth.state"]
|
59
|
+
end
|
60
|
+
|
61
|
+
test "should not store state in the session when present in request params vs. a random one" do
|
62
|
+
@request.stubs(:params).returns({ "state" => "foo" })
|
63
|
+
refute_empty strategy.authorize_params["state"]
|
64
|
+
refute_equal "foo", strategy.authorize_params[:state]
|
65
|
+
refute_empty strategy.session["omniauth.state"]
|
66
|
+
refute_equal "foo", strategy.session["omniauth.state"]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
module TokenParamsTests
|
71
|
+
extend BlockTestHelper
|
72
|
+
|
73
|
+
test "should include any authorize params passed in the :token_params option" do
|
74
|
+
@options = { :token_params => { :foo => "bar", :baz => "zip" } }
|
75
|
+
assert_equal "bar", strategy.token_params["foo"]
|
76
|
+
assert_equal "zip", strategy.token_params["baz"]
|
77
|
+
end
|
78
|
+
|
79
|
+
test "should include top-level options that are marked as :token_options" do
|
80
|
+
@options = { :token_options => [:scope, :foo], :scope => "bar", :foo => "baz" }
|
81
|
+
assert_equal "bar", strategy.token_params["scope"]
|
82
|
+
assert_equal "baz", strategy.token_params["foo"]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|