blob: 7deb12a67bedc90c94949b7b501e765889ce3f04 [file] [log] [blame]
Akron0e1ed242018-10-11 13:22:00 +02001#!/usr/bin/env perl
2use Mojolicious::Lite;
3use Mojo::ByteStream 'b';
4use Mojo::Date;
5use Mojo::JSON qw/true false encode_json decode_json/;
6use strict;
7use warnings;
8use Mojo::JWT;
Akron6d49c1f2018-10-11 14:22:21 +02009use Mojo::File qw/path/;
10use Mojo::Util qw/slugify/;
Akron0e1ed242018-10-11 13:22:00 +020011
12# This is an API fake server with fixtures
13
14my $secret = 's3cr3t';
Akron73f36082018-10-25 15:34:59 +020015my $fixture_path = path(Mojo::File->new(__FILE__)->dirname)->child('..', 'fixtures');
Akron0e1ed242018-10-11 13:22:00 +020016
Akroncdfd9d52019-07-23 11:35:00 +020017our %tokens = (
Akron59992122019-10-29 11:28:45 +010018 'access_token' => "4dcf8784ccfd26fac9bdb82778fe60e2",
19 'refresh_token' => "hlWci75xb8atDiq3924NUSvOdtAh7Nlf9z",
20 'access_token_2' => "abcde",
Akron83209f72021-01-29 17:54:15 +010021 'access_token_3' => 'jvgjbvjgzucgdwuiKHJK',
Akron59992122019-10-29 11:28:45 +010022 'refresh_token_2' => "fghijk",
23 'new_client_id' => 'fCBbQkA2NDA3MzM1Yw==',
Akronb6b156e2022-03-31 14:57:49 +020024 'new_client_id_2' => 'hghGHhjhFRz_gJhjrd==',
Akron59992122019-10-29 11:28:45 +010025 'new_client_secret' => 'KUMaFxs6R1WGud4HM22w3HbmYKHMnNHIiLJ2ihaWtB4N5JxGzZgyqs5GTLutrORj',
Akron83209f72021-01-29 17:54:15 +010026 'auth_token_1' => 'mscajfdghnjdfshtkjcuynxahgz5il'
Akroncdfd9d52019-07-23 11:35:00 +020027);
28
29helper get_token => sub {
30 my ($c, $token) = @_;
31 return $tokens{$token}
32};
33
Akron33f5c672019-06-24 19:40:47 +020034# Legacy:
Akron0e1ed242018-10-11 13:22:00 +020035helper jwt_encode => sub {
36 shift;
37 return Mojo::JWT->new(
38 secret => $secret,
39 token_type => 'api_token',
40 expires => time + (3 * 34 * 60 * 60),
41 claims => { @_ }
42 );
43};
44
Akron33f5c672019-06-24 19:40:47 +020045# Legacy;
Akron0e1ed242018-10-11 13:22:00 +020046helper jwt_decode => sub {
47 my ($c, $auth) = @_;
48 $auth =~ s/\s*api_token\s+//;
49 return Mojo::JWT->new(secret => $secret)->decode($auth);
50};
51
Akroncdfd9d52019-07-23 11:35:00 +020052# Expiration helper
53helper expired => sub {
54 my ($c, $auth, $set) = @_;
55
Akroncdfd9d52019-07-23 11:35:00 +020056 $auth =~ s/^[^ ]+? //;
57 if ($set) {
58 $c->app->log->debug("Set $auth for expiration");
59 $c->app->defaults('auth_' . $auth => 1);
60 return 1;
61 };
62
63 $c->app->log->debug("Check $auth for expiration: " . (
64 $c->app->defaults('auth_' . $auth) // '0'
65 ));
66
67 return $c->app->defaults('auth_' . $auth);
68};
Akron0e1ed242018-10-11 13:22:00 +020069
Akron6d49c1f2018-10-11 14:22:21 +020070# Load fixture responses
71helper 'load_response' => sub {
72 my $c = shift;
73 my $q_name = shift;
74 my $file = $fixture_path->child("response_$q_name.json");
Akron8ea84292018-10-24 13:41:52 +020075 $c->app->log->debug("Load response from $file");
76
Akron6d49c1f2018-10-11 14:22:21 +020077 unless (-f $file) {
78 return {
79 status => 500,
80 json => {
81 errors => [[0, 'Unable to load query response from ' . $file]]
82 }
83 }
84 };
Akron8ea84292018-10-24 13:41:52 +020085
Akron6d49c1f2018-10-11 14:22:21 +020086 my $response = $file->slurp;
Akrona3c353c2019-02-14 23:50:00 +010087 my $decode = decode_json($response);
88 unless ($decode) {
89 return {
90 status => 500,
91 json => {
92 errors => [[0, 'Unable to parse JSON']]
93 }
94 }
95 };
96
97 return $decode;
Akron6d49c1f2018-10-11 14:22:21 +020098};
99
Akron1a9d5be2020-03-19 17:28:33 +0100100app->defaults('oauth.client_list' => []);
101
Akron6d49c1f2018-10-11 14:22:21 +0200102
Akron0e1ed242018-10-11 13:22:00 +0200103# Base page
Akron63d963b2019-07-05 15:35:51 +0200104get '/v1.0/' => sub {
Akron6d49c1f2018-10-11 14:22:21 +0200105 shift->render(text => 'Fake server available');
Akron0e1ed242018-10-11 13:22:00 +0200106};
107
Akron32396632018-10-11 17:08:37 +0200108
Akrond00b4272020-02-05 17:00:33 +0100109get '/v1.0/redirect-target-a' => sub {
110 shift->render(text => 'Redirect Target!');
111} => 'redirect-target';
112
113
114# Base page
115get '/v1.0/redirect' => sub {
116 my $c = shift;
117 $c->res->code(308);
118 $c->res->headers->location($c->url_for('redirect-target')->to_abs);
119 return $c->render(text => '');
120};
121
122
Akron0e1ed242018-10-11 13:22:00 +0200123# Search fixtures
Akron63d963b2019-07-05 15:35:51 +0200124get '/v1.0/search' => sub {
Akron0e1ed242018-10-11 13:22:00 +0200125 my $c = shift;
126 my $v = $c->validation;
127 $v->optional('q');
128 $v->optional('page');
129 $v->optional('ql');
Akroncd42a142019-07-12 18:55:37 +0200130 $v->optional('cq');
Akron0e1ed242018-10-11 13:22:00 +0200131 $v->optional('count');
132 $v->optional('context');
Akron8ea84292018-10-24 13:41:52 +0200133 $v->optional('offset');
Akronc58bfc42020-10-05 12:09:45 +0200134 $v->optional('pipes');
Akron8ea84292018-10-24 13:41:52 +0200135 $v->optional('cutoff')->in(qw/true false/);
Akron0e1ed242018-10-11 13:22:00 +0200136
Akron32396632018-10-11 17:08:37 +0200137 $c->app->log->debug('Receive request');
138
Akron0e1ed242018-10-11 13:22:00 +0200139 # Response q=x&ql=cosmas3
140 if ($v->param('ql') && $v->param('ql') eq 'cosmas3') {
141 return $c->render(
142 status => 400,
143 json => {
144 "\@context" => "http://korap.ids-mannheim.de/ns/koral/0.3/context.jsonld",
145 "errors" => [[307,"cosmas3 is not a supported query language!"]]
146 });
147 };
148
Akron6d49c1f2018-10-11 14:22:21 +0200149 if (!$v->param('q')) {
Akron8ea84292018-10-24 13:41:52 +0200150 return $c->render(%{$c->load_response('query_no_query')});
Akron0e1ed242018-10-11 13:22:00 +0200151 };
152
Akroncce055c2021-07-02 12:18:03 +0200153 if ($v->param('q') eq 'error') {
154 return $c->render(
155 status => 500,
156 inline => '<html><head>ERROR</head></html>'
157 );
158 };
159
Akron8ea84292018-10-24 13:41:52 +0200160 my @slug_base = ($v->param('q'));
161 push @slug_base, 'o' . $v->param('offset') if defined $v->param('offset');
162 push @slug_base, 'c' . $v->param('count') if defined $v->param('count');
163 push @slug_base, 'co' . $v->param('cutoff') if defined $v->param('cutoff');
Akroncd42a142019-07-12 18:55:37 +0200164 push @slug_base, 'cq' if defined $v->param('cq');
Akronc58bfc42020-10-05 12:09:45 +0200165 push @slug_base, 'p' . $v->param('pipes') if defined $v->param('pipes');
Akron8ea84292018-10-24 13:41:52 +0200166
Akron6d49c1f2018-10-11 14:22:21 +0200167 # Get response based on query parameter
Akron8ea84292018-10-24 13:41:52 +0200168 my $response = $c->load_response('query_' . slugify(join('_', @slug_base)));
Akron0e1ed242018-10-11 13:22:00 +0200169
170 # Check authentification
171 if (my $auth = $c->req->headers->header('Authorization')) {
Akron33f5c672019-06-24 19:40:47 +0200172
Akroncdfd9d52019-07-23 11:35:00 +0200173 $c->app->log->debug("There is an authorization header $auth");
Akron33f5c672019-06-24 19:40:47 +0200174 my $jwt;
175 if ($auth =~ /^Bearer/) {
176 # Username unknown in OAuth2
177 $response->{json}->{meta}->{authorized} = 'yes';
178 }
Akroncdfd9d52019-07-23 11:35:00 +0200179 elsif ($auth =~ /^api_token/ && ($jwt = $c->jwt_decode($auth))) {
Akron6d49c1f2018-10-11 14:22:21 +0200180 $response->{json}->{meta}->{authorized} = $jwt->{username} if $jwt->{username};
Akron0e1ed242018-10-11 13:22:00 +0200181 };
Akroncdfd9d52019-07-23 11:35:00 +0200182
183 # Code is expired
184 if ($c->expired($auth)) {
185
186 $c->app->log->debug("The access token has expired");
187
188 return $c->render(
189 status => 401,
190 json => {
191 errors => [[2003, 'Access token is expired']]
192 }
193 );
194 }
195
196 # Auth token is invalid
197 if ($auth =~ /^Bearer inv4lid/) {
198 $c->app->log->debug("The access token is invalid");
199
200 return $c->render(
201 status => 401,
202 json => {
203 errors => [[2011, 'Access token is invalid']]
204 }
205 );
206 }
Akron0e1ed242018-10-11 13:22:00 +0200207 };
208
Akronc58bfc42020-10-05 12:09:45 +0200209 if ($v->param('pipes')) {
210 $response->{json}->{meta}->{pipes} = $v->param('pipes');
Akron7b9a1962020-07-02 09:52:53 +0200211 };
212
Akron6d49c1f2018-10-11 14:22:21 +0200213 # Set page parameter
Akron0e1ed242018-10-11 13:22:00 +0200214 if ($v->param('page')) {
Akron6d49c1f2018-10-11 14:22:21 +0200215 $response->{json}->{meta}->{startIndex} = $v->param("startIndex");
Akron0e1ed242018-10-11 13:22:00 +0200216 };
217
Akron0e1ed242018-10-11 13:22:00 +0200218 # Simple search fixture
Akron32396632018-10-11 17:08:37 +0200219 $c->render(%$response);
220
221 $c->app->log->debug('Rendered result');
222
223 return 1;
Akron0e1ed242018-10-11 13:22:00 +0200224};
225
Akron80a84b22018-10-24 17:44:24 +0200226# Textinfo fixtures
Akron63d963b2019-07-05 15:35:51 +0200227get '/v1.0/corpus/:corpusId/:docId/:textId' => sub {
Akron80a84b22018-10-24 17:44:24 +0200228 my $c = shift;
229
230 my $file = join('_', (
231 'textinfo',
232 $c->stash('corpusId'),
233 $c->stash('docId'),
234 $c->stash('textId')
235 ));
236
237 my $slug = slugify($file);
238
239 # Get response based on query parameter
240 my $response = $c->load_response($slug);
241 return $c->render(%$response);
242};
243
Akron0e1ed242018-10-11 13:22:00 +0200244
Akronb80341d2018-10-15 19:46:23 +0200245# Matchinfo fixtures
Akron63d963b2019-07-05 15:35:51 +0200246get '/v1.0/corpus/:corpusId/:docId/:textId/:matchId/matchInfo' => sub {
Akronb80341d2018-10-15 19:46:23 +0200247 my $c = shift;
248
249 my $file = join('_', (
250 'matchinfo',
251 $c->stash('corpusId'),
252 $c->stash('docId'),
253 $c->stash('textId'),
254 $c->stash('matchId')
255 ));
256
Akronb8d0b402018-10-18 23:51:52 +0200257 my $slug = slugify($file);
258
Akronb80341d2018-10-15 19:46:23 +0200259 # Get response based on query parameter
Akronb8d0b402018-10-18 23:51:52 +0200260 my $response = $c->load_response($slug);
Akronb80341d2018-10-15 19:46:23 +0200261 return $c->render(%$response);
262};
263
Akron0e1ed242018-10-11 13:22:00 +0200264
Akronbe61f4c2018-10-20 00:52:58 +0200265# Statistics endpoint
Akron63d963b2019-07-05 15:35:51 +0200266get '/v1.0/statistics' => sub {
Akronbe61f4c2018-10-20 00:52:58 +0200267 my $c = shift;
268 my $v = $c->validation;
Akron5fa61e92019-07-15 11:56:11 +0200269 $v->optional('cq');
Akronbe61f4c2018-10-20 00:52:58 +0200270
271 my @list = 'corpusinfo';
Akron5fa61e92019-07-15 11:56:11 +0200272 if ($v->param('cq')) {
273 push @list, $v->param('cq');
Akronbe61f4c2018-10-20 00:52:58 +0200274 };
275 my $slug = slugify(join('_', @list));
276
277 # Get response based on query parameter
278 my $response = $c->load_response($slug);
279 return $c->render(%$response);
280};
281
Akron0e1ed242018-10-11 13:22:00 +0200282############
283# Auth API #
284############
285
286# Request API token
Akron63d963b2019-07-05 15:35:51 +0200287get '/v1.0/auth/logout' => sub {
Akron0e1ed242018-10-11 13:22:00 +0200288 my $c = shift;
289
290 if (my $auth = $c->req->headers->header('Authorization')) {
Akroncdfd9d52019-07-23 11:35:00 +0200291
292 if ($auth =~ /^Bearer/) {
293 $c->app->log->debug('Server-Logout: ' . $auth);
294 return $c->render(json => { msg => [[0, 'Fine!']]});
295 }
296
297 elsif (my $jwt = $c->jwt_decode($auth)) {
Akron0e1ed242018-10-11 13:22:00 +0200298 my $user = $jwt->{username} if $jwt->{username};
299
300 $c->app->log->debug('Server-Logout: ' . $user);
301 return $c->render(json => { msg => [[0, 'Fine!']]});
302 };
303 };
304
305 return $c->render(status => 400, json => { error => [[0, 'No!']]});
306};
307
308
309# Request API token
Akron63d963b2019-07-05 15:35:51 +0200310get '/v1.0/auth/apiToken' => sub {
Akron0e1ed242018-10-11 13:22:00 +0200311 my $c = shift;
312
313 # Get auth header
314 my $auth = $c->req->headers->authorization;
315
316 # Authorization missing or not basic
317 if (!$auth || $auth !~ s/\s*Basic\s+//gi) {
318 return $c->render(
319 json => {
320 error => [[2, 'x']]
321 }
322 );
323 };
324
325 # Decode header
326 my ($username, $pwd) = @{b($auth)->b64_decode->split(':')->to_array};
327
328 # the password is 'pass'
329 if ($pwd) {
330
331 # the password is 'pass'
332 if ($pwd eq 'pass') {
333
334 # Render info with token
335 my $jwt = $c->jwt_encode(username => $username);
336
337 # Render in the Kustvakt fashion:
338 return $c->render(
339 format => 'html',
340 text => encode_json({
341 %{$jwt->claims},
342 expires => $jwt->expires,
343 token => $jwt->encode,
344 token_type => 'api_token'
345 })
346 );
Akron3d673062019-01-29 15:54:16 +0100347 }
348
349 elsif ($pwd eq 'ldaperr') {
350 return $c->render(
351 format => 'html',
352 status => 401,
353 json => {
354 "errors" => [[2022,"LDAP Authentication failed due to unknown user or password!"]]
355 }
356 );
Akron0e1ed242018-10-11 13:22:00 +0200357 };
358
359 return $c->render(
360 json => {
361 error => [[2004, undef]]
362 }
363 );
364 };
365
366 return $c->render(
367 json => {
368 error => [[2004, undef]]
369 }
370 );
371};
372
Akron33f5c672019-06-24 19:40:47 +0200373
374# Request API token
Akron63d963b2019-07-05 15:35:51 +0200375post '/v1.0/oauth2/token' => sub {
Akron33f5c672019-06-24 19:40:47 +0200376 my $c = shift;
377
Akron63d963b2019-07-05 15:35:51 +0200378 my $grant_type = $c->param('grant_type') // 'undefined';
379
380 if ($grant_type eq 'password') {
Akron33f5c672019-06-24 19:40:47 +0200381
Akron8bbbecf2019-07-01 18:57:30 +0200382 # Check for wrong client id
383 if ($c->param('client_id') ne '2') {
384 return $c->render(
385 json => {
386 "error_description" => "Unknown client with " . $_->{client_id},
387 "error" => "invalid_client"
388 },
389 status => 401
390 );
391 }
Akron33f5c672019-06-24 19:40:47 +0200392
Akron8bbbecf2019-07-01 18:57:30 +0200393 # Check for wrong client secret
394 elsif ($c->param('client_secret') ne 'k414m4r-s3cr3t') {
395 return $c->render(
396 json => {
397 "error_description" => "Invalid client credentials",
398 "error" => "invalid_client"
399 },
400 status => 401
401 );
402 }
Akron33f5c672019-06-24 19:40:47 +0200403
Akron8bbbecf2019-07-01 18:57:30 +0200404 # Check for wrong user name
Akron6a228db2021-10-14 15:57:00 +0200405 elsif ($c->param('username') !~ /^t.st$/) {
Akron8bbbecf2019-07-01 18:57:30 +0200406 return $c->render(json => {
407 error => [[2004, undef]]
408 });
409 }
410
411 # Check for ldap error
412 elsif ($c->param('password') eq 'ldaperr') {
413 return $c->render(
414 format => 'html',
415 status => 401,
416 json => {
417 "errors" => [
418 [
419 2022,
420 "LDAP Authentication failed due to unknown user or password!"
421 ]
422 ]
423 }
424 );
425 }
426
427 # Check for wrong password
428 elsif ($c->param('password') ne 'pass') {
429 return $c->render(json => {
430 format => 'html',
431 status => 401,
Akron33f5c672019-06-24 19:40:47 +0200432 "errors" => [[2022,"LDAP Authentication failed due to unknown user or password!"]]
Akron8bbbecf2019-07-01 18:57:30 +0200433 });
434 }
435
436 # Return fine access
437 return $c->render(
438 json => {
Akroncdfd9d52019-07-23 11:35:00 +0200439 "access_token" => $c->get_token('access_token'),
440 "refresh_token" => $c->get_token('refresh_token'),
Akron8bbbecf2019-07-01 18:57:30 +0200441 "scope" => "all",
442 "token_type" => "Bearer",
443 "expires_in" => 86400
444 });
445 }
446
447 # Refresh token
Akron63d963b2019-07-05 15:35:51 +0200448 elsif ($grant_type eq 'refresh_token') {
Akroncdfd9d52019-07-23 11:35:00 +0200449
450 if ($c->param('refresh_token') eq 'inv4lid') {
451 return $c->render(
452 status => 400,
453 json => {
454 "error_description" => "Refresh token is expired",
455 "error" => "invalid_grant"
456 }
457 );
458 };
459
460 $c->app->log->debug("Refresh the token in the mock server!");
461
Akron8bbbecf2019-07-01 18:57:30 +0200462 return $c->render(
463 status => 200,
464 json => {
Akroncdfd9d52019-07-23 11:35:00 +0200465 "access_token" => $c->get_token("access_token_2"),
466 "refresh_token" => $c->get_token("refresh_token_2"),
Akron8bbbecf2019-07-01 18:57:30 +0200467 "token_type" => "Bearer",
468 "expires_in" => 86400
Akron33f5c672019-06-24 19:40:47 +0200469 }
470 );
471 }
472
Akron83209f72021-01-29 17:54:15 +0100473 # Get auth_token_1
474 elsif ($grant_type eq 'authorization_code') {
475 if ($c->param('code') eq $tokens{auth_token_1}) {
476 return $c->render(
477 status => 200,
478 json => {
479 "access_token" => $tokens{access_token_3},
480 "expires_in" => 31536000,
481 "scope" => 'match_info search openid',
482 "token_type" => "Bearer"
483 }
484 );
485 };
486 }
487
Akron8bbbecf2019-07-01 18:57:30 +0200488 # Unknown token grant
489 else {
490 return $c->render(
Akron63d963b2019-07-05 15:35:51 +0200491 status => 400,
Akron8bbbecf2019-07-01 18:57:30 +0200492 json => {
493 "errors" => [
494 [
Akron63d963b2019-07-05 15:35:51 +0200495 0, "Grant Type unknown", $grant_type
Akron8bbbecf2019-07-01 18:57:30 +0200496 ]
497 ]
498 }
499 )
Akron33f5c672019-06-24 19:40:47 +0200500 }
Akron33f5c672019-06-24 19:40:47 +0200501};
502
Akron4cefe1f2019-09-04 10:11:28 +0200503# Revoke API token
504post '/v1.0/oauth2/revoke' => sub {
505 my $c = shift;
506
507 my $refresh_token = $c->param('token');
508
509 if ($c->param('client_secret') ne 'k414m4r-s3cr3t') {
510 return $c->render(
511 json => {
512 "error_description" => "Invalid client credentials",
513 "error" => "invalid_client"
514 },
515 status => 401
516 );
517 };
518
519 return $c->render(
520 text => ''
521 )
522};
Akron33f5c672019-06-24 19:40:47 +0200523
Akron59992122019-10-29 11:28:45 +0100524# Register a client
525post '/v1.0/oauth2/client/register' => sub {
526 my $c = shift;
527 my $json = $c->req->json;
528
Akrondc50c892021-05-05 18:12:02 +0200529 if ($json->{redirectURI}) {
530 return $c->render(
531 status => 400,
532 json => {
533 errors => [
534 [
535 201,
536 "Unrecognized field \"redirectURI\" (class de.ids_mannheim.korap.web.input.OAuth2ClientJson), not marked as ignorable (5 known properties: \"redirect_uri\", \"type\", \"name\", \"description\", \"url\"])\n at [Source: (org.eclipse.jetty.server.HttpInputOverHTTP); line: 1, column: 94] (through reference chain: de.ids_mannheim.korap.web.input.OAuth2ClientJson[\"redirectURI\"])"
537 ]
538 ]
539 }
540 );
541 };
542
Akron59992122019-10-29 11:28:45 +0100543 my $name = $json->{name};
Akron1a9d5be2020-03-19 17:28:33 +0100544 my $desc = $json->{description};
Akron59992122019-10-29 11:28:45 +0100545 my $type = $json->{type};
546 my $url = $json->{url};
Akronb6b156e2022-03-31 14:57:49 +0200547 my $redirect_uri = $json->{redirect_uri};
Akron59992122019-10-29 11:28:45 +0100548
Akron1a9d5be2020-03-19 17:28:33 +0100549 my $list = $c->app->defaults('oauth.client_list');
550
551 push @$list, {
Akrondc50c892021-05-05 18:12:02 +0200552 "client_id" => $tokens{new_client_id},
553 "client_name" => $name,
Akronbc94a9c2021-04-15 00:07:35 +0200554 "client_description" => $desc,
Akronb6b156e2022-03-31 14:57:49 +0200555 "client_url" => $url,
556 "client_redirect_uri" => $redirect_uri
557 };
558
559 if ($redirect_uri && $redirect_uri =~ /FAIL$/) {
560 return $c->render(
561 status => 400,
562 json => {
563 "error_description" => $redirect_uri . " is invalid.",
564 "error" => "invalid_request"
565 }
566 )
Akron1a9d5be2020-03-19 17:28:33 +0100567 };
568
Akron59992122019-10-29 11:28:45 +0100569 # Confidential server application
570 if ($type eq 'CONFIDENTIAL') {
Akronb6b156e2022-03-31 14:57:49 +0200571
Akron59992122019-10-29 11:28:45 +0100572 return $c->render(json => {
Akronb6b156e2022-03-31 14:57:49 +0200573 client_id => $tokens{new_client_id_2},
Akron59992122019-10-29 11:28:45 +0100574 client_secret => $tokens{new_client_secret}
575 });
576 };
577
578 # Desktop application
579 return $c->render(json => {
580 client_id => $tokens{new_client_id}
581 });
582};
583
Akron33f5c672019-06-24 19:40:47 +0200584
Akron0f1b93b2020-03-17 11:37:19 +0100585# Register a client
586post '/v1.0/oauth2/client/list' => sub {
587 my $c = shift;
588
Akron276afc02021-06-14 11:00:21 +0200589 my $v = $c->validation;
590
591 $v->required('super_client_id');
592 $v->required('super_client_secret');
593
594 if ($v->has_error) {
595 return $c->render(
596 json => [],
597 status => 400
598 );
599 };
600
Akron0f1b93b2020-03-17 11:37:19 +0100601 # $c->param('client_secret');
Akron1a9d5be2020-03-19 17:28:33 +0100602
603 # Is empty [] when nothing registered
604
Akron0f1b93b2020-03-17 11:37:19 +0100605 return $c->render(
Akron1a9d5be2020-03-19 17:28:33 +0100606 json => $c->stash('oauth.client_list'),
607 status => 200
608 );
609};
610
Akronbc94a9c2021-04-15 00:07:35 +0200611
612# Get token list
613post '/v1.0/oauth2/token/list' => sub {
614 my $c = shift;
615 return $c->render(json => [
616 {
617 "client_description" => "Nur ein Beispiel",
618 "client_id" => $tokens{new_client_id},
619 "client_name" => "Beispiel",
620 "client_url" => "",
621 "created_date" => "2021-04-14T19:40:26.742+02:00[Europe\/Berlin]",
622 "expires_in" => "31533851",
623 "scope" => [
624 "match_info",
625 "search",
626 "openid"
627 ],
628 "token" => "jhkhkjhk_hjgjsfz67i",
629 "user_authentication_time" => "2021-04-14T19:39:41.81+02:00[Europe\/Berlin]"
630 }
631 ]);
632};
633
Akron1a9d5be2020-03-19 17:28:33 +0100634del '/v1.0/oauth2/client/deregister/:client_id' => sub {
635 my $c = shift;
636 my $client_id = $c->stash('client_id');
637
638 my $list = $c->app->defaults('oauth.client_list');
639
640 my $break = -1;
641 for (my $i = 0; $i < @$list; $i++) {
Akrondc50c892021-05-05 18:12:02 +0200642 if ($list->[$i]->{client_id} eq $client_id) {
Akron1a9d5be2020-03-19 17:28:33 +0100643 $break = $i;
644 last;
645 };
646 };
647
648 if ($break != -1) {
649 splice @$list, $break, 1;
650 }
651
652 else {
653 return $c->render(
654 json => {
655 error_description => "Unknown client with $client_id.",
656 error => "invalid_client"
Akron0f1b93b2020-03-17 11:37:19 +0100657 },
Akron1a9d5be2020-03-19 17:28:33 +0100658 status => 401
659 );
660 };
661
662 return $c->render(
663 json => $c->stash('oauth.client_list'),
Akron0f1b93b2020-03-17 11:37:19 +0100664 status => 200
665 );
666};
667
Akron83209f72021-01-29 17:54:15 +0100668post '/v1.0/oauth2/authorize' => sub {
669 my $c = shift;
670 my $type = $c->param('response_type');
671 my $client_id = $c->param('client_id');
Akrona8efaa92022-04-09 14:45:43 +0200672 my $scope = $c->param('scope');
673 my $state = $c->param('state');
674 my $redirect_uri = $c->param('redirect_uri') // 'NO';
Akron83209f72021-01-29 17:54:15 +0100675
Akrona8efaa92022-04-09 14:45:43 +0200676 if ($type eq 'code' && $client_id eq 'xyz') {
677
678 if ($state eq 'fail') {
679 $c->res->headers->location(
680 Mojo::URL->new($redirect_uri)->query({
681 error_description => 'FAIL'
682 })
683 );
684 $c->res->code(400);
685 return $c->rendered;
686 };
687
688 return $c->redirect_to(
689 Mojo::URL->new($redirect_uri)->query({
690 code => $tokens{auth_token_1},
691 scope => $scope,
692 })
693 );
694 }
695
696 elsif ($type eq 'code') {
Akron83209f72021-01-29 17:54:15 +0100697
698 return $c->redirect_to(
699 Mojo::URL->new($redirect_uri)->query({
700 code => $tokens{auth_token_1},
701 scope => 'match_info search openid'
702 })
703 );
704 }
705};
706
Akron0f1b93b2020-03-17 11:37:19 +0100707
Akronabdf9a92021-01-12 19:06:57 +0100708#######################
709# Query Reference API #
710#######################
711
712use CHI;
713my $chi = CHI->new(
714 driver => 'Memory',
715 global => 1
716);
717
718# Store query
719put '/v1.0/query/~:user/:query_name' => sub {
720 my $c = shift;
721 my $user = $c->stash('user');
722 my $qname = $c->stash('query_name');
723
724 if ($chi->is_valid($qname)) {
725 return $c->render(
726 json => {
727 errors => [
728 {
729 message => 'Unable to store query reference'
730 }
731 ]
732 }, status => 400
733 );
734 };
735
736 my $json = $c->req->json;
737
738 my $store = {
739 name => $qname,
740 koralQuery => { '@type' => 'Okay' },
741 query => $json->{query},
742 queryType => $json->{queryType},
743 type => $json->{type},
744 queryLanguage => $json->{queryLanguage},
745 };
746
747 if (exists $json->{description}) {
748 $store->{description} = $json->{description}
749 };
750
751 # Set query reference
752 $chi->set($qname => $store);
753
754 my $queries = $chi->get('~queries') // [];
755 push @$queries, $qname;
756 $chi->set('~queries' => $queries);
757
758 return $c->render(
759 status => 201,
760 text => ''
761 );
762};
763
764# Get query
765get '/v1.0/query/~:user/:query_name' => sub {
766 my $c = shift;
767
768 my $user = $c->stash('user');
769 my $qname = $c->stash('query_name');
770
771 my $json = $chi->get($qname);
772
773 if ($json) {
774 return $c->render(
775 json => $json
776 );
777 };
778
779 return $c->render(
780 json => {
781 errors => [
782 {
783 message => 'Query reference not found'
784 }
785 ]
786 }, status => 404
787 );
788};
789
790
791# Get all queries
792get '/v1.0/query/~:user' => sub {
793 my $c = shift;
794 my $user = $c->stash('user');
795 my $qs = $chi->get('~queries') // [];
796 my @queries = ();
797 foreach (@$qs) {
798 push @queries, $chi->get($_);
799 };
800 return $c->render(json => { refs => \@queries });
801};
802
803
804# Store query
805del '/v1.0/query/~:user/:query_name' => sub {
806 my $c = shift;
807 my $user = $c->stash('user');
808 my $qname = $c->stash('query_name');
809
810 $chi->remove($qname);
811
812 my $queries = $chi->get('~queries') // [];
813
814 my @clean = ();
815 foreach (@$queries) {
816 push @clean, $_ unless $_ eq $qname
817 };
818
819 $chi->set('~queries' => \@clean);
820
821 return $c->render(
822 status => 200,
823 text => ''
824 );
825};
826
Akronc1aaf932021-06-09 12:19:15 +0200827post '/v1.0/oauth2/revoke/super' => sub {
828 my $c = shift;
829
830 my $s_client_id = $c->param('super_client_id');
831 my $s_client_secret = $c->param('super_client_secret');
832 my $token = $c->param('token');
833
834 return $c->render(text => 'SUCCESS');
835};
836
Akrona8efaa92022-04-09 14:45:43 +0200837get '/fakeclient/return' => sub {
838 my $c = shift;
839 $c->render(
840 text => 'welcome back! [' . $c->param('code') . ']'
841 );
842} => 'return_uri';
843
Akron0f1b93b2020-03-17 11:37:19 +0100844
Akron0e1ed242018-10-11 13:22:00 +0200845app->start;
846
847
848__END__
849
850
851 # Temporary:
852 my $collection_query = {
853 '@type' => "koral:docGroup",
854 "operation" => "operation:or",
855 "operands" => [
856 {
857 '@type' => "koral:docGroup",
858 "operation" => "operation:and",
859 "operands" => [
860 {
861 '@type' => "koral:doc",
862 "key" => "title",
863 "match" => "match:eq",
864 "value" => "Der Birnbaum",
865 "type" => "type:string"
866 },
867 {
868 '@type' => "koral:doc",
869 "key" => "pubPlace",
870 "match" => "match:eq",
871 "value" => "Mannheim",
872 "type" => "type:string"
873 },
874 {
875 '@type' => "koral:docGroup",
876 "operation" => "operation:or",
877 "operands" => [
878 {
879 '@type' => "koral:doc",
880 "key" => "subTitle",
881 "match" => "match:eq",
882 "value" => "Aufzucht oder Pflege",
883 "type" => "type:string"
884 },
885 {
886 '@type' => "koral:doc",
887 "key" => "subTitle",
888 "match" => "match:eq",
889 "value" => "Gedichte",
890 "type" => "type:string"
891 }
892 ]
893 }
894 ]
895 },
896 {
897 '@type' => "koral:doc",
898 "key" => "pubDate",
899 "match" => "match:geq",
900 "value" => "2015-03-05",
901 "type" => "type:date"
902 }
903 ]
904 };