omniauth_crowd 2.2.3 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +4 -0
- data/Gemfile.lock +30 -25
- data/lib/omniauth/strategies/crowd.rb +45 -4
- data/lib/omniauth/strategies/crowd/configuration.rb +13 -1
- data/lib/omniauth/strategies/crowd/crowd_validator.rb +100 -37
- data/lib/omniauth_crowd/version.rb +1 -1
- data/spec/omniauth/strategies/crowd_spec.rb +251 -25
- data/spec/spec_helper.rb +1 -0
- metadata +26 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d6496c1f734a4cc164441c2cceb75039fa3751b2
|
4
|
+
data.tar.gz: 9d8e8b2cff8787f2d346dcb45f06486a1596d08d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 598ab5299d7381c0804b8afc81bd7227eea20d868ff2afd3dd38d4e9c518c6486706dc90a7d1b936c46c86e2d6bd6763fa2a7df0285bc6928564fb530a0e4cb2
|
7
|
+
data.tar.gz: 7f88dc8845d027da6983af8a3e05e3c544e5dea8075977eb376708917140298fbd3f30b26173f929877433057aaf1ca56cf8fdec588f933eea463c268f4e638d
|
data/.travis.yml
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
omniauth_crowd (2.
|
4
|
+
omniauth_crowd (2.3.0)
|
5
5
|
activesupport
|
6
6
|
nokogiri (>= 1.4.4)
|
7
7
|
omniauth (~> 1.0)
|
@@ -9,49 +9,51 @@ PATH
|
|
9
9
|
GEM
|
10
10
|
remote: http://rubygems.org/
|
11
11
|
specs:
|
12
|
-
activesupport (4.1
|
13
|
-
i18n (~> 0.
|
12
|
+
activesupport (4.2.5.1)
|
13
|
+
i18n (~> 0.7)
|
14
14
|
json (~> 1.7, >= 1.7.7)
|
15
15
|
minitest (~> 5.1)
|
16
|
-
thread_safe (~> 0.
|
16
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
17
17
|
tzinfo (~> 1.1)
|
18
|
-
addressable (2.
|
19
|
-
crack (0.4.
|
18
|
+
addressable (2.4.0)
|
19
|
+
crack (0.4.3)
|
20
20
|
safe_yaml (~> 1.0.0)
|
21
21
|
diff-lcs (1.2.5)
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
22
|
+
hashdiff (0.3.0)
|
23
|
+
hashie (3.4.3)
|
24
|
+
i18n (0.7.0)
|
25
|
+
json (1.8.3)
|
26
|
+
mini_portile2 (2.0.0)
|
27
|
+
minitest (5.8.4)
|
28
|
+
nokogiri (1.6.7.2)
|
29
|
+
mini_portile2 (~> 2.0.0.rc2)
|
30
|
+
omniauth (1.3.1)
|
30
31
|
hashie (>= 1.2, < 4)
|
31
|
-
rack (
|
32
|
-
rack (1.
|
33
|
-
rack-test (0.6.
|
32
|
+
rack (>= 1.0, < 3)
|
33
|
+
rack (1.6.4)
|
34
|
+
rack-test (0.6.3)
|
34
35
|
rack (>= 1.0)
|
35
|
-
rake (10.
|
36
|
+
rake (10.5.0)
|
36
37
|
rspec (3.0.0)
|
37
38
|
rspec-core (~> 3.0.0)
|
38
39
|
rspec-expectations (~> 3.0.0)
|
39
40
|
rspec-mocks (~> 3.0.0)
|
40
|
-
rspec-core (3.0.
|
41
|
+
rspec-core (3.0.4)
|
41
42
|
rspec-support (~> 3.0.0)
|
42
|
-
rspec-expectations (3.0.
|
43
|
+
rspec-expectations (3.0.4)
|
43
44
|
diff-lcs (>= 1.2.0, < 2.0)
|
44
45
|
rspec-support (~> 3.0.0)
|
45
|
-
rspec-mocks (3.0.
|
46
|
+
rspec-mocks (3.0.4)
|
46
47
|
rspec-support (~> 3.0.0)
|
47
|
-
rspec-support (3.0.
|
48
|
-
safe_yaml (1.0.
|
49
|
-
thread_safe (0.3.
|
48
|
+
rspec-support (3.0.4)
|
49
|
+
safe_yaml (1.0.4)
|
50
|
+
thread_safe (0.3.5)
|
50
51
|
tzinfo (1.2.2)
|
51
52
|
thread_safe (~> 0.1)
|
52
|
-
webmock (1.
|
53
|
+
webmock (1.24.1)
|
53
54
|
addressable (>= 2.3.6)
|
54
55
|
crack (>= 0.3.2)
|
56
|
+
hashdiff
|
55
57
|
|
56
58
|
PLATFORMS
|
57
59
|
ruby
|
@@ -64,3 +66,6 @@ DEPENDENCIES
|
|
64
66
|
rake
|
65
67
|
rspec (~> 3.0.0)
|
66
68
|
webmock
|
69
|
+
|
70
|
+
BUNDLED WITH
|
71
|
+
1.11.2
|
@@ -18,7 +18,13 @@ module OmniAuth
|
|
18
18
|
|
19
19
|
def request_phase
|
20
20
|
if env['REQUEST_METHOD'] == 'GET'
|
21
|
-
|
21
|
+
|
22
|
+
if @configuration.use_sso? && request.cookies[@configuration.session_cookie]
|
23
|
+
redirect callback_url
|
24
|
+
else
|
25
|
+
get_credentials
|
26
|
+
end
|
27
|
+
|
22
28
|
elsif (env['REQUEST_METHOD'] == 'POST') && (not request.params['username'])
|
23
29
|
get_credentials
|
24
30
|
else
|
@@ -27,17 +33,52 @@ module OmniAuth
|
|
27
33
|
end
|
28
34
|
end
|
29
35
|
|
36
|
+
def get_client_ip
|
37
|
+
env['HTTP_X_FORWARDED_FOR'] ? env['HTTP_X_FORWARDED_FOR'] : env['REMOTE_ADDRESS']
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_sso_tokens
|
41
|
+
env['HTTP_COOKIE'].split(';').select { |val|
|
42
|
+
val.strip.start_with?(@configuration.session_cookie)
|
43
|
+
}.map { |val|
|
44
|
+
val.strip.split('=').last
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
30
48
|
def get_credentials
|
49
|
+
|
50
|
+
configuration = @configuration
|
51
|
+
|
31
52
|
OmniAuth::Form.build(:title => (options[:title] || "Crowd Authentication")) do
|
32
53
|
text_field 'Login', 'username'
|
33
54
|
password_field 'Password', 'password'
|
55
|
+
|
56
|
+
if configuration.use_sso? && configuration.sso_url
|
57
|
+
fieldset 'SSO' do
|
58
|
+
html "<a href=\"#{configuration.sso_url}/users/auth/crowd/callback\">" + (configuration.sso_url_image ? "<img src=\"#{configuration.sso_url_image}\" />" : '') + "</a>"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
34
62
|
end.to_response
|
35
|
-
end
|
36
63
|
|
64
|
+
end
|
65
|
+
|
37
66
|
def callback_phase
|
67
|
+
|
38
68
|
creds = session.delete 'omniauth.crowd'
|
39
|
-
|
40
|
-
|
69
|
+
username = creds.nil? ? nil : creds['username']
|
70
|
+
password = creds.nil? ? nil : creds['password']
|
71
|
+
|
72
|
+
unless creds
|
73
|
+
if @configuration.use_sso? && request.cookies[@configuration.session_cookie]
|
74
|
+
validator = CrowdValidator.new(@configuration, username, password, get_client_ip, get_sso_tokens)
|
75
|
+
else
|
76
|
+
return fail!(:no_credentials)
|
77
|
+
end
|
78
|
+
else
|
79
|
+
validator = CrowdValidator.new(@configuration, username, password, get_client_ip, nil)
|
80
|
+
end
|
81
|
+
|
41
82
|
@user_info = validator.user_info
|
42
83
|
|
43
84
|
return fail!(:invalid_credentials) if @user_info.nil? || @user_info.empty?
|
@@ -8,8 +8,9 @@ module OmniAuth
|
|
8
8
|
DEFAULT_AUTHENTICATION_URL = "%s/rest/usermanagement/latest/authentication"
|
9
9
|
DEFAULT_USER_GROUP_URL = "%s/rest/usermanagement/latest/user/group/direct"
|
10
10
|
DEFAULT_CONTENT_TYPE = 'application/xml'
|
11
|
+
DEFAULT_SESSION_COOKIE = 'crowd.token_key'
|
11
12
|
|
12
|
-
attr_reader :crowd_application_name, :crowd_password, :disable_ssl_verification, :include_users_groups, :use_sessions, :session_url, :content_type
|
13
|
+
attr_reader :crowd_application_name, :crowd_password, :disable_ssl_verification, :include_users_groups, :use_sessions, :session_url, :content_type, :session_cookie, :sso_url, :sso_url_image
|
13
14
|
|
14
15
|
alias :"disable_ssl_verification?" :disable_ssl_verification
|
15
16
|
alias :"include_users_groups?" :include_users_groups
|
@@ -29,6 +30,10 @@ module OmniAuth
|
|
29
30
|
# @option params [String, nil] :crowd_user_group_url (:crowd_server_url + '/rest/usermanagement/latest/user/group/direct') the URL to which to
|
30
31
|
# use for retrieving users groups optional if `:crowd_server_url` is specified, or if `:include_user_groups` is false
|
31
32
|
# required otherwise.
|
33
|
+
# @option params [Boolean, false] :use_sessions Use Crowd sessions. If the user logins with user and password create a new Crowd session. Update the session if only a session token is sent (Cookie name set by option session_cookie)
|
34
|
+
# @option params [String, 'crowd.token_key'] :session_cookie Session cookie name. Defaults to: 'crowd.token_key'
|
35
|
+
# @option params [String, nil] :sso_url URL of the external SSO page. If this parameter is defined the login form will have a link which will redirect to the SSO page. The SSO must return to the URL of the page using omniauth_crowd (Path portion '/users/auth/crowd/callback' is appended to the URL)
|
36
|
+
# @option params [String, nil] :sso_url_image Optional image URL to be used in SSO link in the login form
|
32
37
|
def initialize(params)
|
33
38
|
parse_params params
|
34
39
|
end
|
@@ -46,6 +51,10 @@ module OmniAuth
|
|
46
51
|
@user_group_url.nil? ? nil : append_username( @user_group_url, username)
|
47
52
|
end
|
48
53
|
|
54
|
+
def use_sso?()
|
55
|
+
@use_sessions && @sso_url ? true : false
|
56
|
+
end
|
57
|
+
|
49
58
|
private
|
50
59
|
def parse_params(options)
|
51
60
|
options= {:include_user_groups => true}.merge(options || {})
|
@@ -56,6 +65,9 @@ module OmniAuth
|
|
56
65
|
@crowd_password = options[:application_password]
|
57
66
|
@use_sessions = options[:use_sessions]
|
58
67
|
@content_type = options[:content_type] || DEFAULT_CONTENT_TYPE
|
68
|
+
@session_cookie = options[:session_cookie] || DEFAULT_SESSION_COOKIE
|
69
|
+
@sso_url = options[:sso_url]
|
70
|
+
@sso_url_image = options[:sso_url_image]
|
59
71
|
|
60
72
|
unless options.include?(:crowd_server_url) || options.include?(:crowd_authentication_url)
|
61
73
|
raise ArgumentError.new("Either :crowd_server_url or :crowd_authentication_url MUST be provided")
|
@@ -6,22 +6,16 @@ module OmniAuth
|
|
6
6
|
module Strategies
|
7
7
|
class Crowd
|
8
8
|
class CrowdValidator
|
9
|
-
SESSION_REQUEST_BODY = <<-BODY.strip
|
10
|
-
<authentication-context>
|
11
|
-
<username>%s</username>
|
12
|
-
<password>%s</password>
|
13
|
-
</authentication-context>
|
14
|
-
BODY
|
15
9
|
AUTHENTICATION_REQUEST_BODY = "<password><value>%s</value></password>"
|
16
|
-
def initialize(configuration, username, password)
|
17
|
-
@configuration, @username, @password = configuration, username, password
|
10
|
+
def initialize(configuration, username, password, client_ip, tokens)
|
11
|
+
@configuration, @username, @password, @client_ip, @tokens = configuration, username, password, client_ip, tokens
|
18
12
|
@authentiction_uri = URI.parse(@configuration.authentication_url(@username))
|
19
13
|
@session_uri = URI.parse(@configuration.session_url) if @configuration.use_sessions
|
20
|
-
@user_group_uri = @configuration.include_users_groups? ? URI.parse(@configuration.user_group_url(@username)) : nil
|
21
14
|
end
|
22
15
|
|
23
16
|
def user_info
|
24
17
|
user_info_hash = retrieve_user_info!
|
18
|
+
|
25
19
|
if user_info_hash && @configuration.include_users_groups?
|
26
20
|
user_info_hash = add_user_groups!(user_info_hash)
|
27
21
|
else
|
@@ -29,27 +23,36 @@ BODY
|
|
29
23
|
end
|
30
24
|
|
31
25
|
if user_info_hash && @configuration.use_sessions?
|
32
|
-
user_info_hash =
|
26
|
+
user_info_hash = set_session!(user_info_hash)
|
33
27
|
end
|
34
28
|
|
35
29
|
user_info_hash
|
36
30
|
end
|
37
31
|
|
38
32
|
private
|
39
|
-
def
|
40
|
-
|
33
|
+
def set_session!(user_info_hash)
|
34
|
+
|
35
|
+
response = nil
|
36
|
+
|
37
|
+
if user_info_hash["sso_token"]
|
38
|
+
response = make_session_request(user_info_hash["sso_token"])
|
39
|
+
else
|
40
|
+
response = make_session_request(nil)
|
41
|
+
end
|
42
|
+
|
41
43
|
if response.kind_of?(Net::HTTPSuccess) && response.body
|
42
44
|
doc = Nokogiri::XML(response.body)
|
43
45
|
user_info_hash["sso_token"] = doc.xpath('//token/text()').to_s
|
44
46
|
else
|
45
|
-
OmniAuth.logger.send(:warn, "(crowd) [
|
46
|
-
OmniAuth.logger.send(:warn, "(crowd) [
|
47
|
+
OmniAuth.logger.send(:warn, "(crowd) [set_session!] response code: #{response.code.to_s}")
|
48
|
+
OmniAuth.logger.send(:warn, "(crowd) [set_session!] response body: #{response.body}")
|
47
49
|
end
|
50
|
+
|
48
51
|
user_info_hash
|
49
52
|
end
|
50
53
|
|
51
54
|
def add_user_groups!(user_info_hash)
|
52
|
-
response = make_user_group_request
|
55
|
+
response = make_user_group_request(user_info_hash['user'])
|
53
56
|
unless response.code.to_i != 200 || response.body.nil? || response.body == ''
|
54
57
|
doc = Nokogiri::XML(response.body)
|
55
58
|
user_info_hash["groups"] = doc.xpath("//groups/group/@name").map(&:to_s)
|
@@ -59,18 +62,32 @@ BODY
|
|
59
62
|
|
60
63
|
def retrieve_user_info!
|
61
64
|
response = make_authorization_request
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
65
|
+
|
66
|
+
unless response === nil
|
67
|
+
unless response.code.to_i != 200 || response.body.nil? || response.body == ''
|
68
|
+
|
69
|
+
doc = Nokogiri::XML(response.body)
|
70
|
+
result = {
|
71
|
+
"user" => doc.xpath("//user/@name").to_s,
|
72
|
+
"name" => doc.xpath("//user/display-name/text()").to_s,
|
73
|
+
"first_name" => doc.xpath("//user/first-name/text()").to_s,
|
74
|
+
"last_name" => doc.xpath("//user/last-name/text()").to_s,
|
75
|
+
"email" => doc.xpath("//user/email/text()").to_s
|
76
|
+
}
|
77
|
+
|
78
|
+
if doc.at_xpath("//token")
|
79
|
+
result["sso_token"] = doc.xpath("//token/text()").to_s
|
80
|
+
end
|
81
|
+
|
82
|
+
result
|
83
|
+
|
84
|
+
else
|
85
|
+
OmniAuth.logger.send(:warn, "(crowd) [retrieve_user_info!] response code: #{response.code.to_s}")
|
86
|
+
OmniAuth.logger.send(:warn, "(crowd) [retrieve_user_info!] response body: #{response.body}")
|
87
|
+
nil
|
88
|
+
end
|
71
89
|
else
|
72
|
-
OmniAuth.logger.send(:warn, "(crowd) [retrieve_user_info!]
|
73
|
-
OmniAuth.logger.send(:warn, "(crowd) [retrieve_user_info!] response body: #{response.body}")
|
90
|
+
OmniAuth.logger.send(:warn, "(crowd) [retrieve_user_info!] None of the session tokens were valid")
|
74
91
|
nil
|
75
92
|
end
|
76
93
|
end
|
@@ -91,18 +108,58 @@ BODY
|
|
91
108
|
end
|
92
109
|
end
|
93
110
|
|
94
|
-
def make_user_group_request
|
95
|
-
make_request(@
|
111
|
+
def make_user_group_request(username)
|
112
|
+
make_request(URI.parse(@configuration.user_group_url(username)))
|
96
113
|
end
|
97
114
|
|
98
115
|
def make_authorization_request
|
99
|
-
make_request(@authentiction_uri, make_authentication_request_body(@password))
|
100
|
-
end
|
101
116
|
|
102
|
-
|
103
|
-
|
117
|
+
if @configuration.use_sessions? && @tokens.kind_of?(Array)
|
118
|
+
make_session_retrieval_request
|
119
|
+
else
|
120
|
+
make_request(@authentiction_uri, make_authentication_request_body(@password))
|
121
|
+
end
|
104
122
|
end
|
105
123
|
|
124
|
+
def make_session_request(token)
|
125
|
+
|
126
|
+
root = url = validation_factor = nil
|
127
|
+
doc = Nokogiri::XML::Document.new
|
128
|
+
|
129
|
+
if token === nil
|
130
|
+
|
131
|
+
url = @session_uri
|
132
|
+
root = doc.create_element('authentication-context')
|
133
|
+
|
134
|
+
doc.root = root
|
135
|
+
root.add_child(doc.create_element('username', @username))
|
136
|
+
root.add_child(doc.create_element('password', @password))
|
137
|
+
|
138
|
+
else
|
139
|
+
url = URI.parse(@session_uri.to_s() + "/#{token}")
|
140
|
+
end
|
141
|
+
|
142
|
+
if @configuration.use_sessions? || @client_ip
|
143
|
+
|
144
|
+
if root === nil
|
145
|
+
root = doc.create_element('validation-factors')
|
146
|
+
doc.root = root
|
147
|
+
else
|
148
|
+
root.add_child(doc.create_element('validation-factors'))
|
149
|
+
end
|
150
|
+
|
151
|
+
validation_factor = doc.create_element('validation-factor')
|
152
|
+
validation_factor.add_child(doc.create_element('name', 'remote_address'))
|
153
|
+
validation_factor.add_child(doc.create_element('value', @client_ip))
|
154
|
+
|
155
|
+
doc.xpath('//validation-factors').first.add_child(validation_factor)
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
make_request(url, doc.to_s)
|
160
|
+
|
161
|
+
end
|
162
|
+
|
106
163
|
# create the body using Nokogiri so proper encoding of passwords can be ensured
|
107
164
|
def make_authentication_request_body(password)
|
108
165
|
request_body = Nokogiri::XML(AUTHENTICATION_REQUEST_BODY)
|
@@ -111,11 +168,17 @@ BODY
|
|
111
168
|
return request_body.root.to_s # return the body without the xml header
|
112
169
|
end
|
113
170
|
|
114
|
-
def
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
171
|
+
def make_session_retrieval_request
|
172
|
+
|
173
|
+
response = nil
|
174
|
+
|
175
|
+
@tokens.any? { |token|
|
176
|
+
response = make_request(URI.parse(@session_uri.to_s() + "/#{token}"))
|
177
|
+
response.code.to_i == 200 && !response.body.nil? && response.body != ''
|
178
|
+
}
|
179
|
+
|
180
|
+
response
|
181
|
+
|
119
182
|
end
|
120
183
|
end
|
121
184
|
end
|
@@ -9,12 +9,17 @@ describe OmniAuth::Strategies::Crowd, :type=>:strategy do
|
|
9
9
|
[OmniAuth::Strategies::Crowd, {:crowd_server_url => @crowd_server_url,
|
10
10
|
:application_name => @application_name,
|
11
11
|
:application_password => @application_password,
|
12
|
-
:use_sessions => @using_sessions
|
12
|
+
:use_sessions => @using_sessions,
|
13
|
+
:sso_url => @sso_url,
|
14
|
+
:sso_url_image => @sso_url_image
|
15
|
+
}]
|
13
16
|
end
|
14
17
|
|
15
18
|
@using_sessions = false
|
19
|
+
@sso_url = nil
|
20
|
+
@sso_url_image = nil
|
16
21
|
let(:config) { OmniAuth::Strategies::Crowd::Configuration.new(strategy[1]) }
|
17
|
-
let(:validator) { OmniAuth::Strategies::Crowd::CrowdValidator.new(config, 'foo', 'bar') }
|
22
|
+
let(:validator) { OmniAuth::Strategies::Crowd::CrowdValidator.new(config, 'foo', 'bar', nil, nil) }
|
18
23
|
|
19
24
|
describe 'Authentication Request Body' do
|
20
25
|
|
@@ -37,28 +42,6 @@ BODY
|
|
37
42
|
end
|
38
43
|
end
|
39
44
|
|
40
|
-
describe 'Session Request Body' do
|
41
|
-
it 'should send username and password in session request' do
|
42
|
-
body = <<-BODY.strip
|
43
|
-
<authentication-context>
|
44
|
-
<username>foo</username>
|
45
|
-
<password>bar</password>
|
46
|
-
</authentication-context>
|
47
|
-
BODY
|
48
|
-
expect(validator.send(:make_session_request_body, 'foo', 'bar')).to eq(body)
|
49
|
-
end
|
50
|
-
|
51
|
-
it 'should escape special characters username and password in session request' do
|
52
|
-
body = <<-BODY.strip
|
53
|
-
<authentication-context>
|
54
|
-
<username>foo</username>
|
55
|
-
<password>bar<</password>
|
56
|
-
</authentication-context>
|
57
|
-
BODY
|
58
|
-
expect(validator.send(:make_session_request_body, 'foo', 'bar<')).to eq(body)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
45
|
describe 'GET /auth/crowd' do
|
63
46
|
it 'should show the login form' do
|
64
47
|
get '/auth/crowd'
|
@@ -150,7 +133,7 @@ BODY
|
|
150
133
|
describe 'GET /auth/crowd/callback with credentials will fail' do
|
151
134
|
before do
|
152
135
|
stub_request(:post, "https://bogus_app:bogus_app_password@crowd.example.org/rest/usermanagement/latest/authentication?username=foo").
|
153
|
-
to_return(:
|
136
|
+
to_return(:status=>400)
|
154
137
|
get '/auth/crowd/callback', nil, 'rack.session'=>{'omniauth.crowd'=> {"username"=>"foo", "password"=>"ba"}}
|
155
138
|
end
|
156
139
|
it 'should fail' do
|
@@ -158,4 +141,247 @@ BODY
|
|
158
141
|
expect(last_response.headers['Location']).to match(/invalid_credentials/)
|
159
142
|
end
|
160
143
|
end
|
144
|
+
|
145
|
+
describe 'GET /auth/crowd without credentials will redirect to login form' do
|
146
|
+
|
147
|
+
sso_url = 'https://foo.bar'
|
148
|
+
|
149
|
+
before do
|
150
|
+
@using_sessions = true
|
151
|
+
@sso_url = sso_url
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'should have the SSO button in the response body' do
|
155
|
+
|
156
|
+
found_legend = found_anchor = nil
|
157
|
+
|
158
|
+
get '/auth/crowd'
|
159
|
+
|
160
|
+
Nokogiri::HTML(last_response.body).xpath('//html/body/form/fieldset/*').each do |element|
|
161
|
+
|
162
|
+
if element.name === 'legend' && element.content() === 'SSO'
|
163
|
+
found_legend = true
|
164
|
+
elsif element.name === 'a' && element.attr('href') === "#{sso_url}/users/auth/crowd/callback"
|
165
|
+
found_anchor = true
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
expect(found_legend).to(be(true))
|
170
|
+
expect(found_anchor).to(be(true))
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
after do
|
175
|
+
@using_sessions = false
|
176
|
+
@sso_url = nil
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
describe 'GET /auth/crowd without credentials will redirect to login form which has custom image in the SSO link' do
|
182
|
+
|
183
|
+
sso_url = 'https://foo.bar'
|
184
|
+
sso_url_image = 'https://foo.bar/image.png'
|
185
|
+
|
186
|
+
before do
|
187
|
+
@using_sessions = true
|
188
|
+
@sso_url = sso_url
|
189
|
+
@sso_url_image = 'https://foo.bar/image.png'
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'should have the SSO button with a custom image in the response body' do
|
193
|
+
|
194
|
+
found_legend = found_anchor = found_image = false
|
195
|
+
|
196
|
+
get '/auth/crowd'
|
197
|
+
|
198
|
+
Nokogiri::HTML(last_response.body).xpath('//html/body/form/fieldset/*').each do |element|
|
199
|
+
|
200
|
+
if element.name === 'legend' && element.content() === 'SSO'
|
201
|
+
found_legend = true
|
202
|
+
elsif element.name === 'a' && element.attr('href') === "#{sso_url}/users/auth/crowd/callback"
|
203
|
+
|
204
|
+
found_anchor = true
|
205
|
+
|
206
|
+
if element.children.length === 1 && element.children.first.name === 'img' && element.children.first.attr('src') === sso_url_image
|
207
|
+
found_image = true
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
expect(found_legend).to(be(true))
|
214
|
+
expect(found_anchor).to(be(true))
|
215
|
+
expect(found_image).to(be(true))
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
after do
|
220
|
+
@using_sessions = false
|
221
|
+
@sso_url = nil
|
222
|
+
@sso_url_image = nil
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
226
|
+
|
227
|
+
describe 'GET /auth/crowd without credentials but with SSO cookie will redirect to callback' do
|
228
|
+
|
229
|
+
sso_url = 'https://foo.bar'
|
230
|
+
|
231
|
+
before do
|
232
|
+
|
233
|
+
@using_sessions = true
|
234
|
+
@sso_url = sso_url
|
235
|
+
|
236
|
+
set_cookie('crowd.token_key=foobar')
|
237
|
+
|
238
|
+
end
|
239
|
+
|
240
|
+
it 'should redirect to callback' do
|
241
|
+
get '/auth/crowd'
|
242
|
+
expect(last_response).to be_redirect
|
243
|
+
expect(last_response.headers['Location']).to eq('http://example.org/auth/crowd/callback')
|
244
|
+
end
|
245
|
+
|
246
|
+
after do
|
247
|
+
|
248
|
+
@using_sessions = false
|
249
|
+
@sso_url = nil
|
250
|
+
|
251
|
+
clear_cookies()
|
252
|
+
|
253
|
+
end
|
254
|
+
|
255
|
+
end
|
256
|
+
|
257
|
+
describe 'POST /auth/crowd/callback without credentials but with SSO cookie will redirect to login form because session is invalid' do
|
258
|
+
|
259
|
+
sso_url = 'https://foo.bar'
|
260
|
+
token = 'foobar'
|
261
|
+
|
262
|
+
before do
|
263
|
+
|
264
|
+
@using_sessions = true
|
265
|
+
@sso_url = sso_url
|
266
|
+
|
267
|
+
stub_request(:get, "https://bogus_app:bogus_app_password@crowd.example.org/rest/usermanagement/latest/session/#{token}").
|
268
|
+
to_return(:status => [404])
|
269
|
+
|
270
|
+
set_cookie("crowd.token_key=#{token}")
|
271
|
+
|
272
|
+
end
|
273
|
+
|
274
|
+
it 'should redirect to login form' do
|
275
|
+
post '/auth/crowd/callback'
|
276
|
+
expect(last_response).to be_redirect
|
277
|
+
expect(last_response.headers['Location']).to match(/invalid_credentials/)
|
278
|
+
end
|
279
|
+
|
280
|
+
after do
|
281
|
+
|
282
|
+
@using_sessions = false
|
283
|
+
@sso_url = nil
|
284
|
+
|
285
|
+
clear_cookies()
|
286
|
+
|
287
|
+
end
|
288
|
+
|
289
|
+
end
|
290
|
+
|
291
|
+
describe 'GET /auth/crowd/callback without credentials but with SSO cookie will succeed' do
|
292
|
+
|
293
|
+
sso_url = 'https://foo.bar'
|
294
|
+
token = 'rtk8eMvqq00EiGn5iJCMZQ00'
|
295
|
+
|
296
|
+
before do
|
297
|
+
|
298
|
+
@using_sessions = true
|
299
|
+
@sso_url = sso_url
|
300
|
+
|
301
|
+
stub_request(:get, "https://bogus_app:bogus_app_password@crowd.example.org/rest/usermanagement/latest/session/#{token}").
|
302
|
+
to_return(:status => 200, :body => File.read(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'session.xml')))
|
303
|
+
stub_request(:post, "https://bogus_app:bogus_app_password@crowd.example.org/rest/usermanagement/latest/session/#{token}").
|
304
|
+
to_return(:status => 200)
|
305
|
+
stub_request(:get, "https://bogus_app:bogus_app_password@crowd.example.org/rest/usermanagement/latest/user/group/direct?username=foo").
|
306
|
+
to_return(:body => File.read(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'groups.xml')))
|
307
|
+
|
308
|
+
set_cookie("crowd.token_key=#{token}")
|
309
|
+
|
310
|
+
end
|
311
|
+
|
312
|
+
it 'should return user data' do
|
313
|
+
|
314
|
+
auth = nil
|
315
|
+
|
316
|
+
get '/auth/crowd/callback'
|
317
|
+
|
318
|
+
auth = last_request.env['omniauth.auth']
|
319
|
+
|
320
|
+
expect(auth['provider']).to eq(:crowd)
|
321
|
+
expect(auth['uid']).to eq('foo')
|
322
|
+
expect(auth['info']).to be_kind_of(Hash)
|
323
|
+
expect(auth['info']['groups'].sort).to eq(["Developers", "jira-users"].sort)
|
324
|
+
|
325
|
+
end
|
326
|
+
|
327
|
+
after do
|
328
|
+
|
329
|
+
@using_sessions = false
|
330
|
+
@sso_url = nil
|
331
|
+
|
332
|
+
clear_cookies()
|
333
|
+
|
334
|
+
end
|
335
|
+
|
336
|
+
end
|
337
|
+
|
338
|
+
describe 'GET /auth/crowd/callback without credentials but with multiple SSO cookies will succeed because one of them is valid' do
|
339
|
+
|
340
|
+
sso_url = 'https://foo.bar'
|
341
|
+
|
342
|
+
before do
|
343
|
+
|
344
|
+
@using_sessions = true
|
345
|
+
@sso_url = sso_url
|
346
|
+
|
347
|
+
stub_request(:get, "https://bogus_app:bogus_app_password@crowd.example.org/rest/usermanagement/latest/session/foo").
|
348
|
+
to_return(:status => 404)
|
349
|
+
stub_request(:get, "https://bogus_app:bogus_app_password@crowd.example.org/rest/usermanagement/latest/session/fubar").
|
350
|
+
to_return(:status => 404)
|
351
|
+
stub_request(:get, "https://bogus_app:bogus_app_password@crowd.example.org/rest/usermanagement/latest/session/rtk8eMvqq00EiGn5iJCMZQ00").
|
352
|
+
to_return(:status => 200, :body => File.read(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'session.xml')))
|
353
|
+
stub_request(:post, "https://bogus_app:bogus_app_password@crowd.example.org/rest/usermanagement/latest/session/rtk8eMvqq00EiGn5iJCMZQ00").
|
354
|
+
to_return(:status => 200)
|
355
|
+
stub_request(:get, "https://bogus_app:bogus_app_password@crowd.example.org/rest/usermanagement/latest/user/group/direct?username=foo").
|
356
|
+
to_return(:body => File.read(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'groups.xml')))
|
357
|
+
|
358
|
+
header('Cookie', "crowd.token_key=foo;crowd.token_key=rtk8eMvqq00EiGn5iJCMZQ00;crowd.token_key=fubar")
|
359
|
+
|
360
|
+
end
|
361
|
+
|
362
|
+
it 'should return user data' do
|
363
|
+
|
364
|
+
auth = nil
|
365
|
+
|
366
|
+
get '/auth/crowd/callback'
|
367
|
+
|
368
|
+
auth = last_request.env['omniauth.auth']
|
369
|
+
|
370
|
+
expect(auth['provider']).to eq(:crowd)
|
371
|
+
expect(auth['uid']).to eq('foo')
|
372
|
+
expect(auth['info']).to be_kind_of(Hash)
|
373
|
+
expect(auth['info']['groups'].sort).to eq(["Developers", "jira-users"].sort)
|
374
|
+
|
375
|
+
end
|
376
|
+
|
377
|
+
after do
|
378
|
+
|
379
|
+
@using_sessions = false
|
380
|
+
@sso_url = nil
|
381
|
+
|
382
|
+
header('Cookie', nil)
|
383
|
+
|
384
|
+
end
|
385
|
+
|
386
|
+
end
|
161
387
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,139 +1,139 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: omniauth_crowd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Di Marco
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-03-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: omniauth
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '1.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: nokogiri
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: 1.4.4
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 1.4.4
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: activesupport
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '0'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rack
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rake
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- -
|
73
|
+
- - ">="
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: '0'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- -
|
80
|
+
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: rack-test
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- -
|
87
|
+
- - ">="
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: '0'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- -
|
94
|
+
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: rspec
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - ~>
|
101
|
+
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
103
|
version: 3.0.0
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- - ~>
|
108
|
+
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: 3.0.0
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: webmock
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- -
|
115
|
+
- - ">="
|
116
116
|
- !ruby/object:Gem::Version
|
117
117
|
version: '0'
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
|
-
- -
|
122
|
+
- - ">="
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: bundler
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
|
-
- -
|
129
|
+
- - ">"
|
130
130
|
- !ruby/object:Gem::Version
|
131
131
|
version: 1.0.0
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
|
-
- -
|
136
|
+
- - ">"
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: 1.0.0
|
139
139
|
description: This is an OmniAuth provider for Atlassian Crowd's REST API. It allows
|
@@ -144,9 +144,9 @@ executables: []
|
|
144
144
|
extensions: []
|
145
145
|
extra_rdoc_files: []
|
146
146
|
files:
|
147
|
-
- .document
|
148
|
-
- .gitignore
|
149
|
-
- .travis.yml
|
147
|
+
- ".document"
|
148
|
+
- ".gitignore"
|
149
|
+
- ".travis.yml"
|
150
150
|
- Gemfile
|
151
151
|
- Gemfile.lock
|
152
152
|
- LICENSE.txt
|
@@ -172,17 +172,17 @@ require_paths:
|
|
172
172
|
- lib
|
173
173
|
required_ruby_version: !ruby/object:Gem::Requirement
|
174
174
|
requirements:
|
175
|
-
- -
|
175
|
+
- - ">="
|
176
176
|
- !ruby/object:Gem::Version
|
177
177
|
version: '0'
|
178
178
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
179
179
|
requirements:
|
180
|
-
- -
|
180
|
+
- - ">="
|
181
181
|
- !ruby/object:Gem::Version
|
182
182
|
version: '0'
|
183
183
|
requirements: []
|
184
184
|
rubyforge_project:
|
185
|
-
rubygems_version: 2.
|
185
|
+
rubygems_version: 2.4.5.1
|
186
186
|
signing_key:
|
187
187
|
specification_version: 4
|
188
188
|
summary: An OmniAuth provider for Atlassian Crowd REST API
|