casino 3.0.4 → 4.0.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +12 -3
  3. data/app/api/casino/api.rb +7 -0
  4. data/app/api/casino/api/entity/auth_token_ticket.rb +5 -0
  5. data/app/api/casino/api/resource/auth_token_tickets.rb +12 -0
  6. data/app/assets/javascripts/casino/{application.js → application.js.erb} +1 -1
  7. data/app/authenticators/casino/static_authenticator.rb +8 -2
  8. data/app/builders/casino/proxy_response_builder.rb +24 -0
  9. data/app/builders/casino/ticket_validation_response_builder.rb +9 -5
  10. data/app/controllers/casino/application_controller.rb +0 -22
  11. data/app/controllers/casino/auth_tokens_controller.rb +34 -0
  12. data/app/controllers/casino/controller_concern/ticket_validator.rb +30 -0
  13. data/app/controllers/casino/proxy_tickets_controller.rb +42 -2
  14. data/app/controllers/casino/service_tickets_controller.rb +15 -2
  15. data/app/controllers/casino/sessions_controller.rb +59 -8
  16. data/app/controllers/casino/two_factor_authenticators_controller.rb +28 -3
  17. data/app/helpers/casino/sessions_helper.rb +75 -0
  18. data/app/helpers/casino/two_factor_authenticators_helper.rb +12 -0
  19. data/app/models/casino/auth_token_ticket.rb +15 -0
  20. data/app/models/casino/login_ticket.rb +7 -4
  21. data/app/models/casino/model_concern/consumable_ticket.rb +20 -0
  22. data/app/models/casino/model_concern/ticket.rb +28 -0
  23. data/app/models/casino/proxy_granting_ticket.rb +12 -0
  24. data/app/models/casino/proxy_ticket.rb +4 -0
  25. data/app/models/casino/service_ticket.rb +5 -4
  26. data/app/models/casino/ticket_granting_ticket.rb +5 -1
  27. data/app/models/casino/two_factor_authenticator.rb +2 -0
  28. data/app/processors/casino/authentication_processor.rb +73 -0
  29. data/app/processors/casino/browser_processor.rb +12 -0
  30. data/app/processors/casino/proxy_granting_ticket_processor.rb +37 -0
  31. data/app/processors/casino/service_ticket_processor.rb +81 -0
  32. data/app/processors/casino/ticket_granting_ticket_processor.rb +56 -0
  33. data/app/processors/casino/two_factor_authenticator_processor.rb +18 -0
  34. data/app/services/casino/auth_token_validation_service.rb +66 -0
  35. data/app/views/casino/sessions/index.html.erb +2 -2
  36. data/app/views/casino/sessions/new.html.erb +1 -1
  37. data/app/views/casino/sessions/validate_otp.html.erb +1 -1
  38. data/app/views/casino/two_factor_authenticators/new.html.erb +6 -3
  39. data/app/views/layouts/application.html.erb +0 -1
  40. data/casino.gemspec +4 -2
  41. data/config/locales/en.yml +35 -0
  42. data/config/locales/zh-CN.yml +88 -0
  43. data/config/locales/zh-TW.yml +88 -0
  44. data/config/routes.rb +3 -10
  45. data/db/migrate/20140831205255_create_auth_token_tickets.rb +10 -0
  46. data/lib/casino.rb +4 -1
  47. data/lib/casino/tasks/cleanup.rake +13 -1
  48. data/lib/casino/version.rb +1 -1
  49. data/spec/controllers/auth_tokens_controller_spec.rb +75 -0
  50. data/spec/controllers/proxy_tickets_controller_spec.rb +120 -14
  51. data/spec/controllers/service_and_proxy_tickets_controller_spec.rb +224 -0
  52. data/spec/controllers/service_tickets_controller_spec.rb +62 -16
  53. data/spec/controllers/sessions_controller_spec.rb +622 -36
  54. data/spec/controllers/two_factor_authenticators_controller_spec.rb +217 -18
  55. data/spec/dummy/config/cas.yml +3 -0
  56. data/spec/dummy/config/environments/development.rb +0 -4
  57. data/spec/dummy/db/migrate/{20130910094259_create_base_models.casino.rb → 20140831214845_create_core_schema.casino.rb} +55 -32
  58. data/spec/dummy/db/migrate/20140831214846_rename_base_models.casino.rb +102 -0
  59. data/spec/dummy/db/migrate/20140831214847_cleanup_indexes.casino.rb +28 -0
  60. data/spec/dummy/db/migrate/20140831214848_fix_long_index_names.casino.rb +13 -0
  61. data/spec/dummy/db/migrate/20140831214849_change_service_to_text.casino.rb +7 -0
  62. data/spec/dummy/db/migrate/20140831214850_change_user_agent_to_text.casino.rb +6 -0
  63. data/spec/dummy/db/migrate/20140831214851_fix_length_of_text_fields.casino.rb +8 -0
  64. data/spec/dummy/db/migrate/20140831214852_create_auth_token_tickets.casino.rb +11 -0
  65. data/spec/dummy/db/schema.rb +79 -70
  66. data/spec/features/login_spec.rb +0 -9
  67. data/spec/model/auth_token_ticket_spec.rb +23 -0
  68. data/spec/services/auth_token_validation_service_spec.rb +83 -0
  69. data/spec/support/sign_in.rb +4 -0
  70. metadata +139 -210
  71. data/app/controllers/casino/api/v1/tickets_controller.rb +0 -55
  72. data/app/helpers/service_tickets_helper.rb +0 -2
  73. data/app/listeners/casino/legacy_validator_listener.rb +0 -11
  74. data/app/listeners/casino/listener.rb +0 -16
  75. data/app/listeners/casino/login_credential_acceptor_listener.rb +0 -38
  76. data/app/listeners/casino/login_credential_requestor_listener.rb +0 -21
  77. data/app/listeners/casino/logout_listener.rb +0 -12
  78. data/app/listeners/casino/other_sessions_destroyer_listener.rb +0 -7
  79. data/app/listeners/casino/proxy_ticket_provider_listener.rb +0 -11
  80. data/app/listeners/casino/second_factor_authentication_acceptor_listener.rb +0 -26
  81. data/app/listeners/casino/session_destroyer_listener.rb +0 -11
  82. data/app/listeners/casino/session_overview_listener.rb +0 -11
  83. data/app/listeners/casino/ticket_validator_listener.rb +0 -11
  84. data/app/listeners/casino/two_factor_authenticator_activator_listener.rb +0 -23
  85. data/app/listeners/casino/two_factor_authenticator_destroyer_listener.rb +0 -16
  86. data/app/listeners/casino/two_factor_authenticator_overview_listener.rb +0 -11
  87. data/app/listeners/casino/two_factor_authenticator_registrator_listener.rb +0 -11
  88. data/app/processors/casino/api/login_credential_acceptor_processor.rb +0 -46
  89. data/app/processors/casino/api/logout_processor.rb +0 -17
  90. data/app/processors/casino/api/service_ticket_provider_processor.rb +0 -69
  91. data/app/processors/casino/legacy_validator_processor.rb +0 -19
  92. data/app/processors/casino/login_credential_acceptor_processor.rb +0 -63
  93. data/app/processors/casino/login_credential_requestor_processor.rb +0 -70
  94. data/app/processors/casino/logout_processor.rb +0 -23
  95. data/app/processors/casino/other_sessions_destroyer_processor.rb +0 -26
  96. data/app/processors/casino/processor.rb +0 -5
  97. data/app/processors/casino/processor_concern/authentication.rb +0 -87
  98. data/app/processors/casino/processor_concern/browser.rb +0 -14
  99. data/app/processors/casino/processor_concern/login_tickets.rb +0 -28
  100. data/app/processors/casino/processor_concern/proxy_granting_tickets.rb +0 -43
  101. data/app/processors/casino/processor_concern/proxy_tickets.rb +0 -56
  102. data/app/processors/casino/processor_concern/service_tickets.rb +0 -50
  103. data/app/processors/casino/processor_concern/ticket_granting_tickets.rb +0 -65
  104. data/app/processors/casino/processor_concern/tickets.rb +0 -17
  105. data/app/processors/casino/processor_concern/two_factor_authenticators.rb +0 -23
  106. data/app/processors/casino/proxy_ticket_provider_processor.rb +0 -41
  107. data/app/processors/casino/proxy_ticket_validator_processor.rb +0 -22
  108. data/app/processors/casino/second_factor_authentication_acceptor_processor.rb +0 -45
  109. data/app/processors/casino/service_ticket_validator_processor.rb +0 -46
  110. data/app/processors/casino/session_destroyer_processor.rb +0 -25
  111. data/app/processors/casino/session_overview_processor.rb +0 -21
  112. data/app/processors/casino/two_factor_authenticator_activator_processor.rb +0 -41
  113. data/app/processors/casino/two_factor_authenticator_destroyer_processor.rb +0 -33
  114. data/app/processors/casino/two_factor_authenticator_overview_processor.rb +0 -20
  115. data/app/processors/casino/two_factor_authenticator_registrator_processor.rb +0 -24
  116. data/spec/controllers/api/v1/tickets_controller_spec.rb +0 -114
  117. data/spec/controllers/listener/legacy_validator_spec.rb +0 -22
  118. data/spec/controllers/listener/login_credential_acceptor_spec.rb +0 -108
  119. data/spec/controllers/listener/login_credential_requestor_spec.rb +0 -57
  120. data/spec/controllers/listener/logout_spec.rb +0 -38
  121. data/spec/controllers/listener/other_sessions_destroyer_spec.rb +0 -19
  122. data/spec/controllers/listener/proxy_ticket_provider_spec.rb +0 -22
  123. data/spec/controllers/listener/second_factor_authentication_acceptor_spec.rb +0 -74
  124. data/spec/controllers/listener/session_destroyer_spec.rb +0 -25
  125. data/spec/controllers/listener/session_overview_spec.rb +0 -26
  126. data/spec/controllers/listener/ticket_validator_spec.rb +0 -22
  127. data/spec/controllers/listener/two_factor_authenticator_activator_spec.rb +0 -64
  128. data/spec/controllers/listener/two_factor_authenticator_destroyer_spec.rb +0 -40
  129. data/spec/controllers/listener/two_factor_authenticator_overview_spec.rb +0 -16
  130. data/spec/controllers/listener/two_factor_authenticator_registrator_spec.rb +0 -27
  131. data/spec/processor/api/login_credential_acceptor_spec.rb +0 -52
  132. data/spec/processor/api/logout_spec.rb +0 -34
  133. data/spec/processor/api/service_ticket_provider_spec.rb +0 -61
  134. data/spec/processor/legacy_validator_spec.rb +0 -78
  135. data/spec/processor/login_credential_acceptor_spec.rb +0 -164
  136. data/spec/processor/login_credential_requestor_spec.rb +0 -145
  137. data/spec/processor/logout_other_sessions_spec.rb +0 -53
  138. data/spec/processor/logout_spec.rb +0 -72
  139. data/spec/processor/processor_concern/service_tickets_spec.rb +0 -49
  140. data/spec/processor/proxy_ticket_provider_spec.rb +0 -66
  141. data/spec/processor/proxy_ticket_validator_spec.rb +0 -65
  142. data/spec/processor/second_factor_authenticaton_acceptor_spec.rb +0 -94
  143. data/spec/processor/session_destroyer_spec.rb +0 -75
  144. data/spec/processor/session_overview_spec.rb +0 -49
  145. data/spec/processor/ticket_validator_spec.rb +0 -214
  146. data/spec/processor/two_factor_authenticator_activator_spec.rb +0 -122
  147. data/spec/processor/two_factor_authenticator_destroyer_spec.rb +0 -71
  148. data/spec/processor/two_factor_authenticator_overview_spec.rb +0 -56
  149. data/spec/processor/two_factor_authenticator_registrator_spec.rb +0 -48
