blob: 659fe5db15282e61023bcdacc34125878c2af421 [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
Akron33f5c672019-06-24 19:40:47 +02008#####################
9# Start Fake server #
10#####################
Akron63d963b2019-07-05 15:35:51 +020011my $mount_point = '/realapi/';
Akron33f5c672019-06-24 19:40:47 +020012$ENV{KALAMAR_API} = $mount_point;
13
Helge3d1d04d2026-01-28 18:04:39 +010014# Get the fixture path
15my $fixtures_path = path(Mojo::File->new(__FILE__)->dirname, '..', 'server');
16
17my $q = qr!(?:\"|")!;
18
Akroncdfd9d52019-07-23 11:35:00 +020019my $t = Test::Mojo::WithRoles->new('Kalamar' => {
Akron33f5c672019-06-24 19:40:47 +020020 Kalamar => {
21 plugins => ['Auth']
22 },
23 'Kalamar-Auth' => {
24 client_id => 2,
25 client_secret => 'k414m4r-s3cr3t',
Akron59992122019-10-29 11:28:45 +010026 oauth2 => 1,
27 experimental_client_registration => 1
Akron33f5c672019-06-24 19:40:47 +020028 }
29});
30
Helge3d1d04d2026-01-28 18:04:39 +010031my $api_version = $t->app->config('Kalamar')->{api_version};
32
Akron33f5c672019-06-24 19:40:47 +020033# Mount fake backend
Akron33f5c672019-06-24 19:40:47 +020034my $fake_backend = $t->app->plugin(
35 Mount => {
36 $mount_point =>
37 $fixtures_path->child('mock.pl')
38 }
39);
40# Configure fake backend
Akroncdfd9d52019-07-23 11:35:00 +020041my $fake_backend_app = $fake_backend->pattern->defaults->{app};
42
43# Set general app logger for simplicity
44$fake_backend_app->log($t->app->log);
45
46my $access_token = $fake_backend_app->get_token('access_token');
47my $refresh_token = $fake_backend_app->get_token('refresh_token');
48my $access_token_2 = $fake_backend_app->get_token('access_token_2');
49my $refresh_token_2 = $fake_backend_app->get_token('refresh_token_2');
50
51# Some routes to modify the session
52# This expires the session
53$t->app->routes->get('/x/expire')->to(
54 cb => sub {
55 my $c = shift;
56 $c->session(auth_exp => 0);
57 return $c->render(text => 'okay')
58 }
59);
60
61# This expires the session and removes the refresh token
62$t->app->routes->get('/x/expire-no-refresh')->to(
63 cb => sub {
64 my $c = shift;
65 $c->session(auth_exp => 0);
66 delete $c->session->{auth_r};
67 return $c->render(text => 'okay')
68 }
69);
70
71# This sets an invalid token
72$t->app->routes->get('/x/invalid')->to(
73 cb => sub {
74 my $c = shift;
75 $c->session(auth_exp => time + 1000);
76 $c->session(auth_r => $refresh_token_2);
77 $c->session(auth => 'Bearer inv4lid');
78 return $c->render(text => 'okay')
79 }
80);
81
82
83# This sets an invalid token
84$t->app->routes->get('/x/invalid-no-refresh')->to(
85 cb => sub {
86 my $c = shift;
87 $c->session(auth_exp => time + 1000);
88 delete $c->session->{auth_r};
89 $c->session(auth => 'Bearer inv4lid');
90 return $c->render(text => 'okay')
91 }
92);
93
94# This sets an invalid refresh token
95$t->app->routes->get('/x/expired-with-wrong-refresh')->to(
96 cb => sub {
97 my $c = shift;
98 $c->session(auth_exp => 0);
99 $c->session(auth => 'Bearer inv4lid');
100 $c->session(auth_r => 'inv4lid');
101 return $c->render(text => 'okay')
102 }
103);
104
Helge3d1d04d2026-01-28 18:04:39 +0100105$t->get_ok("/realapi/v$api_version")
Akron33f5c672019-06-24 19:40:47 +0200106 ->status_is(200)
Helge3d1d04d2026-01-28 18:04:39 +0100107 ->content_is("Fake server available: $api_version");
Akron33f5c672019-06-24 19:40:47 +0200108
109$t->get_ok('/?q=Baum')
110 ->status_is(200)
111 ->text_like('h1 span', qr/KorAP: Find .Baum./i)
112 ->text_like('#total-results', qr/\d+$/)
Akronbc6b3f22021-01-13 14:53:12 +0100113 ->content_like(qr/${q}authorized${q}:null/)
Akron33f5c672019-06-24 19:40:47 +0200114 ->element_exists_not('div.button.top a')
115 ->element_exists_not('aside.active')
Akron4d17d0f2025-03-05 20:58:44 +0100116 ->element_exists('aside.off')
Akron33f5c672019-06-24 19:40:47 +0200117 ;
118
119$t->get_ok('/')
120 ->status_is(200)
Akron9fa7cc52022-05-12 11:17:20 +0200121 ->element_exists('form[action=/user/login] input[name=handle_or_email]')
Akronbb3da4d2025-12-05 22:47:35 +0100122 ->element_exists('*[data-testid=handle-or-email]')
123 ->element_exists('*[data-testid=pwd]')
124 ->element_exists('*[data-testid=login-submit]')
125 ->element_exists_not('*[data-testid=logout]')
Uyen-Nhu Tran243fe732024-04-10 01:17:24 +0200126 ->element_exists('aside')
127 ->element_exists('aside.invisible')
Akron33f5c672019-06-24 19:40:47 +0200128 ;
129
Akronff088112021-06-15 15:26:04 +0200130$t->get_ok('/settings/oauth')
131 ->status_is(401)
132 ->text_is('p.no-results', 'Not authenticated')
Akronbb3da4d2025-12-05 22:47:35 +0100133 ->text_is('*[data-testid=error-message]','Not authenticated')
Akronff088112021-06-15 15:26:04 +0200134 ;
135
Helge278fbca2022-11-29 18:49:15 +0100136$t->get_ok('/settings/marketplace')
137 ->status_is(401)
138 ->text_is('p.no-results', 'Not authenticated')
139 ;
140
Akron3e0fdc12020-05-15 16:17:21 +0200141# Test for bug with long password
142$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200143 handle_or_email => 'test',
Akron3e0fdc12020-05-15 16:17:21 +0200144 pwd => 'kjskjhndkjndqknaskjnakjdnkjdankajdnkjdsankjdsakjdfkjahzroiuqzriudjoijdmlamdlkmdsalkmdl' })
145 ->status_is(302)
146 ->header_is('Location' => '/');
147
Akron9fa7cc52022-05-12 11:17:20 +0200148$t->post_ok('/user/login' => form => { handle_or_email => 'test', pwd => 'fail' })
Akron33f5c672019-06-24 19:40:47 +0200149 ->status_is(302)
150 ->header_is('Location' => '/');
151
152$t->get_ok('/')
153 ->status_is(200)
154 ->element_exists('div.notify-error')
155 ->text_is('div.notify-error', 'Bad CSRF token')
Akron9fa7cc52022-05-12 11:17:20 +0200156 ->element_exists('input[name=handle_or_email][value=test]')
Akron33f5c672019-06-24 19:40:47 +0200157 ->element_exists_not('div.button.top a')
158 ;
159
Akron9fa7cc52022-05-12 11:17:20 +0200160$t->post_ok('/user/login' => form => { handle_or_email => 'test', pwd => 'pass' })
Akron33f5c672019-06-24 19:40:47 +0200161 ->status_is(302)
162 ->header_is('Location' => '/');
163
164my $csrf = $t->get_ok('/')
165 ->status_is(200)
166 ->element_exists('div.notify-error')
167 ->text_is('div.notify-error', 'Bad CSRF token')
168 ->element_exists_not('div.button.top a')
169 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
170 ;
171
172$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200173 handle_or_email => 'test',
Akron33f5c672019-06-24 19:40:47 +0200174 pwd => 'ldaperr',
175 csrf_token => $csrf
176})
177 ->status_is(302)
178 ->content_is('')
179 ->header_is('Location' => '/');
180
181$csrf = $t->get_ok('/')
182 ->status_is(200)
183 ->element_exists('div.notify-error')
184 ->text_is('div.notify-error', '2022: LDAP Authentication failed due to unknown user or password!')
Akron9fa7cc52022-05-12 11:17:20 +0200185 ->element_exists('input[name=handle_or_email][value=test]')
Akron33f5c672019-06-24 19:40:47 +0200186 ->element_exists_not('div.button.top a')
Akron3b3c7af2020-05-15 16:23:55 +0200187 ->element_exists_not('div.notify-success')
Akron33f5c672019-06-24 19:40:47 +0200188 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
189 ;
190
191$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200192 handle_or_email => 'test',
Akron33f5c672019-06-24 19:40:47 +0200193 pwd => 'unknown',
194 csrf_token => $csrf
195})
196 ->status_is(302)
197 ->content_is('')
198 ->header_is('Location' => '/');
199
200$csrf = $t->get_ok('/')
201 ->status_is(200)
202 ->element_exists('div.notify-error')
Akron8bbbecf2019-07-01 18:57:30 +0200203 ->text_is('div.notify-error', '2022: LDAP Authentication failed due to unknown user or password!')
Akron9fa7cc52022-05-12 11:17:20 +0200204 ->element_exists('input[name=handle_or_email][value=test]')
Akron33f5c672019-06-24 19:40:47 +0200205 ->element_exists_not('div.button.top a')
206 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
207 ;
208
209$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200210 handle_or_email => 'test',
Akron33f5c672019-06-24 19:40:47 +0200211 pwd => 'pass',
212 csrf_token => $csrf
213})
214 ->status_is(302)
215 ->content_is('')
216 ->header_is('Location' => '/');
217
218$t->get_ok('/')
219 ->status_is(200)
220 ->element_exists_not('div.notify-error')
221 ->element_exists('div.notify-success')
222 ->text_is('div.notify-success', 'Login successful')
Akron1d09b532021-06-15 18:18:25 +0200223 ->element_exists_not('aside.off')
Akrondc0b3ab2021-06-18 11:52:43 +0200224 ->element_exists_not('aside.active')
Akron1d09b532021-06-15 18:18:25 +0200225 ->element_exists('aside.settings')
Akron33f5c672019-06-24 19:40:47 +0200226 ;
227
Akron33f5c672019-06-24 19:40:47 +0200228# Now the user is logged in and should be able to
229# search with authorization
230$t->get_ok('/?q=Baum')
231 ->status_is(200)
Akron4cefe1f2019-09-04 10:11:28 +0200232 ->session_has('/auth')
233 ->session_is('/auth', 'Bearer ' . $access_token)
234 ->session_is('/auth_r', $refresh_token)
235 ->session_is('/user', 'test')
Akron33f5c672019-06-24 19:40:47 +0200236 ->text_like('h1 span', qr/KorAP: Find .Baum./i)
237 ->text_like('#total-results', qr/\d+$/)
238 ->element_exists_not('div.notify-error')
Akronbc6b3f22021-01-13 14:53:12 +0100239 ->content_like(qr/${q}authorized${q}:${q}yes${q}/)
Uyen-Nhu Tran243fe732024-04-10 01:17:24 +0200240 ->element_exists('nav.dropdown')
Akronbb3da4d2025-12-05 22:47:35 +0100241 ->element_exists_not('*[data-testid=handle-or-email]')
242 ->element_exists('*[data-testid=logout]')
Uyen-Nhu Tran243fe732024-04-10 01:17:24 +0200243 ->element_exists('a.dropdown-item.logout[title~="test"]')
Akron33f5c672019-06-24 19:40:47 +0200244 ;
245
Akron27031aa2020-04-28 14:57:10 +0200246$t->get_ok('/?q=Paum')
247 ->status_is(200)
248 ->text_like('h1 span', qr/KorAP: Find .Paum./i)
249 ->text_is('#total-results', '')
Akronbc6b3f22021-01-13 14:53:12 +0100250 ->content_like(qr/${q}authorized${q}:${q}yes${q}/)
Akron27031aa2020-04-28 14:57:10 +0200251 ->element_exists_not('p.hint')
252 ;
253
Akroncce055c2021-07-02 12:18:03 +0200254# Query with error
255$t->get_ok('/?q=error')
256 ->status_is(400)
257 ->text_is('#notifications .notify-error','500: Internal Server Error')
258;
Akron27031aa2020-04-28 14:57:10 +0200259
Akron33f5c672019-06-24 19:40:47 +0200260# Logout
261$t->get_ok('/user/logout')
262 ->status_is(302)
Akron4cefe1f2019-09-04 10:11:28 +0200263 ->session_hasnt('/auth')
264 ->session_hasnt('/auth_r')
265 ->session_hasnt('/user')
Akron33f5c672019-06-24 19:40:47 +0200266 ->header_is('Location' => '/');
267
268$t->get_ok('/')
269 ->status_is(200)
270 ->element_exists_not('div.notify-error')
271 ->element_exists('div.notify-success')
272 ->text_is('div.notify-success', 'Logout successful')
Akron9fa7cc52022-05-12 11:17:20 +0200273 ->element_exists("input[name=handle_or_email]")
274 ->element_exists("input[name=handle_or_email][value=test]")
Akron33f5c672019-06-24 19:40:47 +0200275 ;
276
277$t->get_ok('/?q=Baum')
278 ->status_is(200)
279 ->text_like('h1 span', qr/KorAP: Find .Baum./i)
280 ->text_like('#total-results', qr/\d+$/)
Akronbc6b3f22021-01-13 14:53:12 +0100281 ->content_like(qr/${q}authorized${q}:null/)
Akron33f5c672019-06-24 19:40:47 +0200282 ;
283
Akron27031aa2020-04-28 14:57:10 +0200284$t->get_ok('/?q=Paum')
285 ->status_is(200)
286 ->text_like('h1 span', qr/KorAP: Find .Paum./i)
287 ->text_is('#total-results', '')
Akronbc6b3f22021-01-13 14:53:12 +0100288 ->content_like(qr/${q}authorized${q}:null/)
Akron27031aa2020-04-28 14:57:10 +0200289 ->text_is('p.hint', 'Maybe you need to log in first?')
290 ;
291
292
Akron33f5c672019-06-24 19:40:47 +0200293# Get redirect
294my $fwd = $t->get_ok('/?q=Baum&ql=poliqarp')
295 ->status_is(200)
296 ->element_exists_not('div.notify-error')
297 ->tx->res->dom->at('input[name=fwd]')->attr('value')
298 ;
299
300is($fwd, '/?q=Baum&ql=poliqarp', 'Redirect is valid');
301
302$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200303 handle_or_email => 'test',
Akron33f5c672019-06-24 19:40:47 +0200304 pwd => 'pass',
305 csrf_token => $csrf,
306 fwd => 'http://bad.example.com/test'
307})
308 ->status_is(302)
309 ->header_is('Location' => '/');
310
311$t->get_ok('/')
312 ->status_is(200)
313 ->element_exists('div.notify-error')
314 ->element_exists_not('div.notify-success')
315 ->text_is('div.notify-error', 'Redirect failure')
316 ;
317
318$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200319 handle_or_email => 'test',
Akron33f5c672019-06-24 19:40:47 +0200320 pwd => 'pass',
321 csrf_token => $csrf,
322 fwd => $fwd
323})
324 ->status_is(302)
325 ->header_is('Location' => '/?q=Baum&ql=poliqarp');
326
Akron8bbbecf2019-07-01 18:57:30 +0200327$t->get_ok('/?q=Baum&ql=poliqarp')
328 ->status_is(200)
329 ->element_exists_not('div.notify-error')
330 ->element_exists('div.notify-success')
331 ->text_is('div.notify-success', 'Login successful')
Akroncdfd9d52019-07-23 11:35:00 +0200332 ->session_has('/auth')
333 ->session_is('/auth', 'Bearer ' . $access_token)
334 ->session_is('/auth_r', $refresh_token)
335 ->header_isnt('X-Kalamar-Cache', 'true')
Akron8bbbecf2019-07-01 18:57:30 +0200336 ;
337
Akroncdfd9d52019-07-23 11:35:00 +0200338# Expire the session
339# (makes the token be marked as expired - though it isn't serverside)
340$t->get_ok('/x/expire')
Akron8bbbecf2019-07-01 18:57:30 +0200341 ->status_is(200)
Akroncdfd9d52019-07-23 11:35:00 +0200342 ->content_is('okay')
343 ;
344
345## It may be a problem, but the cache is still valid
346$t->get_ok('/?q=Baum')
347 ->status_is(200)
348 ->text_like('h1 span', qr/KorAP: Find .Baum./i)
349 ->text_like('#total-results', qr/\d+$/)
Akronbc6b3f22021-01-13 14:53:12 +0100350 ->content_like(qr/${q}authorized${q}:${q}yes${q}/)
Akroncdfd9d52019-07-23 11:35:00 +0200351 ->header_is('X-Kalamar-Cache', 'true')
352 ;
353
354# Query without partial cache (unfortunately) (but no total results)
Akron58c60992021-09-07 13:11:43 +0200355my $err = $t->get_ok('/?q=baum&cutoff=true')
Akroncdfd9d52019-07-23 11:35:00 +0200356 ->status_is(200)
357 ->session_is('/auth', 'Bearer ' . $access_token_2)
358 ->session_is('/auth_r', $refresh_token_2)
Akroncdfd9d52019-07-23 11:35:00 +0200359 ->text_is('title', 'KorAP: Find »baum« with Poliqarp')
360 ->element_exists('meta[name="DC.title"][content="KorAP: Find »baum« with Poliqarp"]')
361 ->element_exists('body[itemscope][itemtype="http://schema.org/SearchResultsPage"]')
Akronbc6b3f22021-01-13 14:53:12 +0100362 ->content_like(qr/${q}authorized${q}:${q}yes${q}/)
Akroncdfd9d52019-07-23 11:35:00 +0200363 ->header_isnt('X-Kalamar-Cache', 'true')
Akronbc6b3f22021-01-13 14:53:12 +0100364 ->content_like(qr!${q}cutOff${q}:true!)
Akroncdfd9d52019-07-23 11:35:00 +0200365 ->element_exists_not('#total-results')
Akron58c60992021-09-07 13:11:43 +0200366 ->tx->res->dom->at('#error')
Akroncdfd9d52019-07-23 11:35:00 +0200367 ;
Akron58c60992021-09-07 13:11:43 +0200368is(defined $err ? $err->text : '', '');
Akroncdfd9d52019-07-23 11:35:00 +0200369
370# Expire the session and remove the refresh option
371$t->get_ok('/x/expire-no-refresh')
372 ->status_is(200)
373 ->content_is('okay')
374 ;
375
376$t->app->defaults(no_cache => 1);
377
378
379$t->get_ok('/x/invalid-no-refresh')
380 ->status_is(200)
381 ->content_is('okay')
382 ;
383
384# Query without cache
385# The token is invalid and can't be refreshed!
Akron58c60992021-09-07 13:11:43 +0200386$err = $t->get_ok('/?q=baum&cutoff=true')
Akron3c390c42020-03-30 09:06:21 +0200387 ->status_is(400)
Akroncdfd9d52019-07-23 11:35:00 +0200388 ->session_hasnt('/auth')
389 ->session_hasnt('/auth_r')
Akroncdfd9d52019-07-23 11:35:00 +0200390 ->text_is('div.notify-error','Access token invalid')
391 ->text_is('title', 'KorAP: Find »baum« with Poliqarp')
392 ->element_exists('meta[name="DC.title"][content="KorAP: Find »baum« with Poliqarp"]')
393 ->element_exists('body[itemscope][itemtype="http://schema.org/SearchResultsPage"]')
Akronbc6b3f22021-01-13 14:53:12 +0100394 ->content_unlike(qr/${q}authorized${q}:${q}yes${q}/)
Akroncdfd9d52019-07-23 11:35:00 +0200395 ->header_isnt('X-Kalamar-Cache', 'true')
396 ->element_exists('p.no-results')
Akron58c60992021-09-07 13:11:43 +0200397 ->tx->res->dom->at('#error')
Akroncdfd9d52019-07-23 11:35:00 +0200398 ;
Akron58c60992021-09-07 13:11:43 +0200399is(defined $err ? $err->text : '', '');
400
Akroncdfd9d52019-07-23 11:35:00 +0200401
402$t->get_ok('/x/invalid')
403 ->status_is(200)
404 ->content_is('okay')
405 ;
406
407# Query without cache
408# The token is invalid and can't be refreshed!
Akron58c60992021-09-07 13:11:43 +0200409$err = $t->get_ok('/?q=baum&cutoff=true')
Akroncdfd9d52019-07-23 11:35:00 +0200410 ->status_is(200)
411 ->session_is('/auth', 'Bearer ' . $access_token_2)
412 ->session_is('/auth_r', $refresh_token_2)
Akroncdfd9d52019-07-23 11:35:00 +0200413 ->element_exists_not('div.notify-error','Access token invalid')
414 ->text_is('title', 'KorAP: Find »baum« with Poliqarp')
415 ->element_exists('meta[name="DC.title"][content="KorAP: Find »baum« with Poliqarp"]')
416 ->element_exists('body[itemscope][itemtype="http://schema.org/SearchResultsPage"]')
Akronbc6b3f22021-01-13 14:53:12 +0100417 ->content_like(qr/${q}authorized${q}:${q}yes${q}/)
Akroncdfd9d52019-07-23 11:35:00 +0200418 ->header_isnt('X-Kalamar-Cache', 'true')
419 ->element_exists_not('p.no-results')
Akron58c60992021-09-07 13:11:43 +0200420 ->tx->res->dom->at('#error')
Akron8bbbecf2019-07-01 18:57:30 +0200421 ;
Akron58c60992021-09-07 13:11:43 +0200422is(defined $err ? $err->text : '', '');
Akron8bbbecf2019-07-01 18:57:30 +0200423
Akron33f5c672019-06-24 19:40:47 +0200424
Akroncdfd9d52019-07-23 11:35:00 +0200425$t->get_ok('/x/expired-with-wrong-refresh')
426 ->status_is(200)
427 ->content_is('okay')
428 ;
Akron4796e002019-07-05 10:13:15 +0200429
Akron4796e002019-07-05 10:13:15 +0200430
Akroncdfd9d52019-07-23 11:35:00 +0200431# The token is invalid and can't be refreshed!
Akron58c60992021-09-07 13:11:43 +0200432my $dom = $t->get_ok('/?q=baum&cutoff=true')
Akron3c390c42020-03-30 09:06:21 +0200433 ->status_is(400)
Akroncdfd9d52019-07-23 11:35:00 +0200434 ->session_hasnt('/auth')
435 ->session_hasnt('/auth_r')
Akroncdfd9d52019-07-23 11:35:00 +0200436 ->text_is('div.notify-error','Refresh token is expired')
437 ->text_is('title', 'KorAP: Find »baum« with Poliqarp')
Akronbc6b3f22021-01-13 14:53:12 +0100438 ->content_unlike(qr/${q}authorized${q}:${q}yes${q}/)
Akroncdfd9d52019-07-23 11:35:00 +0200439 ->element_exists('p.no-results')
Akron58c60992021-09-07 13:11:43 +0200440 ->tx->res->dom;
441
442$csrf = $dom->at('input[name="csrf_token"]')
Akron59992122019-10-29 11:28:45 +0100443 ->attr('value')
Akroncdfd9d52019-07-23 11:35:00 +0200444 ;
Akron4796e002019-07-05 10:13:15 +0200445
Akron58c60992021-09-07 13:11:43 +0200446$err = $dom->at('#error');
447is(defined $err ? $err->text : '', '');
448
449
Akron6a228db2021-10-14 15:57:00 +0200450# This should fail
451my $wide_char_login = "\x{61}\x{E5}\x{61}"; # "\x{443}\x{434}";
Akron59992122019-10-29 11:28:45 +0100452$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200453 handle_or_email => $wide_char_login,
Akron6a228db2021-10-14 15:57:00 +0200454 pwd => 'pass',
455 csrf_token => $csrf,
456 fwd => $fwd
457})
458 ->status_is(302)
459 ->header_is('Location' => '/');
460
461$t->get_ok('/')
462 ->status_is(200)
463 ->element_exists('div.notify-error')
464 ->text_is('div.notify-error', 'Invalid character in request')
Akron9fa7cc52022-05-12 11:17:20 +0200465 ->element_exists('input[name=handle_or_email]:not([value])')
Akron6a228db2021-10-14 15:57:00 +0200466 ->element_exists_not('div.button.top a')
467 ;
468
469# Login:
470# UTF8 request
471my $username = b('täst')->encode;
472$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200473 handle_or_email => $username,
Akron59992122019-10-29 11:28:45 +0100474 pwd => 'pass',
475 csrf_token => $csrf
476})
477 ->status_is(302)
478 ->content_is('')
479 ->header_is('Location' => '/');
480
481$t->get_ok('/')
482 ->status_is(200)
Akron78e0b6f2023-04-12 12:50:29 +0200483 ->content_type_is('text/html;charset=UTF-8')
Akron59992122019-10-29 11:28:45 +0100484 ->element_exists_not('div.notify-error')
485 ->element_exists('div.notify-success')
486 ->text_is('div.notify-success', 'Login successful')
Akron78e0b6f2023-04-12 12:50:29 +0200487 # Weird error in certain environments otherwise
488 ->attr_like('a.logout', 'title', qr!^Logout: t.+st$!)
Akron1d09b532021-06-15 18:18:25 +0200489 ->element_exists_not('aside.off')
Akrondc0b3ab2021-06-18 11:52:43 +0200490 ->element_exists_not('aside.active')
Akron1d09b532021-06-15 18:18:25 +0200491 ->element_exists('aside.settings')
Akronb0a7a0f2025-02-27 09:51:06 +0100492 ->text_is('nav.dropdown a:first-child span','API tokens')
Akron59992122019-10-29 11:28:45 +0100493 ;
494
Uyen-Nhu Tran94f93b02024-11-20 20:17:57 +0100495
Akron59992122019-10-29 11:28:45 +0100496$t->get_ok('/settings/oauth')
497 ->text_is('form.form-table legend', 'Register new client application')
498 ->attr_is('form.oauth-register','action', '/settings/oauth/register')
Akron9f2ad342022-05-04 16:16:40 +0200499 ->text_is('label[for=name]','Name of the client application')
500 ->text_is('label[for=type]','Type of the client application')
501 ->text_is('label[for=desc]','Short description')
Helge9f9d4852024-12-09 16:06:34 +0100502 ->text_like('label[for=url]'=> '/Homepage/')
503 ->element_exists('label[for=url] > span.field-required')
Akron9f2ad342022-05-04 16:16:40 +0200504 ->text_is('label[for=redirect_uri]','Redirect URI')
505 ->text_is('label[for=src]','Declaration of the plugin (*.json file)')
Akron1a9d5be2020-03-19 17:28:33 +0100506 ->element_exists('ul.client-list')
507 ->element_exists_not('ul.client-list > li')
Akronad011bb2021-06-10 12:16:36 +0200508 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
509 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
510 ->header_is('Pragma','no-cache')
Akron59992122019-10-29 11:28:45 +0100511 ;
Akronad011bb2021-06-10 12:16:36 +0200512
Akron83224302026-01-21 12:05:51 +0100513
514my $log_output = '';
515open my $log_fh, '>', \$log_output or die $!;
516$t->app->log->handle($log_fh);
517$t->app->log->level('warn');
Helge278fbca2022-11-29 18:49:15 +0100518
519$t->get_ok('/settings/marketplace')
520 ->status_is(200)
521 ->text_is('html head title' => 'Marketplace')
Helgedb720ea2023-03-20 09:39:36 +0100522 ->element_exists_not('ul.plugin_list')
523 ->element_exists_not('ul.plugin_in-list')
Helge278fbca2022-11-29 18:49:15 +0100524 ;
525
Akron83224302026-01-21 12:05:51 +0100526is($log_output, '', 'Check log is fine');
Helgedb720ea2023-03-20 09:39:36 +0100527
Akron59992122019-10-29 11:28:45 +0100528$csrf = $t->post_ok('/settings/oauth/register' => form => {
529 name => 'MyApp',
530 type => 'PUBLIC',
Akron6b75d122022-05-12 17:39:05 +0200531 desc => 'This is my plugin application'
Akron59992122019-10-29 11:28:45 +0100532})
533 ->text_is('div.notify-error', 'Bad CSRF token')
534 ->tx->res->dom->at('input[name="csrf_token"]')
535 ->attr('value')
536 ;
537
538$t->post_ok('/settings/oauth/register' => form => {
539 name => 'MyApp',
Akron99968a92022-06-03 12:32:07 +0200540 type => 'PUBLIC',
Akron6b75d122022-05-12 17:39:05 +0200541 desc => 'This is my plugin application',
Akron9f2ad342022-05-04 16:16:40 +0200542 csrf_token => $csrf,
543 src => {
544 filename => '',
545 content => ''
546 }
Akron59992122019-10-29 11:28:45 +0100547})
548 ->status_is(200)
549 ->element_exists('div.notify-success')
550 ->text_is('legend', 'Client Credentials')
551 ->text_is('label[for=client_id]', 'ID of the client application')
552 ->element_exists('input[name=client_id][readonly][value]')
Akron99968a92022-06-03 12:32:07 +0200553 ->element_exists_not('input[name=client_secret][readonly][value]')
Akronad011bb2021-06-10 12:16:36 +0200554 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
555 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
556 ->header_is('Pragma','no-cache')
Akron59992122019-10-29 11:28:45 +0100557 ;
Akron4796e002019-07-05 10:13:15 +0200558
Akron58c60992021-09-07 13:11:43 +0200559my $anchor = $t->get_ok('/settings/oauth')
Akronc1aaf932021-06-09 12:19:15 +0200560 ->text_is('.form-table legend', 'Register new client application')
561 ->attr_is('.oauth-register','action', '/settings/oauth/register')
Akron17de86e2020-04-16 16:03:40 +0200562 ->text_is('ul.client-list > li > span.client-name a', 'MyApp')
Akron6b75d122022-05-12 17:39:05 +0200563 ->text_is('ul.client-list > li > p.client-desc', 'This is my plugin application')
Akronad011bb2021-06-10 12:16:36 +0200564 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
565 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
566 ->header_is('Pragma','no-cache')
Akronb6b156e2022-03-31 14:57:49 +0200567 ->tx->res->dom->at('ul.client-list > li > p.client-url a')
Akron17de86e2020-04-16 16:03:40 +0200568 ;
Akron58c60992021-09-07 13:11:43 +0200569is(defined $anchor ? $anchor->text : '', '');
Akron17de86e2020-04-16 16:03:40 +0200570
Akron041ca4d2021-06-10 11:52:51 +0200571$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akron17de86e2020-04-16 16:03:40 +0200572 ->status_is(200)
Akronc1aaf932021-06-09 12:19:15 +0200573 ->text_is('ul.client-list > li.client > span.client-name', 'MyApp')
Akron6b75d122022-05-12 17:39:05 +0200574 ->text_is('ul.client-list > li.client > p.client-desc', 'This is my plugin application')
Akron17de86e2020-04-16 16:03:40 +0200575 ->text_is('a.client-unregister', 'Unregister')
Akron041ca4d2021-06-10 11:52:51 +0200576 ->attr_is('a.client-unregister', 'href', '/settings/oauth/fCBbQkA2NDA3MzM1Yw==/unregister?name=MyApp')
Akron1a9d5be2020-03-19 17:28:33 +0100577 ;
578
Akron041ca4d2021-06-10 11:52:51 +0200579$csrf = $t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/unregister?name=MyApp')
Akron1a9d5be2020-03-19 17:28:33 +0100580 ->content_like(qr!Do you really want to unregister \<span class="client-name"\>MyApp\<\/span\>?!)
Akronc1aaf932021-06-09 12:19:15 +0200581 ->attr_is('.form-table input[name=client-name]', 'value', 'MyApp')
Akronad011bb2021-06-10 12:16:36 +0200582 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
583 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
584 ->header_is('Pragma','no-cache')
Akron1a9d5be2020-03-19 17:28:33 +0100585 ->tx->res->dom->at('input[name="csrf_token"]')
586 ->attr('value')
587 ;
588
Akron041ca4d2021-06-10 11:52:51 +0200589$t->post_ok('/settings/oauth/xxxx==/unregister' => form => {
Akron1a9d5be2020-03-19 17:28:33 +0100590 'client-name' => 'MyApp',
Akron1a9d5be2020-03-19 17:28:33 +0100591 'csrf_token' => $csrf
592})->status_is(302)
593 ->content_is('')
594 ->header_is('Location' => '/settings/oauth')
595 ;
596
597$t->get_ok('/settings/oauth')
Akronc1aaf932021-06-09 12:19:15 +0200598 ->text_is('.form-table legend', 'Register new client application')
599 ->attr_is('.oauth-register','action', '/settings/oauth/register')
Akron1a9d5be2020-03-19 17:28:33 +0100600 ->element_exists('ul.client-list > li')
601 ->text_is('div.notify', 'Unknown client with xxxx==.')
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')
Akron1a9d5be2020-03-19 17:28:33 +0100605 ;
606
Akron041ca4d2021-06-10 11:52:51 +0200607$t->post_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/unregister' => form => {
Akron1a9d5be2020-03-19 17:28:33 +0100608 'client-name' => 'MyApp',
Akron1a9d5be2020-03-19 17:28:33 +0100609 'csrf_token' => $csrf
610})->status_is(302)
611 ->content_is('')
612 ->header_is('Location' => '/settings/oauth')
613 ;
614
615$t->get_ok('/settings/oauth')
Akronc1aaf932021-06-09 12:19:15 +0200616 ->text_is('.form-table legend', 'Register new client application')
617 ->attr_is('.oauth-register','action', '/settings/oauth/register')
Akron1a9d5be2020-03-19 17:28:33 +0100618 ->element_exists_not('ul.client-list > li')
619 ->text_is('div.notify-success', 'Successfully deleted MyApp')
Akronad011bb2021-06-10 12:16:36 +0200620 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
621 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
622 ->header_is('Pragma','no-cache')
Akron1a9d5be2020-03-19 17:28:33 +0100623 ;
624
Akron83209f72021-01-29 17:54:15 +0100625$t->post_ok('/settings/oauth/register' => form => {
626 name => 'MyApp2',
627 type => 'PUBLIC',
Akron6b75d122022-05-12 17:39:05 +0200628 desc => 'This is my plugin application',
Akron83209f72021-01-29 17:54:15 +0100629 csrf_token => $csrf
630})->status_is(200)
631 ->element_exists('div.notify-success')
632 ->text_is('legend', 'Client Credentials')
633 ->text_is('label[for=client_id]', 'ID of the client application')
634 ->element_exists('input[name=client_id][readonly][value]')
635 ->element_exists_not('input[name=client_secret][readonly][value]')
Akronad011bb2021-06-10 12:16:36 +0200636 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
637 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
638 ->header_is('Pragma','no-cache')
Akronb6b156e2022-03-31 14:57:49 +0200639 ->element_exists('.client-issue-token')
Akron83209f72021-01-29 17:54:15 +0100640 ;
641
Akron041ca4d2021-06-10 11:52:51 +0200642$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akron83209f72021-01-29 17:54:15 +0100643 ->text_is('.client-name', 'MyApp2')
Akron6b75d122022-05-12 17:39:05 +0200644 ->text_is('.client-desc', 'This is my plugin application')
Akrone997bb52021-06-11 16:44:06 +0200645 ->text_is('.client-issue-token', 'Issue new token')
Akron041ca4d2021-06-10 11:52:51 +0200646 ->attr_is('.client-issue-token', 'href', '/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token?name=MyApp2')
Akronad011bb2021-06-10 12:16:36 +0200647 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
648 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
649 ->header_is('Pragma','no-cache')
Akrone997bb52021-06-11 16:44:06 +0200650 ->text_is('ul.token-list label[for=token]', 'Access Token')
651 ->text_is('p[name=created]', 'Created at ')
652 ->text_is('p[name=expires]', 'Expires in 31533851 seconds.')
Akronb6b156e2022-03-31 14:57:49 +0200653 ->element_exists('.client-issue-token')
Akron83209f72021-01-29 17:54:15 +0100654 ;
655
Akron041ca4d2021-06-10 11:52:51 +0200656$csrf = $t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token?name=MyApp2')
Akron83209f72021-01-29 17:54:15 +0100657 ->status_is(200)
Akron041ca4d2021-06-10 11:52:51 +0200658 ->attr_is('#issue-token','action', '/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token')
Akron83209f72021-01-29 17:54:15 +0100659 ->attr_is('input[name=client-id]', 'value', 'fCBbQkA2NDA3MzM1Yw==')
660 ->attr_is('input[name=name]', 'value', 'MyApp2')
Akronad011bb2021-06-10 12:16:36 +0200661 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
662 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
663 ->header_is('Pragma','no-cache')
Akrone997bb52021-06-11 16:44:06 +0200664 ->text_is('a.button-abort', 'Abort')
665 ->attr_is('#issue-token input[type=submit]', 'value', 'Issue new token')
666 ->content_like(qr!Issue a new token for!)
Akron83209f72021-01-29 17:54:15 +0100667 ->tx->res->dom->at('input[name="csrf_token"]')
668 ->attr('value')
669 ;
670
Akron041ca4d2021-06-10 11:52:51 +0200671$t->post_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token' => form => {
Akron83209f72021-01-29 17:54:15 +0100672 csrf_token => $csrf,
673 name => 'MyApp2',
674 'client-id' => 'fCBbQkA2NDA3MzM1Yw=='
675})
Akronbc94a9c2021-04-15 00:07:35 +0200676 ->status_is(302)
Akron041ca4d2021-06-10 11:52:51 +0200677 ->header_is('Location','/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronbc94a9c2021-04-15 00:07:35 +0200678 ;
679
Akron041ca4d2021-06-10 11:52:51 +0200680$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
681 ->status_is(200)
Akronbc94a9c2021-04-15 00:07:35 +0200682 ->text_is('div.notify-success', 'New access token created')
Akronad011bb2021-06-10 12:16:36 +0200683 ->status_is(200)
684 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
685 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
686 ->header_is('Pragma','no-cache')
Akron83209f72021-01-29 17:54:15 +0100687 ;
688
Akrone3daaeb2023-05-08 09:44:18 +0200689$t->post_ok('/settings/oauth/307/token' => form => {
690 csrf_token => $csrf,
691 name => 'MyApp2',
692})
693 ->status_is(302)
694 ->header_is('Location','/settings/oauth/307')
695 ;
696
697$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
698 ->status_is(200)
699 ->text_is('div.notify-success', 'New access token created')
700 ->status_is(200)
701 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
702 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
703 ->header_is('Pragma','no-cache')
704 ;
705
706
Akron041ca4d2021-06-10 11:52:51 +0200707$csrf = $t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200708 ->status_is(200)
Akron041ca4d2021-06-10 11:52:51 +0200709 ->attr_is('form.token-revoke', 'action', '/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token/revoke')
Akronc1aaf932021-06-09 12:19:15 +0200710 ->attr_is('form.token-revoke input[name=token]', 'value', 'jhkhkjhk_hjgjsfz67i')
711 ->attr_is('form.token-revoke input[name=name]', 'value', 'MyApp2')
712 ->tx->res->dom->at('input[name="csrf_token"]')
713 ->attr('value')
714 ;
715
Akron041ca4d2021-06-10 11:52:51 +0200716$t->post_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token/revoke' => form => {
Akronc1aaf932021-06-09 12:19:15 +0200717 csrf_token => $csrf,
718 name => 'MyApp2',
719 token => 'jhkhkjhk_hjgjsfz67i'
720})
721 ->status_is(200)
Akron041ca4d2021-06-10 11:52:51 +0200722 ->attr_is('form#revoke-token','action','/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token?_method=DELETE')
Akronc1aaf932021-06-09 12:19:15 +0200723 ->attr_is('form#revoke-token','method','POST')
724 ->attr_is('form#revoke-token input[name=token]','value','jhkhkjhk_hjgjsfz67i')
Akrone997bb52021-06-11 16:44:06 +0200725 ->text_is('a.button-abort', 'Abort')
726 ->attr_is('#revoke-token input[type=submit]', 'value', 'Revoke')
727 ->content_like(qr!Revoke a token for!)
Akronc1aaf932021-06-09 12:19:15 +0200728;
729
730
731# CSRF missing
Akron041ca4d2021-06-10 11:52:51 +0200732$t->post_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token?_method=DELETE' => form => {
Akronc1aaf932021-06-09 12:19:15 +0200733 name => 'MyApp2',
734 token => 'jhkhkjhk_hjgjsfz67i'
735})->status_is(302)
Akron041ca4d2021-06-10 11:52:51 +0200736 ->header_is('Location','/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200737 ;
738
Akron041ca4d2021-06-10 11:52:51 +0200739$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200740 ->element_exists_not('div.notify-success')
741 ->text_is('div.notify-error', 'Bad CSRF token')
742 ;
743
744# Token missing
Akron041ca4d2021-06-10 11:52:51 +0200745$t->post_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token?_method=DELETE' => form => {
Akronc1aaf932021-06-09 12:19:15 +0200746 name => 'MyApp2',
747 csrf_token => $csrf,
748})->status_is(302)
Akron041ca4d2021-06-10 11:52:51 +0200749 ->header_is('Location','/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200750 ;
751
Akron041ca4d2021-06-10 11:52:51 +0200752$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200753 ->element_exists_not('div.notify-success')
754 ->text_is('div.notify-error', 'Some fields are invalid')
755 ;
756
Akron041ca4d2021-06-10 11:52:51 +0200757$t->post_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token?_method=DELETE' => form => {
Akronc1aaf932021-06-09 12:19:15 +0200758 name => 'MyApp2',
759 csrf_token => $csrf,
760 token => 'jhkhkjhk_hjgjsfz67i'
761})->status_is(302)
Akron041ca4d2021-06-10 11:52:51 +0200762 ->header_is('Location','/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200763 ;
764
Akron041ca4d2021-06-10 11:52:51 +0200765$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200766 ->element_exists_not('div.notify-error')
767 ->text_is('div.notify-success', 'Token was revoked successfully')
768 ;
Akron83209f72021-01-29 17:54:15 +0100769
Akronb6b156e2022-03-31 14:57:49 +0200770$t->app->routes->get('/x/redirect-target')->to(
771 cb => sub {
772 my $c = shift;
773 return $c->render(text => 'redirected');
774 }
775);
776
777$csrf = $t->post_ok('/settings/oauth/register' => form => {
778 name => 'MyConfApp',
779 type => 'CONFIDENTIAL',
Akron6b75d122022-05-12 17:39:05 +0200780 desc => 'This is my plugin application',
Akronb6b156e2022-03-31 14:57:49 +0200781})
782 ->text_is('div.notify-error', 'Bad CSRF token')
783 ->tx->res->dom->at('input[name="csrf_token"]')
784 ->attr('value')
785 ;
786
787$t->post_ok('/settings/oauth/register' => form => {
788 name => 'MyConfApp',
789 type => 'CONFIDENTIAL',
790 desc => 'This is my confidential application',
791 csrf_token => $csrf,
792 redirect_uri => 'http://localhost/redirect-target'
793})
794 ->text_is('div.notify-error', undef)
795 ->text_is('li.client span.client-name', 'MyConfApp')
796 ->text_is('li.client p.client-desc', 'This is my confidential application')
797 ->text_is('li.client .client-redirect-uri tt', 'http://localhost/redirect-target')
798 ->text_is('li.client .client-type tt', 'CONFIDENTIAL')
799 ->element_exists_not('.client-issue-token')
800 ;
801
802$t->post_ok('/settings/oauth/register' => form => {
803 name => 'MyConfApp2',
804 type => 'CONFIDENTIAL',
805 desc => 'This is my second confidential application',
806 csrf_token => $csrf,
807 redirect_uri => 'http://localhost/FAIL'
808})
809 ->status_is(302)
810 ->header_is('location','/settings/oauth/')
811 ;
812
813$t->get_ok('/settings/oauth/')
814 ->text_is('div.notify-error', 'invalid_request: http://localhost/FAIL is invalid.')
815 ;
816
Akrona8efaa92022-04-09 14:45:43 +0200817# OAuth client authorization flow
818$t->get_ok(Mojo::URL->new('/settings/oauth/authorize'))
819 ->status_is(302)
Akron5ea0f5d2023-01-20 11:51:43 +0100820 ->header_is('location','/settings/oauth')
821 ;
822
823$t->get_ok('/settings/oauth/')
Akron2c142ab2023-01-30 13:21:57 +0100824 ->text_is('div.notify-error', 'Client ID required')
825 ;
826
827$t->get_ok(Mojo::URL->new('/settings/oauth/authorize?client_id=xyz'))
828 ->status_is(302)
829 ->header_is('location','/settings/oauth')
830 ;
831
832$t->get_ok('/settings/oauth/')
833 ->text_is('div.notify-error', 'Scope required')
Akrona8efaa92022-04-09 14:45:43 +0200834 ;
835
Akrondb1f4672023-01-24 12:05:07 +0100836# OAuth client authorization flow
837$t->get_ok(Mojo::URL->new('/settings/oauth/authorize?client_id=abc'))
838 ->status_is(302)
839 ->header_is('location','/settings/oauth')
840 ;
841
842$t->get_ok('/settings/oauth/')
Akron2c142ab2023-01-30 13:21:57 +0100843 ->text_is('div.notify-error', 'Scope required')
844 ;
845
846# OAuth client authorization flow
847$t->get_ok(Mojo::URL->new('/settings/oauth/authorize?client_id=abc&scope=match'))
848 ->status_is(302)
849 ->header_is('location','/settings/oauth')
850 ;
851
852$t->get_ok('/settings/oauth/')
Akrondb1f4672023-01-24 12:05:07 +0100853 ->text_is('div.notify-error', 'Unknown client with abc.')
854 ;
855
Akrona8efaa92022-04-09 14:45:43 +0200856# Logout
857$t->get_ok('/x/expired-with-wrong-refresh');
858
859$t->get_ok('/user/logout')
860 ->status_is(302)
861 ->session_hasnt('/auth')
862 ->session_hasnt('/auth_r')
863 ->session_hasnt('/user')
864 ->header_is('Location' => '/');
865
Akron001dcd22023-02-07 08:38:11 +0100866$t->get_ok('/')
Akrona8efaa92022-04-09 14:45:43 +0200867 ->status_is(200)
868 ->element_exists_not('div.notify-error')
869 ->element_exists('div.notify-success')
870 ->text_is('div.notify-success', 'Logout successful')
Akron9fa7cc52022-05-12 11:17:20 +0200871 ->element_exists("input[name=handle_or_email]")
Akron001dcd22023-02-07 08:38:11 +0100872 ;
873
874# OAuth client authorization flow - but user not logged in
875$t->get_ok(Mojo::URL->new('/settings/oauth/authorize'))
876 ->status_is(302)
877 ->header_is('location','/')
878 ;
879
880$csrf = $t->get_ok('/')
881 ->status_is(200)
882 ->element_exists('div.notify-error')
883 ->element_exists_not('div.notify-success')
884 ->text_is('div.notify-error', 'Client ID required')
885 ->element_exists("input[name=handle_or_email]")
Akrona8efaa92022-04-09 14:45:43 +0200886 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
887 ;
888
Akron408bc7c2022-04-28 15:46:43 +0200889$fake_backend_app->add_client({
890 "client_id" => 'xyz',
891 "client_name" => 'New added client',
892 "client_description" => 'This is a new client',
893 "client_url" => 'http://example.com',
894 "client_type" => 'CONFIDENTIAL'
895# "client_redirect_uri" => $redirect_uri
896});
897
Akron0cbcc072024-10-08 14:04:42 +0200898$fake_backend_app->add_client({
899 "client_id" => 'xyz-public',
900 "client_name" => 'New added public client',
901 "client_description" => 'This is a new public client',
902 "client_url" => 'http://example.com',
903 "client_type" => 'PUBLIC'
904# "client_redirect_uri" => $redirect_uri
905});
906
Helge278fbca2022-11-29 18:49:15 +0100907
Akron9d826902023-01-25 10:20:52 +0100908$fake_backend_app->add_client({
909 "client_id" => 'xyz2',
910 "client_name" => 'New added client',
911 "client_description" => 'This is a new client',
912 "client_url" => 'http://example.com',
913 "client_type" => 'CONFIDENTIAL',
914 "client_redirect_uri" => 'http://redirect.url/'
915});
916
Akroneb39cf32023-04-03 14:40:48 +0200917$t->get_ok('/settings/oauth/xyz2')
918 ->status_is(200)
919 ->text_is('li.client span.client-name','New added client')
920 ->attr_is('li.client p.client-url a','href','http://example.com')
921 ->attr_is('li.client input[name=client_id]','value','xyz2')
922 ->element_exists('li.client p.client-type-confidential')
923 ->text_is('li.client p.client-redirect-uri tt','http://redirect.url/')
924 ;
925
Akron9d826902023-01-25 10:20:52 +0100926$fwd = $t->get_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
927 client_id => 'xyz2',
928 scope => 'search match',
929 redirect_uri => 'http://test.com/',
930}))
931 ->status_is(200)
932 ->text_is('div.notify-warn', 'redirect_uri host differs from registered host')
933 ->element_exists_not('div.notify-error')
934 ->element_exists_not('div.notify-success')
935 ->element_exists("input[name=handle_or_email]")
936 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
937 ;
938
939$fwd = $t->get_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
940 client_id => 'xyz2',
941 scope => 'search match',
942 redirect_uri => 'http://redirect.url:9000/',
943}))
944 ->status_is(200)
945 ->text_is('div.notify-warn', 'redirect_uri port differs from registered port')
946 ->element_exists_not('div.notify-error')
947 ->element_exists_not('div.notify-success')
948 ->element_exists("input[name=handle_or_email]")
949 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
950 ;
951
Helge278fbca2022-11-29 18:49:15 +0100952
953$fake_backend_app->add_plugin({
954"source" => {"key1" => 'wert1', "key2" => 'wert2'},
Helge278fbca2022-11-29 18:49:15 +0100955"client_id" => '52abc',
Helgedb720ea2023-03-20 09:39:36 +0100956"permitted" => 'true',
Helge278fbca2022-11-29 18:49:15 +0100957"client_name" => 'Plugin 1',
958"client_type" => 'CONFIDENTIAL',
Helgedb720ea2023-03-20 09:39:36 +0100959"client_description" => 'Description Plugin 1',
960"client_url" => 'http://example.client.de',
961"registration_date" => '2022-05-31T14:30:09+02:00[Europe/Berlin]',
Helge216a4822024-06-17 12:02:34 +0200962"registered_by" => 'testuser',
963"refresh_token_expiry" => '7776000',
Helge278fbca2022-11-29 18:49:15 +0100964});
965
966
Akrona8efaa92022-04-09 14:45:43 +0200967$fwd = $t->get_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
968 client_id => 'xyz',
969 state => 'abcde',
970 scope => 'search match',
971 redirect_uri => 'http://test.com/',
972}))
973 ->status_is(200)
974 ->attr_is('input[name=client_id]','value','xyz')
975 ->attr_is('input[name=state]','value','abcde')
Akrona8efaa92022-04-09 14:45:43 +0200976 ->attr_like('input[name=fwd]','value',qr!test\.com!)
Akron408bc7c2022-04-28 15:46:43 +0200977 ->text_is('span.client-name','New added client')
Akrona8efaa92022-04-09 14:45:43 +0200978 ->text_is('div.intro p:nth-child(2)', 'Please log in!')
979 ->tx->res->dom->at('input[name=fwd]')->attr('value')
980 ;
981
982$fwd = $t->post_ok(Mojo::URL->new('/user/login')->query({
983 csrf_token => $csrf,
984 client_id => 'xyz',
985 state => 'abcde',
986 scope => 'search match',
987 redirect_uri => 'http://test.com/',
Akron9fa7cc52022-05-12 11:17:20 +0200988 handle_or_email => 'test',
Akrona8efaa92022-04-09 14:45:43 +0200989 pwd => 'pass',
990 fwd => $fwd
991}))
992 ->status_is(302)
993 ->header_like('location', qr!/settings/oauth/authorize!)
994 ->tx->res->headers->header('location')
995 ;
996
997$t->get_ok($fwd)
998 ->status_is(200)
999 ->attr_is('input[name=client_id]','value','xyz')
1000 ->attr_is('input[name=state]','value','abcde')
Akron9d826902023-01-25 10:20:52 +01001001 ->attr_like('input[name=redirect_uri]','value', qr!^http://test\.com\/\?crto=.{3,}!)
Akrona8efaa92022-04-09 14:45:43 +02001002 ->text_is('ul#scopes li:nth-child(1)','search')
1003 ->text_is('ul#scopes li:nth-child(2)','match')
Akron408bc7c2022-04-28 15:46:43 +02001004 ->text_is('span.client-name','New added client')
Akrona8efaa92022-04-09 14:45:43 +02001005 ->attr_is('a.form-button','href','http://test.com/')
1006 ->attr_is('a.embedded-link', 'href', '/doc/korap/kalamar')
Akron9d826902023-01-25 10:20:52 +01001007 ->element_exists_not('div.notify-error')
1008 ->element_exists_not('div.notify-warn')
1009 ->element_exists_not('blockquote.warning')
Akron0cbcc072024-10-08 14:04:42 +02001010 ->text_is('h2 + p', ' wants to have access')
Akrona8efaa92022-04-09 14:45:43 +02001011 ;
1012
Akron0cbcc072024-10-08 14:04:42 +02001013$fwd = $t->get_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
1014 client_id => 'xyz-public',
1015 state => 'abcde',
1016 scope => 'search match',
1017 redirect_uri => 'http://test.com/',
1018}))
1019 ->status_is(200)
1020 ->attr_is('input[name=client_id]','value','xyz-public')
1021 ->attr_is('input[name=state]','value','abcde')
1022 ->attr_like('input[name=redirect_uri]','value', qr!^http://test\.com\/\?crto=.{3,}!)
1023 ->text_is('ul#scopes li:nth-child(1)','search')
1024 ->text_is('ul#scopes li:nth-child(2)','match')
1025 ->text_is('span.client-name','New added public client')
1026 ->attr_is('a.form-button','href','http://test.com/')
1027 ->attr_is('a.embedded-link', 'href', '/doc/korap/kalamar')
1028 ->element_exists_not('div.notify-error')
1029 ->element_exists_not('div.notify-warn')
1030 ->text_is('blockquote.warning','Warning - this is a public client!')
1031 ->text_is('h2 + p', ' wants to have access')
1032 ;
1033
1034
Helge278fbca2022-11-29 18:49:15 +01001035$t->get_ok('/settings/marketplace')
1036 ->status_is(200)
1037 ->text_is('html head title' => 'Marketplace')
1038 ->element_exists('ul.plugin-list')
1039 ->element_exists('ul.plugin-list > li')
Helge278fbca2022-11-29 18:49:15 +01001040 ->text_is('span.client-name','Plugin 1')
1041 ->text_is('p.plugin-desc','Description Plugin 1')
Helge216a4822024-06-17 12:02:34 +02001042 ->text_is('p.registration_date', 'Date of registration: 2022-05-31T14:30:09+02:00[Europe/Berlin]')
1043 ->text_is('p.registered_by', 'Registered by: testuser')
Helge278fbca2022-11-29 18:49:15 +01001044 ;
1045
Helgedb720ea2023-03-20 09:39:36 +01001046
1047
Helge278fbca2022-11-29 18:49:15 +01001048$fake_backend_app->add_plugin({
1049"source" => {"one" => '1', "two" => '2'},
1050"permitted" => 'false',
1051"client_id" => '53abc',
1052"client_name" => 'Plugin 2',
1053"client_type" => 'CONFIDENTIAL',
1054"client_description" =>'Description Plugin 2'
1055});
1056
Helge278fbca2022-11-29 18:49:15 +01001057$t->get_ok('/settings/marketplace')
1058 ->status_is(200)
1059 ->element_exists('ul.plugin-list')
1060 ->element_exists('ul.plugin-list > li')
1061 ->text_is('span.client-name','Plugin 1')
1062 ->text_is('p.plugin-desc','Description Plugin 1')
1063 ->element_exists('ul.plugin-list > li + li')
Helgedb720ea2023-03-20 09:39:36 +01001064 ->text_is('ul.plugin-list > li + li >span.client-name','Plugin 2')
1065 ->text_is('ul.plugin-list > li + li >p.plugin-desc','Description Plugin 2')
Helge278fbca2022-11-29 18:49:15 +01001066 ;
1067
Helged36478d2023-06-08 17:43:01 +02001068$t->post_ok('/settings/marketplace/install', form => {'client-id' => '52abc'})
Helgedb720ea2023-03-20 09:39:36 +01001069 ->status_is(302)
1070 ->header_is(location => '/settings/marketplace')
1071 ;
Helgedb720ea2023-03-20 09:39:36 +01001072$t->ua->max_redirects(1);
1073
Helged36478d2023-06-08 17:43:01 +02001074$t->post_ok('/settings/marketplace/install', form => {'client-id' => '52abc'})
Helgedb720ea2023-03-20 09:39:36 +01001075 ->status_is(200)
1076 ->element_exists('ul.plugin-list')
1077 ->element_exists('ul.plugin-list > li')
1078 ->text_is('ul.plugin-list > li > span.client-name','Plugin 2')
1079 ->text_is('ul.plugin-list > li > p.plugin-desc','Description Plugin 2')
1080 ->element_exists_not('ul.plugin-list > li + li')
1081 ->element_exists('ul.plugin_in-list')
1082 ->element_exists('ul.plugin_in-list > li')
1083 ->text_is('ul.plugin_in-list > li > span.client-name','Plugin 1')
Helge216a4822024-06-17 12:02:34 +02001084 ->text_is('ul.plugin_in-list > li > p.inst_date','Installation date: 2022-12-13T16:33:27.621+01:00[Europe/Berlin]')
Helgedb720ea2023-03-20 09:39:36 +01001085 ;
1086
1087$t->ua->max_redirects(0);
1088
Helged36478d2023-06-08 17:43:01 +02001089 $t->post_ok('/settings/marketplace/install', form => {'client-id' => 'unsinn31'})
Helgedb720ea2023-03-20 09:39:36 +01001090 ->status_is(302)
1091 ->header_is(location => '/settings/marketplace')
1092 ;
1093
1094$t->ua->max_redirects(1);
1095
Helged36478d2023-06-08 17:43:01 +02001096$t->post_ok('/settings/marketplace/install', form => {'client-id' => 'unsinn31'})
Helgedb720ea2023-03-20 09:39:36 +01001097 ->status_is(200)
1098 ->text_is('div.notify-error', 'Plugin could not be installed')
1099 ;
1100
Helged36478d2023-06-08 17:43:01 +02001101$t->post_ok('/settings/marketplace/uninstall', form => {'client-id' => '52abc'})
1102 ->status_is(200)
1103 ->element_exists('ul.plugin-list')
1104 ->element_exists('ul.plugin-list > li')
1105 ->text_is('span.client-name','Plugin 1')
1106 ->text_is('p.plugin-desc','Description Plugin 1')
1107 ->element_exists('ul.plugin-list > li + li')
1108 ->text_is('ul.plugin-list > li + li >span.client-name','Plugin 2')
1109 ->text_is('ul.plugin-list > li + li >p.plugin-desc','Description Plugin 2')
1110 ->element_exists_not('ul.plugin_in-list')
1111 ;
1112
1113$t->post_ok('/settings/marketplace/uninstall', form => {'client-id' => 'quatsch12'})
1114 ->status_is(200)
1115 ->text_is('div.notify-error', 'Plugin could not be uninstalled')
1116 ;
1117
Helgedb720ea2023-03-20 09:39:36 +01001118$t->ua->max_redirects(0);
Helge278fbca2022-11-29 18:49:15 +01001119
Akrona8efaa92022-04-09 14:45:43 +02001120$t->get_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
1121 client_id => 'xyz',
1122 state => 'abcde',
1123 scope => 'search match',
1124 redirect_uri => 'http://test.com/'
1125}))
1126 ->status_is(200)
1127 ->attr_is('input[name=client_id]','value','xyz')
1128 ->attr_is('input[name=state]','value','abcde')
Akrona8efaa92022-04-09 14:45:43 +02001129 ->text_is('ul#scopes li:nth-child(1)','search')
1130 ->text_is('ul#scopes li:nth-child(2)','match')
Akron408bc7c2022-04-28 15:46:43 +02001131 ->text_is('span.client-name','New added client')
Akrona8efaa92022-04-09 14:45:43 +02001132 ->attr_is('a.form-button','href','http://test.com/')
1133 ->attr_is('a.embedded-link', 'href', '/doc/korap/kalamar')
1134 ;
1135
1136$t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
1137 client_id => 'xyz',
1138 state => 'abcde',
1139 scope => 'search match',
1140 redirect_uri => 'http://test.com/'
1141}))
1142 ->status_is(302)
Akron9d826902023-01-25 10:20:52 +01001143 ->header_is('location', '/settings/oauth?error_description=Bad+CSRF+token')
Akrona8efaa92022-04-09 14:45:43 +02001144 ;
1145
Akrona8f87cc2023-02-23 12:21:30 +01001146
1147my $local_port = $t->get_ok('/')->tx->local_port;
1148my $remote_port = $t->get_ok('/')->tx->remote_port;
1149
1150like($local_port, qr!^\d+$!);
1151like($remote_port, qr!^\d+$!);
1152
1153my $port = $remote_port;
1154
1155my $redirect_url_fakeapi = $t->app->close_redirect_to(Mojo::URL->new('http://localhost:' . $port)->path($fake_backend_app->url_for('return_uri'))->to_abs->to_string);
1156
Akrona8efaa92022-04-09 14:45:43 +02001157$fwd = $t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
1158 client_id => 'xyz',
1159 state => 'abcde',
1160 scope => 'search match',
Akrona8f87cc2023-02-23 12:21:30 +01001161 redirect_uri_server => 'http://localhost:'.$port,
1162 redirect_uri => "$redirect_url_fakeapi",
Akrona8efaa92022-04-09 14:45:43 +02001163 csrf_token => $csrf,
1164}))
1165 ->status_is(302)
Akrona8f87cc2023-02-23 12:21:30 +01001166 ->header_like('location', qr!^http://localhost:\d+/realapi/fakeclient/return\?code=.+$!)
Akrona8efaa92022-04-09 14:45:43 +02001167 ->tx->res->headers->header('location')
1168 ;
1169
Akronf47813c2023-05-08 16:24:03 +02001170unless ($^O eq 'MSWin32') {
1171 $t->get_ok($fwd)
1172 ->status_is(200)
1173 ->content_like(qr'welcome back! \[(.+?)\]')
1174 ;
1175};
Akrona8efaa92022-04-09 14:45:43 +02001176
Akrona8f87cc2023-02-23 12:21:30 +01001177my $fake_port = $port;
1178
1179while ($fake_port == $remote_port || $fake_port == $local_port) {
1180 $fake_port++;
1181};
1182
1183$redirect_url_fakeapi = $t->app->close_redirect_to(Mojo::URL->new('http://localhost:' . $fake_port)->path($fake_backend_app->url_for('return_uri'))->to_abs->to_string);
1184
1185$fwd = $t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
1186 client_id => 'xyz',
1187 state => 'abcde',
1188 scope => 'search match',
1189 redirect_uri_server => 'http://localhost:'.$port,
1190 redirect_uri => "$redirect_url_fakeapi",
1191 csrf_token => $csrf,
1192}))
1193 ->status_is(302)
1194 ->header_unlike('location', qr!^http://localhost:\d+/realapi/fakeclient/return\?error_description=Connection\+refused$!)
1195 ->header_like('location', qr!^http://localhost:\d+/realapi/fakeclient/return\?code=.+?$!)
1196 ->tx->res->headers->header('location')
1197 ;
1198
Akrona8efaa92022-04-09 14:45:43 +02001199$t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
1200 client_id => 'xyz',
Akron9ccf69a2023-01-31 14:21:37 +01001201 state => 'abcde',
1202 scope => 'search match',
1203 redirect_uri_server => 'http://example.com/',
1204 redirect_uri => $t->app->close_redirect_to('http://wrong'),
1205 csrf_token => $csrf,
1206}))
1207 ->status_is(302)
1208 ->header_is('location', '/settings/oauth')
1209 ->tx->res->headers->header('location')
1210 ;
1211
1212$t->get_ok('/settings/oauth')
1213 ->text_is('div.notify-error', 'Invalid redirect URI')
1214 ;
1215
1216$t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
1217 client_id => 'xyz',
Akrona8efaa92022-04-09 14:45:43 +02001218 state => 'fail',
1219 scope => 'search match',
Akron9d826902023-01-25 10:20:52 +01001220# redirect_uri_server => 'http://example.com/',
Akrona8efaa92022-04-09 14:45:43 +02001221 redirect_uri => $fake_backend_app->url_for('return_uri')->to_abs,
1222 csrf_token => $csrf,
1223}))
1224 ->status_is(302)
Akron9d826902023-01-25 10:20:52 +01001225 ->header_is('location', '/realapi/fakeclient/return?error_description=FAIL')
Akrona8efaa92022-04-09 14:45:43 +02001226 ;
1227
Akron9f2ad342022-05-04 16:16:40 +02001228my $json_post = {
1229 name => 'Funny',
1230 type => 'PUBLIC',
Akron6b75d122022-05-12 17:39:05 +02001231 desc => 'This is my plugin application 2',
Helge9f9d4852024-12-09 16:06:34 +01001232 url => 'https://xyz/123',
Akron9f2ad342022-05-04 16:16:40 +02001233 csrf_token => $csrf,
1234 src => 'hMMM'
1235};
1236
Helge9f9d4852024-12-09 16:06:34 +01001237
Akron9f2ad342022-05-04 16:16:40 +02001238$t->post_ok('/settings/oauth/register' => form => $json_post)
1239 ->status_is(200)
1240 ->element_exists('div.notify-error')
Akron99968a92022-06-03 12:32:07 +02001241 ->text_is('div.notify-error', 'Plugin declarations need to be json files')
1242 ;
1243
1244$json_post->{src} = {
1245 content => 'jjjjjj',
1246 filename => 'fun.txt'
1247};
1248
1249$t->post_ok('/settings/oauth/register' => form => $json_post)
1250 ->status_is(200)
1251 ->element_exists('div.notify-error')
Akron9f2ad342022-05-04 16:16:40 +02001252 ->text_is('div.notify-error', 'Plugins need to be confidential')
1253 ;
1254
1255$json_post->{type} = 'CONFIDENTIAL';
1256
Akron99968a92022-06-03 12:32:07 +02001257# This somehow gets removed in the last form send ...
Akron9f2ad342022-05-04 16:16:40 +02001258$json_post->{src} = {
1259 content => 'jjjjjj',
1260 filename => 'fun.txt'
1261};
1262
1263$t->post_ok('/settings/oauth/register' => form => $json_post)
1264 ->status_is(200)
1265 ->element_exists('div.notify-error')
1266 ->text_is('div.notify-error', 'Plugin declarations need to be json files')
1267 ;
1268
1269$json_post->{src} = {
1270 content => '{"name":"example"}',
1271 filename => 'fun.txt'
1272};
1273
1274$t->post_ok('/settings/oauth/register' => form => $json_post)
1275 ->status_is(200)
1276 ->element_exists_not('div.notify-error')
1277 ->element_exists('div.notify-success')
1278 ->text_is('div.notify-success', 'Registration successful')
1279 ;
1280
Akron6b75d122022-05-12 17:39:05 +02001281$t->get_ok('/settings/oauth/jh0gfjhjbfdsgzjghj==')
1282 ->status_is(200)
1283 ->text_is('div.notify-error', undef)
1284 ->text_is('li.client #client_source', '{"name":"example"}')
1285 ->text_is('li.client span.client-name', 'Funny')
1286 ->text_is('li.client p.client-desc', 'This is my plugin application 2')
1287 ->element_exists_not('li.client .client-redirect-uri tt')
1288 ->text_is('li.client .client-type tt', 'CONFIDENTIAL')
1289 ;
Akron9f2ad342022-05-04 16:16:40 +02001290
Akronb6b156e2022-03-31 14:57:49 +02001291
Akron66ef3b52022-11-22 14:25:15 +01001292# Retest client with super_client_file
1293my $client_file = tempfile;
1294
Akron889bc202024-03-15 17:16:55 +01001295$client_file->spew(
Akron66ef3b52022-11-22 14:25:15 +01001296 '{"client_id":"2","client_secret":"k414m4r-s3cr3t"}'
1297);
1298
1299$t = Test::Mojo::WithRoles->new('Kalamar' => {
1300 Kalamar => {
1301 plugins => ['Auth']
1302 },
1303 'Kalamar-Auth' => {
1304 client_file => $client_file,
1305 oauth2 => 1
1306 }
1307});
1308
1309$t->app->plugin(
1310 Mount => {
1311 $mount_point =>
1312 $fixtures_path->child('mock.pl')
1313 }
1314);
1315
1316$csrf = $t->get_ok('/')
1317 ->status_is(200)
1318 ->element_exists_not('div.button.top a')
1319 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
1320 ;
1321
1322$t->post_ok('/user/login' => form => {
1323 handle_or_email => 'test',
1324 pwd => 'pass',
1325 csrf_token => $csrf
1326})
1327 ->status_is(302)
1328 ->header_is('Location' => '/')
1329 ->content_is('');
1330
1331$t->get_ok('/')
1332 ->status_is(200)
1333 ->element_exists_not('div.notify-error')
1334 ->element_exists('div.notify-success')
1335 ->text_is('div.notify-success', 'Login successful')
1336 ->element_exists_not('aside.off')
1337 ->element_exists_not('aside.active')
1338 ->element_exists('aside.settings')
1339 ;
1340
Akron53a171e2022-12-05 18:22:58 +01001341$main::ENV{KALAMAR_CLIENT_FILE} = $client_file;
1342
1343$t = Test::Mojo::WithRoles->new('Kalamar' => {
1344 Kalamar => {
1345 plugins => ['Auth']
1346 },
1347 'Kalamar-Auth' => {
1348 oauth2 => 1,
1349# client_file => $client_file,
1350 }
1351});
1352
1353$t->app->plugin(
1354 Mount => {
1355 $mount_point =>
1356 $fixtures_path->child('mock.pl')
1357 }
1358);
1359
1360$csrf = $t->get_ok('/')
1361 ->status_is(200)
1362 ->element_exists_not('div.button.top a')
1363 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
1364 ;
1365
1366$t->post_ok('/user/login' => form => {
1367 handle_or_email => 'test',
1368 pwd => 'pass',
1369 csrf_token => $csrf
1370})
1371 ->status_is(302)
1372 ->header_is('Location' => '/')
1373 ->content_is('');
1374
1375$t->get_ok('/')
1376 ->status_is(200)
1377 ->element_exists_not('div.notify-error')
1378 ->element_exists('div.notify-success')
1379 ->text_is('div.notify-success', 'Login successful')
1380 ->element_exists_not('aside.off')
1381 ->element_exists_not('aside.active')
1382 ->element_exists('aside.settings')
1383 ;
1384
Akron83224302026-01-21 12:05:51 +01001385$log_output = '';
Akron66ef3b52022-11-22 14:25:15 +01001386
Akron83224302026-01-21 12:05:51 +01001387# Test non-existing client file (app should initialize without crashing)
1388$t = Test::Mojo->new('Kalamar');
1389$t->app->log->handle($log_fh);
1390$t->app->log->level('error');
1391$t->app->plugin('Mount' => {
1392 $mount_point => $fixtures_path->child('mock.pl')
1393});
1394
1395$t->app->plugin('Auth' => {
1396 client_file => '/nonexistent/client.json',
1397 oauth2 => 1
1398 }
1399);
1400ok($t->app, 'App initializes with non-existing client file');
1401like($log_output, qr'provided client file does not exist', 'Check log is fine');
1402
1403$log_output = '';
1404
1405# Test malformed JSON client file (app should initialize without crashing)
1406my $bad_client_file = tempfile();
1407$bad_client_file->spew('{invalid json}');
1408$t = Test::Mojo->new('Kalamar');
1409$t->app->log->handle($log_fh);
1410$t->app->log->level('error');
1411$t->app->plugin('Mount' => {
1412 $mount_point => $fixtures_path->child('mock.pl')
1413});
1414
1415$t->app->plugin('Auth' => {
1416 client_file => $bad_client_file->to_string,
1417 oauth2 => 1
1418 }
1419);
1420ok($t->app, 'App initializes with malformed JSON client file');
1421like($log_output, qr'provided client file syntax invalid', 'Check log is fine');
Akron33f5c672019-06-24 19:40:47 +02001422done_testing;
1423__END__
Akrona8efaa92022-04-09 14:45:43 +02001424
1425