Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/omniauth-slack/response_adapters.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
require_relative 'response_adapters/base_response_adapter'
require_relative 'response_adapters/app_scoped_response_adapter'
require_relative 'response_adapters/identity_scoped_response_adapter'
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
require 'uri'

module OmniAuth
module Slack
class AppScopedResponseAdapter < BaseResponseAdapter
def info(skip_info)
hash = {
nickname: raw_info['user'],
team: raw_info['team'],
user: raw_info['user'],
team_id: raw_info['team_id'],
user_id: raw_info['user_id']
}

unless skip_info
hash.merge!(
name: user_info['user'].to_h['profile'].to_h['real_name_normalized'],
email: user_info['user'].to_h['profile'].to_h['email'],
image_24: user_info['user'].to_h['profile'].to_h['image_24'],
image_48: user_info['user'].to_h['profile'].to_h['image_48'],
image: user_info['user'].to_h['profile'].to_h['image_192'],
first_name: user_info['user'].to_h['profile'].to_h['first_name'],
last_name: user_info['user'].to_h['profile'].to_h['last_name'],
description: user_info['user'].to_h['profile'].to_h['title'],
team_domain: team_info['team'].to_h['domain'],
is_admin: user_info['user'].to_h['is_admin'],
is_owner: user_info['user'].to_h['is_owner'],
time_zone: user_info['user'].to_h['tz']
)
end

hash
end

def raw_info
@raw_info ||= access_token.get('/api/auth.test').parsed
end

def team_info
@team_info ||= access_token.get('/api/team.info').parsed
end

def user_info
url = URI.parse("/api/users.info")
url.query = Rack::Utils.build_query(user: raw_info['user_id'])
url = url.to_s

@user_info ||= access_token.get(url).parsed
end

def uid
raw_info['user_id']
end
end
end
end
11 changes: 11 additions & 0 deletions lib/omniauth-slack/response_adapters/base_response_adapter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module OmniAuth
module Slack
class BaseResponseAdapter
attr_reader :access_token

def initialize(access_token)
@access_token = access_token
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
module OmniAuth
module Slack
class IdentityScopedResponseAdapter < BaseResponseAdapter
def info(skip_info)
hash = {
nickname: raw_info['user']['name'],
team: raw_info['team']['name'],
user: raw_info['user']['name'],
team_id: raw_info['team']['id'],
user_id: raw_info['user']['id']
}

unless skip_info
hash.merge!(
name: user_info['name'],
email: user_info['email'],
image_24: user_info['image_24'],
image_48: user_info['image_48'],
image: user_info['image_192']
)
end

hash
end

def raw_info
@raw_info ||= access_token.get('/api/users.identity').parsed
end

def team_info
@team_info ||= raw_info['team'].to_h
end

def uid
user_info['id']
end

def user_info
@user_info ||= raw_info['user'].to_h
end
end
end
end
52 changes: 17 additions & 35 deletions lib/omniauth/strategies/slack.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
require 'omniauth/strategies/oauth2'
require 'uri'
require 'omniauth-slack/response_adapters'
require 'rack/utils'

module OmniAuth
Expand All @@ -19,35 +19,10 @@ class Slack < OmniAuth::Strategies::OAuth2
param_name: 'token'
}

uid { raw_info['user_id'] }
uid { response_adapter.uid }

info do
hash = {
nickname: raw_info['user'],
team: raw_info['team'],
user: raw_info['user'],
team_id: raw_info['team_id'],
user_id: raw_info['user_id']
}

unless skip_info?
hash.merge!(
name: user_info['user'].to_h['profile'].to_h['real_name_normalized'],
email: user_info['user'].to_h['profile'].to_h['email'],
first_name: user_info['user'].to_h['profile'].to_h['first_name'],
last_name: user_info['user'].to_h['profile'].to_h['last_name'],
description: user_info['user'].to_h['profile'].to_h['title'],
image_24: user_info['user'].to_h['profile'].to_h['image_24'],
image_48: user_info['user'].to_h['profile'].to_h['image_48'],
image: user_info['user'].to_h['profile'].to_h['image_192'],
team_domain: team_info['team'].to_h['domain'],
is_admin: user_info['user'].to_h['is_admin'],
is_owner: user_info['user'].to_h['is_owner'],
time_zone: user_info['user'].to_h['tz']
)
end

hash
response_adapter.info(skip_info?)
end

extra do
Expand All @@ -68,7 +43,7 @@ class Slack < OmniAuth::Strategies::OAuth2
end

def raw_info
@raw_info ||= access_token.get('/api/auth.test').parsed
response_adapter.raw_info
end

def authorize_params
Expand All @@ -82,15 +57,11 @@ def authorize_params
end

def user_info
url = URI.parse("/api/users.info")
url.query = Rack::Utils.build_query(user: raw_info['user_id'])
url = url.to_s

@user_info ||= access_token.get(url).parsed
response_adapter.user_info
end