@@ -1,25 +1,71 @@
1
- require 'spec_helper'
2
-
3
1
  describe CASino::ServiceTicketsController do
4
2
  describe 'GET "validate"' do
5
- let(:params) { { service: 'https://www.example.com/', use_route: :casino } }
6
- it 'calls the process method of the LegacyValidator' do
7
- CASino::LegacyValidatorProcessor.any_instance.should_receive(:process).with(kind_of(Hash)) do |params|
8
- params.should == controller.params
9
- controller.render nothing: true
3
+ let(:request_options) { params.merge(use_route: :casino) }
4
+ let(:service_ticket) { FactoryGirl.create :service_ticket }
5
+ let(:service) { service_ticket.service }
6
+ let(:parameters) { { service: service, ticket: service_ticket.ticket }}
7
+ let(:params) { parameters }
8
+ let(:username) { service_ticket.ticket_granting_ticket.user.username }
9
+ let(:response_text_success) { "yes\n#{username}\n" }
10
+ let(:response_text_failure) { "no\n\n" }
11
+
12
+ render_views
13
+
14
+ context 'with an unconsumed service ticket' do
15
+ context 'without renew flag' do
16
+ it 'consumes the service ticket' do
17
+ get :validate, request_options
18
+ service_ticket.reload.consumed.should == true
19
+ end
20
+
21
+ it 'answers with the expected response text' do
22
+ get :validate, request_options
23
+ response.body.should == response_text_success
24
+ end
25
+ end
26
+
27
+ context 'with renew flag' do
28
+ let(:params) { parameters.merge renew: 'true' }
29
+
30
+ context 'with a service ticket without issued_from_credentials flag' do
31
+ it 'consumes the service ticket' do
32
+ get :validate, request_options
33
+ service_ticket.reload.consumed.should == true
34
+ end
35
+
36
+ it 'answers with the expected response text' do
37
+ get :validate, request_options
38
+ response.body.should == response_text_failure
39
+ end
40
+ end
41
+
42
+ context 'with a service ticket with issued_from_credentials flag' do
43
+ before(:each) do
44
+ service_ticket.update_attribute(:issued_from_credentials, true)
45
+ end
46
+
47
+ it 'consumes the service ticket' do
48
+ get :validate, request_options
49
+ service_ticket.reload.consumed.should == true
50
+ end
51
+
52
+ it 'answers with the expected response text' do
53
+ get :validate, request_options
54
+ response.body.should == response_text_success
55
+ end
56
+ end
10
57
  end
