blob: 3e5b78c186d8679f2df3e13f3b78dd29caf37225 [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
Akron001dcd22023-02-07 08:38:11 +0100814$t->get_ok('/')
Akrona8efaa92022-04-09 14:45:43 +0200815 ->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]")
Akron001dcd22023-02-07 08:38:11 +0100820 ;
821
822# OAuth client authorization flow - but user not logged in
823$t->get_ok(Mojo::URL->new('/settings/oauth/authorize'))
824 ->status_is(302)
825 ->header_is('location','/')
826 ;
827
828$csrf = $t->get_ok('/')
829 ->status_is(200)
830 ->element_exists('div.notify-error')
831 ->element_exists_not('div.notify-success')
832 ->text_is('div.notify-error', 'Client ID required')
833 ->element_exists("input[name=handle_or_email]")
Akrona8efaa92022-04-09 14:45:43 +0200834 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
835 ;
836
Akron408bc7c2022-04-28 15:46:43 +0200837$fake_backend_app->add_client({
838 "client_id" => 'xyz',
839 "client_name" => 'New added client',
840 "client_description" => 'This is a new client',
841 "client_url" => 'http://example.com',
842 "client_type" => 'CONFIDENTIAL'
843# "client_redirect_uri" => $redirect_uri
844});
845
Akron9d826902023-01-25 10:20:52 +0100846$fake_backend_app->add_client({
847 "client_id" => 'xyz2',
848 "client_name" => 'New added client',
849 "client_description" => 'This is a new client',
850 "client_url" => 'http://example.com',
851 "client_type" => 'CONFIDENTIAL',
852 "client_redirect_uri" => 'http://redirect.url/'
853});
854
855$fwd = $t->get_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
856 client_id => 'xyz2',
857 scope => 'search match',
858 redirect_uri => 'http://test.com/',
859}))
860 ->status_is(200)
861 ->text_is('div.notify-warn', 'redirect_uri host differs from registered host')
862 ->element_exists_not('div.notify-error')
863 ->element_exists_not('div.notify-success')
864 ->element_exists("input[name=handle_or_email]")
865 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
866 ;
867
868$fwd = $t->get_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
869 client_id => 'xyz2',
870 scope => 'search match',
871 redirect_uri => 'http://redirect.url:9000/',
872}))
873 ->status_is(200)
874 ->text_is('div.notify-warn', 'redirect_uri port differs from registered port')
875 ->element_exists_not('div.notify-error')
876 ->element_exists_not('div.notify-success')
877 ->element_exists("input[name=handle_or_email]")
878 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
879 ;
880
Akrona8efaa92022-04-09 14:45:43 +0200881$fwd = $t->get_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
882 client_id => 'xyz',
883 state => 'abcde',
884 scope => 'search match',
885 redirect_uri => 'http://test.com/',
886}))
887 ->status_is(200)
888 ->attr_is('input[name=client_id]','value','xyz')
889 ->attr_is('input[name=state]','value','abcde')
Akrona8efaa92022-04-09 14:45:43 +0200890 ->attr_like('input[name=fwd]','value',qr!test\.com!)
Akron408bc7c2022-04-28 15:46:43 +0200891 ->text_is('span.client-name','New added client')
Akrona8efaa92022-04-09 14:45:43 +0200892 ->text_is('div.intro p:nth-child(2)', 'Please log in!')
893 ->tx->res->dom->at('input[name=fwd]')->attr('value')
894 ;
895
896$fwd = $t->post_ok(Mojo::URL->new('/user/login')->query({
897 csrf_token => $csrf,
898 client_id => 'xyz',
899 state => 'abcde',
900 scope => 'search match',
901 redirect_uri => 'http://test.com/',
Akron9fa7cc52022-05-12 11:17:20 +0200902 handle_or_email => 'test',
Akrona8efaa92022-04-09 14:45:43 +0200903 pwd => 'pass',
904 fwd => $fwd
905}))
906 ->status_is(302)
907 ->header_like('location', qr!/settings/oauth/authorize!)
908 ->tx->res->headers->header('location')
909 ;
910
911$t->get_ok($fwd)
912 ->status_is(200)
913 ->attr_is('input[name=client_id]','value','xyz')
914 ->attr_is('input[name=state]','value','abcde')
Akron9d826902023-01-25 10:20:52 +0100915 ->attr_like('input[name=redirect_uri]','value', qr!^http://test\.com\/\?crto=.{3,}!)
Akrona8efaa92022-04-09 14:45:43 +0200916 ->text_is('ul#scopes li:nth-child(1)','search')
917 ->text_is('ul#scopes li:nth-child(2)','match')
Akron408bc7c2022-04-28 15:46:43 +0200918 ->text_is('span.client-name','New added client')
Akrona8efaa92022-04-09 14:45:43 +0200919 ->attr_is('a.form-button','href','http://test.com/')
920 ->attr_is('a.embedded-link', 'href', '/doc/korap/kalamar')
Akron9d826902023-01-25 10:20:52 +0100921 ->element_exists_not('div.notify-error')
922 ->element_exists_not('div.notify-warn')
923 ->element_exists_not('blockquote.warning')
Akrona8efaa92022-04-09 14:45:43 +0200924 ;
925
926$t->get_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
927 client_id => 'xyz',
928 state => 'abcde',
929 scope => 'search match',
930 redirect_uri => 'http://test.com/'
931}))
932 ->status_is(200)
933 ->attr_is('input[name=client_id]','value','xyz')
934 ->attr_is('input[name=state]','value','abcde')
Akrona8efaa92022-04-09 14:45:43 +0200935 ->text_is('ul#scopes li:nth-child(1)','search')
936 ->text_is('ul#scopes li:nth-child(2)','match')
Akron408bc7c2022-04-28 15:46:43 +0200937 ->text_is('span.client-name','New added client')
Akrona8efaa92022-04-09 14:45:43 +0200938 ->attr_is('a.form-button','href','http://test.com/')
939 ->attr_is('a.embedded-link', 'href', '/doc/korap/kalamar')
940 ;
941
942$t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
943 client_id => 'xyz',
944 state => 'abcde',
945 scope => 'search match',
946 redirect_uri => 'http://test.com/'
947}))
948 ->status_is(302)
Akron9d826902023-01-25 10:20:52 +0100949 ->header_is('location', '/settings/oauth?error_description=Bad+CSRF+token')
Akrona8efaa92022-04-09 14:45:43 +0200950 ;
951
952$fwd = $t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
953 client_id => 'xyz',
954 state => 'abcde',
955 scope => 'search match',
956 redirect_uri_server => 'http://example.com/',
957 redirect_uri => $fake_backend_app->url_for('return_uri')->to_abs,
958 csrf_token => $csrf,
959}))
960 ->status_is(302)
961 ->header_like('location', qr!/realapi/fakeclient/return!)
962 ->tx->res->headers->header('location')
963 ;
964
965$t->get_ok($fwd)
966 ->status_is(200)
967 ->content_like(qr'welcome back! \[(.+?)\]')
968 ;
969
970$t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
971 client_id => 'xyz',
Akron9ccf69a2023-01-31 14:21:37 +0100972 state => 'abcde',
973 scope => 'search match',
974 redirect_uri_server => 'http://example.com/',
975 redirect_uri => $t->app->close_redirect_to('http://wrong'),
976 csrf_token => $csrf,
977}))
978 ->status_is(302)
979 ->header_is('location', '/settings/oauth')
980 ->tx->res->headers->header('location')
981 ;
982
983$t->get_ok('/settings/oauth')
984 ->text_is('div.notify-error', 'Invalid redirect URI')
985 ;
986
987$t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
988 client_id => 'xyz',
Akrona8efaa92022-04-09 14:45:43 +0200989 state => 'fail',
990 scope => 'search match',
Akron9d826902023-01-25 10:20:52 +0100991# redirect_uri_server => 'http://example.com/',
Akrona8efaa92022-04-09 14:45:43 +0200992 redirect_uri => $fake_backend_app->url_for('return_uri')->to_abs,
993 csrf_token => $csrf,
994}))
995 ->status_is(302)
Akron9d826902023-01-25 10:20:52 +0100996 ->header_is('location', '/realapi/fakeclient/return?error_description=FAIL')
Akrona8efaa92022-04-09 14:45:43 +0200997 ;
998
Akron9f2ad342022-05-04 16:16:40 +0200999my $json_post = {
1000 name => 'Funny',
1001 type => 'PUBLIC',
Akron6b75d122022-05-12 17:39:05 +02001002 desc => 'This is my plugin application 2',
Akron9f2ad342022-05-04 16:16:40 +02001003 csrf_token => $csrf,
1004 src => 'hMMM'
1005};
1006
1007$t->post_ok('/settings/oauth/register' => form => $json_post)
1008 ->status_is(200)
1009 ->element_exists('div.notify-error')
Akron99968a92022-06-03 12:32:07 +02001010 ->text_is('div.notify-error', 'Plugin declarations need to be json files')
1011 ;
1012
1013$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')
Akron9f2ad342022-05-04 16:16:40 +02001021 ->text_is('div.notify-error', 'Plugins need to be confidential')
1022 ;
1023
1024$json_post->{type} = 'CONFIDENTIAL';
1025
Akron99968a92022-06-03 12:32:07 +02001026# This somehow gets removed in the last form send ...
Akron9f2ad342022-05-04 16:16:40 +02001027$json_post->{src} = {
1028 content => 'jjjjjj',
1029 filename => 'fun.txt'
1030};
1031
1032$t->post_ok('/settings/oauth/register' => form => $json_post)
1033 ->status_is(200)
1034 ->element_exists('div.notify-error')
1035 ->text_is('div.notify-error', 'Plugin declarations need to be json files')
1036 ;
1037
1038$json_post->{src} = {
1039 content => '{"name":"example"}',
1040 filename => 'fun.txt'
1041};
1042
1043$t->post_ok('/settings/oauth/register' => form => $json_post)
1044 ->status_is(200)
1045 ->element_exists_not('div.notify-error')
1046 ->element_exists('div.notify-success')
1047 ->text_is('div.notify-success', 'Registration successful')
1048 ;
1049
Akron6b75d122022-05-12 17:39:05 +02001050$t->get_ok('/settings/oauth/jh0gfjhjbfdsgzjghj==')
1051 ->status_is(200)
1052 ->text_is('div.notify-error', undef)
1053 ->text_is('li.client #client_source', '{"name":"example"}')
1054 ->text_is('li.client span.client-name', 'Funny')
1055 ->text_is('li.client p.client-desc', 'This is my plugin application 2')
1056 ->element_exists_not('li.client .client-redirect-uri tt')
1057 ->text_is('li.client .client-type tt', 'CONFIDENTIAL')
1058 ;
Akron9f2ad342022-05-04 16:16:40 +02001059
Akronb6b156e2022-03-31 14:57:49 +02001060
Akron66ef3b52022-11-22 14:25:15 +01001061# Retest client with super_client_file
1062my $client_file = tempfile;
1063
1064$client_file->spurt(
1065 '{"client_id":"2","client_secret":"k414m4r-s3cr3t"}'
1066);
1067
1068$t = Test::Mojo::WithRoles->new('Kalamar' => {
1069 Kalamar => {
1070 plugins => ['Auth']
1071 },
1072 'Kalamar-Auth' => {
1073 client_file => $client_file,
1074 oauth2 => 1
1075 }
1076});
1077
1078$t->app->plugin(
1079 Mount => {
1080 $mount_point =>
1081 $fixtures_path->child('mock.pl')
1082 }
1083);
1084
1085$csrf = $t->get_ok('/')
1086 ->status_is(200)
1087 ->element_exists_not('div.button.top a')
1088 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
1089 ;
1090
1091$t->post_ok('/user/login' => form => {
1092 handle_or_email => 'test',
1093 pwd => 'pass',
1094 csrf_token => $csrf
1095})
1096 ->status_is(302)
1097 ->header_is('Location' => '/')
1098 ->content_is('');
1099
1100$t->get_ok('/')
1101 ->status_is(200)
1102 ->element_exists_not('div.notify-error')
1103 ->element_exists('div.notify-success')
1104 ->text_is('div.notify-success', 'Login successful')
1105 ->element_exists_not('aside.off')
1106 ->element_exists_not('aside.active')
1107 ->element_exists('aside.settings')
1108 ;
1109
Akron53a171e2022-12-05 18:22:58 +01001110$main::ENV{KALAMAR_CLIENT_FILE} = $client_file;
1111
1112$t = Test::Mojo::WithRoles->new('Kalamar' => {
1113 Kalamar => {
1114 plugins => ['Auth']
1115 },
1116 'Kalamar-Auth' => {
1117 oauth2 => 1,
1118# client_file => $client_file,
1119 }
1120});
1121
1122$t->app->plugin(
1123 Mount => {
1124 $mount_point =>
1125 $fixtures_path->child('mock.pl')
1126 }
1127);
1128
1129$csrf = $t->get_ok('/')
1130 ->status_is(200)
1131 ->element_exists_not('div.button.top a')
1132 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
1133 ;
1134
1135$t->post_ok('/user/login' => form => {
1136 handle_or_email => 'test',
1137 pwd => 'pass',
1138 csrf_token => $csrf
1139})
1140 ->status_is(302)
1141 ->header_is('Location' => '/')
1142 ->content_is('');
1143
1144$t->get_ok('/')
1145 ->status_is(200)
1146 ->element_exists_not('div.notify-error')
1147 ->element_exists('div.notify-success')
1148 ->text_is('div.notify-success', 'Login successful')
1149 ->element_exists_not('aside.off')
1150 ->element_exists_not('aside.active')
1151 ->element_exists('aside.settings')
1152 ;
1153
Akron66ef3b52022-11-22 14:25:15 +01001154
Akron33f5c672019-06-24 19:40:47 +02001155done_testing;
1156__END__
Akrona8efaa92022-04-09 14:45:43 +02001157
1158