blob: 67ddeddb67894becc8dc3f2954e8041d74201474 [file] [log] [blame]
Akron33f5c672019-06-24 19:40:47 +02001use Mojo::Base -strict;
2use Test::More;
Akron6a228db2021-10-14 15:57:00 +02003use Mojo::ByteStream 'b';
Akroncdfd9d52019-07-23 11:35:00 +02004use Test::Mojo::WithRoles 'Session';
Akron66ef3b52022-11-22 14:25:15 +01005use Mojo::File qw/path tempfile/;
Akron33f5c672019-06-24 19:40:47 +02006use Data::Dumper;
7
8
9#####################
10# Start Fake server #
11#####################
Akron63d963b2019-07-05 15:35:51 +020012my $mount_point = '/realapi/';
Akron33f5c672019-06-24 19:40:47 +020013$ENV{KALAMAR_API} = $mount_point;
14
Akroncdfd9d52019-07-23 11:35:00 +020015my $t = Test::Mojo::WithRoles->new('Kalamar' => {
Akron33f5c672019-06-24 19:40:47 +020016 Kalamar => {
17 plugins => ['Auth']
18 },
19 'Kalamar-Auth' => {
20 client_id => 2,
21 client_secret => 'k414m4r-s3cr3t',
Akron59992122019-10-29 11:28:45 +010022 oauth2 => 1,
23 experimental_client_registration => 1
Akron33f5c672019-06-24 19:40:47 +020024 }
25});
26
27# Mount fake backend
28# Get the fixture path
29my $fixtures_path = path(Mojo::File->new(__FILE__)->dirname, '..', 'server');
30my $fake_backend = $t->app->plugin(
31 Mount => {
32 $mount_point =>
33 $fixtures_path->child('mock.pl')
34 }
35);
36# Configure fake backend
Akroncdfd9d52019-07-23 11:35:00 +020037my $fake_backend_app = $fake_backend->pattern->defaults->{app};
38
39# Set general app logger for simplicity
40$fake_backend_app->log($t->app->log);
41
42my $access_token = $fake_backend_app->get_token('access_token');
43my $refresh_token = $fake_backend_app->get_token('refresh_token');
44my $access_token_2 = $fake_backend_app->get_token('access_token_2');
45my $refresh_token_2 = $fake_backend_app->get_token('refresh_token_2');
46
47# Some routes to modify the session
48# This expires the session
49$t->app->routes->get('/x/expire')->to(
50 cb => sub {
51 my $c = shift;
52 $c->session(auth_exp => 0);
53 return $c->render(text => 'okay')
54 }
55);
56
57# This expires the session and removes the refresh token
58$t->app->routes->get('/x/expire-no-refresh')->to(
59 cb => sub {
60 my $c = shift;
61 $c->session(auth_exp => 0);
62 delete $c->session->{auth_r};
63 return $c->render(text => 'okay')
64 }
65);
66
67# This sets an invalid token
68$t->app->routes->get('/x/invalid')->to(
69 cb => sub {
70 my $c = shift;
71 $c->session(auth_exp => time + 1000);
72 $c->session(auth_r => $refresh_token_2);
73 $c->session(auth => 'Bearer inv4lid');
74 return $c->render(text => 'okay')
75 }
76);
77
78
79# This sets an invalid token
80$t->app->routes->get('/x/invalid-no-refresh')->to(
81 cb => sub {
82 my $c = shift;
83 $c->session(auth_exp => time + 1000);
84 delete $c->session->{auth_r};
85 $c->session(auth => 'Bearer inv4lid');
86 return $c->render(text => 'okay')
87 }
88);
89
90# This sets an invalid refresh token
91$t->app->routes->get('/x/expired-with-wrong-refresh')->to(
92 cb => sub {
93 my $c = shift;
94 $c->session(auth_exp => 0);
95 $c->session(auth => 'Bearer inv4lid');
96 $c->session(auth_r => 'inv4lid');
97 return $c->render(text => 'okay')
98 }
99);
100
Akronbc6b3f22021-01-13 14:53:12 +0100101my $q = qr!(?:\"|")!;
Akron33f5c672019-06-24 19:40:47 +0200102
Akron63d963b2019-07-05 15:35:51 +0200103$t->get_ok('/realapi/v1.0')
Akron33f5c672019-06-24 19:40:47 +0200104 ->status_is(200)
105 ->content_is('Fake server available');
106
107$t->get_ok('/?q=Baum')
108 ->status_is(200)
109 ->text_like('h1 span', qr/KorAP: Find .Baum./i)
110 ->text_like('#total-results', qr/\d+$/)
Akronbc6b3f22021-01-13 14:53:12 +0100111 ->content_like(qr/${q}authorized${q}:null/)
Akron33f5c672019-06-24 19:40:47 +0200112 ->element_exists_not('div.button.top a')
113 ->element_exists_not('aside.active')
114 ->element_exists_not('aside.off')
115 ;
116
117$t->get_ok('/')
118 ->status_is(200)
Akron9fa7cc52022-05-12 11:17:20 +0200119 ->element_exists('form[action=/user/login] input[name=handle_or_email]')
Akron33f5c672019-06-24 19:40:47 +0200120 ->element_exists('aside.active')
121 ->element_exists_not('aside.off')
122 ;
123
Akronff088112021-06-15 15:26:04 +0200124$t->get_ok('/settings/oauth')
125 ->status_is(401)
126 ->text_is('p.no-results', 'Not authenticated')
127 ;
128
Akron3e0fdc12020-05-15 16:17:21 +0200129# Test for bug with long password
130$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200131 handle_or_email => 'test',
Akron3e0fdc12020-05-15 16:17:21 +0200132 pwd => 'kjskjhndkjndqknaskjnakjdnkjdankajdnkjdsankjdsakjdfkjahzroiuqzriudjoijdmlamdlkmdsalkmdl' })
133 ->status_is(302)
134 ->header_is('Location' => '/');
135
Akron9fa7cc52022-05-12 11:17:20 +0200136$t->post_ok('/user/login' => form => { handle_or_email => 'test', pwd => 'fail' })
Akron33f5c672019-06-24 19:40:47 +0200137 ->status_is(302)
138 ->header_is('Location' => '/');
139
140$t->get_ok('/')
141 ->status_is(200)
142 ->element_exists('div.notify-error')
143 ->text_is('div.notify-error', 'Bad CSRF token')
Akron9fa7cc52022-05-12 11:17:20 +0200144 ->element_exists('input[name=handle_or_email][value=test]')
Akron33f5c672019-06-24 19:40:47 +0200145 ->element_exists_not('div.button.top a')
146 ;
147
Akron9fa7cc52022-05-12 11:17:20 +0200148$t->post_ok('/user/login' => form => { handle_or_email => 'test', pwd => 'pass' })
Akron33f5c672019-06-24 19:40:47 +0200149 ->status_is(302)
150 ->header_is('Location' => '/');
151
152my $csrf = $t->get_ok('/')
153 ->status_is(200)
154 ->element_exists('div.notify-error')
155 ->text_is('div.notify-error', 'Bad CSRF token')
156 ->element_exists_not('div.button.top a')
157 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
158 ;
159
160$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200161 handle_or_email => 'test',
Akron33f5c672019-06-24 19:40:47 +0200162 pwd => 'ldaperr',
163 csrf_token => $csrf
164})
165 ->status_is(302)
166 ->content_is('')
167 ->header_is('Location' => '/');
168
169$csrf = $t->get_ok('/')
170 ->status_is(200)
171 ->element_exists('div.notify-error')
172 ->text_is('div.notify-error', '2022: LDAP Authentication failed due to unknown user or password!')
Akron9fa7cc52022-05-12 11:17:20 +0200173 ->element_exists('input[name=handle_or_email][value=test]')
Akron33f5c672019-06-24 19:40:47 +0200174 ->element_exists_not('div.button.top a')
Akron3b3c7af2020-05-15 16:23:55 +0200175 ->element_exists_not('div.notify-success')
Akron33f5c672019-06-24 19:40:47 +0200176 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
177 ;
178
179$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200180 handle_or_email => 'test',
Akron33f5c672019-06-24 19:40:47 +0200181 pwd => 'unknown',
182 csrf_token => $csrf
183})
184 ->status_is(302)
185 ->content_is('')
186 ->header_is('Location' => '/');
187
188$csrf = $t->get_ok('/')
189 ->status_is(200)
190 ->element_exists('div.notify-error')
Akron8bbbecf2019-07-01 18:57:30 +0200191 ->text_is('div.notify-error', '2022: LDAP Authentication failed due to unknown user or password!')
Akron9fa7cc52022-05-12 11:17:20 +0200192 ->element_exists('input[name=handle_or_email][value=test]')
Akron33f5c672019-06-24 19:40:47 +0200193 ->element_exists_not('div.button.top a')
194 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
195 ;
196
197$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200198 handle_or_email => 'test',
Akron33f5c672019-06-24 19:40:47 +0200199 pwd => 'pass',
200 csrf_token => $csrf
201})
202 ->status_is(302)
203 ->content_is('')
204 ->header_is('Location' => '/');
205
206$t->get_ok('/')
207 ->status_is(200)
208 ->element_exists_not('div.notify-error')
209 ->element_exists('div.notify-success')
210 ->text_is('div.notify-success', 'Login successful')
Akron1d09b532021-06-15 18:18:25 +0200211 ->element_exists_not('aside.off')
Akrondc0b3ab2021-06-18 11:52:43 +0200212 ->element_exists_not('aside.active')
Akron1d09b532021-06-15 18:18:25 +0200213 ->element_exists('aside.settings')
Akron33f5c672019-06-24 19:40:47 +0200214 ;
215
Akron33f5c672019-06-24 19:40:47 +0200216# Now the user is logged in and should be able to
217# search with authorization
218$t->get_ok('/?q=Baum')
219 ->status_is(200)
Akron4cefe1f2019-09-04 10:11:28 +0200220 ->session_has('/auth')
221 ->session_is('/auth', 'Bearer ' . $access_token)
222 ->session_is('/auth_r', $refresh_token)
223 ->session_is('/user', 'test')
Akron33f5c672019-06-24 19:40:47 +0200224 ->text_like('h1 span', qr/KorAP: Find .Baum./i)
225 ->text_like('#total-results', qr/\d+$/)
226 ->element_exists_not('div.notify-error')
Akronbc6b3f22021-01-13 14:53:12 +0100227 ->content_like(qr/${q}authorized${q}:${q}yes${q}/)
Akron33f5c672019-06-24 19:40:47 +0200228 ->element_exists('div.button.top a')
229 ->element_exists('div.button.top a.logout[title~="test"]')
230 ;
231
Akron27031aa2020-04-28 14:57:10 +0200232$t->get_ok('/?q=Paum')
233 ->status_is(200)
234 ->text_like('h1 span', qr/KorAP: Find .Paum./i)
235 ->text_is('#total-results', '')
Akronbc6b3f22021-01-13 14:53:12 +0100236 ->content_like(qr/${q}authorized${q}:${q}yes${q}/)
Akron27031aa2020-04-28 14:57:10 +0200237 ->element_exists_not('p.hint')
238 ;
239
Akroncce055c2021-07-02 12:18:03 +0200240# Query with error
241$t->get_ok('/?q=error')
242 ->status_is(400)
243 ->text_is('#notifications .notify-error','500: Internal Server Error')
244;
Akron27031aa2020-04-28 14:57:10 +0200245
Akron33f5c672019-06-24 19:40:47 +0200246# Logout
247$t->get_ok('/user/logout')
248 ->status_is(302)
Akron4cefe1f2019-09-04 10:11:28 +0200249 ->session_hasnt('/auth')
250 ->session_hasnt('/auth_r')
251 ->session_hasnt('/user')
Akron33f5c672019-06-24 19:40:47 +0200252 ->header_is('Location' => '/');
253
254$t->get_ok('/')
255 ->status_is(200)
256 ->element_exists_not('div.notify-error')
257 ->element_exists('div.notify-success')
258 ->text_is('div.notify-success', 'Logout successful')
Akron9fa7cc52022-05-12 11:17:20 +0200259 ->element_exists("input[name=handle_or_email]")
260 ->element_exists("input[name=handle_or_email][value=test]")
Akron33f5c672019-06-24 19:40:47 +0200261 ;
262
263$t->get_ok('/?q=Baum')
264 ->status_is(200)
265 ->text_like('h1 span', qr/KorAP: Find .Baum./i)
266 ->text_like('#total-results', qr/\d+$/)
Akronbc6b3f22021-01-13 14:53:12 +0100267 ->content_like(qr/${q}authorized${q}:null/)
Akron33f5c672019-06-24 19:40:47 +0200268 ;
269
Akron27031aa2020-04-28 14:57:10 +0200270$t->get_ok('/?q=Paum')
271 ->status_is(200)
272 ->text_like('h1 span', qr/KorAP: Find .Paum./i)
273 ->text_is('#total-results', '')
Akronbc6b3f22021-01-13 14:53:12 +0100274 ->content_like(qr/${q}authorized${q}:null/)
Akron27031aa2020-04-28 14:57:10 +0200275 ->text_is('p.hint', 'Maybe you need to log in first?')
276 ;
277
278
Akron33f5c672019-06-24 19:40:47 +0200279# Get redirect
280my $fwd = $t->get_ok('/?q=Baum&ql=poliqarp')
281 ->status_is(200)
282 ->element_exists_not('div.notify-error')
283 ->tx->res->dom->at('input[name=fwd]')->attr('value')
284 ;
285
286is($fwd, '/?q=Baum&ql=poliqarp', 'Redirect is valid');
287
288$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200289 handle_or_email => 'test',
Akron33f5c672019-06-24 19:40:47 +0200290 pwd => 'pass',
291 csrf_token => $csrf,
292 fwd => 'http://bad.example.com/test'
293})
294 ->status_is(302)
295 ->header_is('Location' => '/');
296
297$t->get_ok('/')
298 ->status_is(200)
299 ->element_exists('div.notify-error')
300 ->element_exists_not('div.notify-success')
301 ->text_is('div.notify-error', 'Redirect failure')
302 ;
303
304$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200305 handle_or_email => 'test',
Akron33f5c672019-06-24 19:40:47 +0200306 pwd => 'pass',
307 csrf_token => $csrf,
308 fwd => $fwd
309})
310 ->status_is(302)
311 ->header_is('Location' => '/?q=Baum&ql=poliqarp');
312
Akron8bbbecf2019-07-01 18:57:30 +0200313$t->get_ok('/?q=Baum&ql=poliqarp')
314 ->status_is(200)
315 ->element_exists_not('div.notify-error')
316 ->element_exists('div.notify-success')
317 ->text_is('div.notify-success', 'Login successful')
Akroncdfd9d52019-07-23 11:35:00 +0200318 ->session_has('/auth')
319 ->session_is('/auth', 'Bearer ' . $access_token)
320 ->session_is('/auth_r', $refresh_token)
321 ->header_isnt('X-Kalamar-Cache', 'true')
Akron8bbbecf2019-07-01 18:57:30 +0200322 ;
323
Akroncdfd9d52019-07-23 11:35:00 +0200324# Expire the session
325# (makes the token be marked as expired - though it isn't serverside)
326$t->get_ok('/x/expire')
Akron8bbbecf2019-07-01 18:57:30 +0200327 ->status_is(200)
Akroncdfd9d52019-07-23 11:35:00 +0200328 ->content_is('okay')
329 ;
330
331## It may be a problem, but the cache is still valid
332$t->get_ok('/?q=Baum')
333 ->status_is(200)
334 ->text_like('h1 span', qr/KorAP: Find .Baum./i)
335 ->text_like('#total-results', qr/\d+$/)
Akronbc6b3f22021-01-13 14:53:12 +0100336 ->content_like(qr/${q}authorized${q}:${q}yes${q}/)
Akroncdfd9d52019-07-23 11:35:00 +0200337 ->header_is('X-Kalamar-Cache', 'true')
338 ;
339
340# Query without partial cache (unfortunately) (but no total results)
Akron58c60992021-09-07 13:11:43 +0200341my $err = $t->get_ok('/?q=baum&cutoff=true')
Akroncdfd9d52019-07-23 11:35:00 +0200342 ->status_is(200)
343 ->session_is('/auth', 'Bearer ' . $access_token_2)
344 ->session_is('/auth_r', $refresh_token_2)
Akroncdfd9d52019-07-23 11:35:00 +0200345 ->text_is('title', 'KorAP: Find »baum« with Poliqarp')
346 ->element_exists('meta[name="DC.title"][content="KorAP: Find »baum« with Poliqarp"]')
347 ->element_exists('body[itemscope][itemtype="http://schema.org/SearchResultsPage"]')
Akronbc6b3f22021-01-13 14:53:12 +0100348 ->content_like(qr/${q}authorized${q}:${q}yes${q}/)
Akroncdfd9d52019-07-23 11:35:00 +0200349 ->header_isnt('X-Kalamar-Cache', 'true')
Akronbc6b3f22021-01-13 14:53:12 +0100350 ->content_like(qr!${q}cutOff${q}:true!)
Akroncdfd9d52019-07-23 11:35:00 +0200351 ->element_exists_not('#total-results')
Akron58c60992021-09-07 13:11:43 +0200352 ->tx->res->dom->at('#error')
Akroncdfd9d52019-07-23 11:35:00 +0200353 ;
Akron58c60992021-09-07 13:11:43 +0200354is(defined $err ? $err->text : '', '');
Akroncdfd9d52019-07-23 11:35:00 +0200355
356# Expire the session and remove the refresh option
357$t->get_ok('/x/expire-no-refresh')
358 ->status_is(200)
359 ->content_is('okay')
360 ;
361
362$t->app->defaults(no_cache => 1);
363
364
365$t->get_ok('/x/invalid-no-refresh')
366 ->status_is(200)
367 ->content_is('okay')
368 ;
369
370# Query without cache
371# The token is invalid and can't be refreshed!
Akron58c60992021-09-07 13:11:43 +0200372$err = $t->get_ok('/?q=baum&cutoff=true')
Akron3c390c42020-03-30 09:06:21 +0200373 ->status_is(400)
Akroncdfd9d52019-07-23 11:35:00 +0200374 ->session_hasnt('/auth')
375 ->session_hasnt('/auth_r')
Akroncdfd9d52019-07-23 11:35:00 +0200376 ->text_is('div.notify-error','Access token invalid')
377 ->text_is('title', 'KorAP: Find »baum« with Poliqarp')
378 ->element_exists('meta[name="DC.title"][content="KorAP: Find »baum« with Poliqarp"]')
379 ->element_exists('body[itemscope][itemtype="http://schema.org/SearchResultsPage"]')
Akronbc6b3f22021-01-13 14:53:12 +0100380 ->content_unlike(qr/${q}authorized${q}:${q}yes${q}/)
Akroncdfd9d52019-07-23 11:35:00 +0200381 ->header_isnt('X-Kalamar-Cache', 'true')
382 ->element_exists('p.no-results')
Akron58c60992021-09-07 13:11:43 +0200383 ->tx->res->dom->at('#error')
Akroncdfd9d52019-07-23 11:35:00 +0200384 ;
Akron58c60992021-09-07 13:11:43 +0200385is(defined $err ? $err->text : '', '');
386
Akroncdfd9d52019-07-23 11:35:00 +0200387
388$t->get_ok('/x/invalid')
389 ->status_is(200)
390 ->content_is('okay')
391 ;
392
393# Query without cache
394# The token is invalid and can't be refreshed!
Akron58c60992021-09-07 13:11:43 +0200395$err = $t->get_ok('/?q=baum&cutoff=true')
Akroncdfd9d52019-07-23 11:35:00 +0200396 ->status_is(200)
397 ->session_is('/auth', 'Bearer ' . $access_token_2)
398 ->session_is('/auth_r', $refresh_token_2)
Akroncdfd9d52019-07-23 11:35:00 +0200399 ->element_exists_not('div.notify-error','Access token invalid')
400 ->text_is('title', 'KorAP: Find »baum« with Poliqarp')
401 ->element_exists('meta[name="DC.title"][content="KorAP: Find »baum« with Poliqarp"]')
402 ->element_exists('body[itemscope][itemtype="http://schema.org/SearchResultsPage"]')
Akronbc6b3f22021-01-13 14:53:12 +0100403 ->content_like(qr/${q}authorized${q}:${q}yes${q}/)
Akroncdfd9d52019-07-23 11:35:00 +0200404 ->header_isnt('X-Kalamar-Cache', 'true')
405 ->element_exists_not('p.no-results')
Akron58c60992021-09-07 13:11:43 +0200406 ->tx->res->dom->at('#error')
Akron8bbbecf2019-07-01 18:57:30 +0200407 ;
Akron58c60992021-09-07 13:11:43 +0200408is(defined $err ? $err->text : '', '');
Akron8bbbecf2019-07-01 18:57:30 +0200409
Akron33f5c672019-06-24 19:40:47 +0200410
Akroncdfd9d52019-07-23 11:35:00 +0200411$t->get_ok('/x/expired-with-wrong-refresh')
412 ->status_is(200)
413 ->content_is('okay')
414 ;
Akron4796e002019-07-05 10:13:15 +0200415
Akron4796e002019-07-05 10:13:15 +0200416
Akroncdfd9d52019-07-23 11:35:00 +0200417# The token is invalid and can't be refreshed!
Akron58c60992021-09-07 13:11:43 +0200418my $dom = $t->get_ok('/?q=baum&cutoff=true')
Akron3c390c42020-03-30 09:06:21 +0200419 ->status_is(400)
Akroncdfd9d52019-07-23 11:35:00 +0200420 ->session_hasnt('/auth')
421 ->session_hasnt('/auth_r')
Akroncdfd9d52019-07-23 11:35:00 +0200422 ->text_is('div.notify-error','Refresh token is expired')
423 ->text_is('title', 'KorAP: Find »baum« with Poliqarp')
Akronbc6b3f22021-01-13 14:53:12 +0100424 ->content_unlike(qr/${q}authorized${q}:${q}yes${q}/)
Akroncdfd9d52019-07-23 11:35:00 +0200425 ->element_exists('p.no-results')
Akron58c60992021-09-07 13:11:43 +0200426 ->tx->res->dom;
427
428$csrf = $dom->at('input[name="csrf_token"]')
Akron59992122019-10-29 11:28:45 +0100429 ->attr('value')
Akroncdfd9d52019-07-23 11:35:00 +0200430 ;
Akron4796e002019-07-05 10:13:15 +0200431
Akron58c60992021-09-07 13:11:43 +0200432$err = $dom->at('#error');
433is(defined $err ? $err->text : '', '');
434
435
Akron6a228db2021-10-14 15:57:00 +0200436# This should fail
437my $wide_char_login = "\x{61}\x{E5}\x{61}"; # "\x{443}\x{434}";
Akron59992122019-10-29 11:28:45 +0100438$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200439 handle_or_email => $wide_char_login,
Akron6a228db2021-10-14 15:57:00 +0200440 pwd => 'pass',
441 csrf_token => $csrf,
442 fwd => $fwd
443})
444 ->status_is(302)
445 ->header_is('Location' => '/');
446
447$t->get_ok('/')
448 ->status_is(200)
449 ->element_exists('div.notify-error')
450 ->text_is('div.notify-error', 'Invalid character in request')
Akron9fa7cc52022-05-12 11:17:20 +0200451 ->element_exists('input[name=handle_or_email]:not([value])')
Akron6a228db2021-10-14 15:57:00 +0200452 ->element_exists_not('div.button.top a')
453 ;
454
455# Login:
456# UTF8 request
457my $username = b('täst')->encode;
458$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200459 handle_or_email => $username,
Akron59992122019-10-29 11:28:45 +0100460 pwd => 'pass',
461 csrf_token => $csrf
462})
463 ->status_is(302)
464 ->content_is('')
465 ->header_is('Location' => '/');
466
467$t->get_ok('/')
468 ->status_is(200)
469 ->element_exists_not('div.notify-error')
470 ->element_exists('div.notify-success')
471 ->text_is('div.notify-success', 'Login successful')
Akron6a228db2021-10-14 15:57:00 +0200472 ->attr_is('a.logout', 'title', "Logout: $username")
Akron1d09b532021-06-15 18:18:25 +0200473 ->element_exists_not('aside.off')
Akrondc0b3ab2021-06-18 11:52:43 +0200474 ->element_exists_not('aside.active')
Akron1d09b532021-06-15 18:18:25 +0200475 ->element_exists('aside.settings')
Akron59992122019-10-29 11:28:45 +0100476 ;
477
478$t->get_ok('/settings/oauth')
479 ->text_is('form.form-table legend', 'Register new client application')
480 ->attr_is('form.oauth-register','action', '/settings/oauth/register')
Akron9f2ad342022-05-04 16:16:40 +0200481 ->text_is('label[for=name]','Name of the client application')
482 ->text_is('label[for=type]','Type of the client application')
483 ->text_is('label[for=desc]','Short description')
484 ->text_is('label[for=url]','Homepage')
485 ->text_is('label[for=redirect_uri]','Redirect URI')
486 ->text_is('label[for=src]','Declaration of the plugin (*.json file)')
Akron1a9d5be2020-03-19 17:28:33 +0100487 ->element_exists('ul.client-list')
488 ->element_exists_not('ul.client-list > li')
Akronad011bb2021-06-10 12:16:36 +0200489 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
490 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
491 ->header_is('Pragma','no-cache')
Akron59992122019-10-29 11:28:45 +0100492 ;
Akronad011bb2021-06-10 12:16:36 +0200493
Akron59992122019-10-29 11:28:45 +0100494$csrf = $t->post_ok('/settings/oauth/register' => form => {
495 name => 'MyApp',
496 type => 'PUBLIC',
Akron6b75d122022-05-12 17:39:05 +0200497 desc => 'This is my plugin application'
Akron59992122019-10-29 11:28:45 +0100498})
499 ->text_is('div.notify-error', 'Bad CSRF token')
500 ->tx->res->dom->at('input[name="csrf_token"]')
501 ->attr('value')
502 ;
503
504$t->post_ok('/settings/oauth/register' => form => {
505 name => 'MyApp',
Akron99968a92022-06-03 12:32:07 +0200506 type => 'PUBLIC',
Akron6b75d122022-05-12 17:39:05 +0200507 desc => 'This is my plugin application',
Akron9f2ad342022-05-04 16:16:40 +0200508 csrf_token => $csrf,
509 src => {
510 filename => '',
511 content => ''
512 }
Akron59992122019-10-29 11:28:45 +0100513})
514 ->status_is(200)
515 ->element_exists('div.notify-success')
516 ->text_is('legend', 'Client Credentials')
517 ->text_is('label[for=client_id]', 'ID of the client application')
518 ->element_exists('input[name=client_id][readonly][value]')
Akron99968a92022-06-03 12:32:07 +0200519 ->element_exists_not('input[name=client_secret][readonly][value]')
Akronad011bb2021-06-10 12:16:36 +0200520 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
521 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
522 ->header_is('Pragma','no-cache')
Akron59992122019-10-29 11:28:45 +0100523 ;
Akron4796e002019-07-05 10:13:15 +0200524
Akron58c60992021-09-07 13:11:43 +0200525my $anchor = $t->get_ok('/settings/oauth')
Akronc1aaf932021-06-09 12:19:15 +0200526 ->text_is('.form-table legend', 'Register new client application')
527 ->attr_is('.oauth-register','action', '/settings/oauth/register')
Akron17de86e2020-04-16 16:03:40 +0200528 ->text_is('ul.client-list > li > span.client-name a', 'MyApp')
Akron6b75d122022-05-12 17:39:05 +0200529 ->text_is('ul.client-list > li > p.client-desc', 'This is my plugin application')
Akronad011bb2021-06-10 12:16:36 +0200530 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
531 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
532 ->header_is('Pragma','no-cache')
Akronb6b156e2022-03-31 14:57:49 +0200533 ->tx->res->dom->at('ul.client-list > li > p.client-url a')
Akron17de86e2020-04-16 16:03:40 +0200534 ;
Akron58c60992021-09-07 13:11:43 +0200535is(defined $anchor ? $anchor->text : '', '');
Akron17de86e2020-04-16 16:03:40 +0200536
Akron041ca4d2021-06-10 11:52:51 +0200537$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akron17de86e2020-04-16 16:03:40 +0200538 ->status_is(200)
Akronc1aaf932021-06-09 12:19:15 +0200539 ->text_is('ul.client-list > li.client > span.client-name', 'MyApp')
Akron6b75d122022-05-12 17:39:05 +0200540 ->text_is('ul.client-list > li.client > p.client-desc', 'This is my plugin application')
Akron17de86e2020-04-16 16:03:40 +0200541 ->text_is('a.client-unregister', 'Unregister')
Akron041ca4d2021-06-10 11:52:51 +0200542 ->attr_is('a.client-unregister', 'href', '/settings/oauth/fCBbQkA2NDA3MzM1Yw==/unregister?name=MyApp')
Akron1a9d5be2020-03-19 17:28:33 +0100543 ;
544
Akron041ca4d2021-06-10 11:52:51 +0200545$csrf = $t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/unregister?name=MyApp')
Akron1a9d5be2020-03-19 17:28:33 +0100546 ->content_like(qr!Do you really want to unregister \<span class="client-name"\>MyApp\<\/span\>?!)
Akronc1aaf932021-06-09 12:19:15 +0200547 ->attr_is('.form-table input[name=client-name]', 'value', 'MyApp')
Akronad011bb2021-06-10 12:16:36 +0200548 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
549 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
550 ->header_is('Pragma','no-cache')
Akron1a9d5be2020-03-19 17:28:33 +0100551 ->tx->res->dom->at('input[name="csrf_token"]')
552 ->attr('value')
553 ;
554
Akron041ca4d2021-06-10 11:52:51 +0200555$t->post_ok('/settings/oauth/xxxx==/unregister' => form => {
Akron1a9d5be2020-03-19 17:28:33 +0100556 'client-name' => 'MyApp',
Akron1a9d5be2020-03-19 17:28:33 +0100557 'csrf_token' => $csrf
558})->status_is(302)
559 ->content_is('')
560 ->header_is('Location' => '/settings/oauth')
561 ;
562
563$t->get_ok('/settings/oauth')
Akronc1aaf932021-06-09 12:19:15 +0200564 ->text_is('.form-table legend', 'Register new client application')
565 ->attr_is('.oauth-register','action', '/settings/oauth/register')
Akron1a9d5be2020-03-19 17:28:33 +0100566 ->element_exists('ul.client-list > li')
567 ->text_is('div.notify', 'Unknown client with xxxx==.')
Akronad011bb2021-06-10 12:16:36 +0200568 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
569 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
570 ->header_is('Pragma','no-cache')
Akron1a9d5be2020-03-19 17:28:33 +0100571 ;
572
Akron041ca4d2021-06-10 11:52:51 +0200573$t->post_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/unregister' => form => {
Akron1a9d5be2020-03-19 17:28:33 +0100574 'client-name' => 'MyApp',
Akron1a9d5be2020-03-19 17:28:33 +0100575 'csrf_token' => $csrf
576})->status_is(302)
577 ->content_is('')
578 ->header_is('Location' => '/settings/oauth')
579 ;
580
581$t->get_ok('/settings/oauth')
Akronc1aaf932021-06-09 12:19:15 +0200582 ->text_is('.form-table legend', 'Register new client application')
583 ->attr_is('.oauth-register','action', '/settings/oauth/register')
Akron1a9d5be2020-03-19 17:28:33 +0100584 ->element_exists_not('ul.client-list > li')
585 ->text_is('div.notify-success', 'Successfully deleted MyApp')
Akronad011bb2021-06-10 12:16:36 +0200586 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
587 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
588 ->header_is('Pragma','no-cache')
Akron1a9d5be2020-03-19 17:28:33 +0100589 ;
590
Akron83209f72021-01-29 17:54:15 +0100591$t->post_ok('/settings/oauth/register' => form => {
592 name => 'MyApp2',
593 type => 'PUBLIC',
Akron6b75d122022-05-12 17:39:05 +0200594 desc => 'This is my plugin application',
Akron83209f72021-01-29 17:54:15 +0100595 csrf_token => $csrf
596})->status_is(200)
597 ->element_exists('div.notify-success')
598 ->text_is('legend', 'Client Credentials')
599 ->text_is('label[for=client_id]', 'ID of the client application')
600 ->element_exists('input[name=client_id][readonly][value]')
601 ->element_exists_not('input[name=client_secret][readonly][value]')
Akronad011bb2021-06-10 12:16:36 +0200602 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
603 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
604 ->header_is('Pragma','no-cache')
Akronb6b156e2022-03-31 14:57:49 +0200605 ->element_exists('.client-issue-token')
Akron83209f72021-01-29 17:54:15 +0100606 ;
607
Akron041ca4d2021-06-10 11:52:51 +0200608$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akron83209f72021-01-29 17:54:15 +0100609 ->text_is('.client-name', 'MyApp2')
Akron6b75d122022-05-12 17:39:05 +0200610 ->text_is('.client-desc', 'This is my plugin application')
Akrone997bb52021-06-11 16:44:06 +0200611 ->text_is('.client-issue-token', 'Issue new token')
Akron041ca4d2021-06-10 11:52:51 +0200612 ->attr_is('.client-issue-token', 'href', '/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token?name=MyApp2')
Akronad011bb2021-06-10 12:16:36 +0200613 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
614 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
615 ->header_is('Pragma','no-cache')
Akrone997bb52021-06-11 16:44:06 +0200616 ->text_is('ul.token-list label[for=token]', 'Access Token')
617 ->text_is('p[name=created]', 'Created at ')
618 ->text_is('p[name=expires]', 'Expires in 31533851 seconds.')
Akronb6b156e2022-03-31 14:57:49 +0200619 ->element_exists('.client-issue-token')
Akron83209f72021-01-29 17:54:15 +0100620 ;
621
Akron041ca4d2021-06-10 11:52:51 +0200622$csrf = $t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token?name=MyApp2')
Akron83209f72021-01-29 17:54:15 +0100623 ->status_is(200)
Akron041ca4d2021-06-10 11:52:51 +0200624 ->attr_is('#issue-token','action', '/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token')
Akron83209f72021-01-29 17:54:15 +0100625 ->attr_is('input[name=client-id]', 'value', 'fCBbQkA2NDA3MzM1Yw==')
626 ->attr_is('input[name=name]', 'value', 'MyApp2')
Akronad011bb2021-06-10 12:16:36 +0200627 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
628 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
629 ->header_is('Pragma','no-cache')
Akrone997bb52021-06-11 16:44:06 +0200630 ->text_is('a.button-abort', 'Abort')
631 ->attr_is('#issue-token input[type=submit]', 'value', 'Issue new token')
632 ->content_like(qr!Issue a new token for!)
Akron83209f72021-01-29 17:54:15 +0100633 ->tx->res->dom->at('input[name="csrf_token"]')
634 ->attr('value')
635 ;
636
Akron041ca4d2021-06-10 11:52:51 +0200637$t->post_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token' => form => {
Akron83209f72021-01-29 17:54:15 +0100638 csrf_token => $csrf,
639 name => 'MyApp2',
640 'client-id' => 'fCBbQkA2NDA3MzM1Yw=='
641})
Akronbc94a9c2021-04-15 00:07:35 +0200642 ->status_is(302)
Akron041ca4d2021-06-10 11:52:51 +0200643 ->header_is('Location','/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronbc94a9c2021-04-15 00:07:35 +0200644 ;
645
Akron041ca4d2021-06-10 11:52:51 +0200646$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
647 ->status_is(200)
Akronbc94a9c2021-04-15 00:07:35 +0200648 ->text_is('div.notify-success', 'New access token created')
Akronad011bb2021-06-10 12:16:36 +0200649 ->status_is(200)
650 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
651 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
652 ->header_is('Pragma','no-cache')
Akron83209f72021-01-29 17:54:15 +0100653 ;
654
Akron041ca4d2021-06-10 11:52:51 +0200655$csrf = $t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200656 ->status_is(200)
Akron041ca4d2021-06-10 11:52:51 +0200657 ->attr_is('form.token-revoke', 'action', '/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token/revoke')
Akronc1aaf932021-06-09 12:19:15 +0200658 ->attr_is('form.token-revoke input[name=token]', 'value', 'jhkhkjhk_hjgjsfz67i')
659 ->attr_is('form.token-revoke input[name=name]', 'value', 'MyApp2')
660 ->tx->res->dom->at('input[name="csrf_token"]')
661 ->attr('value')
662 ;
663
Akron041ca4d2021-06-10 11:52:51 +0200664$t->post_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token/revoke' => form => {
Akronc1aaf932021-06-09 12:19:15 +0200665 csrf_token => $csrf,
666 name => 'MyApp2',
667 token => 'jhkhkjhk_hjgjsfz67i'
668})
669 ->status_is(200)
Akron041ca4d2021-06-10 11:52:51 +0200670 ->attr_is('form#revoke-token','action','/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token?_method=DELETE')
Akronc1aaf932021-06-09 12:19:15 +0200671 ->attr_is('form#revoke-token','method','POST')
672 ->attr_is('form#revoke-token input[name=token]','value','jhkhkjhk_hjgjsfz67i')
Akrone997bb52021-06-11 16:44:06 +0200673 ->text_is('a.button-abort', 'Abort')
674 ->attr_is('#revoke-token input[type=submit]', 'value', 'Revoke')
675 ->content_like(qr!Revoke a token for!)
Akronc1aaf932021-06-09 12:19:15 +0200676;
677
678
679# CSRF missing
Akron041ca4d2021-06-10 11:52:51 +0200680$t->post_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token?_method=DELETE' => form => {
Akronc1aaf932021-06-09 12:19:15 +0200681 name => 'MyApp2',
682 token => 'jhkhkjhk_hjgjsfz67i'
683})->status_is(302)
Akron041ca4d2021-06-10 11:52:51 +0200684 ->header_is('Location','/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200685 ;
686
Akron041ca4d2021-06-10 11:52:51 +0200687$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200688 ->element_exists_not('div.notify-success')
689 ->text_is('div.notify-error', 'Bad CSRF token')
690 ;
691
692# Token missing
Akron041ca4d2021-06-10 11:52:51 +0200693$t->post_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token?_method=DELETE' => form => {
Akronc1aaf932021-06-09 12:19:15 +0200694 name => 'MyApp2',
695 csrf_token => $csrf,
696})->status_is(302)
Akron041ca4d2021-06-10 11:52:51 +0200697 ->header_is('Location','/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200698 ;
699
Akron041ca4d2021-06-10 11:52:51 +0200700$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200701 ->element_exists_not('div.notify-success')
702 ->text_is('div.notify-error', 'Some fields are invalid')
703 ;
704
Akron041ca4d2021-06-10 11:52:51 +0200705$t->post_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token?_method=DELETE' => form => {
Akronc1aaf932021-06-09 12:19:15 +0200706 name => 'MyApp2',
707 csrf_token => $csrf,
708 token => 'jhkhkjhk_hjgjsfz67i'
709})->status_is(302)
Akron041ca4d2021-06-10 11:52:51 +0200710 ->header_is('Location','/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200711 ;
712
Akron041ca4d2021-06-10 11:52:51 +0200713$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200714 ->element_exists_not('div.notify-error')
715 ->text_is('div.notify-success', 'Token was revoked successfully')
716 ;
Akron83209f72021-01-29 17:54:15 +0100717
Akronb6b156e2022-03-31 14:57:49 +0200718$t->app->routes->get('/x/redirect-target')->to(
719 cb => sub {
720 my $c = shift;
721 return $c->render(text => 'redirected');
722 }
723);
724
725$csrf = $t->post_ok('/settings/oauth/register' => form => {
726 name => 'MyConfApp',
727 type => 'CONFIDENTIAL',
Akron6b75d122022-05-12 17:39:05 +0200728 desc => 'This is my plugin application',
Akronb6b156e2022-03-31 14:57:49 +0200729})
730 ->text_is('div.notify-error', 'Bad CSRF token')
731 ->tx->res->dom->at('input[name="csrf_token"]')
732 ->attr('value')
733 ;
734
735$t->post_ok('/settings/oauth/register' => form => {
736 name => 'MyConfApp',
737 type => 'CONFIDENTIAL',
738 desc => 'This is my confidential application',
739 csrf_token => $csrf,
740 redirect_uri => 'http://localhost/redirect-target'
741})
742 ->text_is('div.notify-error', undef)
743 ->text_is('li.client span.client-name', 'MyConfApp')
744 ->text_is('li.client p.client-desc', 'This is my confidential application')
745 ->text_is('li.client .client-redirect-uri tt', 'http://localhost/redirect-target')
746 ->text_is('li.client .client-type tt', 'CONFIDENTIAL')
747 ->element_exists_not('.client-issue-token')
748 ;
749
750$t->post_ok('/settings/oauth/register' => form => {
751 name => 'MyConfApp2',
752 type => 'CONFIDENTIAL',
753 desc => 'This is my second confidential application',
754 csrf_token => $csrf,
755 redirect_uri => 'http://localhost/FAIL'
756})
757 ->status_is(302)
758 ->header_is('location','/settings/oauth/')
759 ;
760
761$t->get_ok('/settings/oauth/')
762 ->text_is('div.notify-error', 'invalid_request: http://localhost/FAIL is invalid.')
763 ;
764
Akrona8efaa92022-04-09 14:45:43 +0200765# OAuth client authorization flow
766$t->get_ok(Mojo::URL->new('/settings/oauth/authorize'))
767 ->status_is(302)
Akron5ea0f5d2023-01-20 11:51:43 +0100768 ->header_is('location','/settings/oauth')
769 ;
770
771$t->get_ok('/settings/oauth/')
Akron2c142ab2023-01-30 13:21:57 +0100772 ->text_is('div.notify-error', 'Client ID required')
773 ;
774
775$t->get_ok(Mojo::URL->new('/settings/oauth/authorize?client_id=xyz'))
776 ->status_is(302)
777 ->header_is('location','/settings/oauth')
778 ;
779
780$t->get_ok('/settings/oauth/')
781 ->text_is('div.notify-error', 'Scope required')
Akrona8efaa92022-04-09 14:45:43 +0200782 ;
783
Akrondb1f4672023-01-24 12:05:07 +0100784# OAuth client authorization flow
785$t->get_ok(Mojo::URL->new('/settings/oauth/authorize?client_id=abc'))
786 ->status_is(302)
787 ->header_is('location','/settings/oauth')
788 ;
789
790$t->get_ok('/settings/oauth/')
Akron2c142ab2023-01-30 13:21:57 +0100791 ->text_is('div.notify-error', 'Scope required')
792 ;
793
794# OAuth client authorization flow
795$t->get_ok(Mojo::URL->new('/settings/oauth/authorize?client_id=abc&scope=match'))
796 ->status_is(302)
797 ->header_is('location','/settings/oauth')
798 ;
799
800$t->get_ok('/settings/oauth/')
Akrondb1f4672023-01-24 12:05:07 +0100801 ->text_is('div.notify-error', 'Unknown client with abc.')
802 ;
803
Akrona8efaa92022-04-09 14:45:43 +0200804# Logout
805$t->get_ok('/x/expired-with-wrong-refresh');
806
807$t->get_ok('/user/logout')
808 ->status_is(302)
809 ->session_hasnt('/auth')
810 ->session_hasnt('/auth_r')
811 ->session_hasnt('/user')
812 ->header_is('Location' => '/');
813
814$csrf = $t->get_ok('/')
815 ->status_is(200)
816 ->element_exists_not('div.notify-error')
817 ->element_exists('div.notify-success')
818 ->text_is('div.notify-success', 'Logout successful')
Akron9fa7cc52022-05-12 11:17:20 +0200819 ->element_exists("input[name=handle_or_email]")
Akrona8efaa92022-04-09 14:45:43 +0200820 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
821 ;
822
Akron408bc7c2022-04-28 15:46:43 +0200823$fake_backend_app->add_client({
824 "client_id" => 'xyz',
825 "client_name" => 'New added client',
826 "client_description" => 'This is a new client',
827 "client_url" => 'http://example.com',
828 "client_type" => 'CONFIDENTIAL'
829# "client_redirect_uri" => $redirect_uri
830});
831
Akron9d826902023-01-25 10:20:52 +0100832$fake_backend_app->add_client({
833 "client_id" => 'xyz2',
834 "client_name" => 'New added client',
835 "client_description" => 'This is a new client',
836 "client_url" => 'http://example.com',
837 "client_type" => 'CONFIDENTIAL',
838 "client_redirect_uri" => 'http://redirect.url/'
839});
840
841$fwd = $t->get_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
842 client_id => 'xyz2',
843 scope => 'search match',
844 redirect_uri => 'http://test.com/',
845}))
846 ->status_is(200)
847 ->text_is('div.notify-warn', 'redirect_uri host differs from registered host')
848 ->element_exists_not('div.notify-error')
849 ->element_exists_not('div.notify-success')
850 ->element_exists("input[name=handle_or_email]")
851 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
852 ;
853
854$fwd = $t->get_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
855 client_id => 'xyz2',
856 scope => 'search match',
857 redirect_uri => 'http://redirect.url:9000/',
858}))
859 ->status_is(200)
860 ->text_is('div.notify-warn', 'redirect_uri port differs from registered port')
861 ->element_exists_not('div.notify-error')
862 ->element_exists_not('div.notify-success')
863 ->element_exists("input[name=handle_or_email]")
864 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
865 ;
866
Akrona8efaa92022-04-09 14:45:43 +0200867$fwd = $t->get_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
868 client_id => 'xyz',
869 state => 'abcde',
870 scope => 'search match',
871 redirect_uri => 'http://test.com/',
872}))
873 ->status_is(200)
874 ->attr_is('input[name=client_id]','value','xyz')
875 ->attr_is('input[name=state]','value','abcde')
Akrona8efaa92022-04-09 14:45:43 +0200876 ->attr_like('input[name=fwd]','value',qr!test\.com!)
Akron408bc7c2022-04-28 15:46:43 +0200877 ->text_is('span.client-name','New added client')
Akrona8efaa92022-04-09 14:45:43 +0200878 ->text_is('div.intro p:nth-child(2)', 'Please log in!')
879 ->tx->res->dom->at('input[name=fwd]')->attr('value')
880 ;
881
882$fwd = $t->post_ok(Mojo::URL->new('/user/login')->query({
883 csrf_token => $csrf,
884 client_id => 'xyz',
885 state => 'abcde',
886 scope => 'search match',
887 redirect_uri => 'http://test.com/',
Akron9fa7cc52022-05-12 11:17:20 +0200888 handle_or_email => 'test',
Akrona8efaa92022-04-09 14:45:43 +0200889 pwd => 'pass',
890 fwd => $fwd
891}))
892 ->status_is(302)
893 ->header_like('location', qr!/settings/oauth/authorize!)
894 ->tx->res->headers->header('location')
895 ;
896
897$t->get_ok($fwd)
898 ->status_is(200)
899 ->attr_is('input[name=client_id]','value','xyz')
900 ->attr_is('input[name=state]','value','abcde')
Akron9d826902023-01-25 10:20:52 +0100901 ->attr_like('input[name=redirect_uri]','value', qr!^http://test\.com\/\?crto=.{3,}!)
Akrona8efaa92022-04-09 14:45:43 +0200902 ->text_is('ul#scopes li:nth-child(1)','search')
903 ->text_is('ul#scopes li:nth-child(2)','match')
Akron408bc7c2022-04-28 15:46:43 +0200904 ->text_is('span.client-name','New added client')
Akrona8efaa92022-04-09 14:45:43 +0200905 ->attr_is('a.form-button','href','http://test.com/')
906 ->attr_is('a.embedded-link', 'href', '/doc/korap/kalamar')
Akron9d826902023-01-25 10:20:52 +0100907 ->element_exists_not('div.notify-error')
908 ->element_exists_not('div.notify-warn')
909 ->element_exists_not('blockquote.warning')
Akrona8efaa92022-04-09 14:45:43 +0200910 ;
911
912$t->get_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
913 client_id => 'xyz',
914 state => 'abcde',
915 scope => 'search match',
916 redirect_uri => 'http://test.com/'
917}))
918 ->status_is(200)
919 ->attr_is('input[name=client_id]','value','xyz')
920 ->attr_is('input[name=state]','value','abcde')
Akrona8efaa92022-04-09 14:45:43 +0200921 ->text_is('ul#scopes li:nth-child(1)','search')
922 ->text_is('ul#scopes li:nth-child(2)','match')
Akron408bc7c2022-04-28 15:46:43 +0200923 ->text_is('span.client-name','New added client')
Akrona8efaa92022-04-09 14:45:43 +0200924 ->attr_is('a.form-button','href','http://test.com/')
925 ->attr_is('a.embedded-link', 'href', '/doc/korap/kalamar')
926 ;
927
928$t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
929 client_id => 'xyz',
930 state => 'abcde',
931 scope => 'search match',
932 redirect_uri => 'http://test.com/'
933}))
934 ->status_is(302)
Akron9d826902023-01-25 10:20:52 +0100935 ->header_is('location', '/settings/oauth?error_description=Bad+CSRF+token')
Akrona8efaa92022-04-09 14:45:43 +0200936 ;
937
938$fwd = $t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
939 client_id => 'xyz',
940 state => 'abcde',
941 scope => 'search match',
942 redirect_uri_server => 'http://example.com/',
943 redirect_uri => $fake_backend_app->url_for('return_uri')->to_abs,
944 csrf_token => $csrf,
945}))
946 ->status_is(302)
947 ->header_like('location', qr!/realapi/fakeclient/return!)
948 ->tx->res->headers->header('location')
949 ;
950
951$t->get_ok($fwd)
952 ->status_is(200)
953 ->content_like(qr'welcome back! \[(.+?)\]')
954 ;
955
956$t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
957 client_id => 'xyz',
Akron9ccf69a2023-01-31 14:21:37 +0100958 state => 'abcde',
959 scope => 'search match',
960 redirect_uri_server => 'http://example.com/',
961 redirect_uri => $t->app->close_redirect_to('http://wrong'),
962 csrf_token => $csrf,
963}))
964 ->status_is(302)
965 ->header_is('location', '/settings/oauth')
966 ->tx->res->headers->header('location')
967 ;
968
969$t->get_ok('/settings/oauth')
970 ->text_is('div.notify-error', 'Invalid redirect URI')
971 ;
972
973$t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
974 client_id => 'xyz',
Akrona8efaa92022-04-09 14:45:43 +0200975 state => 'fail',
976 scope => 'search match',
Akron9d826902023-01-25 10:20:52 +0100977# redirect_uri_server => 'http://example.com/',
Akrona8efaa92022-04-09 14:45:43 +0200978 redirect_uri => $fake_backend_app->url_for('return_uri')->to_abs,
979 csrf_token => $csrf,
980}))
981 ->status_is(302)
Akron9d826902023-01-25 10:20:52 +0100982 ->header_is('location', '/realapi/fakeclient/return?error_description=FAIL')
Akrona8efaa92022-04-09 14:45:43 +0200983 ;
984
Akron9f2ad342022-05-04 16:16:40 +0200985my $json_post = {
986 name => 'Funny',
987 type => 'PUBLIC',
Akron6b75d122022-05-12 17:39:05 +0200988 desc => 'This is my plugin application 2',
Akron9f2ad342022-05-04 16:16:40 +0200989 csrf_token => $csrf,
990 src => 'hMMM'
991};
992
993$t->post_ok('/settings/oauth/register' => form => $json_post)
994 ->status_is(200)
995 ->element_exists('div.notify-error')
Akron99968a92022-06-03 12:32:07 +0200996 ->text_is('div.notify-error', 'Plugin declarations need to be json files')
997 ;
998
999$json_post->{src} = {
1000 content => 'jjjjjj',
1001 filename => 'fun.txt'
1002};
1003
1004$t->post_ok('/settings/oauth/register' => form => $json_post)
1005 ->status_is(200)
1006 ->element_exists('div.notify-error')
Akron9f2ad342022-05-04 16:16:40 +02001007 ->text_is('div.notify-error', 'Plugins need to be confidential')
1008 ;
1009
1010$json_post->{type} = 'CONFIDENTIAL';
1011
Akron99968a92022-06-03 12:32:07 +02001012# This somehow gets removed in the last form send ...
Akron9f2ad342022-05-04 16:16:40 +02001013$json_post->{src} = {
1014 content => 'jjjjjj',
1015 filename => 'fun.txt'
1016};
1017
1018$t->post_ok('/settings/oauth/register' => form => $json_post)
1019 ->status_is(200)
1020 ->element_exists('div.notify-error')
1021 ->text_is('div.notify-error', 'Plugin declarations need to be json files')
1022 ;
1023
1024$json_post->{src} = {
1025 content => '{"name":"example"}',
1026 filename => 'fun.txt'
1027};
1028
1029$t->post_ok('/settings/oauth/register' => form => $json_post)
1030 ->status_is(200)
1031 ->element_exists_not('div.notify-error')
1032 ->element_exists('div.notify-success')
1033 ->text_is('div.notify-success', 'Registration successful')
1034 ;
1035
Akron6b75d122022-05-12 17:39:05 +02001036$t->get_ok('/settings/oauth/jh0gfjhjbfdsgzjghj==')
1037 ->status_is(200)
1038 ->text_is('div.notify-error', undef)
1039 ->text_is('li.client #client_source', '{"name":"example"}')
1040 ->text_is('li.client span.client-name', 'Funny')
1041 ->text_is('li.client p.client-desc', 'This is my plugin application 2')
1042 ->element_exists_not('li.client .client-redirect-uri tt')
1043 ->text_is('li.client .client-type tt', 'CONFIDENTIAL')
1044 ;
Akron9f2ad342022-05-04 16:16:40 +02001045
Akronb6b156e2022-03-31 14:57:49 +02001046
Akron66ef3b52022-11-22 14:25:15 +01001047# Retest client with super_client_file
1048my $client_file = tempfile;
1049
1050$client_file->spurt(
1051 '{"client_id":"2","client_secret":"k414m4r-s3cr3t"}'
1052);
1053
1054$t = Test::Mojo::WithRoles->new('Kalamar' => {
1055 Kalamar => {
1056 plugins => ['Auth']
1057 },
1058 'Kalamar-Auth' => {
1059 client_file => $client_file,
1060 oauth2 => 1
1061 }
1062});
1063
1064$t->app->plugin(
1065 Mount => {
1066 $mount_point =>
1067 $fixtures_path->child('mock.pl')
1068 }
1069);
1070
1071$csrf = $t->get_ok('/')
1072 ->status_is(200)
1073 ->element_exists_not('div.button.top a')
1074 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
1075 ;
1076
1077$t->post_ok('/user/login' => form => {
1078 handle_or_email => 'test',
1079 pwd => 'pass',
1080 csrf_token => $csrf
1081})
1082 ->status_is(302)
1083 ->header_is('Location' => '/')
1084 ->content_is('');
1085
1086$t->get_ok('/')
1087 ->status_is(200)
1088 ->element_exists_not('div.notify-error')
1089 ->element_exists('div.notify-success')
1090 ->text_is('div.notify-success', 'Login successful')
1091 ->element_exists_not('aside.off')
1092 ->element_exists_not('aside.active')
1093 ->element_exists('aside.settings')
1094 ;
1095
Akron53a171e2022-12-05 18:22:58 +01001096$main::ENV{KALAMAR_CLIENT_FILE} = $client_file;
1097
1098$t = Test::Mojo::WithRoles->new('Kalamar' => {
1099 Kalamar => {
1100 plugins => ['Auth']
1101 },
1102 'Kalamar-Auth' => {
1103 oauth2 => 1,
1104# client_file => $client_file,
1105 }
1106});
1107
1108$t->app->plugin(
1109 Mount => {
1110 $mount_point =>
1111 $fixtures_path->child('mock.pl')
1112 }
1113);
1114
1115$csrf = $t->get_ok('/')
1116 ->status_is(200)
1117 ->element_exists_not('div.button.top a')
1118 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
1119 ;
1120
1121$t->post_ok('/user/login' => form => {
1122 handle_or_email => 'test',
1123 pwd => 'pass',
1124 csrf_token => $csrf
1125})
1126 ->status_is(302)
1127 ->header_is('Location' => '/')
1128 ->content_is('');
1129
1130$t->get_ok('/')
1131 ->status_is(200)
1132 ->element_exists_not('div.notify-error')
1133 ->element_exists('div.notify-success')
1134 ->text_is('div.notify-success', 'Login successful')
1135 ->element_exists_not('aside.off')
1136 ->element_exists_not('aside.active')
1137 ->element_exists('aside.settings')
1138 ;
1139
Akron66ef3b52022-11-22 14:25:15 +01001140
Akron33f5c672019-06-24 19:40:47 +02001141done_testing;
1142__END__
Akrona8efaa92022-04-09 14:45:43 +02001143
1144