11
- get :validate, params
12
58
  end
13
- end
14
59
 
15
- describe 'GET "serviceValidate"' do
16
- let(:params) { { service: 'https://www.example.com/', use_route: :casino } }
17
- it 'calls the process method of the LegacyValidator' do
18
- CASino::ServiceTicketValidatorProcessor.any_instance.should_receive(:process).with(kind_of(Hash)) do |params|
19
- params.should == controller.params
20
- controller.render nothing: true
60
+ context 'with a consumed service ticket' do
61
+ before(:each) do
62
+ service_ticket.update_attribute(:consumed, true)
63
+ end
64
+
65
+ it 'answers with the expected response text' do
66
+ get :validate, request_options
67
+ response.body.should == response_text_failure
21
68
  end
22
- get :service_validate, params
23
69
  end
24
70
  end
25
71
  end
@@ -1,10 +1,159 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe CASino::SessionsController do
4
+ include CASino::Engine.routes.url_helpers
5
+ let(:params) { { } }
6
+ let(:request_options) { params.merge(use_route: :casino) }
7
+ let(:user_agent) { 'YOLOBrowser 420.00'}
8
+
9
+ before(:each) do
10
+ request.user_agent = user_agent
11
+ end
12
+
4
13
  describe 'GET "new"' do
5
- it 'calls the process method of the LoginCredentialRequestor' do
6
- CASino::LoginCredentialRequestorProcessor.any_instance.should_receive(:process)
7
- get :new, use_route: :casino
14
+ context 'with a not allowed service' do
15
+ before(:each) do
16
+ FactoryGirl.create :service_rule, :regex, url: '^https://.*'
17
+ end
18
+
19
+ let(:service) { 'http://www.example.org/' }
20
+ let(:params) { { service: service } }
21
+
22
+ it 'renders the service_not_allowed template' do
23
+ get :new, request_options
24
+ response.should render_template(:service_not_allowed)
25
+ end
26
+ end
27
+
28
+ context 'when logged out' do
29
+ it 'renders the new template' do
30
+ get :new, request_options
31
+ response.should render_template(:new)
32
+ end
33
+
34
+ context 'with gateway parameter' do
35
+ context 'with a service' do
36
+ let(:service) { 'http://example.com/' }
37
+ let(:params) { { service: service, gateway: 'true' } }
38
+
39
+ it 'redirects to the service' do
40
+ get :new, request_options
41
+ response.should redirect_to(service)
42
+ end
43
+ end
44
+
45
+ context 'without a service' do
46
+ let(:params) { { gateway: 'true' } }
47
+
48
+ it 'renders the new template' do
49
+ get :new, request_options
50
+ response.should render_template(:new)
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ context 'when logged in' do
57
+ let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket }
58
+
59
+ before(:each) do
60
+ sign_in(ticket_granting_ticket)
61
+ end
62
+
63
+ context 'when two-factor authentication is pending' do
64
+ let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, :awaiting_two_factor_authentication }
65
+
66
+ it 'renders the new template' do
67
+ get :new, request_options
68
+ response.should render_template(:new)
69
+ end
70
+ end
71
+
72
+ context 'when ticket-granting ticket expired' do
73
+ before(:each) do
74
+ ticket_granting_ticket.created_at = 25.hours.ago
75
+ ticket_granting_ticket.save!
76
+ end
77
+
78
+ it 'renders the new template' do
79
+ get :new, request_options
80
+ response.should render_template(:new)
81
+ end
82
+ end
83
+
84
+ context 'with a service' do
85
+ let(:service) { 'http://example.com/' }
86
+ let(:params) { { service: service } }
87
+
88
+ it 'redirects to the service' do
89
+ get :new, request_options
90
+ response.location.should =~ /^#{Regexp.escape service}\?ticket=ST-/
91
+ end
92
+
93
+ it 'generates a service ticket' do
94
+ lambda do
95
+ get :new, request_options
96
+ end.should change(CASino::ServiceTicket, :count).by(1)
97
+ end
98
+
99
+ it 'does not set the issued_from_credentials flag on the service ticket' do
100
+ get :new, request_options
101
+ CASino::ServiceTicket.last.should_not be_issued_from_credentials
102
+ end
103
+
104
+ context 'with renew parameter' do
105
+ it 'renders the new template' do
106
+ get :new, request_options.merge(renew: 'true')
107
+ response.should render_template(:new)
108
+ end
109
+ end
110
+ end
111
+
112
+ context 'with a service with nested attributes' do
113
+ let(:service) { 'http://example.com/?a%5B%5D=test&a%5B%5D=example' }
114
+ let(:params) { { service: service } }
115
+
116
+ it 'does not remove the attributes' do
117
+ get :new, request_options
118
+ response.location.should =~ /^#{Regexp.escape service}&ticket=ST-/
119
+ end
120
+ end
121
+
122
+ context 'with a broken service' do
123
+ let(:service) { '%3Atest' }
124
+ let(:params) { { service: service } }
125
+
126
+ it 'redirects to the session overview' do
127
+ get :new, request_options
128
+ response.should redirect_to(sessions_path)
129
+ end
130
+ end
131
+
132
+ context 'without a service' do
133
+ it 'redirects to the session overview' do
134
+ get :new, request_options
135
+ response.should redirect_to(sessions_path)
136
+ end
137
+
138
+ it 'does not generate a service ticket' do
139
+ lambda do
140
+ get :new, request_options
141
+ end.should change(CASino::ServiceTicket, :count).by(0)
142
+ end
143
+
144
+ context 'with a changed browser' do
145
+ let(:user_agent) { 'FooBar 1.0' }
146
+
147
+ before(:each) do
148
+ request.user_agent = user_agent
149
+ end
150
+
151
+ it 'renders the new template' do
152
+ get :new, request_options
153
+ response.should render_template(:new)
154
+ end
155
+ end
156
+ end
8
157
  end
