blob: 10fd3d77e37effd43001e348e29078ed07d42a33 [file] [log] [blame]
Akron33f5c672019-06-24 19:40:47 +02001use Mojo::Base -strict;
2use Test::More;
Akron6a228db2021-10-14 15:57:00 +02003use Mojo::ByteStream 'b';
Akroncdfd9d52019-07-23 11:35:00 +02004use Test::Mojo::WithRoles 'Session';
Akron66ef3b52022-11-22 14:25:15 +01005use Mojo::File qw/path tempfile/;
Akron33f5c672019-06-24 19:40:47 +02006use Data::Dumper;
7
8
9#####################
10# Start Fake server #
11#####################
Akron63d963b2019-07-05 15:35:51 +020012my $mount_point = '/realapi/';
Akron33f5c672019-06-24 19:40:47 +020013$ENV{KALAMAR_API} = $mount_point;
14
Akroncdfd9d52019-07-23 11:35:00 +020015my $t = Test::Mojo::WithRoles->new('Kalamar' => {
Akron33f5c672019-06-24 19:40:47 +020016 Kalamar => {
17 plugins => ['Auth']
18 },
19 'Kalamar-Auth' => {
20 client_id => 2,
21 client_secret => 'k414m4r-s3cr3t',
Akron59992122019-10-29 11:28:45 +010022 oauth2 => 1,
23 experimental_client_registration => 1
Akron33f5c672019-06-24 19:40:47 +020024 }
25});
26
27# Mount fake backend
28# Get the fixture path
29my $fixtures_path = path(Mojo::File->new(__FILE__)->dirname, '..', 'server');
30my $fake_backend = $t->app->plugin(
31 Mount => {
32 $mount_point =>
33 $fixtures_path->child('mock.pl')
34 }
35);
36# Configure fake backend
Akroncdfd9d52019-07-23 11:35:00 +020037my $fake_backend_app = $fake_backend->pattern->defaults->{app};
38
39# Set general app logger for simplicity
40$fake_backend_app->log($t->app->log);
41
42my $access_token = $fake_backend_app->get_token('access_token');
43my $refresh_token = $fake_backend_app->get_token('refresh_token');
44my $access_token_2 = $fake_backend_app->get_token('access_token_2');
45my $refresh_token_2 = $fake_backend_app->get_token('refresh_token_2');
46
47# Some routes to modify the session
48# This expires the session
49$t->app->routes->get('/x/expire')->to(
50 cb => sub {
51 my $c = shift;
52 $c->session(auth_exp => 0);
53 return $c->render(text => 'okay')
54 }
55);
56
57# This expires the session and removes the refresh token
58$t->app->routes->get('/x/expire-no-refresh')->to(
59 cb => sub {
60 my $c = shift;
61 $c->session(auth_exp => 0);
62 delete $c->session->{auth_r};
63 return $c->render(text => 'okay')
64 }
65);
66
67# This sets an invalid token
68$t->app->routes->get('/x/invalid')->to(
69 cb => sub {
70 my $c = shift;
71 $c->session(auth_exp => time + 1000);
72 $c->session(auth_r => $refresh_token_2);
73 $c->session(auth => 'Bearer inv4lid');
74 return $c->render(text => 'okay')
75 }
76);
77
78
79# This sets an invalid token
80$t->app->routes->get('/x/invalid-no-refresh')->to(
81 cb => sub {
82 my $c = shift;
83 $c->session(auth_exp => time + 1000);
84 delete $c->session->{auth_r};
85 $c->session(auth => 'Bearer inv4lid');
86 return $c->render(text => 'okay')
87 }
88);
89
90# This sets an invalid refresh token
91$t->app->routes->get('/x/expired-with-wrong-refresh')->to(
92 cb => sub {
93 my $c = shift;
94 $c->session(auth_exp => 0);
95 $c->session(auth => 'Bearer inv4lid');
96 $c->session(auth_r => 'inv4lid');
97 return $c->render(text => 'okay')
98 }
99);
100
Akronbc6b3f22021-01-13 14:53:12 +0100101my $q = qr!(?:\"|")!;
Akron33f5c672019-06-24 19:40:47 +0200102
Akron63d963b2019-07-05 15:35:51 +0200103$t->get_ok('/realapi/v1.0')
Akron33f5c672019-06-24 19:40:47 +0200104 ->status_is(200)
105 ->content_is('Fake server available');
106
107$t->get_ok('/?q=Baum')
108 ->status_is(200)
109 ->text_like('h1 span', qr/KorAP: Find .Baum./i)
110 ->text_like('#total-results', qr/\d+$/)
Akronbc6b3f22021-01-13 14:53:12 +0100111 ->content_like(qr/${q}authorized${q}:null/)
Akron33f5c672019-06-24 19:40:47 +0200112 ->element_exists_not('div.button.top a')
113 ->element_exists_not('aside.active')
114 ->element_exists_not('aside.off')
115 ;
116
117$t->get_ok('/')
118 ->status_is(200)
Akron9fa7cc52022-05-12 11:17:20 +0200119 ->element_exists('form[action=/user/login] input[name=handle_or_email]')
Akron33f5c672019-06-24 19:40:47 +0200120 ->element_exists('aside.active')
121 ->element_exists_not('aside.off')
122 ;
123
Akronff088112021-06-15 15:26:04 +0200124$t->get_ok('/settings/oauth')
125 ->status_is(401)
126 ->text_is('p.no-results', 'Not authenticated')
127 ;
128
Helge278fbca2022-11-29 18:49:15 +0100129$t->get_ok('/settings/marketplace')
130 ->status_is(401)
131 ->text_is('p.no-results', 'Not authenticated')
132 ;
133
Akron3e0fdc12020-05-15 16:17:21 +0200134# Test for bug with long password
135$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200136 handle_or_email => 'test',
Akron3e0fdc12020-05-15 16:17:21 +0200137 pwd => 'kjskjhndkjndqknaskjnakjdnkjdankajdnkjdsankjdsakjdfkjahzroiuqzriudjoijdmlamdlkmdsalkmdl' })
138 ->status_is(302)
139 ->header_is('Location' => '/');
140
Akron9fa7cc52022-05-12 11:17:20 +0200141$t->post_ok('/user/login' => form => { handle_or_email => 'test', pwd => 'fail' })
Akron33f5c672019-06-24 19:40:47 +0200142 ->status_is(302)
143 ->header_is('Location' => '/');
144
145$t->get_ok('/')
146 ->status_is(200)
147 ->element_exists('div.notify-error')
148 ->text_is('div.notify-error', 'Bad CSRF token')
Akron9fa7cc52022-05-12 11:17:20 +0200149 ->element_exists('input[name=handle_or_email][value=test]')
Akron33f5c672019-06-24 19:40:47 +0200150 ->element_exists_not('div.button.top a')
151 ;
152
Akron9fa7cc52022-05-12 11:17:20 +0200153$t->post_ok('/user/login' => form => { handle_or_email => 'test', pwd => 'pass' })
Akron33f5c672019-06-24 19:40:47 +0200154 ->status_is(302)
155 ->header_is('Location' => '/');
156
157my $csrf = $t->get_ok('/')
158 ->status_is(200)
159 ->element_exists('div.notify-error')
160 ->text_is('div.notify-error', 'Bad CSRF token')
161 ->element_exists_not('div.button.top a')
162 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
163 ;
164
165$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200166 handle_or_email => 'test',
Akron33f5c672019-06-24 19:40:47 +0200167 pwd => 'ldaperr',
168 csrf_token => $csrf
169})
170 ->status_is(302)
171 ->content_is('')
172 ->header_is('Location' => '/');
173
174$csrf = $t->get_ok('/')
175 ->status_is(200)
176 ->element_exists('div.notify-error')
177 ->text_is('div.notify-error', '2022: LDAP Authentication failed due to unknown user or password!')
Akron9fa7cc52022-05-12 11:17:20 +0200178 ->element_exists('input[name=handle_or_email][value=test]')
Akron33f5c672019-06-24 19:40:47 +0200179 ->element_exists_not('div.button.top a')
Akron3b3c7af2020-05-15 16:23:55 +0200180 ->element_exists_not('div.notify-success')
Akron33f5c672019-06-24 19:40:47 +0200181 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
182 ;
183
184$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200185 handle_or_email => 'test',
Akron33f5c672019-06-24 19:40:47 +0200186 pwd => 'unknown',
187 csrf_token => $csrf
188})
189 ->status_is(302)
190 ->content_is('')
191 ->header_is('Location' => '/');
192
193$csrf = $t->get_ok('/')
194 ->status_is(200)
195 ->element_exists('div.notify-error')
Akron8bbbecf2019-07-01 18:57:30 +0200196 ->text_is('div.notify-error', '2022: LDAP Authentication failed due to unknown user or password!')
Akron9fa7cc52022-05-12 11:17:20 +0200197 ->element_exists('input[name=handle_or_email][value=test]')
Akron33f5c672019-06-24 19:40:47 +0200198 ->element_exists_not('div.button.top a')
199 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
200 ;
201
202$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200203 handle_or_email => 'test',
Akron33f5c672019-06-24 19:40:47 +0200204 pwd => 'pass',
205 csrf_token => $csrf
206})
207 ->status_is(302)
208 ->content_is('')
209 ->header_is('Location' => '/');
210
211$t->get_ok('/')
212 ->status_is(200)
213 ->element_exists_not('div.notify-error')
214 ->element_exists('div.notify-success')
215 ->text_is('div.notify-success', 'Login successful')
Akron1d09b532021-06-15 18:18:25 +0200216 ->element_exists_not('aside.off')
Akrondc0b3ab2021-06-18 11:52:43 +0200217 ->element_exists_not('aside.active')
Akron1d09b532021-06-15 18:18:25 +0200218 ->element_exists('aside.settings')
Akron33f5c672019-06-24 19:40:47 +0200219 ;
220
Akron33f5c672019-06-24 19:40:47 +0200221# Now the user is logged in and should be able to
222# search with authorization
223$t->get_ok('/?q=Baum')
224 ->status_is(200)
Akron4cefe1f2019-09-04 10:11:28 +0200225 ->session_has('/auth')
226 ->session_is('/auth', 'Bearer ' . $access_token)
227 ->session_is('/auth_r', $refresh_token)
228 ->session_is('/user', 'test')
Akron33f5c672019-06-24 19:40:47 +0200229 ->text_like('h1 span', qr/KorAP: Find .Baum./i)
230 ->text_like('#total-results', qr/\d+$/)
231 ->element_exists_not('div.notify-error')
Akronbc6b3f22021-01-13 14:53:12 +0100232 ->content_like(qr/${q}authorized${q}:${q}yes${q}/)
Akron33f5c672019-06-24 19:40:47 +0200233 ->element_exists('div.button.top a')
234 ->element_exists('div.button.top a.logout[title~="test"]')
235 ;
236
Akron27031aa2020-04-28 14:57:10 +0200237$t->get_ok('/?q=Paum')
238 ->status_is(200)
239 ->text_like('h1 span', qr/KorAP: Find .Paum./i)
240 ->text_is('#total-results', '')
Akronbc6b3f22021-01-13 14:53:12 +0100241 ->content_like(qr/${q}authorized${q}:${q}yes${q}/)
Akron27031aa2020-04-28 14:57:10 +0200242 ->element_exists_not('p.hint')
243 ;
244
Akroncce055c2021-07-02 12:18:03 +0200245# Query with error
246$t->get_ok('/?q=error')
247 ->status_is(400)
248 ->text_is('#notifications .notify-error','500: Internal Server Error')
249;
Akron27031aa2020-04-28 14:57:10 +0200250
Akron33f5c672019-06-24 19:40:47 +0200251# Logout
252$t->get_ok('/user/logout')
253 ->status_is(302)
Akron4cefe1f2019-09-04 10:11:28 +0200254 ->session_hasnt('/auth')
255 ->session_hasnt('/auth_r')
256 ->session_hasnt('/user')
Akron33f5c672019-06-24 19:40:47 +0200257 ->header_is('Location' => '/');
258
259$t->get_ok('/')
260 ->status_is(200)
261 ->element_exists_not('div.notify-error')
262 ->element_exists('div.notify-success')
263 ->text_is('div.notify-success', 'Logout successful')
Akron9fa7cc52022-05-12 11:17:20 +0200264 ->element_exists("input[name=handle_or_email]")
265 ->element_exists("input[name=handle_or_email][value=test]")
Akron33f5c672019-06-24 19:40:47 +0200266 ;
267
268$t->get_ok('/?q=Baum')
269 ->status_is(200)
270 ->text_like('h1 span', qr/KorAP: Find .Baum./i)
271 ->text_like('#total-results', qr/\d+$/)
Akronbc6b3f22021-01-13 14:53:12 +0100272 ->content_like(qr/${q}authorized${q}:null/)
Akron33f5c672019-06-24 19:40:47 +0200273 ;
274
Akron27031aa2020-04-28 14:57:10 +0200275$t->get_ok('/?q=Paum')
276 ->status_is(200)
277 ->text_like('h1 span', qr/KorAP: Find .Paum./i)
278 ->text_is('#total-results', '')
Akronbc6b3f22021-01-13 14:53:12 +0100279 ->content_like(qr/${q}authorized${q}:null/)
Akron27031aa2020-04-28 14:57:10 +0200280 ->text_is('p.hint', 'Maybe you need to log in first?')
281 ;
282
283
Akron33f5c672019-06-24 19:40:47 +0200284# Get redirect
285my $fwd = $t->get_ok('/?q=Baum&ql=poliqarp')
286 ->status_is(200)
287 ->element_exists_not('div.notify-error')
288 ->tx->res->dom->at('input[name=fwd]')->attr('value')
289 ;
290
291is($fwd, '/?q=Baum&ql=poliqarp', 'Redirect is valid');
292
293$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200294 handle_or_email => 'test',
Akron33f5c672019-06-24 19:40:47 +0200295 pwd => 'pass',
296 csrf_token => $csrf,
297 fwd => 'http://bad.example.com/test'
298})
299 ->status_is(302)
300 ->header_is('Location' => '/');
301
302$t->get_ok('/')
303 ->status_is(200)
304 ->element_exists('div.notify-error')
305 ->element_exists_not('div.notify-success')
306 ->text_is('div.notify-error', 'Redirect failure')
307 ;
308
309$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200310 handle_or_email => 'test',
Akron33f5c672019-06-24 19:40:47 +0200311 pwd => 'pass',
312 csrf_token => $csrf,
313 fwd => $fwd
314})
315 ->status_is(302)
316 ->header_is('Location' => '/?q=Baum&ql=poliqarp');
317
Akron8bbbecf2019-07-01 18:57:30 +0200318$t->get_ok('/?q=Baum&ql=poliqarp')
319 ->status_is(200)
320 ->element_exists_not('div.notify-error')
321 ->element_exists('div.notify-success')
322 ->text_is('div.notify-success', 'Login successful')
Akroncdfd9d52019-07-23 11:35:00 +0200323 ->session_has('/auth')
324 ->session_is('/auth', 'Bearer ' . $access_token)
325 ->session_is('/auth_r', $refresh_token)
326 ->header_isnt('X-Kalamar-Cache', 'true')
Akron8bbbecf2019-07-01 18:57:30 +0200327 ;
328
Akroncdfd9d52019-07-23 11:35:00 +0200329# Expire the session
330# (makes the token be marked as expired - though it isn't serverside)
331$t->get_ok('/x/expire')
Akron8bbbecf2019-07-01 18:57:30 +0200332 ->status_is(200)
Akroncdfd9d52019-07-23 11:35:00 +0200333 ->content_is('okay')
334 ;
335
336## It may be a problem, but the cache is still valid
337$t->get_ok('/?q=Baum')
338 ->status_is(200)
339 ->text_like('h1 span', qr/KorAP: Find .Baum./i)
340 ->text_like('#total-results', qr/\d+$/)
Akronbc6b3f22021-01-13 14:53:12 +0100341 ->content_like(qr/${q}authorized${q}:${q}yes${q}/)
Akroncdfd9d52019-07-23 11:35:00 +0200342 ->header_is('X-Kalamar-Cache', 'true')
343 ;
344
345# Query without partial cache (unfortunately) (but no total results)
Akron58c60992021-09-07 13:11:43 +0200346my $err = $t->get_ok('/?q=baum&cutoff=true')
Akroncdfd9d52019-07-23 11:35:00 +0200347 ->status_is(200)
348 ->session_is('/auth', 'Bearer ' . $access_token_2)
349 ->session_is('/auth_r', $refresh_token_2)
Akroncdfd9d52019-07-23 11:35:00 +0200350 ->text_is('title', 'KorAP: Find »baum« with Poliqarp')
351 ->element_exists('meta[name="DC.title"][content="KorAP: Find »baum« with Poliqarp"]')
352 ->element_exists('body[itemscope][itemtype="http://schema.org/SearchResultsPage"]')
Akronbc6b3f22021-01-13 14:53:12 +0100353 ->content_like(qr/${q}authorized${q}:${q}yes${q}/)
Akroncdfd9d52019-07-23 11:35:00 +0200354 ->header_isnt('X-Kalamar-Cache', 'true')
Akronbc6b3f22021-01-13 14:53:12 +0100355 ->content_like(qr!${q}cutOff${q}:true!)
Akroncdfd9d52019-07-23 11:35:00 +0200356 ->element_exists_not('#total-results')
Akron58c60992021-09-07 13:11:43 +0200357 ->tx->res->dom->at('#error')
Akroncdfd9d52019-07-23 11:35:00 +0200358 ;
Akron58c60992021-09-07 13:11:43 +0200359is(defined $err ? $err->text : '', '');
Akroncdfd9d52019-07-23 11:35:00 +0200360
361# Expire the session and remove the refresh option
362$t->get_ok('/x/expire-no-refresh')
363 ->status_is(200)
364 ->content_is('okay')
365 ;
366
367$t->app->defaults(no_cache => 1);
368
369
370$t->get_ok('/x/invalid-no-refresh')
371 ->status_is(200)
372 ->content_is('okay')
373 ;
374
375# Query without cache
376# The token is invalid and can't be refreshed!
Akron58c60992021-09-07 13:11:43 +0200377$err = $t->get_ok('/?q=baum&cutoff=true')
Akron3c390c42020-03-30 09:06:21 +0200378 ->status_is(400)
Akroncdfd9d52019-07-23 11:35:00 +0200379 ->session_hasnt('/auth')
380 ->session_hasnt('/auth_r')
Akroncdfd9d52019-07-23 11:35:00 +0200381 ->text_is('div.notify-error','Access token invalid')
382 ->text_is('title', 'KorAP: Find »baum« with Poliqarp')
383 ->element_exists('meta[name="DC.title"][content="KorAP: Find »baum« with Poliqarp"]')
384 ->element_exists('body[itemscope][itemtype="http://schema.org/SearchResultsPage"]')
Akronbc6b3f22021-01-13 14:53:12 +0100385 ->content_unlike(qr/${q}authorized${q}:${q}yes${q}/)
Akroncdfd9d52019-07-23 11:35:00 +0200386 ->header_isnt('X-Kalamar-Cache', 'true')
387 ->element_exists('p.no-results')
Akron58c60992021-09-07 13:11:43 +0200388 ->tx->res->dom->at('#error')
Akroncdfd9d52019-07-23 11:35:00 +0200389 ;
Akron58c60992021-09-07 13:11:43 +0200390is(defined $err ? $err->text : '', '');
391
Akroncdfd9d52019-07-23 11:35:00 +0200392
393$t->get_ok('/x/invalid')
394 ->status_is(200)
395 ->content_is('okay')
396 ;
397
398# Query without cache
399# The token is invalid and can't be refreshed!
Akron58c60992021-09-07 13:11:43 +0200400$err = $t->get_ok('/?q=baum&cutoff=true')
Akroncdfd9d52019-07-23 11:35:00 +0200401 ->status_is(200)
402 ->session_is('/auth', 'Bearer ' . $access_token_2)
403 ->session_is('/auth_r', $refresh_token_2)
Akroncdfd9d52019-07-23 11:35:00 +0200404 ->element_exists_not('div.notify-error','Access token invalid')
405 ->text_is('title', 'KorAP: Find »baum« with Poliqarp')
406 ->element_exists('meta[name="DC.title"][content="KorAP: Find »baum« with Poliqarp"]')
407 ->element_exists('body[itemscope][itemtype="http://schema.org/SearchResultsPage"]')
Akronbc6b3f22021-01-13 14:53:12 +0100408 ->content_like(qr/${q}authorized${q}:${q}yes${q}/)
Akroncdfd9d52019-07-23 11:35:00 +0200409 ->header_isnt('X-Kalamar-Cache', 'true')
410 ->element_exists_not('p.no-results')
Akron58c60992021-09-07 13:11:43 +0200411 ->tx->res->dom->at('#error')
Akron8bbbecf2019-07-01 18:57:30 +0200412 ;
Akron58c60992021-09-07 13:11:43 +0200413is(defined $err ? $err->text : '', '');
Akron8bbbecf2019-07-01 18:57:30 +0200414
Akron33f5c672019-06-24 19:40:47 +0200415
Akroncdfd9d52019-07-23 11:35:00 +0200416$t->get_ok('/x/expired-with-wrong-refresh')
417 ->status_is(200)
418 ->content_is('okay')
419 ;
Akron4796e002019-07-05 10:13:15 +0200420
Akron4796e002019-07-05 10:13:15 +0200421
Akroncdfd9d52019-07-23 11:35:00 +0200422# The token is invalid and can't be refreshed!
Akron58c60992021-09-07 13:11:43 +0200423my $dom = $t->get_ok('/?q=baum&cutoff=true')
Akron3c390c42020-03-30 09:06:21 +0200424 ->status_is(400)
Akroncdfd9d52019-07-23 11:35:00 +0200425 ->session_hasnt('/auth')
426 ->session_hasnt('/auth_r')
Akroncdfd9d52019-07-23 11:35:00 +0200427 ->text_is('div.notify-error','Refresh token is expired')
428 ->text_is('title', 'KorAP: Find »baum« with Poliqarp')
Akronbc6b3f22021-01-13 14:53:12 +0100429 ->content_unlike(qr/${q}authorized${q}:${q}yes${q}/)
Akroncdfd9d52019-07-23 11:35:00 +0200430 ->element_exists('p.no-results')
Akron58c60992021-09-07 13:11:43 +0200431 ->tx->res->dom;
432
433$csrf = $dom->at('input[name="csrf_token"]')
Akron59992122019-10-29 11:28:45 +0100434 ->attr('value')
Akroncdfd9d52019-07-23 11:35:00 +0200435 ;
Akron4796e002019-07-05 10:13:15 +0200436
Akron58c60992021-09-07 13:11:43 +0200437$err = $dom->at('#error');
438is(defined $err ? $err->text : '', '');
439
440
Akron6a228db2021-10-14 15:57:00 +0200441# This should fail
442my $wide_char_login = "\x{61}\x{E5}\x{61}"; # "\x{443}\x{434}";
Akron59992122019-10-29 11:28:45 +0100443$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200444 handle_or_email => $wide_char_login,
Akron6a228db2021-10-14 15:57:00 +0200445 pwd => 'pass',
446 csrf_token => $csrf,
447 fwd => $fwd
448})
449 ->status_is(302)
450 ->header_is('Location' => '/');
451
452$t->get_ok('/')
453 ->status_is(200)
454 ->element_exists('div.notify-error')
455 ->text_is('div.notify-error', 'Invalid character in request')
Akron9fa7cc52022-05-12 11:17:20 +0200456 ->element_exists('input[name=handle_or_email]:not([value])')
Akron6a228db2021-10-14 15:57:00 +0200457 ->element_exists_not('div.button.top a')
458 ;
459
460# Login:
461# UTF8 request
462my $username = b('täst')->encode;
463$t->post_ok('/user/login' => form => {
Akron9fa7cc52022-05-12 11:17:20 +0200464 handle_or_email => $username,
Akron59992122019-10-29 11:28:45 +0100465 pwd => 'pass',
466 csrf_token => $csrf
467})
468 ->status_is(302)
469 ->content_is('')
470 ->header_is('Location' => '/');
471
472$t->get_ok('/')
473 ->status_is(200)
474 ->element_exists_not('div.notify-error')
475 ->element_exists('div.notify-success')
476 ->text_is('div.notify-success', 'Login successful')
Akron6a228db2021-10-14 15:57:00 +0200477 ->attr_is('a.logout', 'title', "Logout: $username")
Akron1d09b532021-06-15 18:18:25 +0200478 ->element_exists_not('aside.off')
Akrondc0b3ab2021-06-18 11:52:43 +0200479 ->element_exists_not('aside.active')
Akron1d09b532021-06-15 18:18:25 +0200480 ->element_exists('aside.settings')
Akron59992122019-10-29 11:28:45 +0100481 ;
482
483$t->get_ok('/settings/oauth')
484 ->text_is('form.form-table legend', 'Register new client application')
485 ->attr_is('form.oauth-register','action', '/settings/oauth/register')
Akron9f2ad342022-05-04 16:16:40 +0200486 ->text_is('label[for=name]','Name of the client application')
487 ->text_is('label[for=type]','Type of the client application')
488 ->text_is('label[for=desc]','Short description')
489 ->text_is('label[for=url]','Homepage')
490 ->text_is('label[for=redirect_uri]','Redirect URI')
491 ->text_is('label[for=src]','Declaration of the plugin (*.json file)')
Akron1a9d5be2020-03-19 17:28:33 +0100492 ->element_exists('ul.client-list')
493 ->element_exists_not('ul.client-list > li')
Akronad011bb2021-06-10 12:16:36 +0200494 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
495 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
496 ->header_is('Pragma','no-cache')
Akron59992122019-10-29 11:28:45 +0100497 ;
Akronad011bb2021-06-10 12:16:36 +0200498
Helge278fbca2022-11-29 18:49:15 +0100499
500$t->get_ok('/settings/marketplace')
501 ->status_is(200)
502 ->text_is('html head title' => 'Marketplace')
503 ;
504
Akron59992122019-10-29 11:28:45 +0100505$csrf = $t->post_ok('/settings/oauth/register' => form => {
506 name => 'MyApp',
507 type => 'PUBLIC',
Akron6b75d122022-05-12 17:39:05 +0200508 desc => 'This is my plugin application'
Akron59992122019-10-29 11:28:45 +0100509})
510 ->text_is('div.notify-error', 'Bad CSRF token')
511 ->tx->res->dom->at('input[name="csrf_token"]')
512 ->attr('value')
513 ;
514
515$t->post_ok('/settings/oauth/register' => form => {
516 name => 'MyApp',
Akron99968a92022-06-03 12:32:07 +0200517 type => 'PUBLIC',
Akron6b75d122022-05-12 17:39:05 +0200518 desc => 'This is my plugin application',
Akron9f2ad342022-05-04 16:16:40 +0200519 csrf_token => $csrf,
520 src => {
521 filename => '',
522 content => ''
523 }
Akron59992122019-10-29 11:28:45 +0100524})
525 ->status_is(200)
526 ->element_exists('div.notify-success')
527 ->text_is('legend', 'Client Credentials')
528 ->text_is('label[for=client_id]', 'ID of the client application')
529 ->element_exists('input[name=client_id][readonly][value]')
Akron99968a92022-06-03 12:32:07 +0200530 ->element_exists_not('input[name=client_secret][readonly][value]')
Akronad011bb2021-06-10 12:16:36 +0200531 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
532 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
533 ->header_is('Pragma','no-cache')
Akron59992122019-10-29 11:28:45 +0100534 ;
Akron4796e002019-07-05 10:13:15 +0200535
Akron58c60992021-09-07 13:11:43 +0200536my $anchor = $t->get_ok('/settings/oauth')
Akronc1aaf932021-06-09 12:19:15 +0200537 ->text_is('.form-table legend', 'Register new client application')
538 ->attr_is('.oauth-register','action', '/settings/oauth/register')
Akron17de86e2020-04-16 16:03:40 +0200539 ->text_is('ul.client-list > li > span.client-name a', 'MyApp')
Akron6b75d122022-05-12 17:39:05 +0200540 ->text_is('ul.client-list > li > p.client-desc', 'This is my plugin application')
Akronad011bb2021-06-10 12:16:36 +0200541 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
542 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
543 ->header_is('Pragma','no-cache')
Akronb6b156e2022-03-31 14:57:49 +0200544 ->tx->res->dom->at('ul.client-list > li > p.client-url a')
Akron17de86e2020-04-16 16:03:40 +0200545 ;
Akron58c60992021-09-07 13:11:43 +0200546is(defined $anchor ? $anchor->text : '', '');
Akron17de86e2020-04-16 16:03:40 +0200547
Akron041ca4d2021-06-10 11:52:51 +0200548$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akron17de86e2020-04-16 16:03:40 +0200549 ->status_is(200)
Akronc1aaf932021-06-09 12:19:15 +0200550 ->text_is('ul.client-list > li.client > span.client-name', 'MyApp')
Akron6b75d122022-05-12 17:39:05 +0200551 ->text_is('ul.client-list > li.client > p.client-desc', 'This is my plugin application')
Akron17de86e2020-04-16 16:03:40 +0200552 ->text_is('a.client-unregister', 'Unregister')
Akron041ca4d2021-06-10 11:52:51 +0200553 ->attr_is('a.client-unregister', 'href', '/settings/oauth/fCBbQkA2NDA3MzM1Yw==/unregister?name=MyApp')
Akron1a9d5be2020-03-19 17:28:33 +0100554 ;
555
Akron041ca4d2021-06-10 11:52:51 +0200556$csrf = $t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/unregister?name=MyApp')
Akron1a9d5be2020-03-19 17:28:33 +0100557 ->content_like(qr!Do you really want to unregister \<span class="client-name"\>MyApp\<\/span\>?!)
Akronc1aaf932021-06-09 12:19:15 +0200558 ->attr_is('.form-table input[name=client-name]', 'value', 'MyApp')
Akronad011bb2021-06-10 12:16:36 +0200559 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
560 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
561 ->header_is('Pragma','no-cache')
Akron1a9d5be2020-03-19 17:28:33 +0100562 ->tx->res->dom->at('input[name="csrf_token"]')
563 ->attr('value')
564 ;
565
Akron041ca4d2021-06-10 11:52:51 +0200566$t->post_ok('/settings/oauth/xxxx==/unregister' => form => {
Akron1a9d5be2020-03-19 17:28:33 +0100567 'client-name' => 'MyApp',
Akron1a9d5be2020-03-19 17:28:33 +0100568 'csrf_token' => $csrf
569})->status_is(302)
570 ->content_is('')
571 ->header_is('Location' => '/settings/oauth')
572 ;
573
574$t->get_ok('/settings/oauth')
Akronc1aaf932021-06-09 12:19:15 +0200575 ->text_is('.form-table legend', 'Register new client application')
576 ->attr_is('.oauth-register','action', '/settings/oauth/register')
Akron1a9d5be2020-03-19 17:28:33 +0100577 ->element_exists('ul.client-list > li')
578 ->text_is('div.notify', 'Unknown client with xxxx==.')
Akronad011bb2021-06-10 12:16:36 +0200579 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
580 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
581 ->header_is('Pragma','no-cache')
Akron1a9d5be2020-03-19 17:28:33 +0100582 ;
583
Akron041ca4d2021-06-10 11:52:51 +0200584$t->post_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/unregister' => form => {
Akron1a9d5be2020-03-19 17:28:33 +0100585 'client-name' => 'MyApp',
Akron1a9d5be2020-03-19 17:28:33 +0100586 'csrf_token' => $csrf
587})->status_is(302)
588 ->content_is('')
589 ->header_is('Location' => '/settings/oauth')
590 ;
591
592$t->get_ok('/settings/oauth')
Akronc1aaf932021-06-09 12:19:15 +0200593 ->text_is('.form-table legend', 'Register new client application')
594 ->attr_is('.oauth-register','action', '/settings/oauth/register')
Akron1a9d5be2020-03-19 17:28:33 +0100595 ->element_exists_not('ul.client-list > li')
596 ->text_is('div.notify-success', 'Successfully deleted MyApp')
Akronad011bb2021-06-10 12:16:36 +0200597 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
598 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
599 ->header_is('Pragma','no-cache')
Akron1a9d5be2020-03-19 17:28:33 +0100600 ;
601
Akron83209f72021-01-29 17:54:15 +0100602$t->post_ok('/settings/oauth/register' => form => {
603 name => 'MyApp2',
604 type => 'PUBLIC',
Akron6b75d122022-05-12 17:39:05 +0200605 desc => 'This is my plugin application',
Akron83209f72021-01-29 17:54:15 +0100606 csrf_token => $csrf
607})->status_is(200)
608 ->element_exists('div.notify-success')
609 ->text_is('legend', 'Client Credentials')
610 ->text_is('label[for=client_id]', 'ID of the client application')
611 ->element_exists('input[name=client_id][readonly][value]')
612 ->element_exists_not('input[name=client_secret][readonly][value]')
Akronad011bb2021-06-10 12:16:36 +0200613 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
614 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
615 ->header_is('Pragma','no-cache')
Akronb6b156e2022-03-31 14:57:49 +0200616 ->element_exists('.client-issue-token')
Akron83209f72021-01-29 17:54:15 +0100617 ;
618
Akron041ca4d2021-06-10 11:52:51 +0200619$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akron83209f72021-01-29 17:54:15 +0100620 ->text_is('.client-name', 'MyApp2')
Akron6b75d122022-05-12 17:39:05 +0200621 ->text_is('.client-desc', 'This is my plugin application')
Akrone997bb52021-06-11 16:44:06 +0200622 ->text_is('.client-issue-token', 'Issue new token')
Akron041ca4d2021-06-10 11:52:51 +0200623 ->attr_is('.client-issue-token', 'href', '/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token?name=MyApp2')
Akronad011bb2021-06-10 12:16:36 +0200624 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
625 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
626 ->header_is('Pragma','no-cache')
Akrone997bb52021-06-11 16:44:06 +0200627 ->text_is('ul.token-list label[for=token]', 'Access Token')
628 ->text_is('p[name=created]', 'Created at ')
629 ->text_is('p[name=expires]', 'Expires in 31533851 seconds.')
Akronb6b156e2022-03-31 14:57:49 +0200630 ->element_exists('.client-issue-token')
Akron83209f72021-01-29 17:54:15 +0100631 ;
632
Akron041ca4d2021-06-10 11:52:51 +0200633$csrf = $t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token?name=MyApp2')
Akron83209f72021-01-29 17:54:15 +0100634 ->status_is(200)
Akron041ca4d2021-06-10 11:52:51 +0200635 ->attr_is('#issue-token','action', '/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token')
Akron83209f72021-01-29 17:54:15 +0100636 ->attr_is('input[name=client-id]', 'value', 'fCBbQkA2NDA3MzM1Yw==')
637 ->attr_is('input[name=name]', 'value', 'MyApp2')
Akronad011bb2021-06-10 12:16:36 +0200638 ->header_is('Cache-Control','max-age=0, no-cache, no-store, must-revalidate')
639 ->header_is('Expires','Thu, 01 Jan 1970 00:00:00 GMT')
640 ->header_is('Pragma','no-cache')
Akrone997bb52021-06-11 16:44:06 +0200641 ->text_is('a.button-abort', 'Abort')
642 ->attr_is('#issue-token input[type=submit]', 'value', 'Issue new token')
643 ->content_like(qr!Issue a new token for!)
Akron83209f72021-01-29 17:54:15 +0100644 ->tx->res->dom->at('input[name="csrf_token"]')
645 ->attr('value')
646 ;
647
Akron041ca4d2021-06-10 11:52:51 +0200648$t->post_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token' => form => {
Akron83209f72021-01-29 17:54:15 +0100649 csrf_token => $csrf,
650 name => 'MyApp2',
651 'client-id' => 'fCBbQkA2NDA3MzM1Yw=='
652})
Akronbc94a9c2021-04-15 00:07:35 +0200653 ->status_is(302)
Akron041ca4d2021-06-10 11:52:51 +0200654 ->header_is('Location','/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronbc94a9c2021-04-15 00:07:35 +0200655 ;
656
Akron041ca4d2021-06-10 11:52:51 +0200657$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
658 ->status_is(200)
Akronbc94a9c2021-04-15 00:07:35 +0200659 ->text_is('div.notify-success', 'New access token created')
Akronad011bb2021-06-10 12:16:36 +0200660 ->status_is(200)
661 ->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')
Akron83209f72021-01-29 17:54:15 +0100664 ;
665
Akron041ca4d2021-06-10 11:52:51 +0200666$csrf = $t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200667 ->status_is(200)
Akron041ca4d2021-06-10 11:52:51 +0200668 ->attr_is('form.token-revoke', 'action', '/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token/revoke')
Akronc1aaf932021-06-09 12:19:15 +0200669 ->attr_is('form.token-revoke input[name=token]', 'value', 'jhkhkjhk_hjgjsfz67i')
670 ->attr_is('form.token-revoke input[name=name]', 'value', 'MyApp2')
671 ->tx->res->dom->at('input[name="csrf_token"]')
672 ->attr('value')
673 ;
674
Akron041ca4d2021-06-10 11:52:51 +0200675$t->post_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token/revoke' => form => {
Akronc1aaf932021-06-09 12:19:15 +0200676 csrf_token => $csrf,
677 name => 'MyApp2',
678 token => 'jhkhkjhk_hjgjsfz67i'
679})
680 ->status_is(200)
Akron041ca4d2021-06-10 11:52:51 +0200681 ->attr_is('form#revoke-token','action','/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token?_method=DELETE')
Akronc1aaf932021-06-09 12:19:15 +0200682 ->attr_is('form#revoke-token','method','POST')
683 ->attr_is('form#revoke-token input[name=token]','value','jhkhkjhk_hjgjsfz67i')
Akrone997bb52021-06-11 16:44:06 +0200684 ->text_is('a.button-abort', 'Abort')
685 ->attr_is('#revoke-token input[type=submit]', 'value', 'Revoke')
686 ->content_like(qr!Revoke a token for!)
Akronc1aaf932021-06-09 12:19:15 +0200687;
688
689
690# CSRF missing
Akron041ca4d2021-06-10 11:52:51 +0200691$t->post_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token?_method=DELETE' => form => {
Akronc1aaf932021-06-09 12:19:15 +0200692 name => 'MyApp2',
693 token => 'jhkhkjhk_hjgjsfz67i'
694})->status_is(302)
Akron041ca4d2021-06-10 11:52:51 +0200695 ->header_is('Location','/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200696 ;
697
Akron041ca4d2021-06-10 11:52:51 +0200698$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200699 ->element_exists_not('div.notify-success')
700 ->text_is('div.notify-error', 'Bad CSRF token')
701 ;
702
703# Token missing
Akron041ca4d2021-06-10 11:52:51 +0200704$t->post_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token?_method=DELETE' => form => {
Akronc1aaf932021-06-09 12:19:15 +0200705 name => 'MyApp2',
706 csrf_token => $csrf,
707})->status_is(302)
Akron041ca4d2021-06-10 11:52:51 +0200708 ->header_is('Location','/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200709 ;
710
Akron041ca4d2021-06-10 11:52:51 +0200711$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200712 ->element_exists_not('div.notify-success')
713 ->text_is('div.notify-error', 'Some fields are invalid')
714 ;
715
Akron041ca4d2021-06-10 11:52:51 +0200716$t->post_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==/token?_method=DELETE' => form => {
Akronc1aaf932021-06-09 12:19:15 +0200717 name => 'MyApp2',
718 csrf_token => $csrf,
719 token => 'jhkhkjhk_hjgjsfz67i'
720})->status_is(302)
Akron041ca4d2021-06-10 11:52:51 +0200721 ->header_is('Location','/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200722 ;
723
Akron041ca4d2021-06-10 11:52:51 +0200724$t->get_ok('/settings/oauth/fCBbQkA2NDA3MzM1Yw==')
Akronc1aaf932021-06-09 12:19:15 +0200725 ->element_exists_not('div.notify-error')
726 ->text_is('div.notify-success', 'Token was revoked successfully')
727 ;
Akron83209f72021-01-29 17:54:15 +0100728
Akronb6b156e2022-03-31 14:57:49 +0200729$t->app->routes->get('/x/redirect-target')->to(
730 cb => sub {
731 my $c = shift;
732 return $c->render(text => 'redirected');
733 }
734);
735
736$csrf = $t->post_ok('/settings/oauth/register' => form => {
737 name => 'MyConfApp',
738 type => 'CONFIDENTIAL',
Akron6b75d122022-05-12 17:39:05 +0200739 desc => 'This is my plugin application',
Akronb6b156e2022-03-31 14:57:49 +0200740})
741 ->text_is('div.notify-error', 'Bad CSRF token')
742 ->tx->res->dom->at('input[name="csrf_token"]')
743 ->attr('value')
744 ;
745
746$t->post_ok('/settings/oauth/register' => form => {
747 name => 'MyConfApp',
748 type => 'CONFIDENTIAL',
749 desc => 'This is my confidential application',
750 csrf_token => $csrf,
751 redirect_uri => 'http://localhost/redirect-target'
752})
753 ->text_is('div.notify-error', undef)
754 ->text_is('li.client span.client-name', 'MyConfApp')
755 ->text_is('li.client p.client-desc', 'This is my confidential application')
756 ->text_is('li.client .client-redirect-uri tt', 'http://localhost/redirect-target')
757 ->text_is('li.client .client-type tt', 'CONFIDENTIAL')
758 ->element_exists_not('.client-issue-token')
759 ;
760
761$t->post_ok('/settings/oauth/register' => form => {
762 name => 'MyConfApp2',
763 type => 'CONFIDENTIAL',
764 desc => 'This is my second confidential application',
765 csrf_token => $csrf,
766 redirect_uri => 'http://localhost/FAIL'
767})
768 ->status_is(302)
769 ->header_is('location','/settings/oauth/')
770 ;
771
772$t->get_ok('/settings/oauth/')
773 ->text_is('div.notify-error', 'invalid_request: http://localhost/FAIL is invalid.')
774 ;
775
Akrona8efaa92022-04-09 14:45:43 +0200776# OAuth client authorization flow
777$t->get_ok(Mojo::URL->new('/settings/oauth/authorize'))
778 ->status_is(302)
Akron5ea0f5d2023-01-20 11:51:43 +0100779 ->header_is('location','/settings/oauth')
780 ;
781
782$t->get_ok('/settings/oauth/')
Akron2c142ab2023-01-30 13:21:57 +0100783 ->text_is('div.notify-error', 'Client ID required')
784 ;
785
786$t->get_ok(Mojo::URL->new('/settings/oauth/authorize?client_id=xyz'))
787 ->status_is(302)
788 ->header_is('location','/settings/oauth')
789 ;
790
791$t->get_ok('/settings/oauth/')
792 ->text_is('div.notify-error', 'Scope required')
Akrona8efaa92022-04-09 14:45:43 +0200793 ;
794
Akrondb1f4672023-01-24 12:05:07 +0100795# OAuth client authorization flow
796$t->get_ok(Mojo::URL->new('/settings/oauth/authorize?client_id=abc'))
797 ->status_is(302)
798 ->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', 'Scope required')
803 ;
804
805# OAuth client authorization flow
806$t->get_ok(Mojo::URL->new('/settings/oauth/authorize?client_id=abc&scope=match'))
807 ->status_is(302)
808 ->header_is('location','/settings/oauth')
809 ;
810
811$t->get_ok('/settings/oauth/')
Akrondb1f4672023-01-24 12:05:07 +0100812 ->text_is('div.notify-error', 'Unknown client with abc.')
813 ;
814
Akrona8efaa92022-04-09 14:45:43 +0200815# Logout
816$t->get_ok('/x/expired-with-wrong-refresh');
817
818$t->get_ok('/user/logout')
819 ->status_is(302)
820 ->session_hasnt('/auth')
821 ->session_hasnt('/auth_r')
822 ->session_hasnt('/user')
823 ->header_is('Location' => '/');
824
Akron001dcd22023-02-07 08:38:11 +0100825$t->get_ok('/')
Akrona8efaa92022-04-09 14:45:43 +0200826 ->status_is(200)
827 ->element_exists_not('div.notify-error')
828 ->element_exists('div.notify-success')
829 ->text_is('div.notify-success', 'Logout successful')
Akron9fa7cc52022-05-12 11:17:20 +0200830 ->element_exists("input[name=handle_or_email]")
Akron001dcd22023-02-07 08:38:11 +0100831 ;
832
833# OAuth client authorization flow - but user not logged in
834$t->get_ok(Mojo::URL->new('/settings/oauth/authorize'))
835 ->status_is(302)
836 ->header_is('location','/')
837 ;
838
839$csrf = $t->get_ok('/')
840 ->status_is(200)
841 ->element_exists('div.notify-error')
842 ->element_exists_not('div.notify-success')
843 ->text_is('div.notify-error', 'Client ID required')
844 ->element_exists("input[name=handle_or_email]")
Akrona8efaa92022-04-09 14:45:43 +0200845 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
846 ;
847
Akron408bc7c2022-04-28 15:46:43 +0200848$fake_backend_app->add_client({
849 "client_id" => 'xyz',
850 "client_name" => 'New added client',
851 "client_description" => 'This is a new client',
852 "client_url" => 'http://example.com',
853 "client_type" => 'CONFIDENTIAL'
854# "client_redirect_uri" => $redirect_uri
855});
856
Helge278fbca2022-11-29 18:49:15 +0100857
Akron9d826902023-01-25 10:20:52 +0100858$fake_backend_app->add_client({
859 "client_id" => 'xyz2',
860 "client_name" => 'New added client',
861 "client_description" => 'This is a new client',
862 "client_url" => 'http://example.com',
863 "client_type" => 'CONFIDENTIAL',
864 "client_redirect_uri" => 'http://redirect.url/'
865});
866
Akroneb39cf32023-04-03 14:40:48 +0200867$t->get_ok('/settings/oauth/xyz2')
868 ->status_is(200)
869 ->text_is('li.client span.client-name','New added client')
870 ->attr_is('li.client p.client-url a','href','http://example.com')
871 ->attr_is('li.client input[name=client_id]','value','xyz2')
872 ->element_exists('li.client p.client-type-confidential')
873 ->text_is('li.client p.client-redirect-uri tt','http://redirect.url/')
874 ;
875
Akron9d826902023-01-25 10:20:52 +0100876$fwd = $t->get_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
877 client_id => 'xyz2',
878 scope => 'search match',
879 redirect_uri => 'http://test.com/',
880}))
881 ->status_is(200)
882 ->text_is('div.notify-warn', 'redirect_uri host differs from registered host')
883 ->element_exists_not('div.notify-error')
884 ->element_exists_not('div.notify-success')
885 ->element_exists("input[name=handle_or_email]")
886 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
887 ;
888
889$fwd = $t->get_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
890 client_id => 'xyz2',
891 scope => 'search match',
892 redirect_uri => 'http://redirect.url:9000/',
893}))
894 ->status_is(200)
895 ->text_is('div.notify-warn', 'redirect_uri port differs from registered port')
896 ->element_exists_not('div.notify-error')
897 ->element_exists_not('div.notify-success')
898 ->element_exists("input[name=handle_or_email]")
899 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
900 ;
901
Helge278fbca2022-11-29 18:49:15 +0100902
903$fake_backend_app->add_plugin({
904"source" => {"key1" => 'wert1', "key2" => 'wert2'},
905"client_id" => "52abc",
906"permitted" => 'true',
907"client_id" => '52abc',
908"client_name" => 'Plugin 1',
909"client_type" => 'CONFIDENTIAL',
910"client_description" =>"Description Plugin 1",
911"client_url" => "http://example.client.de",
912"registration_date" => "2022-05-31T14:30:09+02:00[Europe/Berlin]",
913"registered_by" => "system"
914});
915
916
Akrona8efaa92022-04-09 14:45:43 +0200917$fwd = $t->get_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
918 client_id => 'xyz',
919 state => 'abcde',
920 scope => 'search match',
921 redirect_uri => 'http://test.com/',
922}))
923 ->status_is(200)
924 ->attr_is('input[name=client_id]','value','xyz')
925 ->attr_is('input[name=state]','value','abcde')
Akrona8efaa92022-04-09 14:45:43 +0200926 ->attr_like('input[name=fwd]','value',qr!test\.com!)
Akron408bc7c2022-04-28 15:46:43 +0200927 ->text_is('span.client-name','New added client')
Akrona8efaa92022-04-09 14:45:43 +0200928 ->text_is('div.intro p:nth-child(2)', 'Please log in!')
929 ->tx->res->dom->at('input[name=fwd]')->attr('value')
930 ;
931
932$fwd = $t->post_ok(Mojo::URL->new('/user/login')->query({
933 csrf_token => $csrf,
934 client_id => 'xyz',
935 state => 'abcde',
936 scope => 'search match',
937 redirect_uri => 'http://test.com/',
Akron9fa7cc52022-05-12 11:17:20 +0200938 handle_or_email => 'test',
Akrona8efaa92022-04-09 14:45:43 +0200939 pwd => 'pass',
940 fwd => $fwd
941}))
942 ->status_is(302)
943 ->header_like('location', qr!/settings/oauth/authorize!)
944 ->tx->res->headers->header('location')
945 ;
946
947$t->get_ok($fwd)
948 ->status_is(200)
949 ->attr_is('input[name=client_id]','value','xyz')
950 ->attr_is('input[name=state]','value','abcde')
Akron9d826902023-01-25 10:20:52 +0100951 ->attr_like('input[name=redirect_uri]','value', qr!^http://test\.com\/\?crto=.{3,}!)
Akrona8efaa92022-04-09 14:45:43 +0200952 ->text_is('ul#scopes li:nth-child(1)','search')
953 ->text_is('ul#scopes li:nth-child(2)','match')
Akron408bc7c2022-04-28 15:46:43 +0200954 ->text_is('span.client-name','New added client')
Akrona8efaa92022-04-09 14:45:43 +0200955 ->attr_is('a.form-button','href','http://test.com/')
956 ->attr_is('a.embedded-link', 'href', '/doc/korap/kalamar')
Akron9d826902023-01-25 10:20:52 +0100957 ->element_exists_not('div.notify-error')
958 ->element_exists_not('div.notify-warn')
959 ->element_exists_not('blockquote.warning')
Akrona8efaa92022-04-09 14:45:43 +0200960 ;
961
Helge278fbca2022-11-29 18:49:15 +0100962$t->get_ok('/settings/marketplace')
963 ->status_is(200)
964 ->text_is('html head title' => 'Marketplace')
965 ->element_exists('ul.plugin-list')
966 ->element_exists('ul.plugin-list > li')
967 ->element_exists('p.registration_date')
968 ->element_exists('p.registered_by')
969 ->text_is('span.client-name','Plugin 1')
970 ->text_is('p.plugin-desc','Description Plugin 1')
971 ;
972
973$fake_backend_app->add_plugin({
974"source" => {"one" => '1', "two" => '2'},
975"permitted" => 'false',
976"client_id" => '53abc',
977"client_name" => 'Plugin 2',
978"client_type" => 'CONFIDENTIAL',
979"client_description" =>'Description Plugin 2'
980});
981
982$fake_backend_app->add_plugin({
983"source" => {"answer" => '42', "hello" => 'world'},
984"permitted" => 'true',
985"client_id" => '54abc',
986"client_name" => 'Plugin 3',
987"client_type" => 'CONFIDENTIAL',
988"client_description" =>'Description Plugin 3'
989});
990
991$t->get_ok('/settings/marketplace')
992 ->status_is(200)
993 ->element_exists('ul.plugin-list')
994 ->element_exists('ul.plugin-list > li')
995 ->text_is('span.client-name','Plugin 1')
996 ->text_is('p.plugin-desc','Description Plugin 1')
997 ->element_exists('ul.plugin-list > li + li')
998 ->text_isnt('ul.plugin-list > li + li >span.client-name','Plugin 2')
999 ->text_isnt('ul.plugin-list > li + li >p.plugin-desc','Description Plugin 2')
1000 ->text_is('ul.plugin-list > li + li >span.client-name','Plugin 3')
1001 ->text_is('ul.plugin-list > li + li >p.plugin-desc','Description Plugin 3')
1002 ;
1003
1004
Akrona8efaa92022-04-09 14:45:43 +02001005$t->get_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
1006 client_id => 'xyz',
1007 state => 'abcde',
1008 scope => 'search match',
1009 redirect_uri => 'http://test.com/'
1010}))
1011 ->status_is(200)
1012 ->attr_is('input[name=client_id]','value','xyz')
1013 ->attr_is('input[name=state]','value','abcde')
Akrona8efaa92022-04-09 14:45:43 +02001014 ->text_is('ul#scopes li:nth-child(1)','search')
1015 ->text_is('ul#scopes li:nth-child(2)','match')
Akron408bc7c2022-04-28 15:46:43 +02001016 ->text_is('span.client-name','New added client')
Akrona8efaa92022-04-09 14:45:43 +02001017 ->attr_is('a.form-button','href','http://test.com/')
1018 ->attr_is('a.embedded-link', 'href', '/doc/korap/kalamar')
1019 ;
1020
1021$t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
1022 client_id => 'xyz',
1023 state => 'abcde',
1024 scope => 'search match',
1025 redirect_uri => 'http://test.com/'
1026}))
1027 ->status_is(302)
Akron9d826902023-01-25 10:20:52 +01001028 ->header_is('location', '/settings/oauth?error_description=Bad+CSRF+token')
Akrona8efaa92022-04-09 14:45:43 +02001029 ;
1030
Akrona8f87cc2023-02-23 12:21:30 +01001031
1032my $local_port = $t->get_ok('/')->tx->local_port;
1033my $remote_port = $t->get_ok('/')->tx->remote_port;
1034
1035like($local_port, qr!^\d+$!);
1036like($remote_port, qr!^\d+$!);
1037
1038my $port = $remote_port;
1039
1040my $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);
1041
Akrona8efaa92022-04-09 14:45:43 +02001042$fwd = $t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
1043 client_id => 'xyz',
1044 state => 'abcde',
1045 scope => 'search match',
Akrona8f87cc2023-02-23 12:21:30 +01001046 redirect_uri_server => 'http://localhost:'.$port,
1047 redirect_uri => "$redirect_url_fakeapi",
Akrona8efaa92022-04-09 14:45:43 +02001048 csrf_token => $csrf,
1049}))
1050 ->status_is(302)
Akrona8f87cc2023-02-23 12:21:30 +01001051 ->header_like('location', qr!^http://localhost:\d+/realapi/fakeclient/return\?code=.+$!)
Akrona8efaa92022-04-09 14:45:43 +02001052 ->tx->res->headers->header('location')
1053 ;
1054
1055$t->get_ok($fwd)
1056 ->status_is(200)
1057 ->content_like(qr'welcome back! \[(.+?)\]')
1058 ;
1059
Akrona8f87cc2023-02-23 12:21:30 +01001060my $fake_port = $port;
1061
1062while ($fake_port == $remote_port || $fake_port == $local_port) {
1063 $fake_port++;
1064};
1065
1066$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);
1067
1068$fwd = $t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
1069 client_id => 'xyz',
1070 state => 'abcde',
1071 scope => 'search match',
1072 redirect_uri_server => 'http://localhost:'.$port,
1073 redirect_uri => "$redirect_url_fakeapi",
1074 csrf_token => $csrf,
1075}))
1076 ->status_is(302)
1077 ->header_unlike('location', qr!^http://localhost:\d+/realapi/fakeclient/return\?error_description=Connection\+refused$!)
1078 ->header_like('location', qr!^http://localhost:\d+/realapi/fakeclient/return\?code=.+?$!)
1079 ->tx->res->headers->header('location')
1080 ;
1081
Akrona8efaa92022-04-09 14:45:43 +02001082$t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
1083 client_id => 'xyz',
Akron9ccf69a2023-01-31 14:21:37 +01001084 state => 'abcde',
1085 scope => 'search match',
1086 redirect_uri_server => 'http://example.com/',
1087 redirect_uri => $t->app->close_redirect_to('http://wrong'),
1088 csrf_token => $csrf,
1089}))
1090 ->status_is(302)
1091 ->header_is('location', '/settings/oauth')
1092 ->tx->res->headers->header('location')
1093 ;
1094
1095$t->get_ok('/settings/oauth')
1096 ->text_is('div.notify-error', 'Invalid redirect URI')
1097 ;
1098
1099$t->post_ok(Mojo::URL->new('/settings/oauth/authorize')->query({
1100 client_id => 'xyz',
Akrona8efaa92022-04-09 14:45:43 +02001101 state => 'fail',
1102 scope => 'search match',
Akron9d826902023-01-25 10:20:52 +01001103# redirect_uri_server => 'http://example.com/',
Akrona8efaa92022-04-09 14:45:43 +02001104 redirect_uri => $fake_backend_app->url_for('return_uri')->to_abs,
1105 csrf_token => $csrf,
1106}))
1107 ->status_is(302)
Akron9d826902023-01-25 10:20:52 +01001108 ->header_is('location', '/realapi/fakeclient/return?error_description=FAIL')
Akrona8efaa92022-04-09 14:45:43 +02001109 ;
1110
Akron9f2ad342022-05-04 16:16:40 +02001111my $json_post = {
1112 name => 'Funny',
1113 type => 'PUBLIC',
Akron6b75d122022-05-12 17:39:05 +02001114 desc => 'This is my plugin application 2',
Akron9f2ad342022-05-04 16:16:40 +02001115 csrf_token => $csrf,
1116 src => 'hMMM'
1117};
1118
1119$t->post_ok('/settings/oauth/register' => form => $json_post)
1120 ->status_is(200)
1121 ->element_exists('div.notify-error')
Akron99968a92022-06-03 12:32:07 +02001122 ->text_is('div.notify-error', 'Plugin declarations need to be json files')
1123 ;
1124
1125$json_post->{src} = {
1126 content => 'jjjjjj',
1127 filename => 'fun.txt'
1128};
1129
1130$t->post_ok('/settings/oauth/register' => form => $json_post)
1131 ->status_is(200)
1132 ->element_exists('div.notify-error')
Akron9f2ad342022-05-04 16:16:40 +02001133 ->text_is('div.notify-error', 'Plugins need to be confidential')
1134 ;
1135
1136$json_post->{type} = 'CONFIDENTIAL';
1137
Akron99968a92022-06-03 12:32:07 +02001138# This somehow gets removed in the last form send ...
Akron9f2ad342022-05-04 16:16:40 +02001139$json_post->{src} = {
1140 content => 'jjjjjj',
1141 filename => 'fun.txt'
1142};
1143
1144$t->post_ok('/settings/oauth/register' => form => $json_post)
1145 ->status_is(200)
1146 ->element_exists('div.notify-error')
1147 ->text_is('div.notify-error', 'Plugin declarations need to be json files')
1148 ;
1149
1150$json_post->{src} = {
1151 content => '{"name":"example"}',
1152 filename => 'fun.txt'
1153};
1154
1155$t->post_ok('/settings/oauth/register' => form => $json_post)
1156 ->status_is(200)
1157 ->element_exists_not('div.notify-error')
1158 ->element_exists('div.notify-success')
1159 ->text_is('div.notify-success', 'Registration successful')
1160 ;
1161
Akron6b75d122022-05-12 17:39:05 +02001162$t->get_ok('/settings/oauth/jh0gfjhjbfdsgzjghj==')
1163 ->status_is(200)
1164 ->text_is('div.notify-error', undef)
1165 ->text_is('li.client #client_source', '{"name":"example"}')
1166 ->text_is('li.client span.client-name', 'Funny')
1167 ->text_is('li.client p.client-desc', 'This is my plugin application 2')
1168 ->element_exists_not('li.client .client-redirect-uri tt')
1169 ->text_is('li.client .client-type tt', 'CONFIDENTIAL')
1170 ;
Akron9f2ad342022-05-04 16:16:40 +02001171
Akronb6b156e2022-03-31 14:57:49 +02001172
Akron66ef3b52022-11-22 14:25:15 +01001173# Retest client with super_client_file
1174my $client_file = tempfile;
1175
1176$client_file->spurt(
1177 '{"client_id":"2","client_secret":"k414m4r-s3cr3t"}'
1178);
1179
1180$t = Test::Mojo::WithRoles->new('Kalamar' => {
1181 Kalamar => {
1182 plugins => ['Auth']
1183 },
1184 'Kalamar-Auth' => {
1185 client_file => $client_file,
1186 oauth2 => 1
1187 }
1188});
1189
1190$t->app->plugin(
1191 Mount => {
1192 $mount_point =>
1193 $fixtures_path->child('mock.pl')
1194 }
1195);
1196
1197$csrf = $t->get_ok('/')
1198 ->status_is(200)
1199 ->element_exists_not('div.button.top a')
1200 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
1201 ;
1202
1203$t->post_ok('/user/login' => form => {
1204 handle_or_email => 'test',
1205 pwd => 'pass',
1206 csrf_token => $csrf
1207})
1208 ->status_is(302)
1209 ->header_is('Location' => '/')
1210 ->content_is('');
1211
1212$t->get_ok('/')
1213 ->status_is(200)
1214 ->element_exists_not('div.notify-error')
1215 ->element_exists('div.notify-success')
1216 ->text_is('div.notify-success', 'Login successful')
1217 ->element_exists_not('aside.off')
1218 ->element_exists_not('aside.active')
1219 ->element_exists('aside.settings')
1220 ;
1221
Akron53a171e2022-12-05 18:22:58 +01001222$main::ENV{KALAMAR_CLIENT_FILE} = $client_file;
1223
1224$t = Test::Mojo::WithRoles->new('Kalamar' => {
1225 Kalamar => {
1226 plugins => ['Auth']
1227 },
1228 'Kalamar-Auth' => {
1229 oauth2 => 1,
1230# client_file => $client_file,
1231 }
1232});
1233
1234$t->app->plugin(
1235 Mount => {
1236 $mount_point =>
1237 $fixtures_path->child('mock.pl')
1238 }
1239);
1240
1241$csrf = $t->get_ok('/')
1242 ->status_is(200)
1243 ->element_exists_not('div.button.top a')
1244 ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
1245 ;
1246
1247$t->post_ok('/user/login' => form => {
1248 handle_or_email => 'test',
1249 pwd => 'pass',
1250 csrf_token => $csrf
1251})
1252 ->status_is(302)
1253 ->header_is('Location' => '/')
1254 ->content_is('');
1255
1256$t->get_ok('/')
1257 ->status_is(200)
1258 ->element_exists_not('div.notify-error')
1259 ->element_exists('div.notify-success')
1260 ->text_is('div.notify-success', 'Login successful')
1261 ->element_exists_not('aside.off')
1262 ->element_exists_not('aside.active')
1263 ->element_exists('aside.settings')
1264 ;
1265
Akron66ef3b52022-11-22 14:25:15 +01001266
Akron33f5c672019-06-24 19:40:47 +02001267done_testing;
1268__END__
Akrona8efaa92022-04-09 14:45:43 +02001269
1270