blob: e64f14e6482bc1705528550f8734b56c9719226a [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;
Akron6d49c1f2018-10-11 14:22:21 +02008use Mojo::File qw/path/;
9use Mojo::Util qw/slugify/;
Akron2fc697a2024-06-28 10:35:10 +020010use Kalamar::Controller::Search;
11
12our @default_search_fields = @Kalamar::Controller::Search::search_fields;
Akron0e1ed242018-10-11 13:22:00 +020013
14# This is an API fake server with fixtures
15
16my $secret = 's3cr3t';
Akron73f36082018-10-25 15:34:59 +020017my $fixture_path = path(Mojo::File->new(__FILE__)->dirname)->child('..', 'fixtures');
Akron0e1ed242018-10-11 13:22:00 +020018
Akroncdfd9d52019-07-23 11:35:00 +020019our %tokens = (
Akron59992122019-10-29 11:28:45 +010020 'access_token' => "4dcf8784ccfd26fac9bdb82778fe60e2",
21 'refresh_token' => "hlWci75xb8atDiq3924NUSvOdtAh7Nlf9z",
22 'access_token_2' => "abcde",
Akron83209f72021-01-29 17:54:15 +010023 'access_token_3' => 'jvgjbvjgzucgdwuiKHJK',
Akron59992122019-10-29 11:28:45 +010024 'refresh_token_2' => "fghijk",
25 'new_client_id' => 'fCBbQkA2NDA3MzM1Yw==',
Akronb6b156e2022-03-31 14:57:49 +020026 'new_client_id_2' => 'hghGHhjhFRz_gJhjrd==',
Akron6b75d122022-05-12 17:39:05 +020027 'new_client_id_3' => 'jh0gfjhjbfdsgzjghj==',
Akron59992122019-10-29 11:28:45 +010028 'new_client_secret' => 'KUMaFxs6R1WGud4HM22w3HbmYKHMnNHIiLJ2ihaWtB4N5JxGzZgyqs5GTLutrORj',
Akron83209f72021-01-29 17:54:15 +010029 'auth_token_1' => 'mscajfdghnjdfshtkjcuynxahgz5il'
Akroncdfd9d52019-07-23 11:35:00 +020030);
31
32helper get_token => sub {
33 my ($c, $token) = @_;
34 return $tokens{$token}
35};
36
Akroncdfd9d52019-07-23 11:35:00 +020037# Expiration helper
38helper expired => sub {
39 my ($c, $auth, $set) = @_;
40
Akroncdfd9d52019-07-23 11:35:00 +020041 $auth =~ s/^[^ ]+? //;
42 if ($set) {
43 $c->app->log->debug("Set $auth for expiration");
44 $c->app->defaults('auth_' . $auth => 1);
45 return 1;
46 };
47
48 $c->app->log->debug("Check $auth for expiration: " . (
49 $c->app->defaults('auth_' . $auth) // '0'
50 ));
51
52 return $c->app->defaults('auth_' . $auth);
53};
Akron0e1ed242018-10-11 13:22:00 +020054
Akron408bc7c2022-04-28 15:46:43 +020055
56helper 'add_client' => sub {
57 my $c = shift;
58 my $client = shift;
Akrondb1f4672023-01-24 12:05:07 +010059 my $list = $c->stash('oauth.client_list');
Akron408bc7c2022-04-28 15:46:43 +020060 push @$list, $client;
61};
62
Helge278fbca2022-11-29 18:49:15 +010063# Add plugin to plugin list for marketplace
64helper 'add_plugin' => sub {
65 my $c = shift;
66 my $cplugin = shift;
67 my $pl_list = $c->app->defaults('oauth.plugin_list');
68 push @$pl_list, $cplugin;
69};
Akron408bc7c2022-04-28 15:46:43 +020070
Helgedb720ea2023-03-20 09:39:36 +010071helper 'add_instplugin' => sub {
72 my $c = shift;
73 my $cplugin = shift;
74 my $pl_list = $c->app->defaults('oauth.pluginin_list');
75 push @$pl_list, $cplugin;
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' => []);
Helge278fbca2022-11-29 18:49:15 +0100110app->defaults('oauth.plugin_list' => []);
Helgedb720ea2023-03-20 09:39:36 +0100111app->defaults('oauth.pluginin_list' => []);
Akron6d49c1f2018-10-11 14:22:21 +0200112
Akron0e1ed242018-10-11 13:22:00 +0200113# Base page
Akron63d963b2019-07-05 15:35:51 +0200114get '/v1.0/' => sub {
Akron6d49c1f2018-10-11 14:22:21 +0200115 shift->render(text => 'Fake server available');
Akron0e1ed242018-10-11 13:22:00 +0200116};
117
Akron32396632018-10-11 17:08:37 +0200118
Akrond00b4272020-02-05 17:00:33 +0100119get '/v1.0/redirect-target-a' => sub {
120 shift->render(text => 'Redirect Target!');
121} => 'redirect-target';
122
123
124# Base page
125get '/v1.0/redirect' => sub {
126 my $c = shift;
127 $c->res->code(308);
128 $c->res->headers->location($c->url_for('redirect-target')->to_abs);
129 return $c->render(text => '');
130};
131
132
Akron0e1ed242018-10-11 13:22:00 +0200133# Search fixtures
Akron63d963b2019-07-05 15:35:51 +0200134get '/v1.0/search' => sub {
Akron0e1ed242018-10-11 13:22:00 +0200135 my $c = shift;
136 my $v = $c->validation;
137 $v->optional('q');
138 $v->optional('page');
139 $v->optional('ql');
Akroncd42a142019-07-12 18:55:37 +0200140 $v->optional('cq');
Akron0e1ed242018-10-11 13:22:00 +0200141 $v->optional('count');
142 $v->optional('context');
Akron8ea84292018-10-24 13:41:52 +0200143 $v->optional('offset');
Akronc58bfc42020-10-05 12:09:45 +0200144 $v->optional('pipes');
Akron2fc697a2024-06-28 10:35:10 +0200145 $v->optional('fields');
Akron8ea84292018-10-24 13:41:52 +0200146 $v->optional('cutoff')->in(qw/true false/);
Akron0e1ed242018-10-11 13:22:00 +0200147
Akron32396632018-10-11 17:08:37 +0200148 $c->app->log->debug('Receive request');
149
Akron0e1ed242018-10-11 13:22:00 +0200150 # Response q=x&ql=cosmas3
151 if ($v->param('ql') && $v->param('ql') eq 'cosmas3') {
152 return $c->render(
153 status => 400,
154 json => {
155 "\@context" => "http://korap.ids-mannheim.de/ns/koral/0.3/context.jsonld",
156 "errors" => [[307,"cosmas3 is not a supported query language!"]]
157 });
158 };
159
Akron6d49c1f2018-10-11 14:22:21 +0200160 if (!$v->param('q')) {
Akron8ea84292018-10-24 13:41:52 +0200161 return $c->render(%{$c->load_response('query_no_query')});
Akron0e1ed242018-10-11 13:22:00 +0200162 };
163
Akroncce055c2021-07-02 12:18:03 +0200164 if ($v->param('q') eq 'error') {
165 return $c->render(
166 status => 500,
167 inline => '<html><head>ERROR</head></html>'
168 );
169 };
170
Akron8ea84292018-10-24 13:41:52 +0200171 my @slug_base = ($v->param('q'));
172 push @slug_base, 'o' . $v->param('offset') if defined $v->param('offset');
173 push @slug_base, 'c' . $v->param('count') if defined $v->param('count');
174 push @slug_base, 'co' . $v->param('cutoff') if defined $v->param('cutoff');
Akroncd42a142019-07-12 18:55:37 +0200175 push @slug_base, 'cq' if defined $v->param('cq');
Akronc58bfc42020-10-05 12:09:45 +0200176 push @slug_base, 'p' . $v->param('pipes') if defined $v->param('pipes');
Akron8ea84292018-10-24 13:41:52 +0200177
Akron2fc697a2024-06-28 10:35:10 +0200178 if (defined $v->param('fields') && ($v->param('fields') ne join(',', @default_search_fields))) {
179 push @slug_base, 'f' .join('-', split(',', $v->param('fields')));
180 };
181
Akron6d49c1f2018-10-11 14:22:21 +0200182 # Get response based on query parameter
Akron8ea84292018-10-24 13:41:52 +0200183 my $response = $c->load_response('query_' . slugify(join('_', @slug_base)));
Akron0e1ed242018-10-11 13:22:00 +0200184
185 # Check authentification
186 if (my $auth = $c->req->headers->header('Authorization')) {
Akron33f5c672019-06-24 19:40:47 +0200187
Akroncdfd9d52019-07-23 11:35:00 +0200188 $c->app->log->debug("There is an authorization header $auth");
Akron33f5c672019-06-24 19:40:47 +0200189 if ($auth =~ /^Bearer/) {
190 # Username unknown in OAuth2
191 $response->{json}->{meta}->{authorized} = 'yes';
Akron0e1ed242018-10-11 13:22:00 +0200192 };
Akroncdfd9d52019-07-23 11:35:00 +0200193
194 # Code is expired
195 if ($c->expired($auth)) {
196
197 $c->app->log->debug("The access token has expired");
198
199 return $c->render(
200 status => 401,
201 json => {
202 errors => [[2003, 'Access token is expired']]
203 }
204 );
205 }
206
207 # Auth token is invalid
208 if ($auth =~ /^Bearer inv4lid/) {
209 $c->app->log->debug("The access token is invalid");
210
211 return $c->render(
212 status => 401,
213 json => {
214 errors => [[2011, 'Access token is invalid']]
215 }
216 );
217 }
Akron0e1ed242018-10-11 13:22:00 +0200218 };
219
Akronc58bfc42020-10-05 12:09:45 +0200220 if ($v->param('pipes')) {
221 $response->{json}->{meta}->{pipes} = $v->param('pipes');
Akron7b9a1962020-07-02 09:52:53 +0200222 };
223
Akron6d49c1f2018-10-11 14:22:21 +0200224 # Set page parameter
Akron0e1ed242018-10-11 13:22:00 +0200225 if ($v->param('page')) {
Akron6d49c1f2018-10-11 14:22:21 +0200226 $response->{json}->{meta}->{startIndex} = $v->param("startIndex");
Akron0e1ed242018-10-11 13:22:00 +0200227 };
228
Akron0e1ed242018-10-11 13:22:00 +0200229 # Simple search fixture
Akron32396632018-10-11 17:08:37 +0200230 $c->render(%$response);
231
232 $c->app->log->debug('Rendered result');
233
234 return 1;
Akron0e1ed242018-10-11 13:22:00 +0200235};
236
Akron80a84b22018-10-24 17:44:24 +0200237# Textinfo fixtures
Akron4e413fb2023-09-26 13:11:11 +0200238get '/v1.0/corpus/#corpusId/#docId/#textId' => sub {
Akron80a84b22018-10-24 17:44:24 +0200239 my $c = shift;
240
241 my $file = join('_', (
242 'textinfo',
243 $c->stash('corpusId'),
244 $c->stash('docId'),
245 $c->stash('textId')
246 ));
247
248 my $slug = slugify($file);
249
250 # Get response based on query parameter
251 my $response = $c->load_response($slug);
252 return $c->render(%$response);
253};
254
Akron0e1ed242018-10-11 13:22:00 +0200255
Akronb80341d2018-10-15 19:46:23 +0200256# Matchinfo fixtures
Akron06d4d1f2024-06-05 11:59:20 +0200257get '/v1.0/corpus/#corpusId/#docId/#textId/#matchId' => sub {
Akronb80341d2018-10-15 19:46:23 +0200258 my $c = shift;
259
260 my $file = join('_', (
261 'matchinfo',
262 $c->stash('corpusId'),
263 $c->stash('docId'),
264 $c->stash('textId'),
265 $c->stash('matchId')
266 ));
267
Akronb8d0b402018-10-18 23:51:52 +0200268 my $slug = slugify($file);
269
Akronb80341d2018-10-15 19:46:23 +0200270 # Get response based on query parameter
Akronb8d0b402018-10-18 23:51:52 +0200271 my $response = $c->load_response($slug);
Akronb80341d2018-10-15 19:46:23 +0200272 return $c->render(%$response);
273};
274
Akron0e1ed242018-10-11 13:22:00 +0200275
Akronbe61f4c2018-10-20 00:52:58 +0200276# Statistics endpoint
Akron63d963b2019-07-05 15:35:51 +0200277get '/v1.0/statistics' => sub {
Akronbe61f4c2018-10-20 00:52:58 +0200278 my $c = shift;
279 my $v = $c->validation;
Akron5fa61e92019-07-15 11:56:11 +0200280 $v->optional('cq');
Akronbe61f4c2018-10-20 00:52:58 +0200281
282 my @list = 'corpusinfo';
Akron5fa61e92019-07-15 11:56:11 +0200283 if ($v->param('cq')) {
284 push @list, $v->param('cq');
Akronbe61f4c2018-10-20 00:52:58 +0200285 };
286 my $slug = slugify(join('_', @list));
287
288 # Get response based on query parameter
289 my $response = $c->load_response($slug);
290 return $c->render(%$response);
291};
292
Akron0e1ed242018-10-11 13:22:00 +0200293############
294# Auth API #
295############
296
297# Request API token
Akron63d963b2019-07-05 15:35:51 +0200298get '/v1.0/auth/logout' => sub {
Akron0e1ed242018-10-11 13:22:00 +0200299 my $c = shift;
300
301 if (my $auth = $c->req->headers->header('Authorization')) {
Akroncdfd9d52019-07-23 11:35:00 +0200302
303 if ($auth =~ /^Bearer/) {
304 $c->app->log->debug('Server-Logout: ' . $auth);
305 return $c->render(json => { msg => [[0, 'Fine!']]});
Akron0e1ed242018-10-11 13:22:00 +0200306 };
307 };
308
309 return $c->render(status => 400, json => { error => [[0, 'No!']]});
310};
311
312
313# Request API token
Akron63d963b2019-07-05 15:35:51 +0200314get '/v1.0/auth/apiToken' => sub {
Akron0e1ed242018-10-11 13:22:00 +0200315 my $c = shift;
316
317 # Get auth header
318 my $auth = $c->req->headers->authorization;
319
320 # Authorization missing or not basic
321 if (!$auth || $auth !~ s/\s*Basic\s+//gi) {
322 return $c->render(
323 json => {
324 error => [[2, 'x']]
325 }
326 );
327 };
328
329 # Decode header
330 my ($username, $pwd) = @{b($auth)->b64_decode->split(':')->to_array};
331
332 # the password is 'pass'
333 if ($pwd) {
Akrona205b142022-11-28 13:35:19 +0100334 if ($pwd eq 'ldaperr') {
Akron3d673062019-01-29 15:54:16 +0100335 return $c->render(
336 format => 'html',
337 status => 401,
338 json => {
339 "errors" => [[2022,"LDAP Authentication failed due to unknown user or password!"]]
340 }
341 );
Akron0e1ed242018-10-11 13:22:00 +0200342 };
343
344 return $c->render(
345 json => {
346 error => [[2004, undef]]
347 }
348 );
349 };
350
351 return $c->render(
352 json => {
353 error => [[2004, undef]]
354 }
355 );
356};
357
Akron33f5c672019-06-24 19:40:47 +0200358
359# Request API token
Akron63d963b2019-07-05 15:35:51 +0200360post '/v1.0/oauth2/token' => sub {
Akron33f5c672019-06-24 19:40:47 +0200361 my $c = shift;
362
Akron63d963b2019-07-05 15:35:51 +0200363 my $grant_type = $c->param('grant_type') // 'undefined';
364
365 if ($grant_type eq 'password') {
Akron33f5c672019-06-24 19:40:47 +0200366
Akron8bbbecf2019-07-01 18:57:30 +0200367 # Check for wrong client id
368 if ($c->param('client_id') ne '2') {
369 return $c->render(
370 json => {
371 "error_description" => "Unknown client with " . $_->{client_id},
372 "error" => "invalid_client"
373 },
374 status => 401
375 );
376 }
Akron33f5c672019-06-24 19:40:47 +0200377
Akron8bbbecf2019-07-01 18:57:30 +0200378 # Check for wrong client secret
379 elsif ($c->param('client_secret') ne 'k414m4r-s3cr3t') {
380 return $c->render(
381 json => {
382 "error_description" => "Invalid client credentials",
383 "error" => "invalid_client"
384 },
385 status => 401
386 );
387 }
Akron33f5c672019-06-24 19:40:47 +0200388
Akron8bbbecf2019-07-01 18:57:30 +0200389 # Check for wrong user name
Akron6a228db2021-10-14 15:57:00 +0200390 elsif ($c->param('username') !~ /^t.st$/) {
Akron8bbbecf2019-07-01 18:57:30 +0200391 return $c->render(json => {
392 error => [[2004, undef]]
393 });
394 }
395
396 # Check for ldap error
397 elsif ($c->param('password') eq 'ldaperr') {
398 return $c->render(
399 format => 'html',
400 status => 401,
401 json => {
402 "errors" => [
403 [
404 2022,
405 "LDAP Authentication failed due to unknown user or password!"
406 ]
407 ]
408 }
409 );
410 }
411
412 # Check for wrong password
413 elsif ($c->param('password') ne 'pass') {
414 return $c->render(json => {
415 format => 'html',
416 status => 401,
Akron33f5c672019-06-24 19:40:47 +0200417 "errors" => [[2022,"LDAP Authentication failed due to unknown user or password!"]]
Akron8bbbecf2019-07-01 18:57:30 +0200418 });
419 }
420
421 # Return fine access
422 return $c->render(
423 json => {
Akroncdfd9d52019-07-23 11:35:00 +0200424 "access_token" => $c->get_token('access_token'),
425 "refresh_token" => $c->get_token('refresh_token'),
Akron8bbbecf2019-07-01 18:57:30 +0200426 "scope" => "all",
427 "token_type" => "Bearer",
428 "expires_in" => 86400
429 });
430 }
431
432 # Refresh token
Akron63d963b2019-07-05 15:35:51 +0200433 elsif ($grant_type eq 'refresh_token') {
Akroncdfd9d52019-07-23 11:35:00 +0200434
435 if ($c->param('refresh_token') eq 'inv4lid') {
436 return $c->render(
437 status => 400,
438 json => {
439 "error_description" => "Refresh token is expired",
440 "error" => "invalid_grant"
441 }
442 );
443 };
444
445 $c->app->log->debug("Refresh the token in the mock server!");
446
Akron8bbbecf2019-07-01 18:57:30 +0200447 return $c->render(
448 status => 200,
449 json => {
Akroncdfd9d52019-07-23 11:35:00 +0200450 "access_token" => $c->get_token("access_token_2"),
451 "refresh_token" => $c->get_token("refresh_token_2"),
Akron8bbbecf2019-07-01 18:57:30 +0200452 "token_type" => "Bearer",
453 "expires_in" => 86400
Akron33f5c672019-06-24 19:40:47 +0200454 }
455 );
456 }
457
Akron83209f72021-01-29 17:54:15 +0100458 # Get auth_token_1
459 elsif ($grant_type eq 'authorization_code') {
Akrone3daaeb2023-05-08 09:44:18 +0200460 if ($c->param('code') && $c->param('code') eq $tokens{auth_token_1}) {
Akron83209f72021-01-29 17:54:15 +0100461 return $c->render(
462 status => 200,
463 json => {
464 "access_token" => $tokens{access_token_3},
465 "expires_in" => 31536000,
466 "scope" => 'match_info search openid',
467 "token_type" => "Bearer"
468 }
469 );
470 };
471 }
472
Akron8bbbecf2019-07-01 18:57:30 +0200473 # Unknown token grant
474 else {
475 return $c->render(
Akron63d963b2019-07-05 15:35:51 +0200476 status => 400,
Akron8bbbecf2019-07-01 18:57:30 +0200477 json => {
478 "errors" => [
479 [
Akron63d963b2019-07-05 15:35:51 +0200480 0, "Grant Type unknown", $grant_type
Akron8bbbecf2019-07-01 18:57:30 +0200481 ]
482 ]
483 }
484 )
Akron33f5c672019-06-24 19:40:47 +0200485 }
Akron33f5c672019-06-24 19:40:47 +0200486};
487
Akron4cefe1f2019-09-04 10:11:28 +0200488# Revoke API token
489post '/v1.0/oauth2/revoke' => sub {
490 my $c = shift;
491
492 my $refresh_token = $c->param('token');
493
494 if ($c->param('client_secret') ne 'k414m4r-s3cr3t') {
495 return $c->render(
496 json => {
497 "error_description" => "Invalid client credentials",
498 "error" => "invalid_client"
499 },
500 status => 401
501 );
502 };
503
504 return $c->render(
505 text => ''
506 )
507};
Akron33f5c672019-06-24 19:40:47 +0200508
Akron59992122019-10-29 11:28:45 +0100509# Register a client
510post '/v1.0/oauth2/client/register' => sub {
511 my $c = shift;
512 my $json = $c->req->json;
513
Akrondc50c892021-05-05 18:12:02 +0200514 if ($json->{redirectURI}) {
515 return $c->render(
516 status => 400,
517 json => {
518 errors => [
519 [
520 201,
521 "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\"])"
522 ]
523 ]
524 }
525 );
526 };
527
Akron59992122019-10-29 11:28:45 +0100528 my $name = $json->{name};
Akron1a9d5be2020-03-19 17:28:33 +0100529 my $desc = $json->{description};
Akron59992122019-10-29 11:28:45 +0100530 my $type = $json->{type};
531 my $url = $json->{url};
Akron9f2ad342022-05-04 16:16:40 +0200532 my $src = $json->{source};
Akronb6b156e2022-03-31 14:57:49 +0200533 my $redirect_uri = $json->{redirect_uri};
Akron59992122019-10-29 11:28:45 +0100534
Akron1a9d5be2020-03-19 17:28:33 +0100535 my $list = $c->app->defaults('oauth.client_list');
536
Akron6b75d122022-05-12 17:39:05 +0200537 my $obj = {
Akrondc50c892021-05-05 18:12:02 +0200538 "client_id" => $tokens{new_client_id},
539 "client_name" => $name,
Akronbc94a9c2021-04-15 00:07:35 +0200540 "client_description" => $desc,
Akronb6b156e2022-03-31 14:57:49 +0200541 "client_url" => $url,
Akron9f2ad342022-05-04 16:16:40 +0200542 "client_redirect_uri" => $redirect_uri,
Akron6b75d122022-05-12 17:39:05 +0200543 "client_type" => $type
Akronb6b156e2022-03-31 14:57:49 +0200544 };
545
Akron6b75d122022-05-12 17:39:05 +0200546 # Plugin!
547 if ($src) {
548 $obj->{source} = $src;
549 $obj->{client_id} = $tokens{new_client_id_3};
550 };
551
552 push @$list, $obj;
553
Akronb6b156e2022-03-31 14:57:49 +0200554 if ($redirect_uri && $redirect_uri =~ /FAIL$/) {
555 return $c->render(
556 status => 400,
557 json => {
558 "error_description" => $redirect_uri . " is invalid.",
559 "error" => "invalid_request"
560 }
561 )
Akron1a9d5be2020-03-19 17:28:33 +0100562 };
563
Akron59992122019-10-29 11:28:45 +0100564 # Confidential server application
565 if ($type eq 'CONFIDENTIAL') {
Akronb6b156e2022-03-31 14:57:49 +0200566
Akron59992122019-10-29 11:28:45 +0100567 return $c->render(json => {
Akronb6b156e2022-03-31 14:57:49 +0200568 client_id => $tokens{new_client_id_2},
Akron59992122019-10-29 11:28:45 +0100569 client_secret => $tokens{new_client_secret}
570 });
571 };
572
573 # Desktop application
574 return $c->render(json => {
575 client_id => $tokens{new_client_id}
576 });
577};
578
Helgedb720ea2023-03-20 09:39:36 +0100579# Mock API list plugins
Helge278fbca2022-11-29 18:49:15 +0100580post '/v1.0/plugins' => sub {
Akron0f1b93b2020-03-17 11:37:19 +0100581 my $c = shift;
Akron276afc02021-06-14 11:00:21 +0200582 my $v = $c->validation;
Helgedb720ea2023-03-20 09:39:36 +0100583 $v->required('super_client_id');
584 $v->required('super_client_secret');
585 if ($v->has_error) {
586 return $c->render(
587 json => [],
588 status => 400
589 );
590 };
Akron276afc02021-06-14 11:00:21 +0200591
Helgedb720ea2023-03-20 09:39:36 +0100592 return $c->render(
593 json => $c->stash('oauth.plugin_list'),
594 status => 200
595 );
596};
597
598# Mock API list installed plugins
599post '/v1.0/plugins/installed' => sub {
600 my $c = shift;
601 my $v = $c->validation;
Akron276afc02021-06-14 11:00:21 +0200602 $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 };
Helge278fbca2022-11-29 18:49:15 +0100611
Helgedb720ea2023-03-20 09:39:36 +0100612 return $c->render(
613 json => $c->stash('oauth.pluginin_list'),
614 status => 200
615 );
616};
617
618
619# Mock API plugin installation
620post '/v1.0/plugins/install' => sub {
621 my $c = shift;
622 my $v = $c->validation;
623 $v->required('super_client_id');
624 $v->required('super_client_secret');
625 $v->required('client_id');
626 my $cl_id = $c->param('client_id');
627 if ($v->has_error) {
Helge278fbca2022-11-29 18:49:15 +0100628 return $c->render(
Helgedb720ea2023-03-20 09:39:36 +0100629 json => [],
630 status => 400
631 );
632 };
633
634 my $date = "2022-12-13T16:33:27.621+01:00[Europe/Berlin]";
635 my $pl_list = $c->app->defaults('oauth.plugin_list');
636 my $cl_name = (grep{($_->{client_id} eq $cl_id)}@$pl_list)[0]->{client_name};
637
638 if (length $cl_name){
639
640 my %inst_plugin = (
641 "name" => $cl_name,
642 "client_id" => $cl_id,
643 "installed_date" => $date,
644 );
645
646 $c->add_instplugin(\%inst_plugin);
647
648 return $c->render(
649 json => %inst_plugin,
Helge278fbca2022-11-29 18:49:15 +0100650 status => 200
651 );
652 }
Helge278fbca2022-11-29 18:49:15 +0100653
Helgedb720ea2023-03-20 09:39:36 +0100654 return $c->render(
655 json => [],
656 status => 400
657 );
Helge278fbca2022-11-29 18:49:15 +0100658};
659
Helged36478d2023-06-08 17:43:01 +0200660# Mock API plugin uninstallation
661post '/v1.0/plugins/uninstall' => sub {
662 my $c = shift;
663 my $v = $c->validation;
664 $v->required('super_client_id');
665 $v->required('super_client_secret');
666 $v->required('client_id');
667 if ($v->has_error) {
668 return $c->render(
669 json => [],
670 status => 400
671 );
672 };
673 my $cl_id = $c->param('client_id');
674
675 my $plin_list = $c->app->defaults('oauth.pluginin_list');
676 my @new_list = grep{!($_->{client_id} eq $cl_id)}@$plin_list;
677 $c->app->defaults('oauth.pluginin_list' => \@new_list);
678
679 if(scalar @new_list eq scalar @$plin_list){
680 return $c->render(
681 status => 404
682 );
683 }
684 return $c->render(
685 json => $c->stash('oauth.pluginin_list'),
686 status => 200
687 );
688 };
Helgedb720ea2023-03-20 09:39:36 +0100689
Helge278fbca2022-11-29 18:49:15 +0100690# Register a client
691post '/v1.0/oauth2/client/list' => sub {
692 my $c = shift;
693
694 my $v = $c->validation;
695 $v->required('super_client_id');
696 $v->required('super_client_secret');
697
Helge05436702024-08-05 17:08:44 +0200698 $v->optional('filter_by' );
699 $v->optional('authorized_only' );
700
Helge278fbca2022-11-29 18:49:15 +0100701 if ($v->has_error) {
702 return $c->render(
703 json => [],
704 status => 400
705 );
706 };
707
Akron276afc02021-06-14 11:00:21 +0200708
Akron0f1b93b2020-03-17 11:37:19 +0100709 # $c->param('client_secret');
Akron1a9d5be2020-03-19 17:28:33 +0100710
711 # Is empty [] when nothing registered
712
Akron0f1b93b2020-03-17 11:37:19 +0100713 return $c->render(
Akron1a9d5be2020-03-19 17:28:33 +0100714 json => $c->stash('oauth.client_list'),
715 status => 200
716 );
717};
718
Akrondb1f4672023-01-24 12:05:07 +0100719# Get client info
720post '/v1.0/oauth2/client/:client_id' => sub {
721 my $c = shift;
722
723 # Validate input
724 my $v = $c->validation;
725 $v->required('super_client_id');
726 $v->required('super_client_secret');
727
728 if ($v->has_error) {
729 return $c->render(
730 status => 400,
731 json => {
732 error_description => "No super client",
733 error => "no_superclient"
734 }
735 );
736 };
737
738 my $client_id = $c->stash('client_id');
739
740 my $list = $c->stash('oauth.client_list');
741
742 foreach (@$list) {
743 if ($_->{client_id} eq $client_id) {
744 return $c->render(
745 json => $_,
746 status => 200
747 );
748 };
749 };
750
751 return $c->render(
752 json => {
753 error_description => "Unknown client with $client_id.",
754 error => "invalid_client"
755 },
756 status => 401
757 );
758};
759
Akronbc94a9c2021-04-15 00:07:35 +0200760
761# Get token list
762post '/v1.0/oauth2/token/list' => sub {
763 my $c = shift;
764 return $c->render(json => [
765 {
766 "client_description" => "Nur ein Beispiel",
767 "client_id" => $tokens{new_client_id},
768 "client_name" => "Beispiel",
769 "client_url" => "",
770 "created_date" => "2021-04-14T19:40:26.742+02:00[Europe\/Berlin]",
771 "expires_in" => "31533851",
772 "scope" => [
773 "match_info",
774 "search",
775 "openid"
776 ],
777 "token" => "jhkhkjhk_hjgjsfz67i",
778 "user_authentication_time" => "2021-04-14T19:39:41.81+02:00[Europe\/Berlin]"
779 }
780 ]);
781};
782
Akron1a9d5be2020-03-19 17:28:33 +0100783del '/v1.0/oauth2/client/deregister/:client_id' => sub {
784 my $c = shift;
785 my $client_id = $c->stash('client_id');
786
787 my $list = $c->app->defaults('oauth.client_list');
788
789 my $break = -1;
790 for (my $i = 0; $i < @$list; $i++) {
Akrondc50c892021-05-05 18:12:02 +0200791 if ($list->[$i]->{client_id} eq $client_id) {
Akron1a9d5be2020-03-19 17:28:33 +0100792 $break = $i;
793 last;
794 };
795 };
796
797 if ($break != -1) {
798 splice @$list, $break, 1;
799 }
800
801 else {
802 return $c->render(
803 json => {
804 error_description => "Unknown client with $client_id.",
805 error => "invalid_client"
Akron0f1b93b2020-03-17 11:37:19 +0100806 },
Akron1a9d5be2020-03-19 17:28:33 +0100807 status => 401
808 );
809 };
810
811 return $c->render(
812 json => $c->stash('oauth.client_list'),
Akron0f1b93b2020-03-17 11:37:19 +0100813 status => 200
814 );
815};
816
Akron83209f72021-01-29 17:54:15 +0100817post '/v1.0/oauth2/authorize' => sub {
818 my $c = shift;
819 my $type = $c->param('response_type');
820 my $client_id = $c->param('client_id');
Akrona8efaa92022-04-09 14:45:43 +0200821 my $scope = $c->param('scope');
822 my $state = $c->param('state');
823 my $redirect_uri = $c->param('redirect_uri') // 'NO';
Akron83209f72021-01-29 17:54:15 +0100824
Akrona8efaa92022-04-09 14:45:43 +0200825 if ($type eq 'code' && $client_id eq 'xyz') {
826
827 if ($state eq 'fail') {
828 $c->res->headers->location(
829 Mojo::URL->new($redirect_uri)->query({
830 error_description => 'FAIL'
831 })
832 );
833 $c->res->code(400);
834 return $c->rendered;
835 };
836
Akron9ccf69a2023-01-31 14:21:37 +0100837 if (index($redirect_uri,'http://wrong') >= 0) {
838 return $c->render(
839 code => 400,
840 content_type => 'text/plain',
841 text => '{"error_description":"Invalid redirect URI","state":"ZMwDGTZ2RY","error":"invalid_request"}'
842 );
843 };
844
Akrona8efaa92022-04-09 14:45:43 +0200845 return $c->redirect_to(
846 Mojo::URL->new($redirect_uri)->query({
847 code => $tokens{auth_token_1},
848 scope => $scope,
849 })
850 );
851 }
852
853 elsif ($type eq 'code') {
Akrone3daaeb2023-05-08 09:44:18 +0200854 my $loc = Mojo::URL->new($redirect_uri)->query({
855 code => $tokens{auth_token_1},
856 scope => 'match_info search openid'
857 });
Akron83209f72021-01-29 17:54:15 +0100858
Akrone3daaeb2023-05-08 09:44:18 +0200859 my $res = $c->res;
860 $res->headers->location($loc);
861 return $c->rendered($client_id eq '307' ? 307 : 302);
862 # return $c->rendered(302);
Akron9ccf69a2023-01-31 14:21:37 +0100863 };
864
865 return $c->render(
866 code => 400,
867 content_type => 'text/plain',
868 content => 'Unknown'
869 );
Akron83209f72021-01-29 17:54:15 +0100870};
871
Akron0f1b93b2020-03-17 11:37:19 +0100872
Akronabdf9a92021-01-12 19:06:57 +0100873#######################
874# Query Reference API #
875#######################
876
877use CHI;
878my $chi = CHI->new(
879 driver => 'Memory',
880 global => 1
881);
882
883# Store query
884put '/v1.0/query/~:user/:query_name' => sub {
885 my $c = shift;
886 my $user = $c->stash('user');
887 my $qname = $c->stash('query_name');
888
889 if ($chi->is_valid($qname)) {
890 return $c->render(
891 json => {
892 errors => [
893 {
894 message => 'Unable to store query reference'
895 }
896 ]
897 }, status => 400
898 );
899 };
900
901 my $json = $c->req->json;
902
903 my $store = {
904 name => $qname,
905 koralQuery => { '@type' => 'Okay' },
906 query => $json->{query},
907 queryType => $json->{queryType},
908 type => $json->{type},
909 queryLanguage => $json->{queryLanguage},
910 };
911
912 if (exists $json->{description}) {
913 $store->{description} = $json->{description}
914 };
915
916 # Set query reference
917 $chi->set($qname => $store);
918
919 my $queries = $chi->get('~queries') // [];
920 push @$queries, $qname;
921 $chi->set('~queries' => $queries);
922
923 return $c->render(
924 status => 201,
925 text => ''
926 );
927};
928
929# Get query
930get '/v1.0/query/~:user/:query_name' => sub {
931 my $c = shift;
932
933 my $user = $c->stash('user');
934 my $qname = $c->stash('query_name');
935
936 my $json = $chi->get($qname);
937
938 if ($json) {
939 return $c->render(
940 json => $json
941 );
942 };
943
944 return $c->render(
945 json => {
946 errors => [
947 {
948 message => 'Query reference not found'
949 }
950 ]
951 }, status => 404
952 );
953};
954
955
956# Get all queries
957get '/v1.0/query/~:user' => sub {
958 my $c = shift;
959 my $user = $c->stash('user');
960 my $qs = $chi->get('~queries') // [];
961 my @queries = ();
962 foreach (@$qs) {
963 push @queries, $chi->get($_);
964 };
965 return $c->render(json => { refs => \@queries });
966};
967
968
969# Store query
970del '/v1.0/query/~:user/:query_name' => sub {
971 my $c = shift;
972 my $user = $c->stash('user');
973 my $qname = $c->stash('query_name');
974
975 $chi->remove($qname);
976
977 my $queries = $chi->get('~queries') // [];
978
979 my @clean = ();
980 foreach (@$queries) {
981 push @clean, $_ unless $_ eq $qname
982 };
983
984 $chi->set('~queries' => \@clean);
985
986 return $c->render(
987 status => 200,
988 text => ''
989 );
990};
991
Akronc1aaf932021-06-09 12:19:15 +0200992post '/v1.0/oauth2/revoke/super' => sub {
993 my $c = shift;
994
995 my $s_client_id = $c->param('super_client_id');
996 my $s_client_secret = $c->param('super_client_secret');
997 my $token = $c->param('token');
998
999 return $c->render(text => 'SUCCESS');
1000};
1001
Akrona8efaa92022-04-09 14:45:43 +02001002get '/fakeclient/return' => sub {
1003 my $c = shift;
1004 $c->render(
1005 text => 'welcome back! [' . $c->param('code') . ']'
1006 );
1007} => 'return_uri';
1008
Akron0f1b93b2020-03-17 11:37:19 +01001009
Akron0e1ed242018-10-11 13:22:00 +02001010app->start;
1011
1012
1013__END__
1014
1015
1016 # Temporary:
1017 my $collection_query = {
1018 '@type' => "koral:docGroup",
1019 "operation" => "operation:or",
1020 "operands" => [
1021 {
1022 '@type' => "koral:docGroup",
1023 "operation" => "operation:and",
1024 "operands" => [
1025 {
1026 '@type' => "koral:doc",
1027 "key" => "title",
1028 "match" => "match:eq",
1029 "value" => "Der Birnbaum",
1030 "type" => "type:string"
1031 },
1032 {
1033 '@type' => "koral:doc",
1034 "key" => "pubPlace",
1035 "match" => "match:eq",
1036 "value" => "Mannheim",
1037 "type" => "type:string"
1038 },
1039 {
1040 '@type' => "koral:docGroup",
1041 "operation" => "operation:or",
1042 "operands" => [
1043 {
1044 '@type' => "koral:doc",
1045 "key" => "subTitle",
1046 "match" => "match:eq",
1047 "value" => "Aufzucht oder Pflege",
1048 "type" => "type:string"
1049 },
1050 {
1051 '@type' => "koral:doc",
1052 "key" => "subTitle",
1053 "match" => "match:eq",
1054 "value" => "Gedichte",
1055 "type" => "type:string"
1056 }
1057 ]
1058 }
1059 ]
1060 },
1061 {
1062 '@type' => "koral:doc",
1063 "key" => "pubDate",
1064 "match" => "match:geq",
1065 "value" => "2015-03-05",
1066 "type" => "type:date"
1067 }
1068 ]
1069 };