9
158
 
10
159
  context 'with an unsupported format' do
@@ -16,63 +165,500 @@ describe CASino::SessionsController do
16
165
  end
17
166
 
18
167
  describe 'POST "create"' do
19
- it 'calls the process method of the LoginCredentialAcceptor' do
20
- CASino::LoginCredentialAcceptorProcessor.any_instance.should_receive(:process) do
21
- @controller.render nothing: true
168
+ context 'without a valid login ticket' do
169
+ it 'renders the new template' do
170
+ post :create, request_options
171
+ response.should render_template(:new)
172
+ end
173
+ end
174
+
175
+ context 'with an expired login ticket' do
176
+ let(:expired_login_ticket) { FactoryGirl.create :login_ticket, :expired }
177
+ let(:params) { { lt: expired_login_ticket.ticket }}
178
+
179
+ it 'renders the new template' do
180
+ post :create, request_options
181
+ response.should render_template(:new)
182
+ end
183
+ end
184
+
185
+ context 'with a valid login ticket' do
186
+ let(:login_ticket) { FactoryGirl.create :login_ticket }
187
+ let(:params) { { lt: login_ticket.ticket }}
188
+
189
+ context 'with invalid credentials' do
190
+ it 'renders the new template' do
191
+ post :create, request_options
192
+ response.should render_template(:new)
193
+ end
194
+ end
195
+
196
+ context 'with valid credentials' do
197
+ let(:service) { 'https://www.example.org' }
198
+ let(:username) { 'testuser' }
199
+ let(:authenticator) { 'static' }
200
+ let(:params) { { lt: login_ticket.ticket, username: username, password: 'foobar123', service: service } }
201
+
202
+ it 'creates a cookie' do
203
+ post :create, request_options
204
+ response.cookies['tgt'].should_not be_nil
205
+ end
206
+
207
+ context 'with rememberMe set' do
208
+ let(:cookie_jar) { HashWithIndifferentAccess.new }
209
+
210
+ before(:each) do
211
+ params[:rememberMe] = true
212
+ controller.stub(:cookies).and_return(cookie_jar)
213
+ end
214
+
215
+ it 'creates a cookie with an expiration date set' do
216
+ post :create, request_options
217
+ cookie_jar['tgt']['expires'].should be_kind_of(Time)
218
+ end
219
+
220
+ it 'creates a long-term ticket-granting ticket' do
221
+ post :create, request_options
222
+ tgt = CASino::TicketGrantingTicket.last
223
+ tgt.long_term.should == true
224
+ end
225
+ end
226
+
227
+ context 'with two-factor authentication enabled' do
228
+ let!(:user) { CASino::User.create! username: username, authenticator: authenticator }
229
+ let!(:two_factor_authenticator) { FactoryGirl.create :two_factor_authenticator, user: user }
230
+
231
+ it 'renders the validate_otp template' do
232
+ post :create, request_options
233
+ response.should render_template(:validate_otp)
234
+ end
235
+ end
236
+
237
+ context 'with a not allowed service' do
238
+ before(:each) do
239
+ FactoryGirl.create :service_rule, :regex, url: '^https://.*'
240
+ end
241
+ let(:service) { 'http://www.example.org/' }
242
+
243
+ it 'renders the service_not_allowed template' do
244
+ post :create, request_options
245
+ response.should render_template(:service_not_allowed)
246
+ end
247
+ end
248
+
249
+ context 'when all authenticators raise an error' do
250
+ before(:each) do
251
+ CASino::StaticAuthenticator.any_instance.stub(:validate) do
252
+ raise CASino::Authenticator::AuthenticatorError, 'error123'
253
+ end
254
+ end
255
+
256
+ it 'renders the new template' do
257
+ post :create, request_options
258
+ response.should render_template(:new)
259
+ end
260
+ end
261
+
262
+ context 'without a service' do
263
+ let(:service) { nil }
264
+
265
+ it 'redirects to the session overview' do
266
+ post :create, request_options
267
+ response.should redirect_to(sessions_path)
268
+ end
269
+
270
+ it 'generates a ticket-granting ticket' do
271
+ lambda do
272
+ post :create, request_options
273
+ end.should change(CASino::TicketGrantingTicket, :count).by(1)
274
+ end
275
+
276
+ context 'when the user does not exist yet' do
277
+ it 'generates exactly one user' do
278
+ lambda do
279
+ post :create, request_options
280
+ end.should change(CASino::User, :count).by(1)
281
+ end
282
+
283
+ it 'sets the users attributes' do
284
+ post :create, request_options
285
+ user = CASino::User.last
286
+ user.username.should == username
287
+ user.authenticator.should == authenticator
288
+ end
289
+ end
290
+
291
+ context 'when the user already exists' do
292
+ let!(:user) { CASino::User.create! username: username, authenticator: authenticator }
293
+
294
+ it 'does not regenerate the user' do
295
+ lambda do
296
+ post :create, request_options
297
+ end.should_not change(CASino::User, :count)
298
+ end
299
+
300
+ it 'updates the extra attributes' do
301
+ lambda do
302
+ post :create, request_options
303
+ user.reload
304
+ end.should change(user, :extra_attributes)
305
+ end
306
+ end
307
+ end
308
+
309
+ context 'with a service' do
310
+ let(:service) { 'https://www.example.com' }
311
+
312
+ it 'redirects to the service' do
313
+ post :create, request_options
314
+ response.location.should =~ /^#{Regexp.escape service}\/\?ticket=ST-/
315
+ end
316
+
317
+ it 'generates a service ticket' do
318
+ lambda do
319
+ post :create, request_options
320
+ end.should change(CASino::ServiceTicket, :count).by(1)
321
+ end
322
+
323
+ it 'does set the issued_from_credentials flag on the service ticket' do
324
+ post :create, request_options
325
+ CASino::ServiceTicket.last.should be_issued_from_credentials
326
+ end
327
+
328
+ it 'generates a ticket-granting ticket' do
329
+ lambda do
330
+ post :create, request_options
331
+ end.should change(CASino::TicketGrantingTicket, :count).by(1)
332
+ end
333
+ end
22
334
  end
