ginjo-omniauth-slack 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|