blob: 1f32a3976cb38c48f3a87035c02a49127e06cdc3 [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
Akron408bc7c2022-04-28 15:46:43 +020070
71helper 'add_client' => sub {
72 my $c = shift;
73 my $client = shift;
74 my $list = $c->app->defaults('oauth.client_list');
75 push @$list, $client;
76};
77
78
Akron6d49c1f2018-10-11 14:22:21 +020079# Load fixture responses
80helper 'load_response' => sub {
81 my $c = shift;
82 my $q_name = shift;
83 my $file = $fixture_path->child("response_$q_name.json");
Akron8ea84292018-10-24 13:41:52 +020084 $c->app->log->debug("Load response from $file");
85
Akron6d49c1f2018-10-11 14:22:21 +020086 unless (-f $file) {
87 return {
88 status => 500,
89 json => {
90 errors => [[0, 'Unable to load query response from ' . $file]]
91 }
92 }
93 };
Akron8ea84292018-10-24 13:41:52 +020094
Akron6d49c1f2018-10-11 14:22:21 +020095 my $response = $file->slurp;
Akrona3c353c2019-02-14 23:50:00 +010096 my $decode = decode_json($response);
97 unless ($decode) {
98 return {
99 status => 500,
100 json => {
101 errors => [[0, 'Unable to parse JSON']]
102 }
103 }
104 };
105
106 return $decode;
Akron6d49c1f2018-10-11 14:22:21 +0200107};
108
Akron1a9d5be2020-03-19 17:28:33 +0100109app->defaults('oauth.client_list' => []);
110
Akron6d49c1f2018-10-11 14:22:21 +0200111
Akron0e1ed242018-10-11 13:22:00 +0200112# Base page
Akron63d963b2019-07-05 15:35:51 +0200113get '/v1.0/' => sub {
Akron6d49c1f2018-10-11 14:22:21 +0200114 shift->render(text => 'Fake server available');
Akron0e1ed242018-10-11 13:22:00 +0200115};
116
Akron32396632018-10-11 17:08:37 +0200117
Akrond00b4272020-02-05 17:00:33 +0100118get '/v1.0/redirect-target-a' => sub {
119 shift->render(text => 'Redirect Target!');
120} => 'redirect-target';
121
122
123# Base page
124get '/v1.0/redirect' => sub {
125 my $c = shift;
126 $c->res->code(308);
127 $c->res->headers->location($c->url_for('redirect-target')->to_abs);
128 return $c->render(text => '');
129};
130
131
Akron0e1ed242018-10-11 13:22:00 +0200132# Search fixtures
Akron63d963b2019-07-05 15:35:51 +0200133get '/v1.0/search' => sub {
Akron0e1ed242018-10-11 13:22:00 +0200134 my $c = shift;
135 my $v = $c->validation;
136 $v->optional('q');
137 $v->optional('page');
138 $v->optional('ql');
Akroncd42a142019-07-12 18:55:37 +0200139 $v->optional('cq');
Akron0e1ed242018-10-11 13:22:00 +0200140 $v->optional('count');
141 $v->optional('context');
Akron8ea84292018-10-24 13:41:52 +0200142 $v->optional('offset');
Akronc58bfc42020-10-05 12:09:45 +0200143 $v->optional('pipes');
Akron8ea84292018-10-24 13:41:52 +0200144 $v->optional('cutoff')->in(qw/true false/);
Akron0e1ed242018-10-11 13:22:00 +0200145
Akron32396632018-10-11 17:08:37 +0200146 $c->app->log->debug('Receive request');
147
Akron0e1ed242018-10-11 13:22:00 +0200148 # Response q=x&ql=cosmas3
149 if ($v->param('ql') && $v->param('ql') eq 'cosmas3') {
150 return $c->render(
151 status => 400,
152 json => {
153 "\@context" => "http://korap.ids-mannheim.de/ns/koral/0.3/context.jsonld",
154 "errors" => [[307,"cosmas3 is not a supported query language!"]]
155 });
156 };
157
Akron6d49c1f2018-10-11 14:22:21 +0200158 if (!$v->param('q')) {
Akron8ea84292018-10-24 13:41:52 +0200159 return $c->render(%{$c->load_response('query_no_query')});
Akron0e1ed242018-10-11 13:22:00 +0200160 };
161
Akroncce055c2021-07-02 12:18:03 +0200162 if ($v->param('q') eq 'error') {
163 return $c->render(
164 status => 500,
165 inline => '<html><head>ERROR</head></html>'
166 );
167 };
168
Akron8ea84292018-10-24 13:41:52 +0200169 my @slug_base = ($v->param('q'));
170 push @slug_base, 'o' . $v->param('offset') if defined $v->param('offset');
171 push @slug_base, 'c' . $v->param('count') if defined $v->param('count');
172 push @slug_base, 'co' . $v->param('cutoff') if defined $v->param('cutoff');
Akroncd42a142019-07-12 18:55:37 +0200173 push @slug_base, 'cq' if defined $v->param('cq');
Akronc58bfc42020-10-05 12:09:45 +0200174 push @slug_base, 'p' . $v->param('pipes') if defined $v->param('pipes');
Akron8ea84292018-10-24 13:41:52 +0200175
Akron6d49c1f2018-10-11 14:22:21 +0200176 # Get response based on query parameter
Akron8ea84292018-10-24 13:41:52 +0200177 my $response = $c->load_response('query_' . slugify(join('_', @slug_base)));
Akron0e1ed242018-10-11 13:22:00 +0200178
179 # Check authentification
180 if (my $auth = $c->req->headers->header('Authorization')) {
Akron33f5c672019-06-24 19:40:47 +0200181
Akroncdfd9d52019-07-23 11:35:00 +0200182 $c->app->log->debug("There is an authorization header $auth");
Akron33f5c672019-06-24 19:40:47 +0200183 my $jwt;
184 if ($auth =~ /^Bearer/) {
185 # Username unknown in OAuth2
186 $response->{json}->{meta}->{authorized} = 'yes';
187 }
Akroncdfd9d52019-07-23 11:35:00 +0200188 elsif ($auth =~ /^api_token/ && ($jwt = $c->jwt_decode($auth))) {
Akron6d49c1f2018-10-11 14:22:21 +0200189 $response->{json}->{meta}->{authorized} = $jwt->{username} if $jwt->{username};
Akron0e1ed242018-10-11 13:22:00 +0200190 };
Akroncdfd9d52019-07-23 11:35:00 +0200191
192 # Code is expired
193 if ($c->expired($auth)) {
194
195 $c->app->log->debug("The access token has expired");
196
197 return $c->render(
198 status => 401,
199 json => {
200 errors => [[2003, 'Access token is expired']]
201 }
202 );
203 }
204
205 # Auth token is invalid
206 if ($auth =~ /^Bearer inv4lid/) {
207 $c->app->log->debug("The access token is invalid");
208
209 return $c->render(
210 status => 401,
211 json => {
212 errors => [[2011, 'Access token is invalid']]
213 }
214 );
215 }
Akron0e1ed242018-10-11 13:22:00 +0200216 };
217
Akronc58bfc42020-10-05 12:09:45 +0200218 if ($v->param('pipes')) {
219 $response->{json}->{meta}->{pipes} = $v->param('pipes');
Akron7b9a1962020-07-02 09:52:53 +0200220 };
221
Akron6d49c1f2018-10-11 14:22:21 +0200222 # Set page parameter
Akron0e1ed242018-10-11 13:22:00 +0200223 if ($v->param('page')) {
Akron6d49c1f2018-10-11 14:22:21 +0200224 $response->{json}->{meta}->{startIndex} = $v->param("startIndex");
Akron0e1ed242018-10-11 13:22:00 +0200225 };
226
Akron0e1ed242018-10-11 13:22:00 +0200227 # Simple search fixture
Akron32396632018-10-11 17:08:37 +0200228 $c->render(%$response);
229
230 $c->app->log->debug('Rendered result');
231
232 return 1;
Akron0e1ed242018-10-11 13:22:00 +0200233};
234
Akron80a84b22018-10-24 17:44:24 +0200235# Textinfo fixtures
Akron63d963b2019-07-05 15:35:51 +0200236get '/v1.0/corpus/:corpusId/:docId/:textId' => sub {
Akron80a84b22018-10-24 17:44:24 +0200237 my $c = shift;
238
239 my $file = join('_', (
240 'textinfo',
241 $c->stash('corpusId'),
242 $c->stash('docId'),
243 $c->stash('textId')
244 ));
245
246 my $slug = slugify($file);
247
248 # Get response based on query parameter
249 my $response = $c->load_response($slug);
250 return $c->render(%$response);
251};
252
Akron0e1ed242018-10-11 13:22:00 +0200253
Akronb80341d2018-10-15 19:46:23 +0200254# Matchinfo fixtures
Akron63d963b2019-07-05 15:35:51 +0200255get '/v1.0/corpus/:corpusId/:docId/:textId/:matchId/matchInfo' => sub {
Akronb80341d2018-10-15 19:46:23 +0200256 my $c = shift;
257
258 my $file = join('_', (
259 'matchinfo',
260 $c->stash('corpusId'),
261 $c->stash('docId'),
262 $c->stash('textId'),
263 $c->stash('matchId')
264 ));
265
Akronb8d0b402018-10-18 23:51:52 +0200266 my $slug = slugify($file);
267
Akronb80341d2018-10-15 19:46:23 +0200268 # Get response based on query parameter
Akronb8d0b402018-10-18 23:51:52 +0200269 my $response = $c->load_response($slug);
Akronb80341d2018-10-15 19:46:23 +0200270 return $c->render(%$response);
271};
272
Akron0e1ed242018-10-11 13:22:00 +0200273
Akronbe61f4c2018-10-20 00:52:58 +0200274# Statistics endpoint
Akron63d963b2019-07-05 15:35:51 +0200275get '/v1.0/statistics' => sub {
Akronbe61f4c2018-10-20 00:52:58 +0200276 my $c = shift;
277 my $v = $c->validation;
Akron5fa61e92019-07-15 11:56:11 +0200278 $v->optional('cq');
Akronbe61f4c2018-10-20 00:52:58 +0200279
280 my @list = 'corpusinfo';
Akron5fa61e92019-07-15 11:56:11 +0200281 if ($v->param('cq')) {
282 push @list, $v->param('cq');
Akronbe61f4c2018-10-20 00:52:58 +0200283 };
284 my $slug = slugify(join('_', @list));
285
286 # Get response based on query parameter
287 my $response = $c->load_response($slug);
288 return $c->render(%$response);
289};
290
Akron0e1ed242018-10-11 13:22:00 +0200291############
292# Auth API #
293############
294
295# Request API token
Akron63d963b2019-07-05 15:35:51 +0200296get '/v1.0/auth/logout' => sub {
Akron0e1ed242018-10-11 13:22:00 +0200297 my $c = shift;
298
299 if (my $auth = $c->req->headers->header('Authorization')) {
Akroncdfd9d52019-07-23 11:35:00 +0200300
301 if ($auth =~ /^Bearer/) {
302 $c->app->log->debug('Server-Logout: ' . $auth);
303 return $c->render(json => { msg => [[0, 'Fine!']]});
304 }
305
306 elsif (my $jwt = $c->jwt_decode($auth)) {
Akron0e1ed242018-10-11 13:22:00 +0200307 my $user = $jwt->{username} if $jwt->{username};
308
309 $c->app->log->debug('Server-Logout: ' . $user);
310 return $c->render(json => { msg => [[0, 'Fine!']]});
311 };
312 };
313
314 return $c->render(status => 400, json => { error => [[0, 'No!']]});
315};
316
317
318# Request API token
Akron63d963b2019-07-05 15:35:51 +0200319get '/v1.0/auth/apiToken' => sub {
Akron0e1ed242018-10-11 13:22:00 +0200320 my $c = shift;
321
322 # Get auth header
323 my $auth = $c->req->headers->authorization;
324
325 # Authorization missing or not basic
326 if (!$auth || $auth !~ s/\s*Basic\s+//gi) {
327 return $c->render(
328 json => {
329 error => [[2, 'x']]
330 }
331 );
332 };
333
334 # Decode header
335 my ($username, $pwd) = @{b($auth)->b64_decode->split(':')->to_array};
336
337 # the password is 'pass'
338 if ($pwd) {
339
340 # the password is 'pass'
341 if ($pwd eq 'pass') {
342
343 # Render info with token
344 my $jwt = $c->jwt_encode(username => $username);
345
346 # Render in the Kustvakt fashion:
347 return $c->render(
348 format => 'html',
349 text => encode_json({
350 %{$jwt->claims},
351 expires => $jwt->expires,
352 token => $jwt->encode,
353 token_type => 'api_token'
354 })
355 );
Akron3d673062019-01-29 15:54:16 +0100356 }
357
358 elsif ($pwd eq 'ldaperr') {
359 return $c->render(
360 format => 'html',
361 status => 401,
362 json => {
363 "errors" => [[2022,"LDAP Authentication failed due to unknown user or password!"]]
364 }
365 );
Akron0e1ed242018-10-11 13:22:00 +0200366 };
367
368 return $c->render(
369 json => {
370 error => [[2004, undef]]
371 }
372 );
373 };
374
375 return $c->render(
376 json => {
377 error => [[2004, undef]]
378 }
379 );
380};
381
Akron33f5c672019-06-24 19:40:47 +0200382
383# Request API token
Akron63d963b2019-07-05 15:35:51 +0200384post '/v1.0/oauth2/token' => sub {
Akron33f5c672019-06-24 19:40:47 +0200385 my $c = shift;
386
Akron63d963b2019-07-05 15:35:51 +0200387 my $grant_type = $c->param('grant_type') // 'undefined';
388
389 if ($grant_type eq 'password') {
Akron33f5c672019-06-24 19:40:47 +0200390
Akron8bbbecf2019-07-01 18:57:30 +0200391 # Check for wrong client id
392 if ($c->param('client_id') ne '2') {
393 return $c->render(
394 json => {
395 "error_description" => "Unknown client with " . $_->{client_id},
396 "error" => "invalid_client"
397 },
398 status => 401
399 );
400 }
Akron33f5c672019-06-24 19:40:47 +0200401
Akron8bbbecf2019-07-01 18:57:30 +0200402 # Check for wrong client secret
403 elsif ($c->param('client_secret') ne 'k414m4r-s3cr3t') {
404 return $c->render(
405 json => {
406 "error_description" => "Invalid client credentials",
407 "error" => "invalid_client"
408 },
409 status => 401
410 );
411 }
Akron33f5c672019-06-24 19:40:47 +0200412
Akron8bbbecf2019-07-01 18:57:30 +0200413 # Check for wrong user name
Akron6a228db2021-10-14 15:57:00 +0200414 elsif ($c->param('username') !~ /^t.st$/) {
Akron8bbbecf2019-07-01 18:57:30 +0200415 return $c->render(json => {
416 error => [[2004, undef]]
417 });
418 }
419
420 # Check for ldap error
421 elsif ($c->param('password') eq 'ldaperr') {
422 return $c->render(
423 format => 'html',
424 status => 401,
425 json => {
426 "errors" => [
427 [
428 2022,
429 "LDAP Authentication failed due to unknown user or password!"
430 ]
431 ]
432 }
433 );
434 }
435
436 # Check for wrong password
437 elsif ($c->param('password') ne 'pass') {
438 return $c->render(json => {
439 format => 'html',
440 status => 401,
Akron33f5c672019-06-24 19:40:47 +0200441 "errors" => [[2022,"LDAP Authentication failed due to unknown user or password!"]]
Akron8bbbecf2019-07-01 18:57:30 +0200442 });
443 }
444
445 # Return fine access
446 return $c->render(
447 json => {
Akroncdfd9d52019-07-23 11:35:00 +0200448 "access_token" => $c->get_token('access_token'),
449 "refresh_token" => $c->get_token('refresh_token'),
Akron8bbbecf2019-07-01 18:57:30 +0200450 "scope" => "all",
451 "token_type" => "Bearer",
452 "expires_in" => 86400
453 });
454 }
455
456 # Refresh token
Akron63d963b2019-07-05 15:35:51 +0200457 elsif ($grant_type eq 'refresh_token') {
Akroncdfd9d52019-07-23 11:35:00 +0200458
459 if ($c->param('refresh_token') eq 'inv4lid') {
460 return $c->render(
461 status => 400,
462 json => {
463 "error_description" => "Refresh token is expired",
464 "error" => "invalid_grant"
465 }
466 );
467 };
468
469 $c->app->log->debug("Refresh the token in the mock server!");
470
Akron8bbbecf2019-07-01 18:57:30 +0200471 return $c->render(
472 status => 200,
473 json => {
Akroncdfd9d52019-07-23 11:35:00 +0200474 "access_token" => $c->get_token("access_token_2"),
475 "refresh_token" => $c->get_token("refresh_token_2"),
Akron8bbbecf2019-07-01 18:57:30 +0200476 "token_type" => "Bearer",
477 "expires_in" => 86400
Akron33f5c672019-06-24 19:40:47 +0200478 }
479 );
480 }
481
Akron83209f72021-01-29 17:54:15 +0100482 # Get auth_token_1
483 elsif ($grant_type eq 'authorization_code') {
484 if ($c->param('code') eq $tokens{auth_token_1}) {
485 return $c->render(
486 status => 200,
487 json => {
488 "access_token" => $tokens{access_token_3},
489 "expires_in" => 31536000,
490 "scope" => 'match_info search openid',
491 "token_type" => "Bearer"
492 }
493 );
494 };
495 }
496
Akron8bbbecf2019-07-01 18:57:30 +0200497 # Unknown token grant
498 else {
499 return $c->render(
Akron63d963b2019-07-05 15:35:51 +0200500 status => 400,
Akron8bbbecf2019-07-01 18:57:30 +0200501 json => {
502 "errors" => [
503 [
Akron63d963b2019-07-05 15:35:51 +0200504 0, "Grant Type unknown", $grant_type
Akron8bbbecf2019-07-01 18:57:30 +0200505 ]
506 ]
507 }
508 )
Akron33f5c672019-06-24 19:40:47 +0200509 }
Akron33f5c672019-06-24 19:40:47 +0200510};
511
Akron4cefe1f2019-09-04 10:11:28 +0200512# Revoke API token
513post '/v1.0/oauth2/revoke' => sub {
514 my $c = shift;
515
516 my $refresh_token = $c->param('token');
517
518 if ($c->param('client_secret') ne 'k414m4r-s3cr3t') {
519 return $c->render(
520 json => {
521 "error_description" => "Invalid client credentials",
522 "error" => "invalid_client"
523 },
524 status => 401
525 );
526 };
527
528 return $c->render(
529 text => ''
530 )
531};
Akron33f5c672019-06-24 19:40:47 +0200532
Akron59992122019-10-29 11:28:45 +0100533# Register a client
534post '/v1.0/oauth2/client/register' => sub {
535 my $c = shift;
536 my $json = $c->req->json;
537
Akrondc50c892021-05-05 18:12:02 +0200538 if ($json->{redirectURI}) {
539 return $c->render(
540 status => 400,
541 json => {
542 errors => [
543 [
544 201,
545 "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\"])"
546 ]
547 ]
548 }
549 );
550 };
551
Akron59992122019-10-29 11:28:45 +0100552 my $name = $json->{name};
Akron1a9d5be2020-03-19 17:28:33 +0100553 my $desc = $json->{description};
Akron59992122019-10-29 11:28:45 +0100554 my $type = $json->{type};
555 my $url = $json->{url};
Akron9f2ad342022-05-04 16:16:40 +0200556 my $src = $json->{source};
Akronb6b156e2022-03-31 14:57:49 +0200557 my $redirect_uri = $json->{redirect_uri};
Akron59992122019-10-29 11:28:45 +0100558
Akron1a9d5be2020-03-19 17:28:33 +0100559 my $list = $c->app->defaults('oauth.client_list');
560
561 push @$list, {
Akrondc50c892021-05-05 18:12:02 +0200562 "client_id" => $tokens{new_client_id},
563 "client_name" => $name,
Akronbc94a9c2021-04-15 00:07:35 +0200564 "client_description" => $desc,
Akronb6b156e2022-03-31 14:57:49 +0200565 "client_url" => $url,
Akron9f2ad342022-05-04 16:16:40 +0200566 "client_redirect_uri" => $redirect_uri,
567 "client_source" => $src
Akronb6b156e2022-03-31 14:57:49 +0200568 };
569
570 if ($redirect_uri && $redirect_uri =~ /FAIL$/) {
571 return $c->render(
572 status => 400,
573 json => {
574 "error_description" => $redirect_uri . " is invalid.",
575 "error" => "invalid_request"
576 }
577 )
Akron1a9d5be2020-03-19 17:28:33 +0100578 };
579
Akron59992122019-10-29 11:28:45 +0100580 # Confidential server application
581 if ($type eq 'CONFIDENTIAL') {
Akronb6b156e2022-03-31 14:57:49 +0200582
Akron59992122019-10-29 11:28:45 +0100583 return $c->render(json => {
Akronb6b156e2022-03-31 14:57:49 +0200584 client_id => $tokens{new_client_id_2},
Akron59992122019-10-29 11:28:45 +0100585 client_secret => $tokens{new_client_secret}
586 });
587 };
588
589 # Desktop application
590 return $c->render(json => {
591 client_id => $tokens{new_client_id}
592 });
593};
594
Akron33f5c672019-06-24 19:40:47 +0200595
Akron0f1b93b2020-03-17 11:37:19 +0100596# Register a client
597post '/v1.0/oauth2/client/list' => sub {
598 my $c = shift;
599
Akron276afc02021-06-14 11:00:21 +0200600 my $v = $c->validation;
601
602 $v->required('super_client_id');
603 $v->required('super_client_secret');
604
605 if ($v->has_error) {
606 return $c->render(
607 json => [],
608 status => 400
609 );
610 };
611
Akron0f1b93b2020-03-17 11:37:19 +0100612 # $c->param('client_secret');
Akron1a9d5be2020-03-19 17:28:33 +0100613
614 # Is empty [] when nothing registered
615
Akron0f1b93b2020-03-17 11:37:19 +0100616 return $c->render(
Akron1a9d5be2020-03-19 17:28:33 +0100617 json => $c->stash('oauth.client_list'),
618 status => 200
619 );
620};
621
Akronbc94a9c2021-04-15 00:07:35 +0200622
623# Get token list
624post '/v1.0/oauth2/token/list' => sub {
625 my $c = shift;
626 return $c->render(json => [
627 {
628 "client_description" => "Nur ein Beispiel",
629 "client_id" => $tokens{new_client_id},
630 "client_name" => "Beispiel",
631 "client_url" => "",
632 "created_date" => "2021-04-14T19:40:26.742+02:00[Europe\/Berlin]",
633 "expires_in" => "31533851",
634 "scope" => [
635 "match_info",
636 "search",
637 "openid"
638 ],
639 "token" => "jhkhkjhk_hjgjsfz67i",
640 "user_authentication_time" => "2021-04-14T19:39:41.81+02:00[Europe\/Berlin]"
641 }
642 ]);
643};
644
Akron1a9d5be2020-03-19 17:28:33 +0100645del '/v1.0/oauth2/client/deregister/:client_id' => sub {
646 my $c = shift;
647 my $client_id = $c->stash('client_id');
648
649 my $list = $c->app->defaults('oauth.client_list');
650
651 my $break = -1;
652 for (my $i = 0; $i < @$list; $i++) {
Akrondc50c892021-05-05 18:12:02 +0200653 if ($list->[$i]->{client_id} eq $client_id) {
Akron1a9d5be2020-03-19 17:28:33 +0100654 $break = $i;
655 last;
656 };
657 };
658
659 if ($break != -1) {
660 splice @$list, $break, 1;
661 }
662
663 else {
664 return $c->render(
665 json => {
666 error_description => "Unknown client with $client_id.",
667 error => "invalid_client"
Akron0f1b93b2020-03-17 11:37:19 +0100668 },
Akron1a9d5be2020-03-19 17:28:33 +0100669 status => 401
670 );
671 };
672
673 return $c->render(
674 json => $c->stash('oauth.client_list'),
Akron0f1b93b2020-03-17 11:37:19 +0100675 status => 200
676 );
677};
678
Akron83209f72021-01-29 17:54:15 +0100679post '/v1.0/oauth2/authorize' => sub {
680 my $c = shift;
681 my $type = $c->param('response_type');
682 my $client_id = $c->param('client_id');
Akrona8efaa92022-04-09 14:45:43 +0200683 my $scope = $c->param('scope');
684 my $state = $c->param('state');
685 my $redirect_uri = $c->param('redirect_uri') // 'NO';
Akron83209f72021-01-29 17:54:15 +0100686
Akrona8efaa92022-04-09 14:45:43 +0200687 if ($type eq 'code' && $client_id eq 'xyz') {
688
689 if ($state eq 'fail') {
690 $c->res->headers->location(
691 Mojo::URL->new($redirect_uri)->query({
692 error_description => 'FAIL'
693 })
694 );
695 $c->res->code(400);
696 return $c->rendered;
697 };
698
699 return $c->redirect_to(
700 Mojo::URL->new($redirect_uri)->query({
701 code => $tokens{auth_token_1},
702 scope => $scope,
703 })
704 );
705 }
706
707 elsif ($type eq 'code') {
Akron83209f72021-01-29 17:54:15 +0100708
709 return $c->redirect_to(
710 Mojo::URL->new($redirect_uri)->query({
711 code => $tokens{auth_token_1},
712 scope => 'match_info search openid'
713 })
714 );
715 }
716};
717
Akron0f1b93b2020-03-17 11:37:19 +0100718
Akronabdf9a92021-01-12 19:06:57 +0100719#######################
720# Query Reference API #
721#######################
722
723use CHI;
724my $chi = CHI->new(
725 driver => 'Memory',
726 global => 1
727);
728
729# Store query
730put '/v1.0/query/~:user/:query_name' => sub {
731 my $c = shift;
732 my $user = $c->stash('user');
733 my $qname = $c->stash('query_name');
734
735 if ($chi->is_valid($qname)) {
736 return $c->render(
737 json => {
738 errors => [
739 {
740 message => 'Unable to store query reference'
741 }
742 ]
743 }, status => 400
744 );
745 };
746
747 my $json = $c->req->json;
748
749 my $store = {
750 name => $qname,
751 koralQuery => { '@type' => 'Okay' },
752 query => $json->{query},
753 queryType => $json->{queryType},
754 type => $json->{type},
755 queryLanguage => $json->{queryLanguage},
756 };
757
758 if (exists $json->{description}) {
759 $store->{description} = $json->{description}
760 };
761
762 # Set query reference
763 $chi->set($qname => $store);
764
765 my $queries = $chi->get('~queries') // [];
766 push @$queries, $qname;
767 $chi->set('~queries' => $queries);
768
769 return $c->render(
770 status => 201,
771 text => ''
772 );
773};
774
775# Get query
776get '/v1.0/query/~:user/:query_name' => sub {
777 my $c = shift;
778
779 my $user = $c->stash('user');
780 my $qname = $c->stash('query_name');
781
782 my $json = $chi->get($qname);
783
784 if ($json) {
785 return $c->render(
786 json => $json
787 );
788 };
789
790 return $c->render(
791 json => {
792 errors => [
793 {
794 message => 'Query reference not found'
795 }
796 ]
797 }, status => 404
798 );
799};
800
801
802# Get all queries
803get '/v1.0/query/~:user' => sub {
804 my $c = shift;
805 my $user = $c->stash('user');
806 my $qs = $chi->get('~queries') // [];
807 my @queries = ();
808 foreach (@$qs) {
809 push @queries, $chi->get($_);
810 };
811 return $c->render(json => { refs => \@queries });
812};
813
814
815# Store query
816del '/v1.0/query/~:user/:query_name' => sub {
817 my $c = shift;
818 my $user = $c->stash('user');
819 my $qname = $c->stash('query_name');
820
821 $chi->remove($qname);
822
823 my $queries = $chi->get('~queries') // [];
824
825 my @clean = ();
826 foreach (@$queries) {
827 push @clean, $_ unless $_ eq $qname
828 };
829
830 $chi->set('~queries' => \@clean);
831
832 return $c->render(
833 status => 200,
834 text => ''
835 );
836};
837
Akronc1aaf932021-06-09 12:19:15 +0200838post '/v1.0/oauth2/revoke/super' => sub {
839 my $c = shift;
840
841 my $s_client_id = $c->param('super_client_id');
842 my $s_client_secret = $c->param('super_client_secret');
843 my $token = $c->param('token');
844
845 return $c->render(text => 'SUCCESS');
846};
847
Akrona8efaa92022-04-09 14:45:43 +0200848get '/fakeclient/return' => sub {
849 my $c = shift;
850 $c->render(
851 text => 'welcome back! [' . $c->param('code') . ']'
852 );
853} => 'return_uri';
854
Akron0f1b93b2020-03-17 11:37:19 +0100855
Akron0e1ed242018-10-11 13:22:00 +0200856app->start;
857
858
859__END__
860
861
862 # Temporary:
863 my $collection_query = {
864 '@type' => "koral:docGroup",
865 "operation" => "operation:or",
866 "operands" => [
867 {
868 '@type' => "koral:docGroup",
869 "operation" => "operation:and",
870 "operands" => [
871 {
872 '@type' => "koral:doc",
873 "key" => "title",
874 "match" => "match:eq",
875 "value" => "Der Birnbaum",
876 "type" => "type:string"
877 },
878 {
879 '@type' => "koral:doc",
880 "key" => "pubPlace",
881 "match" => "match:eq",
882 "value" => "Mannheim",
883 "type" => "type:string"
884 },
885 {
886 '@type' => "koral:docGroup",
887 "operation" => "operation:or",
888 "operands" => [
889 {
890 '@type' => "koral:doc",
891 "key" => "subTitle",
892 "match" => "match:eq",
893 "value" => "Aufzucht oder Pflege",
894 "type" => "type:string"
895 },
896 {
897 '@type' => "koral:doc",
898 "key" => "subTitle",
899 "match" => "match:eq",
900 "value" => "Gedichte",
901 "type" => "type:string"
902 }
903 ]
904 }
905 ]
906 },
907 {
908 '@type' => "koral:doc",
909 "key" => "pubDate",
910 "match" => "match:geq",
911 "value" => "2015-03-05",
912 "type" => "type:date"
913 }
914 ]
915 };