23
- post :create, use_route: :casino
24
335
  end
25
336
  end
26
337
 
27
338
  describe 'POST "validate_otp"' do
28
- it 'calls the process method of the SecondFactorAuthenticatonAcceptor' do
29
- CASino::SecondFactorAuthenticationAcceptorProcessor.any_instance.should_receive(:process) do
30
- @controller.render nothing: true
339
+ context 'with an existing ticket-granting ticket' do
340
+ let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, :awaiting_two_factor_authentication }
341
+ let(:user) { ticket_granting_ticket.user }
342
+ let(:tgt) { ticket_granting_ticket.ticket }
343
+ let(:user_agent) { ticket_granting_ticket.user_agent }
344
+ let(:otp) { '123456' }
345
+ let(:service) { 'http://www.example.com/testing' }
346
+ let(:params) { { tgt: tgt, otp: otp, service: service }}
347
+
348
+ context 'with an active authenticator' do
349
+ let!(:two_factor_authenticator) { FactoryGirl.create :two_factor_authenticator, user: user }
350
+
351
+ context 'with a valid OTP' do
352
+ before(:each) do
353
+ ROTP::TOTP.any_instance.should_receive(:verify_with_drift).with(otp, 30).and_return(true)
354
+ end
355
+
356
+ it 'redirects to the service' do
357
+ post :validate_otp, request_options
358
+ response.location.should =~ /^#{Regexp.escape service}\?ticket=ST-/
359
+ end
360
+
361
+ it 'does activate the ticket-granting ticket' do
362
+ post :validate_otp, request_options
363
+ ticket_granting_ticket.reload.should_not be_awaiting_two_factor_authentication
364
+ end
365
+
366
+ context 'with a long-term ticket-granting ticket' do
367
+ let(:cookie_jar) { HashWithIndifferentAccess.new }
368
+
369
+ before(:each) do
370
+ ticket_granting_ticket.update_attributes! long_term: true
371
+ controller.stub(:cookies).and_return(cookie_jar)
372
+ end
373
+
374
+ it 'creates a cookie with an expiration date set' do
375
+ post :validate_otp, request_options
376
+ cookie_jar['tgt']['expires'].should be_kind_of(Time)
377
+ end
378
+ end
379
+
380
+ context 'with a not allowed service' do
381
+ before(:each) do
382
+ FactoryGirl.create :service_rule, :regex, url: '^https://.*'
383
+ end
384
+ let(:service) { 'http://www.example.org/' }
385
+
386
+ it 'renders the service_not_allowed template' do
387
+ post :validate_otp, request_options
388
+ response.should render_template(:service_not_allowed)
389
+ end
390
+ end
391
+ end
392
+
393
+ context 'with an invalid OTP' do
394
+ before(:each) do
395
+ ROTP::TOTP.any_instance.should_receive(:verify_with_drift).with(otp, 30).and_return(false)
396
+ end
397
+
398
+ it 'renders the validate_otp template' do
399
+ post :validate_otp, request_options
400
+ response.should render_template(:validate_otp)
401
+ end
402
+
403
+ it 'does not activate the ticket-granting ticket' do
404
+ post :validate_otp, request_options
405
+ ticket_granting_ticket.reload.should be_awaiting_two_factor_authentication
406
+ end
407
+ end
408
+ end
409
+ end
410
+
411
+ context 'without a ticket-granting ticket' do
412
+ it 'redirects to the login page' do
413
+ post :validate_otp, request_options
414
+ response.should redirect_to(login_path)
31
415
  end
