blob: 2c661df488fc6040691fb9e7eacf8835282af31d [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
Akroncdfd9d52019-07-23 11:35:00 +020014my $t = Test::Mojo::WithRoles->new('Kalamar' => {
Akron33f5c672019-06-24 19:40:47 +020015 Kalamar => {
16 plugins => ['Auth']
17 },
18 'Kalamar-Auth' => {
19 client_id => 2,
20 client_secret => 'k414m4r-s3cr3t',
Akron59992122019-10-29 11:28:45 +010021 oauth2 => 1,
22 experimental_client_registration => 1
Akron33f5c672019-06-24 19:40:47 +020023 }
24});
25
26# Mount fake backend
27# Get the fixture path
28my $fixtures_path = path(Mojo::File->new(__FILE__)->dirname, '..', 'server');
29my $fake_backend = $t->app->plugin(
30 Mount => {
31 $mount_point =>
32 $fixtures_path->child('mock.pl')
33 }
34);
35# Configure fake backend
Akroncdfd9d52019-07-23 11:35:00 +020036my $fake_backend_app = $fake_backend->pattern->defaults->{app};
37
38# Set general app logger for simplicity
39$fake_backend_app->log($t->app->log);
40
41my $access_token = $fake_backend_app->get_token('access_token');
42my $refresh_token = $fake_backend_app->get_token('refresh_token');
43my $access_token_2 = $fake_backend_app->get_token('access_token_2');
44my $refresh_token_2 = $fake_backend_app->get_token('refresh_token_2');
45
46# Some routes to modify the session
47# This expires the session
48$t->app->routes->get('/x/expire')->to(
49 cb => sub {
50 my $c = shift;
51 $c->session(auth_exp => 0);
52 return $c->render(text => 'okay')
53 }
54);
55
56# This expires the session and removes the refresh token
57$t->app->routes->get('/x/expire-no-refresh')->to(
58 cb => sub {
59 my $c = shift;
60 $c->session(auth_exp => 0);
61 delete $c->session->{auth_r};
62 return $c->render(text => 'okay')
63 }
64);
65
66# This sets an invalid token
67$t->app->routes->get('/x/invalid')->to(
68 cb => sub {
69 my $c = shift;
70 $c->session(auth_exp => time + 1000);
71 $c->session(auth_r => $refresh_token_2);
72 $c->session(auth => 'Bearer inv4lid');
73 return $c->render(text => 'okay')
74 }
75);
76
77
78# This sets an invalid token
79$t->app->routes->get('/x/invalid-no-refresh')->to(
80 cb => sub {
81 my $c = shift;
82 $c->session(auth_exp => time + 1000);
83 delete $c->session->{auth_r};
84 $c->session(auth => 'Bearer inv4lid');
85 return $c->render(text => 'okay')
86 }
87);
88
89# This sets an invalid refresh token
90$t->app->routes->get('/x/expired-with-wrong-refresh')->to(
91 cb => sub {
92 my $c = shift;
93 $c->session(auth_exp => 0);
94 $c->session(auth => 'Bearer inv4lid');
95 $c->session(auth_r => 'inv4lid');
96 return $c->render(text => 'okay')
97 }
98);
99
Akronbc6b3f22021-01-13 14:53:12 +0100100my $q = qr!(?:\"|")!;
Akron33f5c672019-06-24 19:40:47 +0200101
Akron63d963b2019-07-05 15:35:51 +0200102$t->get_ok('/realapi/v1.0')
Akron33f5c672019-06-24 19:40:47 +0200103 ->status_is(200)
104 ->content_is('Fake server available');
105
106$t->get_ok('/?q=Baum')
107 ->status_is(200)
108 ->text_like('h1 span', qr/KorAP: Find .Baum./i)
109 ->text_like('#total-results', qr/\d+$/)
Akronbc6b3f22021-01-13 14:53:12 +0100110 ->content_like(qr/${q}authorized${q}:null/)
Akron33f5c672019-06-24 19:40:47 +0200111 ->element_exists_not('div.button.top a')
112 ->element_exists_not('aside.active')
113 ->element_exists_not('aside.off')
114 ;
115
116$t->get_ok('/')
117 ->status_is(200)
Akron9fa7cc52022-05-12 11:17:20 +0200118 ->element_exists('form[action=/user/login] input[name=handle_or_email]')
Akron33f5c672019-06-24 19:40:47 +0200119 ->element_exists('aside.active')
120 ->element_exists_not('aside.off')
121 ;
122
Akronff088112021-06-15 15:26:04 +0200123$t->get_ok('/settings/oauth')
124 ->status_is(401)
125 ->text_is('p.no-results', 'Not authenticated')
126 ;
127
Helge278fbca2022-11-29 18:49:15 +0100128$t->get_ok('/settings/marketplace')
129 ->status_is(401)
130 ->text_is('p.no-results', 'Not authenticated')
131 ;
132
Akron3e0fdc12020-05-15 16:17:21 +0200133# Test for bug with long password
134$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200135 handle_or_email => 'test',
Akron3e0fdc12020-05-15 16:17:21 +0200136 pwd => 'kjskjhndkjndqknaskjnakjdnkjdankajdnkjdsankjdsakjdfkjahzroiuqzriudjoijdmlamdlkmdsalkmdl' })
137 ->status_is(302)
138 ->header_is('Location' => '/');
139
Akron9fa7cc52022-05-12 11:17:20 +0200140$t->post_ok('/user/login' => form => { handle_or_email => 'test', pwd => 'fail' })
Akron33f5c672019-06-24 19:40:47 +0200141 ->status_is(302)
142 ->header_is('Location' => '/');
143
144$t->get_ok('/')
145 ->status_is(200)
146 ->element_exists('div.notify-error')
147 ->text_is('div.notify-error', 'Bad CSRF token')
Akron9fa7cc52022-05-12 11:17:20 +0200148 ->element_exists('input[name=handle_or_email][value=test]')
Akron33f5c672019-06-24 19:40:47 +0200149 ->element_exists_not('div.button.top a')
150 ;
151
Akron9fa7cc52022-05-12 11:17:20 +0200152$t->post_ok('/user/login' => form => { handle_or_email => 'test', pwd => 'pass' })
Akron33f5c672019-06-24 19:40:47 +0200153 ->status_is(302)
154 ->header_is('Location' => '/');
155
156my $csrf = $t->get_ok('/')
157 ->status_is(200)
158 ->element_exists('div.notify-error')
159 ->text_is('div.notify-error', 'Bad CSRF token')
160 ->element_exists_not('div.button.top a')
161 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
162 ;
163
164$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200165 handle_or_email => 'test',
Akron33f5c672019-06-24 19:40:47 +0200166 pwd => 'ldaperr',
167 csrf_token => $csrf
168})
169 ->status_is(302)
170 ->content_is('')
171 ->header_is('Location' => '/');
172
173$csrf = $t->get_ok('/')
174 ->status_is(200)
175 ->element_exists('div.notify-error')
176 ->text_is('div.notify-error', '2022: LDAP Authentication failed due to unknown user or password!')
Akron9fa7cc52022-05-12 11:17:20 +0200177 ->element_exists('input[name=handle_or_email][value=test]')
Akron33f5c672019-06-24 19:40:47 +0200178 ->element_exists_not('div.button.top a')
Akron3b3c7af2020-05-15 16:23:55 +0200179 ->element_exists_not('div.notify-success')
Akron33f5c672019-06-24 19:40:47 +0200180 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
181 ;
182
183$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200184 handle_or_email => 'test',
Akron33f5c672019-06-24 19:40:47 +0200185 pwd => 'unknown',
186 csrf_token => $csrf
187})
188 ->status_is(302)
189 ->content_is('')
190 ->header_is('Location' => '/');
191
192$csrf = $t->get_ok('/')
193 ->status_is(200)
194 ->element_exists('div.notify-error')
Akron8bbbecf2019-07-01 18:57:30 +0200195 ->text_is('div.notify-error', '2022: LDAP Authentication failed due to unknown user or password!')
Akron9fa7cc52022-05-12 11:17:20 +0200196 ->element_exists('input[name=handle_or_email][value=test]')
Akron33f5c672019-06-24 19:40:47 +0200197 ->element_exists_not('div.button.top a')
198 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
199 ;
200
201$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200202 handle_or_email => 'test',
Akron33f5c672019-06-24 19:40:47 +0200203 pwd => 'pass',
204 csrf_token => $csrf
205})
206 ->status_is(302)
207 ->content_is('')
208 ->header_is('Location' => '/');
209
210$t->get_ok('/')
211 ->status_is(200)
212 ->element_exists_not('div.notify-error')
213 ->element_exists('div.notify-success')
214 ->text_is('div.notify-success', 'Login successful')
Akron1d09b532021-06-15 18:18:25 +0200215 ->element_exists_not('aside.off')
Akrondc0b3ab2021-06-18 11:52:43 +0200216 ->element_exists_not('aside.active')
Akron1d09b532021-06-15 18:18:25 +0200217 ->element_exists('aside.settings')
Akron33f5c672019-06-24 19:40:47 +0200218 ;
219
Akron33f5c672019-06-24 19:40:47 +0200220# Now the user is logged in and should be able to
221# search with authorization
222$t->get_ok('/?q=Baum')
223 ->status_is(200)
Akron4cefe1f2019-09-04 10:11:28 +0200224 ->session_has('/auth')
225 ->session_is('/auth', 'Bearer ' . $access_token)
226 ->session_is('/auth_r', $refresh_token)
227 ->session_is('/user', 'test')
Akron33f5c672019-06-24 19:40:47 +0200228 ->text_like('h1 span', qr/KorAP: Find .Baum./i)
229 ->text_like('#total-results', qr/\d+$/)
230 ->element_exists_not('div.notify-error')
Akronbc6b3f22021-01-13 14:53:12 +0100231 ->content_like(qr/${q}authorized${q}:${q}yes${q}/)
Akron33f5c672019-06-24 19:40:47 +0200232 ->element_exists('div.button.top a')
233 ->element_exists('div.button.top a.logout[title~="test"]')
234 ;
235
Akron27031aa2020-04-28 14:57:10 +0200236$t->get_ok('/?q=Paum')
237 ->status_is(200)
238 ->text_like('h1 span', qr/KorAP: Find .Paum./i)
239 ->text_is('#total-results', '')
Akronbc6b3f22021-01-13 14:53:12 +0100240 ->content_like(qr/${q}authorized${q}:${q}yes${q}/)
Akron27031aa2020-04-28 14:57:10 +0200241 ->element_exists_not('p.hint')
242 ;
243
Akroncce055c2021-07-02 12:18:03 +0200244# Query with error
245$t->get_ok('/?q=error')
246 ->status_is(400)
247 ->text_is('#notifications .notify-error','500: Internal Server Error')
248;
Akron27031aa2020-04-28 14:57:10 +0200249
Akron33f5c672019-06-24 19:40:47 +0200250# Logout
251$t->get_ok('/user/logout')
252 ->status_is(302)
Akron4cefe1f2019-09-04 10:11:28 +0200253 ->session_hasnt('/auth')
254 ->session_hasnt('/auth_r')
255 ->session_hasnt('/user')
Akron33f5c672019-06-24 19:40:47 +0200256 ->header_is('Location' => '/');
257
258$t->get_ok('/')
259 ->status_is(200)
260 ->element_exists_not('div.notify-error')
261 ->element_exists('div.notify-success')
262 ->text_is('div.notify-success', 'Logout successful')
Akron9fa7cc52022-05-12 11:17:20 +0200263 ->element_exists("input[name=handle_or_email]")
264 ->element_exists("input[name=handle_or_email][value=test]")
Akron33f5c672019-06-24 19:40:47 +0200265 ;
266
267$t->get_ok('/?q=Baum')
268 ->status_is(200)
269 ->text_like('h1 span', qr/KorAP: Find .Baum./i)
270 ->text_like('#total-results', qr/\d+$/)
Akronbc6b3f22021-01-13 14:53:12 +0100271 ->content_like(qr/${q}authorized${q}:null/)
Akron33f5c672019-06-24 19:40:47 +0200272 ;
273
Akron27031aa2020-04-28 14:57:10 +0200274$t->get_ok('/?q=Paum')
275 ->status_is(200)
276 ->text_like('h1 span', qr/KorAP: Find .Paum./i)
277 ->text_is('#total-results', '')
Akronbc6b3f22021-01-13 14:53:12 +0100278 ->content_like(qr/${q}authorized${q}:null/)
Akron27031aa2020-04-28 14:57:10 +0200279 ->text_is('p.hint', 'Maybe you need to log in first?')
280 ;
281
282
Akron33f5c672019-06-24 19:40:47 +0200283# Get redirect
284my $fwd = $t->get_ok('/?q=Baum&ql=poliqarp')
285 ->status_is(200)
286 ->element_exists_not('div.notify-error')
287 ->tx->res->dom->at('input[name=fwd]')->attr('value')
288 ;
289
290is($fwd, '/?q=Baum&ql=poliqarp', 'Redirect is valid');
291
292$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200293 handle_or_email => 'test',
Akron33f5c672019-06-24 19:40:47 +0200294 pwd => 'pass',
295 csrf_token => $csrf,
296 fwd => 'http://bad.example.com/test'
297})
298 ->status_is(302)
299 ->header_is('Location' => '/');
300
301$t->get_ok('/')
302 ->status_is(200)
303 ->element_exists('div.notify-error')
304 ->element_exists_not('div.notify-success')
305 ->text_is('div.notify-error', 'Redirect failure')
306 ;
307
308$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200309 handle_or_email => 'test',
Akron33f5c672019-06-24 19:40:47 +0200310 pwd => 'pass',
311 csrf_token => $csrf,
312 fwd => $fwd
313})
314 ->status_is(302)
315 ->header_is('Location' => '/?q=Baum&ql=poliqarp');
316
Akron8bbbecf2019-07-01 18:57:30 +0200317$t->get_ok('/?q=Baum&ql=poliqarp')
318 ->status_is(200)
319 ->element_exists_not('div.notify-error')
320 ->element_exists('div.notify-success')
321 ->text_is('div.notify-success', 'Login successful')
Akroncdfd9d52019-07-23 11:35:00 +0200322 ->session_has('/auth')
323 ->session_is('/auth', 'Bearer ' . $access_token)
324 ->session_is('/auth_r', $refresh_token)
325 ->header_isnt('X-Kalamar-Cache', 'true')
Akron8bbbecf2019-07-01 18:57:30 +0200326 ;
327
Akroncdfd9d52019-07-23 11:35:00 +0200328# Expire the session
329# (makes the token be marked as expired - though it isn't serverside)
330$t->get_ok('/x/expire')
Akron8bbbecf2019-07-01 18:57:30 +0200331 ->status_is(200)
Akroncdfd9d52019-07-23 11:35:00 +0200332 ->content_is('okay')
333 ;
334
335## It may be a problem, but the cache is still valid
336$t->get_ok('/?q=Baum')
337 ->status_is(200)
338 ->text_like('h1 span', qr/KorAP: Find .Baum./i)
339 ->text_like('#total-results', qr/\d+$/)
Akronbc6b3f22021-01-13 14:53:12 +0100340 ->content_like(qr/${q}authorized${q}:${q}yes${q}/)
Akroncdfd9d52019-07-23 11:35:00 +0200341 ->header_is('X-Kalamar-Cache', 'true')
342 ;
343
344# Query without partial cache (unfortunately) (but no total results)
Akron58c60992021-09-07 13:11:43 +0200345my $err = $t->get_ok('/?q=baum&cutoff=true')
Akroncdfd9d52019-07-23 11:35:00 +0200346 ->status_is(200)
347 ->session_is('/auth', 'Bearer ' . $access_token_2)
348 ->session_is('/auth_r', $refresh_token_2)
Akroncdfd9d52019-07-23 11:35:00 +0200349 ->text_is('title', 'KorAP: Find »baum« with Poliqarp')
350 ->element_exists('meta[name="DC.title"][content="KorAP: Find »baum« with Poliqarp"]')
351 ->element_exists('body[itemscope][itemtype="http://schema.org/SearchResultsPage"]')
Akronbc6b3f22021-01-13 14:53:12 +0100352 ->content_like(qr/${q}authorized${q}:${q}yes${q}/)
Akroncdfd9d52019-07-23 11:35:00 +0200353 ->header_isnt('X-Kalamar-Cache', 'true')
Akronbc6b3f22021-01-13 14:53:12 +0100354 ->content_like(qr!${q}cutOff${q}:true!)
Akroncdfd9d52019-07-23 11:35:00 +0200355 ->element_exists_not('#total-results')
Akron58c60992021-09-07 13:11:43 +0200356 ->tx->res->dom->at('#error')
Akroncdfd9d52019-07-23 11:35:00 +0200357 ;
Akron58c60992021-09-07 13:11:43 +0200358is(defined $err ? $err->text : '', '');
Akroncdfd9d52019-07-23 11:35:00 +0200359
360# Expire the session and remove the refresh option
361$t->get_ok('/x/expire-no-refresh')
362 ->status_is(200)
363 ->content_is('okay')
364 ;
365
366$t->app->defaults(no_cache => 1);
367
368
369$t->get_ok('/x/invalid-no-refresh')
370 ->status_is(200)
371 ->content_is('okay')
372 ;
373
374# Query without cache
375# The token is invalid and can't be refreshed!
Akron58c60992021-09-07 13:11:43 +0200376$err = $t->get_ok('/?q=baum&cutoff=true')
Akron3c390c42020-03-30 09:06:21 +0200377 ->status_is(400)
Akroncdfd9d52019-07-23 11:35:00 +0200378 ->session_hasnt('/auth')
379 ->session_hasnt('/auth_r')
Akroncdfd9d52019-07-23 11:35:00 +0200380 ->text_is('div.notify-error','Access token invalid')
381 ->text_is('title', 'KorAP: Find »baum« with Poliqarp')
382 ->element_exists('meta[name="DC.title"][content="KorAP: Find »baum« with Poliqarp"]')
383 ->element_exists('body[itemscope][itemtype="http://schema.org/SearchResultsPage"]')
Akronbc6b3f22021-01-13 14:53:12 +0100384 ->content_unlike(qr/${q}authorized${q}:${q}yes${q}/)
Akroncdfd9d52019-07-23 11:35:00 +0200385 ->header_isnt('X-Kalamar-Cache', 'true')
386 ->element_exists('p.no-results')
Akron58c60992021-09-07 13:11:43 +0200387 ->tx->res->dom->at('#error')
Akroncdfd9d52019-07-23 11:35:00 +0200388 ;
Akron58c60992021-09-07 13:11:43 +0200389is(defined $err ? $err->text : '', '');
390
Akroncdfd9d52019-07-23 11:35:00 +0200391
392$t->get_ok('/x/invalid')
393 ->status_is(200)
394 ->content_is('okay')
395 ;
396
397# Query without cache
398# The token is invalid and can't be refreshed!
Akron58c60992021-09-07 13:11:43 +0200399$err = $t->get_ok('/?q=baum&cutoff=true')
Akroncdfd9d52019-07-23 11:35:00 +0200400 ->status_is(200)
401 ->session_is('/auth', 'Bearer ' . $access_token_2)
402 ->session_is('/auth_r', $refresh_token_2)
Akroncdfd9d52019-07-23 11:35:00 +0200403 ->element_exists_not('div.notify-error','Access token invalid')
404 ->text_is('title', 'KorAP: Find »baum« with Poliqarp')
405 ->element_exists('meta[name="DC.title"][content="KorAP: Find »baum« with Poliqarp"]')
406 ->element_exists('body[itemscope][itemtype="http://schema.org/SearchResultsPage"]')
Akronbc6b3f22021-01-13 14:53:12 +0100407 ->content_like(qr/${q}authorized${q}:${q}yes${q}/)
Akroncdfd9d52019-07-23 11:35:00 +0200408 ->header_isnt('X-Kalamar-Cache', 'true')
409 ->element_exists_not('p.no-results')
Akron58c60992021-09-07 13:11:43 +0200410 ->tx->res->dom->at('#error')
Akron8bbbecf2019-07-01 18:57:30 +0200411 ;
Akron58c60992021-09-07 13:11:43 +0200412is(defined $err ? $err->text : '', '');
Akron8bbbecf2019-07-01 18:57:30 +0200413
Akron33f5c672019-06-24 19:40:47 +0200414
Akroncdfd9d52019-07-23 11:35:00 +0200415$t->get_ok('/x/expired-with-wrong-refresh')
416 ->status_is(200)
417 ->content_is('okay')
418 ;
Akron4796e002019-07-05 10:13:15 +0200419
Akron4796e002019-07-05 10:13:15 +0200420
Akroncdfd9d52019-07-23 11:35:00 +0200421# The token is invalid and can't be refreshed!
Akron58c60992021-09-07 13:11:43 +0200422my $dom = $t->get_ok('/?q=baum&cutoff=true')
Akron3c390c42020-03-30 09:06:21 +0200423 ->status_is(400)
Akroncdfd9d52019-07-23 11:35:00 +0200424 ->session_hasnt('/auth')
425 ->session_hasnt('/auth_r')
Akroncdfd9d52019-07-23 11:35:00 +0200426 ->text_is('div.notify-error','Refresh token is expired')
427 ->text_is('title', 'KorAP: Find »baum« with Poliqarp')
Akronbc6b3f22021-01-13 14:53:12 +0100428 ->content_unlike(qr/${q}authorized${q}:${q}yes${q}/)
Akroncdfd9d52019-07-23 11:35:00 +0200429 ->element_exists('p.no-results')
Akron58c60992021-09-07 13:11:43 +0200430 ->tx->res->dom;
431
432$csrf = $dom->at('input[name="csrf_token"]')
Akron59992122019-10-29 11:28:45 +0100433 ->attr('value')
Akroncdfd9d52019-07-23 11:35:00 +0200434 ;
Akron4796e002019-07-05 10:13:15 +0200435
Akron58c60992021-09-07 13:11:43 +0200436$err = $dom->at('#error');
437is(defined $err ? $err->text : '', '');
438
439
Akron6a228db2021-10-14 15:57:00 +0200440# This should fail
441my $wide_char_login = "\x{61}\x{E5}\x{61}"; # "\x{443}\x{434}";
Akron59992122019-10-29 11:28:45 +0100442$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200443 handle_or_email => $wide_char_login,
Akron6a228db2021-10-14 15:57:00 +0200444 pwd => 'pass',
445 csrf_token => $csrf,
446 fwd => $fwd
447})
448 ->status_is(302)
449 ->header_is('Location' => '/');
450
451$t->get_ok('/')
452 ->status_is(200)
453 ->element_exists('div.notify-error')
454 ->text_is('div.notify-error', 'Invalid character in request')
Akron9fa7cc52022-05-12 11:17:20 +0200455 ->element_exists('input[name=handle_or_email]:not([value])')
Akron6a228db2021-10-14 15:57:00 +0200456 ->element_exists_not('div.button.top a')
457 ;
458
459# Login:
460# UTF8 request
461my $username = b('täst')->encode;
462$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200463 handle_or_email => $username,
Akron59992122019-10-29 11:28:45 +0100464 pwd => 'pass',
465 csrf_token => $csrf
466})
467 ->status_is(302)
468 ->content_is('')
469 ->header_is('Location' => '/');
470
471$t->get_ok('/')
472 ->status_is(200)
Akron78e0b6f2023-04-12 12:50:29 +0200473 ->content_type_is('text/html;charset=UTF-8')
Akron59992122019-10-29 11:28:45 +0100474 ->element_exists_not('div.notify-error')
475 ->element_exists('div.notify-success')
476 ->text_is('div.notify-success', 'Login successful')
Akron78e0b6f2023-04-12 12:50:29 +0200477 # Weird error in certain environments otherwise
478 ->attr_like('a.logout', 'title', qr!^Logout: t.+st$!)
Akron1d09b532021-06-15 18:18:25 +0200479 ->element_exists_not('aside.off')
Akrondc0b3ab2021-06-18 11:52:43 +0200480 ->element_exists_not('aside.active')
Akron1d09b532021-06-15 18:18:25 +0200481 ->element_exists('aside.settings')
Akron59992122019-10-29 11:28:45 +0100482 ;
483
484$t->get_ok('/settings/oauth')
485 ->text_is('form.form-table legend', 'Register new client application')
486 ->attr_is('form.oauth-register','action', '/settings/oauth/register')
Akron9f2ad342022-05-04 16:16:40 +0200487 ->text_is('label[for=name]','Name of the client application')
488 ->text_is('label[for=type]','Type of the client application')
489 ->text_is('label[for=desc]','Short description')
490 ->text_is('label[for=url]','Homepage')
491 ->text_is('label[for=redirect_uri]','Redirect URI')
492 ->text_is('label[for=src]','Declaration of the plugin (*.json file)')
Akron1a9d5be2020-03-19 17:28:33 +0100493 ->element_exists('ul.client-list')
494 ->element_exists_not('ul.client-list > li')
Akronad011bb2021-06-10 12:16:36 +0200495 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
496 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
497 ->header_is('Pragma','no-cache')
Akron59992122019-10-29 11:28:45 +0100498 ;
Akronad011bb2021-06-10 12:16:36 +0200499
Helge278fbca2022-11-29 18:49:15 +0100500
501$t->get_ok('/settings/marketplace')
502 ->status_is(200)
503 ->text_is('html head title' => 'Marketplace')
504 ;
505
Akron59992122019-10-29 11:28:45 +0100506$csrf = $t->post_ok('/settings/oauth/register' => form => {
507 name => 'MyApp',
508 type => 'PUBLIC',
Akron6b75d122022-05-12 17:39:05 +0200509 desc => 'This is my plugin application'
Akron59992122019-10-29 11:28:45 +0100510})
511 ->text_is('div.notify-error', 'Bad CSRF token')
512 ->tx->res->dom->at('input[name="csrf_token"]')
513 ->attr('value')
514 ;
515
516$t->post_ok('/settings/oauth/register' => form => {
517 name => 'MyApp',
Akron99968a92022-06-03 12:32:07 +0200518 type => 'PUBLIC',
Akron6b75d122022-05-12 17:39:05 +0200519 desc => 'This is my plugin application',
Akron9f2ad342022-05-04 16:16:40 +0200520 csrf_token => $csrf,
521 src => {
522 filename => '',
523 content => ''
524 }
Akron59992122019-10-29 11:28:45 +0100525})
526 ->status_is(200)
527 ->element_exists('div.notify-success')
528 ->text_is('legend', 'Client Credentials')
529 ->text_is('label[for=client_id]', 'ID of the client application')
530 ->element_exists('input[name=client_id][readonly][value]')
Akron99968a92022-06-03 12:32:07 +0200531 ->element_exists_not('input[name=client_secret][readonly][value]')
Akronad011bb2021-06-10 12:16:36 +0200532 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
533 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
534 ->header_is('Pragma','no-cache')
Akron59992122019-10-29 11:28:45 +0100535 ;
Akron4796e002019-07-05 10:13:15 +0200536
Akron58c60992021-09-07 13:11:43 +0200537my $anchor = $t->get_ok('/settings/oauth')
Akronc1aaf932021-06-09 12:19:15 +0200538 ->text_is('.form-table legend', 'Register new client application')
539 ->attr_is('.oauth-register','action', '/settings/oauth/register')
Akron17de86e2020-04-16 16:03:40 +0200540 ->text_is('ul.client-list > li > span.client-name a', 'MyApp')
Akron6b75d122022-05-12 17:39:05 +0200541 ->text_is('ul.client-list > li > p.client-desc', 'This is my plugin application')
Akronad011bb2021-06-10 12:16:36 +0200542 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
543 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
544 ->header_is('Pragma','no-cache')
Akronb6b156e2022-03-31 14:57:49 +0200545 ->tx->res->dom->at('ul.client-list > li > p.client-url a')
Akron17de86e2020-04-16 16:03:40 +0200546 ;
Akron58c60992021-09-07 13:11:43 +0200547is(defined $anchor ? $anchor->text : '', '');
Akron17de86e2020-04-16 16:03:40 +0200548
Akron041ca4d2021-06-10 11:52:51 +0200549$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akron17de86e2020-04-16 16:03:40 +0200550 ->status_is(200)
Akronc1aaf932021-06-09 12:19:15 +0200551 ->text_is('ul.client-list > li.client > span.client-name', 'MyApp')
Akron6b75d122022-05-12 17:39:05 +0200552 ->text_is('ul.client-list > li.client > p.client-desc', 'This is my plugin application')
Akron17de86e2020-04-16 16:03:40 +0200553 ->text_is('a.client-unregister', 'Unregister')
Akron041ca4d2021-06-10 11:52:51 +0200554 ->attr_is('a.client-unregister', 'href', '/settings/oauth/fCBbQkA2NDA3MzM1Yw==/unregister?name=MyApp')
Akron1a9d5be2020-03-19 17:28:33 +0100555 ;
556
Akron041ca4d2021-06-10 11:52:51 +0200557$csrf = $t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/unregister?name=MyApp')
Akron1a9d5be2020-03-19 17:28:33 +0100558 ->content_like(qr!Do you really want to unregister \<span class="client-name"\>MyApp\<\/span\>?!)
Akronc1aaf932021-06-09 12:19:15 +0200559 ->attr_is('.form-table input[name=client-name]', 'value', 'MyApp')
Akronad011bb2021-06-10 12:16:36 +0200560 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
561 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
562 ->header_is('Pragma','no-cache')
Akron1a9d5be2020-03-19 17:28:33 +0100563 ->tx->res->dom->at('input[name="csrf_token"]')
564 ->attr('value')
565 ;
566
Akron041ca4d2021-06-10 11:52:51 +0200567$t->post_ok('/settings/oauth/xxxx==/unregister' => form => {
Akron1a9d5be2020-03-19 17:28:33 +0100568 'client-name' => 'MyApp',
Akron1a9d5be2020-03-19 17:28:33 +0100569 'csrf_token' => $csrf
570})->status_is(302)
571 ->content_is('')
572 ->header_is('Location' => '/settings/oauth')
573 ;
574
575$t->get_ok('/settings/oauth')
Akronc1aaf932021-06-09 12:19:15 +0200576 ->text_is('.form-table legend', 'Register new client application')
577 ->attr_is('.oauth-register','action', '/settings/oauth/register')
Akron1a9d5be2020-03-19 17:28:33 +0100578 ->element_exists('ul.client-list > li')
579 ->text_is('div.notify', 'Unknown client with xxxx==.')
Akronad011bb2021-06-10 12:16:36 +0200580 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
581 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
582 ->header_is('Pragma','no-cache')
Akron1a9d5be2020-03-19 17:28:33 +0100583 ;
584
Akron041ca4d2021-06-10 11:52:51 +0200585$t->post_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/unregister' => form => {
Akron1a9d5be2020-03-19 17:28:33 +0100586 'client-name' => 'MyApp',
Akron1a9d5be2020-03-19 17:28:33 +0100587 'csrf_token' => $csrf
588})->status_is(302)
589 ->content_is('')
590 ->header_is('Location' => '/settings/oauth')
591 ;
592
593$t->get_ok('/settings/oauth')
Akronc1aaf932021-06-09 12:19:15 +0200594 ->text_is('.form-table legend', 'Register new client application')
595 ->attr_is('.oauth-register','action', '/settings/oauth/register')
Akron1a9d5be2020-03-19 17:28:33 +0100596 ->element_exists_not('ul.client-list > li')
597 ->text_is('div.notify-success', 'Successfully deleted MyApp')
Akronad011bb2021-06-10 12:16:36 +0200598 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
599 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
600 ->header_is('Pragma','no-cache')
Akron1a9d5be2020-03-19 17:28:33 +0100601 ;
602
Akron83209f72021-01-29 17:54:15 +0100603$t->post_ok('/settings/oauth/register' => form => {
604 name => 'MyApp2',
605 type => 'PUBLIC',
Akron6b75d122022-05-12 17:39:05 +0200606 desc => 'This is my plugin application',
Akron83209f72021-01-29 17:54:15 +0100607 csrf_token => $csrf
608})->status_is(200)
609 ->element_exists('div.notify-success')
610 ->text_is('legend', 'Client Credentials')
611 ->text_is('label[for=client_id]', 'ID of the client application')
612 ->element_exists('input[name=client_id][readonly][value]')
613 ->element_exists_not('input[name=client_secret][readonly][value]')
Akronad011bb2021-06-10 12:16:36 +0200614 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
615 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
616 ->header_is('Pragma','no-cache')
Akronb6b156e2022-03-31 14:57:49 +0200617 ->element_exists('.client-issue-token')
Akron83209f72021-01-29 17:54:15 +0100618 ;
619
Akron041ca4d2021-06-10 11:52:51 +0200620$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akron83209f72021-01-29 17:54:15 +0100621 ->text_is('.client-name', 'MyApp2')
Akron6b75d122022-05-12 17:39:05 +0200622 ->text_is('.client-desc', 'This is my plugin application')
Akrone997bb52021-06-11 16:44:06 +0200623 ->text_is('.client-issue-token', 'Issue new token')
Akron041ca4d2021-06-10 11:52:51 +0200624 ->attr_is('.client-issue-token', 'href', '/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token?name=MyApp2')
Akronad011bb2021-06-10 12:16:36 +0200625 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
626 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
627 ->header_is('Pragma','no-cache')
Akrone997bb52021-06-11 16:44:06 +0200628 ->text_is('ul.token-list label[for=token]', 'Access Token')
629 ->text_is('p[name=created]', 'Created at ')
630 ->text_is('p[name=expires]', 'Expires in 31533851 seconds.')
Akronb6b156e2022-03-31 14:57:49 +0200631 ->element_exists('.client-issue-token')
Akron83209f72021-01-29 17:54:15 +0100632 ;
633
Akron041ca4d2021-06-10 11:52:51 +0200634$csrf = $t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token?name=MyApp2')
Akron83209f72021-01-29 17:54:15 +0100635 ->status_is(200)
Akron041ca4d2021-06-10 11:52:51 +0200636 ->attr_is('#issue-token','action', '/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token')
Akron83209f72021-01-29 17:54:15 +0100637 ->attr_is('input[name=client-id]', 'value', 'fCBbQkA2NDA3MzM1Yw==')
638 ->attr_is('input[name=name]', 'value', 'MyApp2')
Akronad011bb2021-06-10 12:16:36 +0200639 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
640 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
641 ->header_is('Pragma','no-cache')
Akrone997bb52021-06-11 16:44:06 +0200642 ->text_is('a.button-abort', 'Abort')
643 ->attr_is('#issue-token input[type=submit]', 'value', 'Issue new token')
644 ->content_like(qr!Issue a new token for!)
Akron83209f72021-01-29 17:54:15 +0100645 ->tx->res->dom->at('input[name="csrf_token"]')
646 ->attr('value')
647 ;
648
Akron041ca4d2021-06-10 11:52:51 +0200649$t->post_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token' => form => {
Akron83209f72021-01-29 17:54:15 +0100650 csrf_token => $csrf,
651 name => 'MyApp2',
652 'client-id' => 'fCBbQkA2NDA3MzM1Yw=='
653})
Akronbc94a9c2021-04-15 00:07:35 +0200654 ->status_is(302)
Akron041ca4d2021-06-10 11:52:51 +0200655 ->header_is('Location','/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronbc94a9c2021-04-15 00:07:35 +0200656 ;
657
Akron041ca4d2021-06-10 11:52:51 +0200658$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
659 ->status_is(200)
Akronbc94a9c2021-04-15 00:07:35 +0200660 ->text_is('div.notify-success', 'New access token created')
Akronad011bb2021-06-10 12:16:36 +0200661 ->status_is(200)
662 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
663 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
664 ->header_is('Pragma','no-cache')
Akron83209f72021-01-29 17:54:15 +0100665 ;
666
Akrone3daaeb2023-05-08 09:44:18 +0200667$t->post_ok('/settings/oauth/307/token' => form => {
668 csrf_token => $csrf,
669 name => 'MyApp2',
670})
671 ->status_is(302)
672 ->header_is('Location','/settings/oauth/307')
673 ;
674
675$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
676 ->status_is(200)
677 ->text_is('div.notify-success', 'New access token created')
678 ->status_is(200)
679 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
680 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
681 ->header_is('Pragma','no-cache')
682 ;
683
684
Akron041ca4d2021-06-10 11:52:51 +0200685$csrf = $t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200686 ->status_is(200)
Akron041ca4d2021-06-10 11:52:51 +0200687 ->attr_is('form.token-revoke', 'action', '/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token/revoke')
Akronc1aaf932021-06-09 12:19:15 +0200688 ->attr_is('form.token-revoke input[name=token]', 'value', 'jhkhkjhk_hjgjsfz67i')
689 ->attr_is('form.token-revoke input[name=name]', 'value', 'MyApp2')
690 ->tx->res->dom->at('input[name="csrf_token"]')
691 ->attr('value')
692 ;
693
Akron041ca4d2021-06-10 11:52:51 +0200694$t->post_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token/revoke' => form => {
Akronc1aaf932021-06-09 12:19:15 +0200695 csrf_token => $csrf,
696 name => 'MyApp2',
697 token => 'jhkhkjhk_hjgjsfz67i'
698})
699 ->status_is(200)
Akron041ca4d2021-06-10 11:52:51 +0200700 ->attr_is('form#revoke-token','action','/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token?_method=DELETE')
Akronc1aaf932021-06-09 12:19:15 +0200701 ->attr_is('form#revoke-token','method','POST')
702 ->attr_is('form#revoke-token input[name=token]','value','jhkhkjhk_hjgjsfz67i')
Akrone997bb52021-06-11 16:44:06 +0200703 ->text_is('a.button-abort', 'Abort')
704 ->attr_is('#revoke-token input[type=submit]', 'value', 'Revoke')
705 ->content_like(qr!Revoke a token for!)
Akronc1aaf932021-06-09 12:19:15 +0200706;
707
708
709# CSRF missing
Akron041ca4d2021-06-10 11:52:51 +0200710$t->post_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token?_method=DELETE' => form => {
Akronc1aaf932021-06-09 12:19:15 +0200711 name => 'MyApp2',
712 token => 'jhkhkjhk_hjgjsfz67i'
713})->status_is(302)
Akron041ca4d2021-06-10 11:52:51 +0200714 ->header_is('Location','/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200715 ;
716
Akron041ca4d2021-06-10 11:52:51 +0200717$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200718 ->element_exists_not('div.notify-success')
719 ->text_is('div.notify-error', 'Bad CSRF token')
720 ;
721
722# Token missing
Akron041ca4d2021-06-10 11:52:51 +0200723$t->post_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token?_method=DELETE' => form => {
Akronc1aaf932021-06-09 12:19:15 +0200724 name => 'MyApp2',
725 csrf_token => $csrf,
726})->status_is(302)
Akron041ca4d2021-06-10 11:52:51 +0200727 ->header_is('Location','/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200728 ;
729
Akron041ca4d2021-06-10 11:52:51 +0200730$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200731 ->element_exists_not('div.notify-success')
732 ->text_is('div.notify-error', 'Some fields are invalid')
733 ;
734
Akron041ca4d2021-06-10 11:52:51 +0200735$t->post_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token?_method=DELETE' => form => {
Akronc1aaf932021-06-09 12:19:15 +0200736 name => 'MyApp2',
737 csrf_token => $csrf,
738 token => 'jhkhkjhk_hjgjsfz67i'
739})->status_is(302)
Akron041ca4d2021-06-10 11:52:51 +0200740 ->header_is('Location','/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200741 ;
742
Akron041ca4d2021-06-10 11:52:51 +0200743$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200744 ->element_exists_not('div.notify-error')
745 ->text_is('div.notify-success', 'Token was revoked successfully')
746 ;
Akron83209f72021-01-29 17:54:15 +0100747
Akronb6b156e2022-03-31 14:57:49 +0200748$t->app->routes->get('/x/redirect-target')->to(
749 cb => sub {
750 my $c = shift;
751 return $c->render(text => 'redirected');
752 }
753);
754
755$csrf = $t->post_ok('/settings/oauth/register' => form => {
756 name => 'MyConfApp',
757 type => 'CONFIDENTIAL',
Akron6b75d122022-05-12 17:39:05 +0200758 desc => 'This is my plugin application',
Akronb6b156e2022-03-31 14:57:49 +0200759})
760 ->text_is('div.notify-error', 'Bad CSRF token')
761 ->tx->res->dom->at('input[name="csrf_token"]')
762 ->attr('value')
763 ;
764
765$t->post_ok('/settings/oauth/register' => form => {
766 name => 'MyConfApp',
767 type => 'CONFIDENTIAL',
768 desc => 'This is my confidential application',
769 csrf_token => $csrf,
770 redirect_uri => 'http://localhost/redirect-target'
771})
772 ->text_is('div.notify-error', undef)
773 ->text_is('li.client span.client-name', 'MyConfApp')
774 ->text_is('li.client p.client-desc', 'This is my confidential application')
775 ->text_is('li.client .client-redirect-uri tt', 'http://localhost/redirect-target')
776 ->text_is('li.client .client-type tt', 'CONFIDENTIAL')
777 ->element_exists_not('.client-issue-token')
778 ;
779
780$t->post_ok('/settings/oauth/register' => form => {
781 name => 'MyConfApp2',
782 type => 'CONFIDENTIAL',
783 desc => 'This is my second confidential application',
784 csrf_token => $csrf,
785 redirect_uri => 'http://localhost/FAIL'
786})
787 ->status_is(302)
788 ->header_is('location','/settings/oauth/')
789 ;
790
791$t->get_ok('/settings/oauth/')
792 ->text_is('div.notify-error', 'invalid_request: http://localhost/FAIL is invalid.')
793 ;
794
Akrona8efaa92022-04-09 14:45:43 +0200795# OAuth client authorization flow
796$t->get_ok(Mojo::URL->new('/settings/oauth/authorize'))
797 ->status_is(302)
Akron5ea0f5d2023-01-20 11:51:43 +0100798 ->header_is('location','/settings/oauth')
799 ;
800
801$t->get_ok('/settings/oauth/')
Akron2c142ab2023-01-30 13:21:57 +0100802 ->text_is('div.notify-error', 'Client ID required')
803 ;
804
805$t->get_ok(Mojo::URL->new('/settings/oauth/authorize?client_id=xyz'))
806 ->status_is(302)
807 ->header_is('location','/settings/oauth')
808 ;
809
810$t->get_ok('/settings/oauth/')
811 ->text_is('div.notify-error', 'Scope required')
Akrona8efaa92022-04-09 14:45:43 +0200812 ;
813
Akrondb1f4672023-01-24 12:05:07 +0100814# OAuth client authorization flow
815$t->get_ok(Mojo::URL->new('/settings/oauth/authorize?client_id=abc'))
816 ->status_is(302)
817 ->header_is('location','/settings/oauth')
818 ;
819
820$t->get_ok('/settings/oauth/')
Akron2c142ab2023-01-30 13:21:57 +0100821 ->text_is('div.notify-error', 'Scope required')
822 ;
823
824# OAuth client authorization flow
825$t->get_ok(Mojo::URL->new('/settings/oauth/authorize?client_id=abc&scope=match'))
826 ->status_is(302)
827 ->header_is('location','/settings/oauth')
828 ;
829
830$t->get_ok('/settings/oauth/')
Akrondb1f4672023-01-24 12:05:07 +0100831 ->text_is('div.notify-error', 'Unknown client with abc.')
832 ;
833
Akrona8efaa92022-04-09 14:45:43 +0200834# Logout
835$t->get_ok('/x/expired-with-wrong-refresh');
836
837$t->get_ok('/user/logout')
838 ->status_is(302)
839 ->session_hasnt('/auth')
840 ->session_hasnt('/auth_r')
841 ->session_hasnt('/user')
842 ->header_is('Location' => '/');
843
Akron001dcd22023-02-07 08:38:11 +0100844$t->get_ok('/')
Akrona8efaa92022-04-09 14:45:43 +0200845 ->status_is(200)
846 ->element_exists_not('div.notify-error')
847 ->element_exists('div.notify-success')
848 ->text_is('div.notify-success', 'Logout successful')
Akron9fa7cc52022-05-12 11:17:20 +0200849 ->element_exists("input[name=handle_or_email]")
Akron001dcd22023-02-07 08:38:11 +0100850 ;
851
852# OAuth client authorization flow - but user not logged in
853$t->get_ok(Mojo::URL->new('/settings/oauth/authorize'))
854 ->status_is(302)
855 ->header_is('location','/')
856 ;
857
858$csrf = $t->get_ok('/')
859 ->status_is(200)
860 ->element_exists('div.notify-error')
861 ->element_exists_not('div.notify-success')
862 ->text_is('div.notify-error', 'Client ID required')
863 ->element_exists("input[name=handle_or_email]")
Akrona8efaa92022-04-09 14:45:43 +0200864 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
865 ;
866
Akron408bc7c2022-04-28 15:46:43 +0200867$fake_backend_app->add_client({
868 "client_id" => 'xyz',
869 "client_name" => 'New added client',
870 "client_description" => 'This is a new client',
871 "client_url" => 'http://example.com',
872 "client_type" => 'CONFIDENTIAL'
873# "client_redirect_uri" => $redirect_uri
874});
875
Helge278fbca2022-11-29 18:49:15 +0100876
Akron9d826902023-01-25 10:20:52 +0100877$fake_backend_app->add_client({
878 "client_id" => 'xyz2',
879 "client_name" => 'New added client',
880 "client_description" => 'This is a new client',
881 "client_url" => 'http://example.com',
882 "client_type" => 'CONFIDENTIAL',
883 "client_redirect_uri" => 'http://redirect.url/'
884});
885
Akroneb39cf32023-04-03 14:40:48 +0200886$t->get_ok('/settings/oauth/xyz2')
887 ->status_is(200)
888 ->text_is('li.client span.client-name','New added client')
889 ->attr_is('li.client p.client-url a','href','http://example.com')
890 ->attr_is('li.client input[name=client_id]','value','xyz2')
891 ->element_exists('li.client p.client-type-confidential')
892 ->text_is('li.client p.client-redirect-uri tt','http://redirect.url/')
893 ;
894
Akron9d826902023-01-25 10:20:52 +0100895$fwd = $t->get_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
896 client_id => 'xyz2',
897 scope => 'search match',
898 redirect_uri => 'http://test.com/',
899}))
900 ->status_is(200)
901 ->text_is('div.notify-warn', 'redirect_uri host differs from registered host')
902 ->element_exists_not('div.notify-error')
903 ->element_exists_not('div.notify-success')
904 ->element_exists("input[name=handle_or_email]")
905 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
906 ;
907
908$fwd = $t->get_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
909 client_id => 'xyz2',
910 scope => 'search match',
911 redirect_uri => 'http://redirect.url:9000/',
912}))
913 ->status_is(200)
914 ->text_is('div.notify-warn', 'redirect_uri port differs from registered port')
915 ->element_exists_not('div.notify-error')
916 ->element_exists_not('div.notify-success')
917 ->element_exists("input[name=handle_or_email]")
918 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
919 ;
920
Helge278fbca2022-11-29 18:49:15 +0100921
922$fake_backend_app->add_plugin({
923"source" => {"key1" => 'wert1', "key2" => 'wert2'},
924"client_id" => "52abc",
925"permitted" => 'true',
926"client_id" => '52abc',
927"client_name" => 'Plugin 1',
928"client_type" => 'CONFIDENTIAL',
929"client_description" =>"Description Plugin 1",
930"client_url" => "http://example.client.de",
931"registration_date" => "2022-05-31T14:30:09+02:00[Europe/Berlin]",
932"registered_by" => "system"
933});
934
935
Akrona8efaa92022-04-09 14:45:43 +0200936$fwd = $t->get_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
937 client_id => 'xyz',
938 state => 'abcde',
939 scope => 'search match',
940 redirect_uri => 'http://test.com/',
941}))
942 ->status_is(200)
943 ->attr_is('input[name=client_id]','value','xyz')
944 ->attr_is('input[name=state]','value','abcde')
Akrona8efaa92022-04-09 14:45:43 +0200945 ->attr_like('input[name=fwd]','value',qr!test\.com!)
Akron408bc7c2022-04-28 15:46:43 +0200946 ->text_is('span.client-name','New added client')
Akrona8efaa92022-04-09 14:45:43 +0200947 ->text_is('div.intro p:nth-child(2)', 'Please log in!')
948 ->tx->res->dom->at('input[name=fwd]')->attr('value')
949 ;
950
951$fwd = $t->post_ok(Mojo::URL->new('/user/login')->query({
952 csrf_token => $csrf,
953 client_id => 'xyz',
954 state => 'abcde',
955 scope => 'search match',
956 redirect_uri => 'http://test.com/',
Akron9fa7cc52022-05-12 11:17:20 +0200957 handle_or_email => 'test',
Akrona8efaa92022-04-09 14:45:43 +0200958 pwd => 'pass',
959 fwd => $fwd
960}))
961 ->status_is(302)
962 ->header_like('location', qr!/settings/oauth/authorize!)
963 ->tx->res->headers->header('location')
964 ;
965
966$t->get_ok($fwd)
967 ->status_is(200)
968 ->attr_is('input[name=client_id]','value','xyz')
969 ->attr_is('input[name=state]','value','abcde')
Akron9d826902023-01-25 10:20:52 +0100970 ->attr_like('input[name=redirect_uri]','value', qr!^http://test\.com\/\?crto=.{3,}!)
Akrona8efaa92022-04-09 14:45:43 +0200971 ->text_is('ul#scopes li:nth-child(1)','search')
972 ->text_is('ul#scopes li:nth-child(2)','match')
Akron408bc7c2022-04-28 15:46:43 +0200973 ->text_is('span.client-name','New added client')
Akrona8efaa92022-04-09 14:45:43 +0200974 ->attr_is('a.form-button','href','http://test.com/')
975 ->attr_is('a.embedded-link', 'href', '/doc/korap/kalamar')
Akron9d826902023-01-25 10:20:52 +0100976 ->element_exists_not('div.notify-error')
977 ->element_exists_not('div.notify-warn')
978 ->element_exists_not('blockquote.warning')
Akrona8efaa92022-04-09 14:45:43 +0200979 ;
980
Helge278fbca2022-11-29 18:49:15 +0100981$t->get_ok('/settings/marketplace')
982 ->status_is(200)
983 ->text_is('html head title' => 'Marketplace')
984 ->element_exists('ul.plugin-list')
985 ->element_exists('ul.plugin-list > li')
986 ->element_exists('p.registration_date')
987 ->element_exists('p.registered_by')
988 ->text_is('span.client-name','Plugin 1')
989 ->text_is('p.plugin-desc','Description Plugin 1')
990 ;
991
992$fake_backend_app->add_plugin({
993"source" => {"one" => '1', "two" => '2'},
994"permitted" => 'false',
995"client_id" => '53abc',
996"client_name" => 'Plugin 2',
997"client_type" => 'CONFIDENTIAL',
998"client_description" =>'Description Plugin 2'
999});
1000
1001$fake_backend_app->add_plugin({
1002"source" => {"answer" => '42', "hello" => 'world'},
1003"permitted" => 'true',
1004"client_id" => '54abc',
1005"client_name" => 'Plugin 3',
1006"client_type" => 'CONFIDENTIAL',
1007"client_description" =>'Description Plugin 3'
1008});
1009
1010$t->get_ok('/settings/marketplace')
1011 ->status_is(200)
1012 ->element_exists('ul.plugin-list')
1013 ->element_exists('ul.plugin-list > li')
1014 ->text_is('span.client-name','Plugin 1')
1015 ->text_is('p.plugin-desc','Description Plugin 1')
1016 ->element_exists('ul.plugin-list > li + li')
1017 ->text_isnt('ul.plugin-list > li + li >span.client-name','Plugin 2')
1018 ->text_isnt('ul.plugin-list > li + li >p.plugin-desc','Description Plugin 2')
1019 ->text_is('ul.plugin-list > li + li >span.client-name','Plugin 3')
1020 ->text_is('ul.plugin-list > li + li >p.plugin-desc','Description Plugin 3')
1021 ;
1022
1023
Akrona8efaa92022-04-09 14:45:43 +02001024$t->get_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
1025 client_id => 'xyz',
1026 state => 'abcde',
1027 scope => 'search match',
1028 redirect_uri => 'http://test.com/'
1029}))
1030 ->status_is(200)
1031 ->attr_is('input[name=client_id]','value','xyz')
1032 ->attr_is('input[name=state]','value','abcde')
Akrona8efaa92022-04-09 14:45:43 +02001033 ->text_is('ul#scopes li:nth-child(1)','search')
1034 ->text_is('ul#scopes li:nth-child(2)','match')
Akron408bc7c2022-04-28 15:46:43 +02001035 ->text_is('span.client-name','New added client')
Akrona8efaa92022-04-09 14:45:43 +02001036 ->attr_is('a.form-button','href','http://test.com/')
1037 ->attr_is('a.embedded-link', 'href', '/doc/korap/kalamar')
1038 ;
1039
1040$t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
1041 client_id => 'xyz',
1042 state => 'abcde',
1043 scope => 'search match',
1044 redirect_uri => 'http://test.com/'
1045}))
1046 ->status_is(302)
Akron9d826902023-01-25 10:20:52 +01001047 ->header_is('location', '/settings/oauth?error_description=Bad+CSRF+token')
Akrona8efaa92022-04-09 14:45:43 +02001048 ;
1049
Akrona8f87cc2023-02-23 12:21:30 +01001050
1051my $local_port = $t->get_ok('/')->tx->local_port;
1052my $remote_port = $t->get_ok('/')->tx->remote_port;
1053
1054like($local_port, qr!^\d+$!);
1055like($remote_port, qr!^\d+$!);
1056
1057my $port = $remote_port;
1058
1059my $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);
1060
Akrona8efaa92022-04-09 14:45:43 +02001061$fwd = $t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
1062 client_id => 'xyz',
1063 state => 'abcde',
1064 scope => 'search match',
Akrona8f87cc2023-02-23 12:21:30 +01001065 redirect_uri_server => 'http://localhost:'.$port,
1066 redirect_uri => "$redirect_url_fakeapi",
Akrona8efaa92022-04-09 14:45:43 +02001067 csrf_token => $csrf,
1068}))
1069 ->status_is(302)
Akrona8f87cc2023-02-23 12:21:30 +01001070 ->header_like('location', qr!^http://localhost:\d+/realapi/fakeclient/return\?code=.+$!)
Akrona8efaa92022-04-09 14:45:43 +02001071 ->tx->res->headers->header('location')
1072 ;
1073
Akronf47813c2023-05-08 16:24:03 +02001074unless ($^O eq 'MSWin32') {
1075 $t->get_ok($fwd)
1076 ->status_is(200)
1077 ->content_like(qr'welcome back! \[(.+?)\]')
1078 ;
1079};
Akrona8efaa92022-04-09 14:45:43 +02001080
Akrona8f87cc2023-02-23 12:21:30 +01001081my $fake_port = $port;
1082
1083while ($fake_port == $remote_port || $fake_port == $local_port) {
1084 $fake_port++;
1085};
1086
1087$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);
1088
1089$fwd = $t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
1090 client_id => 'xyz',
1091 state => 'abcde',
1092 scope => 'search match',
1093 redirect_uri_server => 'http://localhost:'.$port,
1094 redirect_uri => "$redirect_url_fakeapi",
1095 csrf_token => $csrf,
1096}))
1097 ->status_is(302)
1098 ->header_unlike('location', qr!^http://localhost:\d+/realapi/fakeclient/return\?error_description=Connection\+refused$!)
1099 ->header_like('location', qr!^http://localhost:\d+/realapi/fakeclient/return\?code=.+?$!)
1100 ->tx->res->headers->header('location')
1101 ;
1102
Akrona8efaa92022-04-09 14:45:43 +02001103$t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
1104 client_id => 'xyz',
Akron9ccf69a2023-01-31 14:21:37 +01001105 state => 'abcde',
1106 scope => 'search match',
1107 redirect_uri_server => 'http://example.com/',
1108 redirect_uri => $t->app->close_redirect_to('http://wrong'),
1109 csrf_token => $csrf,
1110}))
1111 ->status_is(302)
1112 ->header_is('location', '/settings/oauth')
1113 ->tx->res->headers->header('location')
1114 ;
1115
1116$t->get_ok('/settings/oauth')
1117 ->text_is('div.notify-error', 'Invalid redirect URI')
1118 ;
1119
1120$t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
1121 client_id => 'xyz',
Akrona8efaa92022-04-09 14:45:43 +02001122 state => 'fail',
1123 scope => 'search match',
Akron9d826902023-01-25 10:20:52 +01001124# redirect_uri_server => 'http://example.com/',
Akrona8efaa92022-04-09 14:45:43 +02001125 redirect_uri => $fake_backend_app->url_for('return_uri')->to_abs,
1126 csrf_token => $csrf,
1127}))
1128 ->status_is(302)
Akron9d826902023-01-25 10:20:52 +01001129 ->header_is('location', '/realapi/fakeclient/return?error_description=FAIL')
Akrona8efaa92022-04-09 14:45:43 +02001130 ;
1131
Akron9f2ad342022-05-04 16:16:40 +02001132my $json_post = {
1133 name => 'Funny',
1134 type => 'PUBLIC',
Akron6b75d122022-05-12 17:39:05 +02001135 desc => 'This is my plugin application 2',
Akron9f2ad342022-05-04 16:16:40 +02001136 csrf_token => $csrf,
1137 src => 'hMMM'
1138};
1139
1140$t->post_ok('/settings/oauth/register' => form => $json_post)
1141 ->status_is(200)
1142 ->element_exists('div.notify-error')
Akron99968a92022-06-03 12:32:07 +02001143 ->text_is('div.notify-error', 'Plugin declarations need to be json files')
1144 ;
1145
1146$json_post->{src} = {
1147 content => 'jjjjjj',
1148 filename => 'fun.txt'
1149};
1150
1151$t->post_ok('/settings/oauth/register' => form => $json_post)
1152 ->status_is(200)
1153 ->element_exists('div.notify-error')
Akron9f2ad342022-05-04 16:16:40 +02001154 ->text_is('div.notify-error', 'Plugins need to be confidential')
1155 ;
1156
1157$json_post->{type} = 'CONFIDENTIAL';
1158
Akron99968a92022-06-03 12:32:07 +02001159# This somehow gets removed in the last form send ...
Akron9f2ad342022-05-04 16:16:40 +02001160$json_post->{src} = {
1161 content => 'jjjjjj',
1162 filename => 'fun.txt'
1163};
1164
1165$t->post_ok('/settings/oauth/register' => form => $json_post)
1166 ->status_is(200)
1167 ->element_exists('div.notify-error')
1168 ->text_is('div.notify-error', 'Plugin declarations need to be json files')
1169 ;
1170
1171$json_post->{src} = {
1172 content => '{"name":"example"}',
1173 filename => 'fun.txt'
1174};
1175
1176$t->post_ok('/settings/oauth/register' => form => $json_post)
1177 ->status_is(200)
1178 ->element_exists_not('div.notify-error')
1179 ->element_exists('div.notify-success')
1180 ->text_is('div.notify-success', 'Registration successful')
1181 ;
1182
Akron6b75d122022-05-12 17:39:05 +02001183$t->get_ok('/settings/oauth/jh0gfjhjbfdsgzjghj==')
1184 ->status_is(200)
1185 ->text_is('div.notify-error', undef)
1186 ->text_is('li.client #client_source', '{"name":"example"}')
1187 ->text_is('li.client span.client-name', 'Funny')
1188 ->text_is('li.client p.client-desc', 'This is my plugin application 2')
1189 ->element_exists_not('li.client .client-redirect-uri tt')
1190 ->text_is('li.client .client-type tt', 'CONFIDENTIAL')
1191 ;
Akron9f2ad342022-05-04 16:16:40 +02001192
Akronb6b156e2022-03-31 14:57:49 +02001193
Akron66ef3b52022-11-22 14:25:15 +01001194# Retest client with super_client_file
1195my $client_file = tempfile;
1196
1197$client_file->spurt(
1198 '{"client_id":"2","client_secret":"k414m4r-s3cr3t"}'
1199);
1200
1201$t = Test::Mojo::WithRoles->new('Kalamar' => {
1202 Kalamar => {
1203 plugins => ['Auth']
1204 },
1205 'Kalamar-Auth' => {
1206 client_file => $client_file,
1207 oauth2 => 1
1208 }
1209});
1210
1211$t->app->plugin(
1212 Mount => {
1213 $mount_point =>
1214 $fixtures_path->child('mock.pl')
1215 }
1216);
1217
1218$csrf = $t->get_ok('/')
1219 ->status_is(200)
1220 ->element_exists_not('div.button.top a')
1221 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
1222 ;
1223
1224$t->post_ok('/user/login' => form => {
1225 handle_or_email => 'test',
1226 pwd => 'pass',
1227 csrf_token => $csrf
1228})
1229 ->status_is(302)
1230 ->header_is('Location' => '/')
1231 ->content_is('');
1232
1233$t->get_ok('/')
1234 ->status_is(200)
1235 ->element_exists_not('div.notify-error')
1236 ->element_exists('div.notify-success')
1237 ->text_is('div.notify-success', 'Login successful')
1238 ->element_exists_not('aside.off')
1239 ->element_exists_not('aside.active')
1240 ->element_exists('aside.settings')
1241 ;
1242
Akron53a171e2022-12-05 18:22:58 +01001243$main::ENV{KALAMAR_CLIENT_FILE} = $client_file;
1244
1245$t = Test::Mojo::WithRoles->new('Kalamar' => {
1246 Kalamar => {
1247 plugins => ['Auth']
1248 },
1249 'Kalamar-Auth' => {
1250 oauth2 => 1,
1251# client_file => $client_file,
1252 }
1253});
1254
1255$t->app->plugin(
1256 Mount => {
1257 $mount_point =>
1258 $fixtures_path->child('mock.pl')
1259 }
1260);
1261
1262$csrf = $t->get_ok('/')
1263 ->status_is(200)
1264 ->element_exists_not('div.button.top a')
1265 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
1266 ;
1267
1268$t->post_ok('/user/login' => form => {
1269 handle_or_email => 'test',
1270 pwd => 'pass',
1271 csrf_token => $csrf
1272})
1273 ->status_is(302)
1274 ->header_is('Location' => '/')
1275 ->content_is('');
1276
1277$t->get_ok('/')
1278 ->status_is(200)
1279 ->element_exists_not('div.notify-error')
1280 ->element_exists('div.notify-success')
1281 ->text_is('div.notify-success', 'Login successful')
1282 ->element_exists_not('aside.off')
1283 ->element_exists_not('aside.active')
1284 ->element_exists('aside.settings')
1285 ;
1286
Akron66ef3b52022-11-22 14:25:15 +01001287
Akron33f5c672019-06-24 19:40:47 +02001288done_testing;
1289__END__
Akrona8efaa92022-04-09 14:45:43 +02001290
1291