blob: 59a6766fb0629092600e1800a86d19ea65815850 [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==',
24 'new_client_secret' => 'KUMaFxs6R1WGud4HM22w3HbmYKHMnNHIiLJ2ihaWtB4N5JxGzZgyqs5GTLutrORj',
Akron83209f72021-01-29 17:54:15 +010025 'auth_token_1' => 'mscajfdghnjdfshtkjcuynxahgz5il'
Akroncdfd9d52019-07-23 11:35:00 +020026);
27
28helper get_token => sub {
29 my ($c, $token) = @_;
30 return $tokens{$token}
31};
32
Akron33f5c672019-06-24 19:40:47 +020033# Legacy:
Akron0e1ed242018-10-11 13:22:00 +020034helper jwt_encode => sub {
35 shift;
36 return Mojo::JWT->new(
37 secret => $secret,
38 token_type => 'api_token',
39 expires => time + (3 * 34 * 60 * 60),
40 claims => { @_ }
41 );
42};
43
Akron33f5c672019-06-24 19:40:47 +020044# Legacy;
Akron0e1ed242018-10-11 13:22:00 +020045helper jwt_decode => sub {
46 my ($c, $auth) = @_;
47 $auth =~ s/\s*api_token\s+//;
48 return Mojo::JWT->new(secret => $secret)->decode($auth);
49};
50
Akroncdfd9d52019-07-23 11:35:00 +020051# Expiration helper
52helper expired => sub {
53 my ($c, $auth, $set) = @_;
54
Akroncdfd9d52019-07-23 11:35:00 +020055 $auth =~ s/^[^ ]+? //;
56 if ($set) {
57 $c->app->log->debug("Set $auth for expiration");
58 $c->app->defaults('auth_' . $auth => 1);
59 return 1;
60 };
61
62 $c->app->log->debug("Check $auth for expiration: " . (
63 $c->app->defaults('auth_' . $auth) // '0'
64 ));
65
66 return $c->app->defaults('auth_' . $auth);
67};
Akron0e1ed242018-10-11 13:22:00 +020068
Akron6d49c1f2018-10-11 14:22:21 +020069# Load fixture responses
70helper 'load_response' => sub {
71 my $c = shift;
72 my $q_name = shift;
73 my $file = $fixture_path->child("response_$q_name.json");
Akron8ea84292018-10-24 13:41:52 +020074 $c->app->log->debug("Load response from $file");
75
Akron6d49c1f2018-10-11 14:22:21 +020076 unless (-f $file) {
77 return {
78 status => 500,
79 json => {
80 errors => [[0, 'Unable to load query response from ' . $file]]
81 }
82 }
83 };
Akron8ea84292018-10-24 13:41:52 +020084
Akron6d49c1f2018-10-11 14:22:21 +020085 my $response = $file->slurp;
Akrona3c353c2019-02-14 23:50:00 +010086 my $decode = decode_json($response);
87 unless ($decode) {
88 return {
89 status => 500,
90 json => {
91 errors => [[0, 'Unable to parse JSON']]
92 }
93 }
94 };
95
96 return $decode;
Akron6d49c1f2018-10-11 14:22:21 +020097};
98
Akron1a9d5be2020-03-19 17:28:33 +010099app->defaults('oauth.client_list' => []);
100
Akron6d49c1f2018-10-11 14:22:21 +0200101
Akron0e1ed242018-10-11 13:22:00 +0200102# Base page
Akron63d963b2019-07-05 15:35:51 +0200103get '/v1.0/' => sub {
Akron6d49c1f2018-10-11 14:22:21 +0200104 shift->render(text => 'Fake server available');
Akron0e1ed242018-10-11 13:22:00 +0200105};
106
Akron32396632018-10-11 17:08:37 +0200107
Akrond00b4272020-02-05 17:00:33 +0100108get '/v1.0/redirect-target-a' => sub {
109 shift->render(text => 'Redirect Target!');
110} => 'redirect-target';
111
112
113# Base page
114get '/v1.0/redirect' => sub {
115 my $c = shift;
116 $c->res->code(308);
117 $c->res->headers->location($c->url_for('redirect-target')->to_abs);
118 return $c->render(text => '');
119};
120
121
Akron0e1ed242018-10-11 13:22:00 +0200122# Search fixtures
Akron63d963b2019-07-05 15:35:51 +0200123get '/v1.0/search' => sub {
Akron0e1ed242018-10-11 13:22:00 +0200124 my $c = shift;
125 my $v = $c->validation;
126 $v->optional('q');
127 $v->optional('page');
128 $v->optional('ql');
Akroncd42a142019-07-12 18:55:37 +0200129 $v->optional('cq');
Akron0e1ed242018-10-11 13:22:00 +0200130 $v->optional('count');
131 $v->optional('context');
Akron8ea84292018-10-24 13:41:52 +0200132 $v->optional('offset');
Akronc58bfc42020-10-05 12:09:45 +0200133 $v->optional('pipes');
Akron8ea84292018-10-24 13:41:52 +0200134 $v->optional('cutoff')->in(qw/true false/);
Akron0e1ed242018-10-11 13:22:00 +0200135
Akron32396632018-10-11 17:08:37 +0200136 $c->app->log->debug('Receive request');
137
Akron0e1ed242018-10-11 13:22:00 +0200138 # Response q=x&ql=cosmas3
139 if ($v->param('ql') && $v->param('ql') eq 'cosmas3') {
140 return $c->render(
141 status => 400,
142 json => {
143 "\@context" => "http://korap.ids-mannheim.de/ns/koral/0.3/context.jsonld",
144 "errors" => [[307,"cosmas3 is not a supported query language!"]]
145 });
146 };
147
Akron6d49c1f2018-10-11 14:22:21 +0200148 if (!$v->param('q')) {
Akron8ea84292018-10-24 13:41:52 +0200149 return $c->render(%{$c->load_response('query_no_query')});
Akron0e1ed242018-10-11 13:22:00 +0200150 };
151
Akroncce055c2021-07-02 12:18:03 +0200152 if ($v->param('q') eq 'error') {
153 return $c->render(
154 status => 500,
155 inline => '<html><head>ERROR</head></html>'
156 );
157 };
158
Akron8ea84292018-10-24 13:41:52 +0200159 my @slug_base = ($v->param('q'));
160 push @slug_base, 'o' . $v->param('offset') if defined $v->param('offset');
161 push @slug_base, 'c' . $v->param('count') if defined $v->param('count');
162 push @slug_base, 'co' . $v->param('cutoff') if defined $v->param('cutoff');
Akroncd42a142019-07-12 18:55:37 +0200163 push @slug_base, 'cq' if defined $v->param('cq');
Akronc58bfc42020-10-05 12:09:45 +0200164 push @slug_base, 'p' . $v->param('pipes') if defined $v->param('pipes');
Akron8ea84292018-10-24 13:41:52 +0200165
Akron6d49c1f2018-10-11 14:22:21 +0200166 # Get response based on query parameter
Akron8ea84292018-10-24 13:41:52 +0200167 my $response = $c->load_response('query_' . slugify(join('_', @slug_base)));
Akron0e1ed242018-10-11 13:22:00 +0200168
169 # Check authentification
170 if (my $auth = $c->req->headers->header('Authorization')) {
Akron33f5c672019-06-24 19:40:47 +0200171
Akroncdfd9d52019-07-23 11:35:00 +0200172 $c->app->log->debug("There is an authorization header $auth");
Akron33f5c672019-06-24 19:40:47 +0200173 my $jwt;
174 if ($auth =~ /^Bearer/) {
175 # Username unknown in OAuth2
176 $response->{json}->{meta}->{authorized} = 'yes';
177 }
Akroncdfd9d52019-07-23 11:35:00 +0200178 elsif ($auth =~ /^api_token/ && ($jwt = $c->jwt_decode($auth))) {
Akron6d49c1f2018-10-11 14:22:21 +0200179 $response->{json}->{meta}->{authorized} = $jwt->{username} if $jwt->{username};
Akron0e1ed242018-10-11 13:22:00 +0200180 };
Akroncdfd9d52019-07-23 11:35:00 +0200181
182 # Code is expired
183 if ($c->expired($auth)) {
184
185 $c->app->log->debug("The access token has expired");
186
187 return $c->render(
188 status => 401,
189 json => {
190 errors => [[2003, 'Access token is expired']]
191 }
192 );
193 }
194
195 # Auth token is invalid
196 if ($auth =~ /^Bearer inv4lid/) {
197 $c->app->log->debug("The access token is invalid");
198
199 return $c->render(
200 status => 401,
201 json => {
202 errors => [[2011, 'Access token is invalid']]
203 }
204 );
205 }
Akron0e1ed242018-10-11 13:22:00 +0200206 };
207
Akronc58bfc42020-10-05 12:09:45 +0200208 if ($v->param('pipes')) {
209 $response->{json}->{meta}->{pipes} = $v->param('pipes');
Akron7b9a1962020-07-02 09:52:53 +0200210 };
211
Akron6d49c1f2018-10-11 14:22:21 +0200212 # Set page parameter
Akron0e1ed242018-10-11 13:22:00 +0200213 if ($v->param('page')) {
Akron6d49c1f2018-10-11 14:22:21 +0200214 $response->{json}->{meta}->{startIndex} = $v->param("startIndex");
Akron0e1ed242018-10-11 13:22:00 +0200215 };
216
Akron0e1ed242018-10-11 13:22:00 +0200217 # Simple search fixture
Akron32396632018-10-11 17:08:37 +0200218 $c->render(%$response);
219
220 $c->app->log->debug('Rendered result');
221
222 return 1;
Akron0e1ed242018-10-11 13:22:00 +0200223};
224
Akron80a84b22018-10-24 17:44:24 +0200225# Textinfo fixtures
Akron63d963b2019-07-05 15:35:51 +0200226get '/v1.0/corpus/:corpusId/:docId/:textId' => sub {
Akron80a84b22018-10-24 17:44:24 +0200227 my $c = shift;
228
229 my $file = join('_', (
230 'textinfo',
231 $c->stash('corpusId'),
232 $c->stash('docId'),
233 $c->stash('textId')
234 ));
235
236 my $slug = slugify($file);
237
238 # Get response based on query parameter
239 my $response = $c->load_response($slug);
240 return $c->render(%$response);
241};
242
Akron0e1ed242018-10-11 13:22:00 +0200243
Akronb80341d2018-10-15 19:46:23 +0200244# Matchinfo fixtures
Akron63d963b2019-07-05 15:35:51 +0200245get '/v1.0/corpus/:corpusId/:docId/:textId/:matchId/matchInfo' => sub {
Akronb80341d2018-10-15 19:46:23 +0200246 my $c = shift;
247
248 my $file = join('_', (
249 'matchinfo',
250 $c->stash('corpusId'),
251 $c->stash('docId'),
252 $c->stash('textId'),
253 $c->stash('matchId')
254 ));
255
Akronb8d0b402018-10-18 23:51:52 +0200256 my $slug = slugify($file);
257
Akronb80341d2018-10-15 19:46:23 +0200258 # Get response based on query parameter
Akronb8d0b402018-10-18 23:51:52 +0200259 my $response = $c->load_response($slug);
Akronb80341d2018-10-15 19:46:23 +0200260 return $c->render(%$response);
261};
262
Akron0e1ed242018-10-11 13:22:00 +0200263
Akronbe61f4c2018-10-20 00:52:58 +0200264# Statistics endpoint
Akron63d963b2019-07-05 15:35:51 +0200265get '/v1.0/statistics' => sub {
Akronbe61f4c2018-10-20 00:52:58 +0200266 my $c = shift;
267 my $v = $c->validation;
Akron5fa61e92019-07-15 11:56:11 +0200268 $v->optional('cq');
Akronbe61f4c2018-10-20 00:52:58 +0200269
270 my @list = 'corpusinfo';
Akron5fa61e92019-07-15 11:56:11 +0200271 if ($v->param('cq')) {
272 push @list, $v->param('cq');
Akronbe61f4c2018-10-20 00:52:58 +0200273 };
274 my $slug = slugify(join('_', @list));
275
276 # Get response based on query parameter
277 my $response = $c->load_response($slug);
278 return $c->render(%$response);
279};
280
Akron0e1ed242018-10-11 13:22:00 +0200281############
282# Auth API #
283############
284
285# Request API token
Akron63d963b2019-07-05 15:35:51 +0200286get '/v1.0/auth/logout' => sub {
Akron0e1ed242018-10-11 13:22:00 +0200287 my $c = shift;
288
289 if (my $auth = $c->req->headers->header('Authorization')) {
Akroncdfd9d52019-07-23 11:35:00 +0200290
291 if ($auth =~ /^Bearer/) {
292 $c->app->log->debug('Server-Logout: ' . $auth);
293 return $c->render(json => { msg => [[0, 'Fine!']]});
294 }
295
296 elsif (my $jwt = $c->jwt_decode($auth)) {
Akron0e1ed242018-10-11 13:22:00 +0200297 my $user = $jwt->{username} if $jwt->{username};
298
299 $c->app->log->debug('Server-Logout: ' . $user);
300 return $c->render(json => { msg => [[0, 'Fine!']]});
301 };
302 };
303
304 return $c->render(status => 400, json => { error => [[0, 'No!']]});
305};
306
307
308# Request API token
Akron63d963b2019-07-05 15:35:51 +0200309get '/v1.0/auth/apiToken' => sub {
Akron0e1ed242018-10-11 13:22:00 +0200310 my $c = shift;
311
312 # Get auth header
313 my $auth = $c->req->headers->authorization;
314
315 # Authorization missing or not basic
316 if (!$auth || $auth !~ s/\s*Basic\s+//gi) {
317 return $c->render(
318 json => {
319 error => [[2, 'x']]
320 }
321 );
322 };
323
324 # Decode header
325 my ($username, $pwd) = @{b($auth)->b64_decode->split(':')->to_array};
326
327 # the password is 'pass'
328 if ($pwd) {
329
330 # the password is 'pass'
331 if ($pwd eq 'pass') {
332
333 # Render info with token
334 my $jwt = $c->jwt_encode(username => $username);
335
336 # Render in the Kustvakt fashion:
337 return $c->render(
338 format => 'html',
339 text => encode_json({
340 %{$jwt->claims},
341 expires => $jwt->expires,
342 token => $jwt->encode,
343 token_type => 'api_token'
344 })
345 );
Akron3d673062019-01-29 15:54:16 +0100346 }
347
348 elsif ($pwd eq 'ldaperr') {
349 return $c->render(
350 format => 'html',
351 status => 401,
352 json => {
353 "errors" => [[2022,"LDAP Authentication failed due to unknown user or password!"]]
354 }
355 );
Akron0e1ed242018-10-11 13:22:00 +0200356 };
357
358 return $c->render(
359 json => {
360 error => [[2004, undef]]
361 }
362 );
363 };
364
365 return $c->render(
366 json => {
367 error => [[2004, undef]]
368 }
369 );
370};
371
Akron33f5c672019-06-24 19:40:47 +0200372
373# Request API token
Akron63d963b2019-07-05 15:35:51 +0200374post '/v1.0/oauth2/token' => sub {
Akron33f5c672019-06-24 19:40:47 +0200375 my $c = shift;
376
Akron63d963b2019-07-05 15:35:51 +0200377 my $grant_type = $c->param('grant_type') // 'undefined';
378
379 if ($grant_type eq 'password') {
Akron33f5c672019-06-24 19:40:47 +0200380
Akron8bbbecf2019-07-01 18:57:30 +0200381 # Check for wrong client id
382 if ($c->param('client_id') ne '2') {
383 return $c->render(
384 json => {
385 "error_description" => "Unknown client with " . $_->{client_id},
386 "error" => "invalid_client"
387 },
388 status => 401
389 );
390 }
Akron33f5c672019-06-24 19:40:47 +0200391
Akron8bbbecf2019-07-01 18:57:30 +0200392 # Check for wrong client secret
393 elsif ($c->param('client_secret') ne 'k414m4r-s3cr3t') {
394 return $c->render(
395 json => {
396 "error_description" => "Invalid client credentials",
397 "error" => "invalid_client"
398 },
399 status => 401
400 );
401 }
Akron33f5c672019-06-24 19:40:47 +0200402
Akron8bbbecf2019-07-01 18:57:30 +0200403 # Check for wrong user name
Akron6a228db2021-10-14 15:57:00 +0200404 elsif ($c->param('username') !~ /^t.st$/) {
Akron8bbbecf2019-07-01 18:57:30 +0200405 return $c->render(json => {
406 error => [[2004, undef]]
407 });
408 }
409
410 # Check for ldap error
411 elsif ($c->param('password') eq 'ldaperr') {
412 return $c->render(
413 format => 'html',
414 status => 401,
415 json => {
416 "errors" => [
417 [
418 2022,
419 "LDAP Authentication failed due to unknown user or password!"
420 ]
421 ]
422 }
423 );
424 }
425
426 # Check for wrong password
427 elsif ($c->param('password') ne 'pass') {
428 return $c->render(json => {
429 format => 'html',
430 status => 401,
Akron33f5c672019-06-24 19:40:47 +0200431 "errors" => [[2022,"LDAP Authentication failed due to unknown user or password!"]]
Akron8bbbecf2019-07-01 18:57:30 +0200432 });
433 }
434
435 # Return fine access
436 return $c->render(
437 json => {
Akroncdfd9d52019-07-23 11:35:00 +0200438 "access_token" => $c->get_token('access_token'),
439 "refresh_token" => $c->get_token('refresh_token'),
Akron8bbbecf2019-07-01 18:57:30 +0200440 "scope" => "all",
441 "token_type" => "Bearer",
442 "expires_in" => 86400
443 });
444 }
445
446 # Refresh token
Akron63d963b2019-07-05 15:35:51 +0200447 elsif ($grant_type eq 'refresh_token') {
Akroncdfd9d52019-07-23 11:35:00 +0200448
449 if ($c->param('refresh_token') eq 'inv4lid') {
450 return $c->render(
451 status => 400,
452 json => {
453 "error_description" => "Refresh token is expired",
454 "error" => "invalid_grant"
455 }
456 );
457 };
458
459 $c->app->log->debug("Refresh the token in the mock server!");
460
Akron8bbbecf2019-07-01 18:57:30 +0200461 return $c->render(
462 status => 200,
463 json => {
Akroncdfd9d52019-07-23 11:35:00 +0200464 "access_token" => $c->get_token("access_token_2"),
465 "refresh_token" => $c->get_token("refresh_token_2"),
Akron8bbbecf2019-07-01 18:57:30 +0200466 "token_type" => "Bearer",
467 "expires_in" => 86400
Akron33f5c672019-06-24 19:40:47 +0200468 }
469 );
470 }
471
Akron83209f72021-01-29 17:54:15 +0100472 # Get auth_token_1
473 elsif ($grant_type eq 'authorization_code') {
474 if ($c->param('code') eq $tokens{auth_token_1}) {
475 return $c->render(
476 status => 200,
477 json => {
478 "access_token" => $tokens{access_token_3},
479 "expires_in" => 31536000,
480 "scope" => 'match_info search openid',
481 "token_type" => "Bearer"
482 }
483 );
484 };
485 }
486
Akron8bbbecf2019-07-01 18:57:30 +0200487 # Unknown token grant
488 else {
489 return $c->render(
Akron63d963b2019-07-05 15:35:51 +0200490 status => 400,
Akron8bbbecf2019-07-01 18:57:30 +0200491 json => {
492 "errors" => [
493 [
Akron63d963b2019-07-05 15:35:51 +0200494 0, "Grant Type unknown", $grant_type
Akron8bbbecf2019-07-01 18:57:30 +0200495 ]
496 ]
497 }
498 )
Akron33f5c672019-06-24 19:40:47 +0200499 }
Akron33f5c672019-06-24 19:40:47 +0200500};
501
Akron4cefe1f2019-09-04 10:11:28 +0200502# Revoke API token
503post '/v1.0/oauth2/revoke' => sub {
504 my $c = shift;
505
506 my $refresh_token = $c->param('token');
507
508 if ($c->param('client_secret') ne 'k414m4r-s3cr3t') {
509 return $c->render(
510 json => {
511 "error_description" => "Invalid client credentials",
512 "error" => "invalid_client"
513 },
514 status => 401
515 );
516 };
517
518 return $c->render(
519 text => ''
520 )
521};
Akron33f5c672019-06-24 19:40:47 +0200522
Akron59992122019-10-29 11:28:45 +0100523# Register a client
524post '/v1.0/oauth2/client/register' => sub {
525 my $c = shift;
526 my $json = $c->req->json;
527
Akrondc50c892021-05-05 18:12:02 +0200528 if ($json->{redirectURI}) {
529 return $c->render(
530 status => 400,
531 json => {
532 errors => [
533 [
534 201,
535 "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\"])"
536 ]
537 ]
538 }
539 );
540 };
541
Akron59992122019-10-29 11:28:45 +0100542 my $name = $json->{name};
Akron1a9d5be2020-03-19 17:28:33 +0100543 my $desc = $json->{description};
Akron59992122019-10-29 11:28:45 +0100544 my $type = $json->{type};
545 my $url = $json->{url};
Akrondc50c892021-05-05 18:12:02 +0200546 my $redirect_url = $json->{redirect_uri};
Akron59992122019-10-29 11:28:45 +0100547
Akron1a9d5be2020-03-19 17:28:33 +0100548 my $list = $c->app->defaults('oauth.client_list');
549
550 push @$list, {
Akrondc50c892021-05-05 18:12:02 +0200551 "client_id" => $tokens{new_client_id},
552 "client_name" => $name,
Akronbc94a9c2021-04-15 00:07:35 +0200553 "client_description" => $desc,
554 "client_url" => $url
Akron1a9d5be2020-03-19 17:28:33 +0100555 };
556
Akron59992122019-10-29 11:28:45 +0100557 # Confidential server application
558 if ($type eq 'CONFIDENTIAL') {
559 return $c->render(json => {
560 client_id => $tokens{new_client_id},
561 client_secret => $tokens{new_client_secret}
562 });
563 };
564
565 # Desktop application
566 return $c->render(json => {
567 client_id => $tokens{new_client_id}
568 });
569};
570
Akron33f5c672019-06-24 19:40:47 +0200571
Akron0f1b93b2020-03-17 11:37:19 +0100572# Register a client
573post '/v1.0/oauth2/client/list' => sub {
574 my $c = shift;
575
Akron276afc02021-06-14 11:00:21 +0200576 my $v = $c->validation;
577
578 $v->required('super_client_id');
579 $v->required('super_client_secret');
580
581 if ($v->has_error) {
582 return $c->render(
583 json => [],
584 status => 400
585 );
586 };
587
Akron0f1b93b2020-03-17 11:37:19 +0100588 # $c->param('client_secret');
Akron1a9d5be2020-03-19 17:28:33 +0100589
590 # Is empty [] when nothing registered
591
Akron0f1b93b2020-03-17 11:37:19 +0100592 return $c->render(
Akron1a9d5be2020-03-19 17:28:33 +0100593 json => $c->stash('oauth.client_list'),
594 status => 200
595 );
596};
597
Akronbc94a9c2021-04-15 00:07:35 +0200598
599# Get token list
600post '/v1.0/oauth2/token/list' => sub {
601 my $c = shift;
602 return $c->render(json => [
603 {
604 "client_description" => "Nur ein Beispiel",
605 "client_id" => $tokens{new_client_id},
606 "client_name" => "Beispiel",
607 "client_url" => "",
608 "created_date" => "2021-04-14T19:40:26.742+02:00[Europe\/Berlin]",
609 "expires_in" => "31533851",
610 "scope" => [
611 "match_info",
612 "search",
613 "openid"
614 ],
615 "token" => "jhkhkjhk_hjgjsfz67i",
616 "user_authentication_time" => "2021-04-14T19:39:41.81+02:00[Europe\/Berlin]"
617 }
618 ]);
619};
620
Akron1a9d5be2020-03-19 17:28:33 +0100621del '/v1.0/oauth2/client/deregister/:client_id' => sub {
622 my $c = shift;
623 my $client_id = $c->stash('client_id');
624
625 my $list = $c->app->defaults('oauth.client_list');
626
627 my $break = -1;
628 for (my $i = 0; $i < @$list; $i++) {
Akrondc50c892021-05-05 18:12:02 +0200629 if ($list->[$i]->{client_id} eq $client_id) {
Akron1a9d5be2020-03-19 17:28:33 +0100630 $break = $i;
631 last;
632 };
633 };
634
635 if ($break != -1) {
636 splice @$list, $break, 1;
637 }
638
639 else {
640 return $c->render(
641 json => {
642 error_description => "Unknown client with $client_id.",
643 error => "invalid_client"
Akron0f1b93b2020-03-17 11:37:19 +0100644 },
Akron1a9d5be2020-03-19 17:28:33 +0100645 status => 401
646 );
647 };
648
649 return $c->render(
650 json => $c->stash('oauth.client_list'),
Akron0f1b93b2020-03-17 11:37:19 +0100651 status => 200
652 );
653};
654
Akron83209f72021-01-29 17:54:15 +0100655post '/v1.0/oauth2/authorize' => sub {
656 my $c = shift;
657 my $type = $c->param('response_type');
658 my $client_id = $c->param('client_id');
659 my $redirect_uri = $c->param('redirect_uri');
660
661 if ($type eq 'code') {
662
663 return $c->redirect_to(
664 Mojo::URL->new($redirect_uri)->query({
665 code => $tokens{auth_token_1},
666 scope => 'match_info search openid'
667 })
668 );
669 }
670};
671
Akron0f1b93b2020-03-17 11:37:19 +0100672
Akronabdf9a92021-01-12 19:06:57 +0100673#######################
674# Query Reference API #
675#######################
676
677use CHI;
678my $chi = CHI->new(
679 driver => 'Memory',
680 global => 1
681);
682
683# Store query
684put '/v1.0/query/~:user/:query_name' => sub {
685 my $c = shift;
686 my $user = $c->stash('user');
687 my $qname = $c->stash('query_name');
688
689 if ($chi->is_valid($qname)) {
690 return $c->render(
691 json => {
692 errors => [
693 {
694 message => 'Unable to store query reference'
695 }
696 ]
697 }, status => 400
698 );
699 };
700
701 my $json = $c->req->json;
702
703 my $store = {
704 name => $qname,
705 koralQuery => { '@type' => 'Okay' },
706 query => $json->{query},
707 queryType => $json->{queryType},
708 type => $json->{type},
709 queryLanguage => $json->{queryLanguage},
710 };
711
712 if (exists $json->{description}) {
713 $store->{description} = $json->{description}
714 };
715
716 # Set query reference
717 $chi->set($qname => $store);
718
719 my $queries = $chi->get('~queries') // [];
720 push @$queries, $qname;
721 $chi->set('~queries' => $queries);
722
723 return $c->render(
724 status => 201,
725 text => ''
726 );
727};
728
729# Get query
730get '/v1.0/query/~:user/:query_name' => sub {
731 my $c = shift;
732
733 my $user = $c->stash('user');
734 my $qname = $c->stash('query_name');
735
736 my $json = $chi->get($qname);
737
738 if ($json) {
739 return $c->render(
740 json => $json
741 );
742 };
743
744 return $c->render(
745 json => {
746 errors => [
747 {
748 message => 'Query reference not found'
749 }
750 ]
751 }, status => 404
752 );
753};
754
755
756# Get all queries
757get '/v1.0/query/~:user' => sub {
758 my $c = shift;
759 my $user = $c->stash('user');
760 my $qs = $chi->get('~queries') // [];
761 my @queries = ();
762 foreach (@$qs) {
763 push @queries, $chi->get($_);
764 };
765 return $c->render(json => { refs => \@queries });
766};
767
768
769# Store query
770del '/v1.0/query/~:user/:query_name' => sub {
771 my $c = shift;
772 my $user = $c->stash('user');
773 my $qname = $c->stash('query_name');
774
775 $chi->remove($qname);
776
777 my $queries = $chi->get('~queries') // [];
778
779 my @clean = ();
780 foreach (@$queries) {
781 push @clean, $_ unless $_ eq $qname
782 };
783
784 $chi->set('~queries' => \@clean);
785
786 return $c->render(
787 status => 200,
788 text => ''
789 );
790};
791
Akronc1aaf932021-06-09 12:19:15 +0200792post '/v1.0/oauth2/revoke/super' => sub {
793 my $c = shift;
794
795 my $s_client_id = $c->param('super_client_id');
796 my $s_client_secret = $c->param('super_client_secret');
797 my $token = $c->param('token');
798
799 return $c->render(text => 'SUCCESS');
800};
801
Akron0f1b93b2020-03-17 11:37:19 +0100802
Akron0e1ed242018-10-11 13:22:00 +0200803app->start;
804
805
806__END__
807
808
809 # Temporary:
810 my $collection_query = {
811 '@type' => "koral:docGroup",
812 "operation" => "operation:or",
813 "operands" => [
814 {
815 '@type' => "koral:docGroup",
816 "operation" => "operation:and",
817 "operands" => [
818 {
819 '@type' => "koral:doc",
820 "key" => "title",
821 "match" => "match:eq",
822 "value" => "Der Birnbaum",
823 "type" => "type:string"
824 },
825 {
826 '@type' => "koral:doc",
827 "key" => "pubPlace",
828 "match" => "match:eq",
829 "value" => "Mannheim",
830 "type" => "type:string"
831 },
832 {
833 '@type' => "koral:docGroup",
834 "operation" => "operation:or",
835 "operands" => [
836 {
837 '@type' => "koral:doc",
838 "key" => "subTitle",
839 "match" => "match:eq",
840 "value" => "Aufzucht oder Pflege",
841 "type" => "type:string"
842 },
843 {
844 '@type' => "koral:doc",
845 "key" => "subTitle",
846 "match" => "match:eq",
847 "value" => "Gedichte",
848 "type" => "type:string"
849 }
850 ]
851 }
852 ]
853 },
854 {
855 '@type' => "koral:doc",
856 "key" => "pubDate",
857 "match" => "match:geq",
858 "value" => "2015-03-05",
859 "type" => "type:date"
860 }
861 ]
862 };