32
- post :validate_otp, use_route: :casino
33
416
  end
34
417
  end
35
418
 
36
419
  describe 'GET "logout"' do
37
- it 'calls the process method of the Logout processor' do
38
- CASino::LogoutProcessor.any_instance.should_receive(:process) do |params, cookies, user_agent|
39
- params.should == controller.params
40
- cookies.should == controller.cookies
41
- user_agent.should == request.user_agent
420
+ let(:url) { nil }
421
+ let(:params) { { :url => url } }
422
+
423
+ context 'with an existing ticket-granting ticket' do
424
+ let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket }
425
+
426
+ before(:each) do
427
+ sign_in(ticket_granting_ticket)
428
+ end
429
+
430
+ it 'deletes the ticket-granting ticket' do
431
+ get :logout, request_options
432
+ CASino::TicketGrantingTicket.where(id: ticket_granting_ticket.id).first.should == nil
433
+ end
434
+
435
+ it 'renders the logout template' do
436
+ get :logout, request_options
437
+ response.should render_template(:logout)
438
+ end
439
+
440
+ context 'with an URL' do
441
+ let(:url) { 'http://www.example.com' }
442
+
443
+ it 'assigns the URL' do
444
+ get :logout, request_options
445
+ assigns(:url).should == url
446
+ end
447
+ end
448
+
449
+ context 'with a service' do
450
+ let(:params) { { :service => url } }
451
+ let(:url) { 'http://www.example.org' }
452
+
453
+ context 'when whitelisted' do
454
+ it 'redirects to the service' do
455
+ get :logout, request_options
456
+ response.should redirect_to(url)
457
+ end
458
+ end
459
+
460
+ context 'when not whitelisted' do
461
+ before(:each) do
462
+ FactoryGirl.create :service_rule, :regex, url: '^https://.*'
463
+ end
464
+
465
+ it 'renders the logout template' do
466
+ get :logout, request_options
467
+ response.should render_template(:logout)
468
+ end
469
+
470
+ it 'does not assign the URL' do
471
+ get :logout, request_options
472
+ assigns(:url).should be_nil
473
+ end
474
+ end
475
+ end
476
+ end
477
+
478
+ context 'without a ticket-granting ticket' do
479
+ it 'renders the logout template' do
480
+ get :logout, request_options
481
+ response.should render_template(:logout)
42
482
  end
