omniauth-cas 1.1.0.beta.1 → 1.1.0.pre.rc.1
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 +6 -14
- data/.editorconfig +16 -0
- data/.ruby-version +1 -0
- data/.travis.yml +1 -0
- data/README.md +36 -10
- data/lib/omniauth-cas.rb +1 -1
- data/lib/omniauth/cas/version.rb +1 -1
- data/lib/omniauth/strategies/cas.rb +99 -49
- data/lib/omniauth/strategies/cas/logout_request.rb +58 -0
- data/lib/omniauth/strategies/cas/service_ticket_validator.rb +18 -10
- data/omniauth-cas.gemspec +5 -7
- data/spec/fixtures/cas_success_jasig.xml +16 -0
- data/spec/omniauth/strategies/cas/logout_request_spec.rb +103 -0
- data/spec/omniauth/strategies/cas/service_ticket_validator_spec.rb +35 -13
- data/spec/omniauth/strategies/cas_spec.rb +174 -77
- data/spec/spec_helper.rb +0 -4
- metadata +39 -51
- data/.rvmrc +0 -1
- data/History.md +0 -58
- data/lib/omniauth/strategies/cas/configuration.rb +0 -34
- data/spec/omniauth/strategies/cas/configuration_spec.rb +0 -60
data/omniauth-cas.gemspec
CHANGED
@@ -4,8 +4,8 @@ require File.expand_path('../lib/omniauth/cas/version', __FILE__)
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
5
|
gem.authors = ["Derek Lindahl"]
|
6
6
|
gem.email = ["dlindahl@customink.com"]
|
7
|
-
# gem.description = %q{TODO: Write a gem description}
|
8
7
|
gem.summary = %q{CAS Strategy for OmniAuth}
|
8
|
+
gem.description = gem.summary
|
9
9
|
gem.homepage = "https://github.com/dlindahl/omniauth-cas"
|
10
10
|
|
11
11
|
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
@@ -15,16 +15,14 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
gem.version = Omniauth::Cas::VERSION
|
17
17
|
|
18
|
-
gem.add_dependency 'omniauth', '~> 1.
|
18
|
+
gem.add_dependency 'omniauth', '~> 1.2.0'
|
19
19
|
gem.add_dependency 'nokogiri', '~> 1.5'
|
20
20
|
gem.add_dependency 'addressable', '~> 2.3'
|
21
21
|
|
22
|
-
gem.add_development_dependency 'rake', '~> 0
|
23
|
-
gem.add_development_dependency 'webmock', '~> 1.
|
24
|
-
gem.add_development_dependency '
|
25
|
-
gem.add_development_dependency 'rspec', '~> 2.11'
|
22
|
+
gem.add_development_dependency 'rake', '~> 10.0'
|
23
|
+
gem.add_development_dependency 'webmock', '~> 1.19.0'
|
24
|
+
gem.add_development_dependency 'rspec', '~> 3.1.0'
|
26
25
|
gem.add_development_dependency 'rack-test', '~> 0.6'
|
27
26
|
|
28
27
|
gem.add_development_dependency 'awesome_print'
|
29
|
-
|
30
28
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
|
2
|
+
<cas:authenticationSuccess>
|
3
|
+
<cas:user>psegel</cas:user>
|
4
|
+
<cas:attributes>
|
5
|
+
<cas:employeeid>54</cas:employeeid>
|
6
|
+
<cas:first_name>P. Segel</cas:first_name>
|
7
|
+
<cas:first_name>Peter</cas:first_name>
|
8
|
+
<cas:last_name>Segel</cas:last_name>
|
9
|
+
<cas:email>psegel@intridea.com</cas:email>
|
10
|
+
<cas:location>Washington, D.C.</cas:location>
|
11
|
+
<cas:image>/images/user.jpg</cas:image>
|
12
|
+
<cas:phone>555-555-5555</cas:phone>
|
13
|
+
<cas:hire_date>2004-07-13</cas:hire_date>
|
14
|
+
</cas:attributes>
|
15
|
+
</cas:authenticationSuccess>
|
16
|
+
</cas:serviceResponse>
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OmniAuth::Strategies::CAS::LogoutRequest do
|
4
|
+
let(:strategy) { double('strategy') }
|
5
|
+
let(:env) do
|
6
|
+
{ 'rack.input' => StringIO.new('','r') }
|
7
|
+
end
|
8
|
+
let(:request) { double('request', params:params, env:env) }
|
9
|
+
let(:params) { { 'url' => url, 'logoutRequest' => logoutRequest } }
|
10
|
+
let(:url) { 'http://notes.dev/signed_in' }
|
11
|
+
let(:logoutRequest) do
|
12
|
+
%Q[
|
13
|
+
<samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion\" ID="123abc-1234-ab12-cd34-1234abcd" Version="2.0" IssueInstant="#{Time.now.to_s}">
|
14
|
+
<saml:NameID>@NOT_USED@</saml:NameID>
|
15
|
+
<samlp:SessionIndex>ST-123456-123abc456def</samlp:SessionIndex>
|
16
|
+
</samlp:LogoutRequest>
|
17
|
+
]
|
18
|
+
end
|
19
|
+
|
20
|
+
subject { described_class.new(strategy, request).call(options) }
|
21
|
+
|
22
|
+
describe 'SAML attributes' do
|
23
|
+
let(:callback) { Proc.new{} }
|
24
|
+
let(:options) do
|
25
|
+
{ on_single_sign_out: callback }
|
26
|
+
end
|
27
|
+
|
28
|
+
before do
|
29
|
+
@rack_input = nil
|
30
|
+
allow(callback).to receive(:call) do |req|
|
31
|
+
@rack_input = req.env['rack.input'].read
|
32
|
+
true
|
33
|
+
end
|
34
|
+
subject
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'are parsed and injected into the Rack Request parameters' do
|
38
|
+
expect(@rack_input).to eq 'name_id=%40NOT_USED%40&session_index=ST-123456-123abc456def'
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'that raise when parsed' do
|
42
|
+
let(:env) { { 'rack.input' => nil } }
|
43
|
+
|
44
|
+
before do
|
45
|
+
allow(strategy).to receive(:fail!)
|
46
|
+
subject
|
47
|
+
expect(strategy).to have_received(:fail!)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'responds with an error' do
|
51
|
+
expect(strategy).to have_received(:fail!)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe 'with a configured callback' do
|
57
|
+
let(:options) do
|
58
|
+
{ on_single_sign_out: callback }
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'that returns TRUE' do
|
62
|
+
let(:callback) { Proc.new{true} }
|
63
|
+
|
64
|
+
it 'responds with OK' do
|
65
|
+
expect(subject[0]).to eq 200
|
66
|
+
expect(subject[2].body).to eq ['OK']
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'that returns Nil' do
|
71
|
+
let(:callback) { Proc.new{} }
|
72
|
+
|
73
|
+
it 'responds with OK' do
|
74
|
+
expect(subject[0]).to eq 200
|
75
|
+
expect(subject[2].body).to eq ['OK']
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'that returns a tuple' do
|
80
|
+
let(:callback) { Proc.new{ [400,{},'Bad Request'] } }
|
81
|
+
|
82
|
+
it 'responds with OK' do
|
83
|
+
expect(subject[0]).to eq 400
|
84
|
+
expect(subject[2].body).to eq ['Bad Request']
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'that raises an error' do
|
89
|
+
let(:exception) { RuntimeError.new('error' )}
|
90
|
+
let(:callback) { Proc.new{raise exception} }
|
91
|
+
|
92
|
+
before do
|
93
|
+
allow(strategy).to receive(:fail!)
|
94
|
+
subject
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'responds with an error' do
|
98
|
+
expect(strategy).to have_received(:fail!)
|
99
|
+
.with(:logout_request, exception)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -1,33 +1,55 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe OmniAuth::Strategies::CAS::ServiceTicketValidator do
|
4
|
-
let(:
|
5
|
-
|
4
|
+
let(:strategy) do
|
5
|
+
double('strategy',
|
6
6
|
service_validate_url: 'https://example.org/serviceValidate'
|
7
7
|
)
|
8
8
|
end
|
9
|
-
|
10
9
|
let(:provider_options) do
|
11
|
-
|
10
|
+
double('provider_options',
|
12
11
|
disable_ssl_verification?: false,
|
13
12
|
ca_path: '/etc/ssl/certsZOMG'
|
14
13
|
)
|
15
14
|
end
|
16
|
-
|
17
15
|
let(:validator) do
|
18
|
-
OmniAuth::Strategies::CAS::ServiceTicketValidator.new(
|
16
|
+
OmniAuth::Strategies::CAS::ServiceTicketValidator.new( strategy, provider_options, '/foo', nil )
|
19
17
|
end
|
20
18
|
|
21
|
-
describe '#
|
22
|
-
|
23
|
-
stub_request(:get, 'https://example.org/serviceValidate?')
|
24
|
-
|
19
|
+
describe '#call' do
|
20
|
+
before do
|
21
|
+
stub_request(:get, 'https://example.org/serviceValidate?')
|
22
|
+
.to_return(status: 200, body: '')
|
25
23
|
end
|
26
24
|
|
27
|
-
|
28
|
-
provider_options.should_receive :ca_path
|
25
|
+
subject { validator.call }
|
29
26
|
|
27
|
+
it 'returns itself' do
|
28
|
+
expect(subject).to eq validator
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'uses the configured CA path' do
|
30
32
|
subject
|
33
|
+
expect(provider_options).to have_received :ca_path
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#user_info' do
|
38
|
+
let(:ok_fixture) do
|
39
|
+
File.expand_path(File.join(File.dirname(__FILE__), '../../../fixtures/cas_success.xml'))
|
40
|
+
end
|
41
|
+
let(:service_response) { File.read(ok_fixture) }
|
42
|
+
|
43
|
+
before do
|
44
|
+
stub_request(:get, 'https://example.org/serviceValidate?')
|
45
|
+
.to_return(status: 200, body:service_response)
|
46
|
+
validator.call
|
47
|
+
end
|
48
|
+
|
49
|
+
subject { validator.user_info }
|
50
|
+
|
51
|
+
it 'parses user info from the response' do
|
52
|
+
expect(subject).to include 'user' => 'psegel'
|
31
53
|
end
|
32
54
|
end
|
33
|
-
end
|
55
|
+
end
|
@@ -3,11 +3,14 @@ require 'spec_helper'
|
|
3
3
|
describe OmniAuth::Strategies::CAS, type: :strategy do
|
4
4
|
include Rack::Test::Methods
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
let(:my_cas_provider) { Class.new(OmniAuth::Strategies::CAS) }
|
7
|
+
before do
|
8
|
+
stub_const 'MyCasProvider', my_cas_provider
|
9
|
+
end
|
10
|
+
let(:app) do
|
8
11
|
Rack::Builder.new {
|
9
12
|
use OmniAuth::Test::PhonySession
|
10
|
-
use MyCasProvider, name: :cas, host: 'cas.example.org', ssl: false, port: 8080,
|
13
|
+
use MyCasProvider, name: :cas, host: 'cas.example.org', ssl: false, port: 8080, uid_field: :employeeid
|
11
14
|
run lambda { |env| [404, {'Content-Type' => 'text/plain'}, [env.key?('omniauth.auth').to_s]] }
|
12
15
|
}.to_app
|
13
16
|
end
|
@@ -22,13 +25,56 @@ describe OmniAuth::Strategies::CAS, type: :strategy do
|
|
22
25
|
|
23
26
|
it { should be_redirect }
|
24
27
|
|
25
|
-
it '
|
26
|
-
subject.headers
|
28
|
+
it 'redirects to the CAS server' do
|
29
|
+
expect(subject.headers).to include 'Location' => "http://cas.example.org:8080/login?#{redirect_params}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#cas_url' do
|
34
|
+
let(:params) { Hash.new }
|
35
|
+
let(:provider) { MyCasProvider.new(nil, params) }
|
36
|
+
|
37
|
+
subject { provider.cas_url }
|
38
|
+
|
39
|
+
it 'raises an ArgumentError' do
|
40
|
+
expect{subject}.to raise_error ArgumentError, %r{:host and :login_url MUST be provided}
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'with an explicit :url option' do
|
44
|
+
let(:url) { 'https://example.org:8080/my_cas' }
|
45
|
+
let(:params) { super().merge url:url }
|
46
|
+
|
47
|
+
before { subject }
|
48
|
+
|
49
|
+
it { should eq url }
|
50
|
+
|
51
|
+
it 'parses the URL into it the appropriate strategy options' do
|
52
|
+
expect(provider.options).to include ssl:true
|
53
|
+
expect(provider.options).to include host:'example.org'
|
54
|
+
expect(provider.options).to include port:8080
|
55
|
+
expect(provider.options).to include path:'/my_cas'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'with explicit URL component' do
|
60
|
+
let(:params) { super().merge host:'example.org', port:1234, ssl:true, path:'/a/path' }
|
61
|
+
|
62
|
+
before { subject }
|
63
|
+
|
64
|
+
it { should eq 'https://example.org:1234/a/path' }
|
65
|
+
|
66
|
+
it 'parses the URL into it the appropriate strategy options' do
|
67
|
+
expect(provider.options).to include ssl:true
|
68
|
+
expect(provider.options).to include host:'example.org'
|
69
|
+
expect(provider.options).to include port:1234
|
70
|
+
expect(provider.options).to include path:'/a/path'
|
71
|
+
end
|
27
72
|
end
|
28
73
|
end
|
29
74
|
|
30
75
|
describe 'defaults' do
|
31
76
|
subject { MyCasProvider.default_options.to_hash }
|
77
|
+
|
32
78
|
it { should include('ssl' => true) }
|
33
79
|
end
|
34
80
|
|
@@ -52,102 +98,153 @@ describe OmniAuth::Strategies::CAS, type: :strategy do
|
|
52
98
|
end
|
53
99
|
end
|
54
100
|
|
55
|
-
describe 'GET /auth/cas/callback
|
56
|
-
|
57
|
-
|
58
|
-
subject { last_response }
|
101
|
+
describe 'GET /auth/cas/callback' do
|
102
|
+
context 'without a ticket' do
|
103
|
+
before { get '/auth/cas/callback' }
|
59
104
|
|
60
|
-
|
105
|
+
subject { last_response }
|
61
106
|
|
62
|
-
|
63
|
-
subject.headers['Location'].should == '/auth/failure?message=no_ticket&strategy=cas'
|
64
|
-
end
|
65
|
-
end
|
107
|
+
it { should be_redirect }
|
66
108
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
to_return( body: File.read('spec/fixtures/cas_failure.xml') )
|
71
|
-
get '/auth/cas/callback?ticket=9391d'
|
72
|
-
end
|
73
|
-
|
74
|
-
subject { last_response }
|
75
|
-
|
76
|
-
it { should be_redirect }
|
77
|
-
|
78
|
-
it 'should have a failure message' do
|
79
|
-
subject.headers['Location'].should == '/auth/failure?message=invalid_ticket&strategy=cas'
|
109
|
+
it 'redirects with a failure message' do
|
110
|
+
expect(subject.headers).to include 'Location' => '/auth/failure?message=no_ticket&strategy=cas'
|
111
|
+
end
|
80
112
|
end
|
81
|
-
end
|
82
|
-
|
83
|
-
describe 'GET /auth/cas/callback with a valid ticket' do
|
84
|
-
let(:return_url) { 'http://127.0.0.10/?some=parameter' }
|
85
113
|
|
86
|
-
|
87
|
-
|
88
|
-
.
|
89
|
-
|
114
|
+
context 'with an invalid ticket' do
|
115
|
+
before do
|
116
|
+
stub_request(:get, /^http:\/\/cas.example.org:8080?\/serviceValidate\?([^&]+&)?ticket=9391d/).
|
117
|
+
to_return( body: File.read('spec/fixtures/cas_failure.xml') )
|
118
|
+
get '/auth/cas/callback?ticket=9391d'
|
119
|
+
end
|
90
120
|
|
91
|
-
|
92
|
-
end
|
121
|
+
subject { last_response }
|
93
122
|
|
94
|
-
|
95
|
-
@request_uri.scan('ticket=').length.should == 1
|
96
|
-
end
|
123
|
+
it { should be_redirect }
|
97
124
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
ticket: '593af',
|
102
|
-
service: 'http://example.org/auth/cas/callback?url=' + Rack::Utils.escape('http://127.0.0.10/?some=parameter')
|
103
|
-
})
|
125
|
+
it 'redirects with a failure message' do
|
126
|
+
expect(subject.headers).to include 'Location' => '/auth/failure?message=invalid_ticket&strategy=cas'
|
127
|
+
end
|
104
128
|
end
|
105
129
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
130
|
+
describe 'with a valid ticket' do
|
131
|
+
shared_examples :successful_validation do
|
132
|
+
before do
|
133
|
+
stub_request(:get, /^http:\/\/cas.example.org:8080?\/serviceValidate\?([^&]+&)?ticket=593af/)
|
134
|
+
.with { |request| @request_uri = request.uri.to_s }
|
135
|
+
.to_return( body: File.read("spec/fixtures/#{xml_file_name}") )
|
136
|
+
|
137
|
+
get "/auth/cas/callback?ticket=593af&url=#{return_url}"
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'strips the ticket parameter from the callback URL' do
|
141
|
+
expect(@request_uri.scan('ticket=').size).to eq 1
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'properly encodes the service URL' do
|
145
|
+
expect(WebMock).to have_requested(:get, 'http://cas.example.org:8080/serviceValidate')
|
146
|
+
.with(query: {
|
147
|
+
ticket: '593af',
|
148
|
+
service: 'http://example.org/auth/cas/callback?url=' + Rack::Utils.escape('http://127.0.0.10/?some=parameter')
|
149
|
+
})
|
150
|
+
end
|
151
|
+
|
152
|
+
context "request.env['omniauth.auth']" do
|
153
|
+
subject { last_request.env['omniauth.auth'] }
|
154
|
+
|
155
|
+
it { should be_kind_of Hash }
|
156
|
+
|
157
|
+
it 'identifes the provider' do
|
158
|
+
expect(subject.provider).to eq :cas
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'returns the UID of the user' do
|
162
|
+
expect(subject.uid).to eq '54'
|
163
|
+
end
|
164
|
+
|
165
|
+
context 'the info hash' do
|
166
|
+
subject { last_request.env['omniauth.auth']['info'] }
|
167
|
+
|
168
|
+
it 'includes user info attributes' do
|
169
|
+
expect(subject.name).to eq 'Peter Segel'
|
170
|
+
expect(subject.first_name).to eq 'Peter'
|
171
|
+
expect(subject.last_name).to eq 'Segel'
|
172
|
+
expect(subject.nickname).to eq 'psegel'
|
173
|
+
expect(subject.email).to eq 'psegel@intridea.com'
|
174
|
+
expect(subject.location).to eq 'Washington, D.C.'
|
175
|
+
expect(subject.image).to eq '/images/user.jpg'
|
176
|
+
expect(subject.phone).to eq '555-555-5555'
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
context 'the extra hash' do
|
181
|
+
subject { last_request.env['omniauth.auth']['extra'] }
|
182
|
+
|
183
|
+
it 'includes additional user attributes' do
|
184
|
+
expect(subject.user).to eq 'psegel'
|
185
|
+
expect(subject.employeeid).to eq '54'
|
186
|
+
expect(subject.hire_date).to eq '2004-07-13'
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
context 'the credentials hash' do
|
191
|
+
subject { last_request.env['omniauth.auth']['credentials'] }
|
192
|
+
|
193
|
+
it 'has a ticket value' do
|
194
|
+
expect(subject.ticket).to eq '593af'
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'calls through to the master app' do
|
200
|
+
expect(last_response.body).to eq 'true'
|
201
|
+
end
|
202
|
+
end
|
110
203
|
|
111
|
-
|
204
|
+
let(:return_url) { 'http://127.0.0.10/?some=parameter' }
|
112
205
|
|
113
|
-
|
206
|
+
context 'with JASIG flavored XML' do
|
207
|
+
let(:xml_file_name) { 'cas_success_jasig.xml' }
|
114
208
|
|
115
|
-
|
116
|
-
|
209
|
+
it_behaves_like :successful_validation
|
210
|
+
end
|
117
211
|
|
118
|
-
|
212
|
+
context 'with classic XML' do
|
213
|
+
let(:xml_file_name) { 'cas_success.xml' }
|
119
214
|
|
120
|
-
|
121
|
-
its(:first_name) { should == 'Peter' }
|
122
|
-
its(:last_name) { should == 'Segel' }
|
123
|
-
its(:email) { should == 'psegel@intridea.com' }
|
124
|
-
its(:location) { should == 'Washington, D.C.' }
|
125
|
-
its(:image) { should == '/images/user.jpg' }
|
126
|
-
its(:phone) { should == '555-555-5555' }
|
215
|
+
it_behaves_like :successful_validation
|
127
216
|
end
|
217
|
+
end
|
218
|
+
end
|
128
219
|
|
129
|
-
|
130
|
-
|
220
|
+
describe 'POST /auth/cas/callback' do
|
221
|
+
describe 'with a Single Sign-Out logoutRequest' do
|
222
|
+
let(:logoutRequest) do
|
223
|
+
%Q[
|
224
|
+
<samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion\" ID="123abc-1234-ab12-cd34-1234abcd" Version="2.0" IssueInstant="#{Time.now.to_s}">
|
225
|
+
<saml:NameID>@NOT_USED@</saml:NameID>
|
226
|
+
<samlp:SessionIndex>ST-123456-123abc456def</samlp:SessionIndex>
|
227
|
+
</samlp:LogoutRequest>
|
228
|
+
]
|
229
|
+
end
|
131
230
|
|
132
|
-
|
231
|
+
let(:logout_request) { double('logout_request', call:[200,{},'OK']) }
|
133
232
|
|
134
|
-
|
135
|
-
|
136
|
-
its(:hire_date) { should == '2004-07-13' }
|
233
|
+
subject do
|
234
|
+
post 'auth/cas/callback', logoutRequest:logoutRequest
|
137
235
|
end
|
138
236
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
237
|
+
before do
|
238
|
+
allow_any_instance_of(MyCasProvider)
|
239
|
+
.to receive(:logout_request_service)
|
240
|
+
.and_return double('LogoutRequest', new:logout_request)
|
143
241
|
|
144
|
-
|
242
|
+
subject
|
145
243
|
end
|
146
|
-
end
|
147
244
|
|
148
|
-
|
149
|
-
|
245
|
+
it 'initializes a LogoutRequest' do
|
246
|
+
expect(logout_request).to have_received :call
|
247
|
+
end
|
150
248
|
end
|
151
249
|
end
|
152
|
-
|
153
250
|
end
|