def team_info
@team_info ||= access_token.get('/api/team.info').parsed
response_adapter.team_info
end

def web_hook_info
Expand All @@ -102,6 +73,17 @@ def bot_info
return {} unless access_token.params.key? 'bot'
access_token.params['bot']
end

def response_adapter
@response_adapter ||=
identity_scoped? ?
OmniAuth::Slack::IdentityScopedResponseAdapter.new(access_token) :
OmniAuth::Slack::AppScopedResponseAdapter.new(access_token)
end

def identity_scoped?
authorize_params[:scope] =~ /identity\.basic/
end
end
end
end
1 change: 1 addition & 0 deletions test/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def setup

@client_id = "123"
@client_secret = "53cr3tz"
@options = nil
end

def strategy
Expand Down
71 changes: 62 additions & 9 deletions test/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,14 @@ class CallbackUrlTest < StrategyTestCase
end

class UidTest < StrategyTestCase
def setup
super
strategy.stubs(:raw_info).returns("user_id" => "U123")
test "returns the user ID from raw_info" do
strategy.response_adapter.stubs(:raw_info).returns("user_id" => "U123")
assert_equal "U123", strategy.uid
end

test "returns the user ID from raw_info" do
test "returns the user ID from raw info for identity scoped requests" do
strategy.stubs(:identity_scoped?).returns true
strategy.response_adapter.stubs(:raw_info).returns("user" => { "id" => "U123" })
assert_equal "U123", strategy.uid
end
end
Expand Down Expand Up @@ -99,6 +101,49 @@ def setup
end
end

class IdentityScopeTest < StrategyTestCase
test "identity scoped if it includes an identity.basic scope" do
@options = { authorize_options: [:scope], scope: "identity.basic" }
assert strategy.identity_scoped?
end

test "identity scoped if scope includes additional scopes" do
@options = { authorize_options: [:scope],
scope: "team.read,identity.basic,users.read" }
assert strategy.identity_scoped?
end

test "not identity scope if it does not include identity.basic scope" do
@options = { authorize_options: [:scope], scope: "identity.email" }
assert !strategy.identity_scoped?
end

test "not identity scope if a scope is not included" do
assert !strategy.identity_scoped?
end
end

class RawInfoTest < StrategyTestCase
def setup
super
@access_token = stub("OAuth2::AccessToken")
strategy.stubs(:access_token).returns(@access_token)
end

test "performs a GET to https://slack.com/api/auth.test" do
@access_token.expects(:get).with("/api/auth.test")
.returns(stub_everything("OAuth2::Response"))
strategy.raw_info
end

test "performs a GET to https://slack.com/api/users.identity for identity scopes" do
strategy.stubs(:identity_scoped?).returns(true)
@access_token.expects(:get).with("/api/users.identity")
.returns(stub_everything("OAuth2::Response"))
strategy.raw_info
end
end

class UserInfoTest < StrategyTestCase

def setup
Expand All @@ -108,32 +153,40 @@ def setup
end

test "performs a GET to https://slack.com/api/users.info" do
strategy.stubs(:raw_info).returns("user_id" => "U123")
strategy.response_adapter.stubs(:raw_info).returns("user_id" => "U123")
@access_token.expects(:get).with("/api/users.info?user=U123")
.returns(stub_everything("OAuth2::Response"))
strategy.user_info
end

test "URI escapes user ID" do
strategy.stubs(:raw_info).returns("user_id" => "../haxx?U123#abc")
strategy.response_adapter.stubs(:raw_info).returns("user_id" => "../haxx?U123#abc")
@access_token.expects(:get).with("/api/users.info?user=..%2Fhaxx%3FU123%23abc")
.returns(stub_everything("OAuth2::Response"))
strategy.user_info
end

test "returns the existing user info for identity scopes" do
strategy.stubs(:identity_scoped?).returns(true)
user_info = { "id" => "U123", "name" => "Jimmy Page" }
strategy.response_adapter.stubs(:raw_info).returns("user" => user_info)
assert_equal strategy.user_info, user_info
end
end

class SkipInfoTest < StrategyTestCase

test 'info should not include extended info when skip_info is specified' do
@options = { skip_info: true }
strategy.stubs(:raw_info).returns({})
strategy.response_adapter.stubs(:raw_info).returns({})
assert_equal %w[nickname team user team_id user_id], strategy.info.keys.map(&:to_s)
end

test 'extra should not include extended info when skip_info is specified' do
@options = { skip_info: true }
strategy.stubs(:raw_info).returns({})
strategy.stubs(:webhook_info).returns({})
strategy.response_adapter.stubs(:raw_info).returns({})
strategy.stubs(:bot_info).returns({})
strategy.stubs(:web_hook_info).returns({})
assert_equal %w[raw_info web_hook_info bot_info], strategy.extra.keys.map(&:to_s)
end

Expand Down