43
- get :logout, use_route: :casino
44
483
  end
45
484
  end
46
485
 
47
486
  describe 'GET "index"' do
48
- it 'calls the process method of the SessionOverview processor' do
49
- CASino::TwoFactorAuthenticatorOverviewProcessor.any_instance.should_receive(:process)
50
- CASino::SessionOverviewProcessor.any_instance.should_receive(:process)
51
- get :index, use_route: :casino
487
+ context 'with an existing ticket-granting ticket' do
488
+ before(:each) do
489
+ sign_in(ticket_granting_ticket)
490
+ end
491
+
492
+ describe 'two-factor authenticator settings' do
493
+ let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket }
494
+ let(:user) { ticket_granting_ticket.user }
495
+
496
+ context 'without a two-factor authenticator registered' do
497
+ it 'does not assign any two-factor authenticators' do
498
+ get :index, request_options
499
+ assigns(:two_factor_authenticators).should == []
500
+ end
501
+ end
502
+
503
+ context 'with an inactive two-factor authenticator' do
504
+ let!(:two_factor_authenticator) { FactoryGirl.create :two_factor_authenticator, :inactive, user: user }
505
+
506
+ it 'does not assign any two-factor authenticators' do
507
+ get :index, request_options
508
+ assigns(:two_factor_authenticators).should == []
509
+ end
510
+ end
511
+
512
+ context 'with a two-factor authenticator registered' do
513
+ let(:two_factor_authenticator) { FactoryGirl.create :two_factor_authenticator, user: user }
514
+ let!(:other_two_factor_authenticator) { FactoryGirl.create :two_factor_authenticator }
515
+
516
+ it 'does assign the two-factor authenticator' do
517
+ get :index, request_options
518
+ assigns(:two_factor_authenticators).should == [two_factor_authenticator]
519
+ end
520
+ end
521
+ end
522
+
523
+ describe 'sessions overview' do
524
+ let!(:other_ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket }
525
+ let(:user) { other_ticket_granting_ticket.user }
526
+
527
+ context 'as user owning the other ticket granting ticket' do
528
+ let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, user: user }
529
+
530
+ it 'assigns both ticket granting tickets' do
531
+ get :index, request_options
532
+ assigns(:ticket_granting_tickets).should == [ticket_granting_ticket, other_ticket_granting_ticket]
533
+ end
534
+ end
535
+
536
+ context 'with a ticket-granting ticket with same username but different authenticator' do
537
+ let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket }
538
+ let(:tgt) { ticket_granting_ticket.ticket }
539
+
540
+ it 'does not assign the other ticket granting ticket' do
541
+ get :index, request_options
542
+ assigns(:ticket_granting_tickets).should == [ticket_granting_ticket]
543
+ end
544
+ end
545
+ end
546
+ end
547
+
548
+ context 'without a ticket-granting ticket' do
549
+ it 'redirects to the login page' do
550
+ get :index, request_options
551
+ response.should redirect_to(login_path)
552
+ end
52
553
  end
53
554
  end
54
555
 
55
556
  describe 'DELETE "destroy"' do
56
- let(:id) { '123' }
57
- let(:tgt) { 'TGT-foobar' }
58
- it 'calls the process method of the SessionOverview processor' do
59
- request.cookies[:tgt] = tgt
60
- CASino::SessionDestroyerProcessor.any_instance.should_receive(:process) do |params, cookies, user_agent|
61
- params[:id].should == id
62
- cookies[:tgt].should == tgt
63
- user_agent.should == request.user_agent
64
- @controller.render nothing: true
65
- end
66
- delete :destroy, id:id, use_route: :casino
557
+ let(:owner_ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket }
558
+ let(:user) { owner_ticket_granting_ticket.user }
559
+
560
+ before(:each) do
561
+ sign_in(owner_ticket_granting_ticket)
562
+ end
563
+
564
+ context 'with an existing ticket-granting ticket' do
565
+ let!(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, user: user }
566
+ let(:service_ticket) { FactoryGirl.create :service_ticket, ticket_granting_ticket: ticket_granting_ticket }
567
+ let(:consumed_service_ticket) { FactoryGirl.create :service_ticket, :consumed, ticket_granting_ticket: ticket_granting_ticket }
568
+ let(:params) { { id: ticket_granting_ticket.id } }
569
+
570
+ it 'deletes exactly one ticket-granting ticket' do
571
+ lambda do
572
+ delete :destroy, request_options
573
+ end.should change(CASino::TicketGrantingTicket, :count).by(-1)
574
+ end
575
+
576
+ it 'deletes the ticket-granting ticket' do
577
+ delete :destroy, request_options
578
+ CASino::TicketGrantingTicket.where(id: params[:id]).length.should == 0
579
+ end
580
+
581
+ it 'redirects to the session overview' do
582
+ delete :destroy, request_options
583
+ response.should redirect_to(sessions_path)
584
+ end
585
+ end
586
+
587
+ context 'with an invalid ticket-granting ticket' do
588
+ let(:params) { { id: 99999 } }
589
+ it 'does not delete a ticket-granting ticket' do
590
+ lambda do
591
+ delete :destroy, request_options
592
+ end.should_not change(CASino::TicketGrantingTicket, :count)
593
+ end
594
+
595
+ it 'redirects to the session overview' do
596
+ delete :destroy, request_options
597
+ response.should redirect_to(sessions_path)
598
+ end
599
+ end
600
+
601
+ context 'when trying to delete ticket-granting ticket of another user' do
602
+ let!(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket }
603
+ let(:params) { { id: ticket_granting_ticket.id } }
604
+
605
+ it 'does not delete a ticket-granting ticket' do
606
+ lambda do
607
+ delete :destroy, request_options
608
+ end.should_not change(CASino::TicketGrantingTicket, :count)
609
+ end
610
+
611
+ it 'redirects to the session overview' do
612
+ delete :destroy, request_options
613
+ response.should redirect_to(sessions_path)
614
+ end
67
615
  end
68
616
  end
69
617
 
70
618
  describe 'GET "destroy_others"' do
71
- it 'calls the process method of the OtherSessionsDestroyer' do
72
- CASino::OtherSessionsDestroyerProcessor.any_instance.should_receive(:process) do
73
- @controller.render nothing: true
619
+ let(:url) { nil }
620
+ let(:params) { { :service => url } }
621
+
622
+ context 'with an existing ticket-granting ticket' do
623
+ let(:user) { FactoryGirl.create :user }
624
+ let!(:other_users_ticket_granting_tickets) { FactoryGirl.create_list :ticket_granting_ticket, 3 }
625
+ let!(:other_ticket_granting_tickets) { FactoryGirl.create_list :ticket_granting_ticket, 3, user: user }
626
+ let!(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, user: user }
627
+
628
+ before(:each) do
629
+ sign_in(ticket_granting_ticket)
630
+ end
631
+
632
+ it 'deletes all other ticket-granting tickets' do
633
+ lambda do
634
+ get :destroy_others, request_options
635
+ end.should change(CASino::TicketGrantingTicket, :count).by(-3)
636
+ end
637
+
638
+ it 'redirects to the session overview' do
639
+ get :destroy_others, request_options
640
+ response.should redirect_to(sessions_path)
641
+ end
642
+
643
+ context 'with an URL' do
644
+ let(:url) { 'http://www.example.com' }
645
+
646
+ it 'redirects to the service' do
647
+ get :destroy_others, request_options
648
+ response.should redirect_to(url)
649
+ end
650
+ end
651
+ end
652
+
653
+ context 'without a ticket-granting ticket' do
654
+ context 'with an URL' do
655
+ let(:url) { 'http://www.example.com' }
656
+
657
+ it 'redirects to the service' do
658
+ get :destroy_others, request_options
659
+ response.should redirect_to(url)
660
+ end
74
661
  end
75
- get :destroy_others, use_route: :casino
76
662
  end
77
663
  end
78
664
  end