blob: 32485fb00751f3fbb9ceabc2eba8393be057e38a [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
Akron9b829012024-09-23 14:56:53 +0200171 if ($v->param('q') eq $Kalamar::Controller::Search::query_placeholder) {
172 # Get response based on query parameter
173 my $response = $c->load_response('query_baum_o0_c25_cq');
174 return $c->render(%$response);
175 };
176
Akron8ea84292018-10-24 13:41:52 +0200177 my @slug_base = ($v->param('q'));
178 push @slug_base, 'o' . $v->param('offset') if defined $v->param('offset');
179 push @slug_base, 'c' . $v->param('count') if defined $v->param('count');
180 push @slug_base, 'co' . $v->param('cutoff') if defined $v->param('cutoff');
Akroncd42a142019-07-12 18:55:37 +0200181 push @slug_base, 'cq' if defined $v->param('cq');
Akronc58bfc42020-10-05 12:09:45 +0200182 push @slug_base, 'p' . $v->param('pipes') if defined $v->param('pipes');
Akron8ea84292018-10-24 13:41:52 +0200183
Akron2fc697a2024-06-28 10:35:10 +0200184 if (defined $v->param('fields') && ($v->param('fields') ne join(',', @default_search_fields))) {
185 push @slug_base, 'f' .join('-', split(',', $v->param('fields')));
186 };
187
Akron6d49c1f2018-10-11 14:22:21 +0200188 # Get response based on query parameter
Akron8ea84292018-10-24 13:41:52 +0200189 my $response = $c->load_response('query_' . slugify(join('_', @slug_base)));
Akron0e1ed242018-10-11 13:22:00 +0200190
191 # Check authentification
192 if (my $auth = $c->req->headers->header('Authorization')) {
Akron33f5c672019-06-24 19:40:47 +0200193
Akroncdfd9d52019-07-23 11:35:00 +0200194 $c->app->log->debug("There is an authorization header $auth");
Akron33f5c672019-06-24 19:40:47 +0200195 if ($auth =~ /^Bearer/) {
196 # Username unknown in OAuth2
197 $response->{json}->{meta}->{authorized} = 'yes';
Akron0e1ed242018-10-11 13:22:00 +0200198 };
Akroncdfd9d52019-07-23 11:35:00 +0200199
200 # Code is expired
201 if ($c->expired($auth)) {
202
203 $c->app->log->debug("The access token has expired");
204
205 return $c->render(
206 status => 401,
207 json => {
208 errors => [[2003, 'Access token is expired']]
209 }
210 );
211 }
212
213 # Auth token is invalid
214 if ($auth =~ /^Bearer inv4lid/) {
215 $c->app->log->debug("The access token is invalid");
216
217 return $c->render(
218 status => 401,
219 json => {
220 errors => [[2011, 'Access token is invalid']]
221 }
222 );
223 }
Akron0e1ed242018-10-11 13:22:00 +0200224 };
225
Akronc58bfc42020-10-05 12:09:45 +0200226 if ($v->param('pipes')) {
227 $response->{json}->{meta}->{pipes} = $v->param('pipes');
Akron7b9a1962020-07-02 09:52:53 +0200228 };
229
Akron6d49c1f2018-10-11 14:22:21 +0200230 # Set page parameter
Akron0e1ed242018-10-11 13:22:00 +0200231 if ($v->param('page')) {
Akron6d49c1f2018-10-11 14:22:21 +0200232 $response->{json}->{meta}->{startIndex} = $v->param("startIndex");
Akron0e1ed242018-10-11 13:22:00 +0200233 };
234
Akron0e1ed242018-10-11 13:22:00 +0200235 # Simple search fixture
Akron32396632018-10-11 17:08:37 +0200236 $c->render(%$response);
237
238 $c->app->log->debug('Rendered result');
239
240 return 1;
Akron0e1ed242018-10-11 13:22:00 +0200241};
242
Akron80a84b22018-10-24 17:44:24 +0200243# Textinfo fixtures
Akron4e413fb2023-09-26 13:11:11 +0200244get '/v1.0/corpus/#corpusId/#docId/#textId' => sub {
Akron80a84b22018-10-24 17:44:24 +0200245 my $c = shift;
246
247 my $file = join('_', (
248 'textinfo',
249 $c->stash('corpusId'),
250 $c->stash('docId'),
251 $c->stash('textId')
252 ));
253
254 my $slug = slugify($file);
255
256 # Get response based on query parameter
257 my $response = $c->load_response($slug);
258 return $c->render(%$response);
259};
260
Akron0e1ed242018-10-11 13:22:00 +0200261
Akronb80341d2018-10-15 19:46:23 +0200262# Matchinfo fixtures
Akron06d4d1f2024-06-05 11:59:20 +0200263get '/v1.0/corpus/#corpusId/#docId/#textId/#matchId' => sub {
Akronb80341d2018-10-15 19:46:23 +0200264 my $c = shift;
265
266 my $file = join('_', (
267 'matchinfo',
268 $c->stash('corpusId'),
269 $c->stash('docId'),
270 $c->stash('textId'),
271 $c->stash('matchId')
272 ));
273
Akronb8d0b402018-10-18 23:51:52 +0200274 my $slug = slugify($file);
275
Akronb80341d2018-10-15 19:46:23 +0200276 # Get response based on query parameter
Akronb8d0b402018-10-18 23:51:52 +0200277 my $response = $c->load_response($slug);
Akronb80341d2018-10-15 19:46:23 +0200278 return $c->render(%$response);
279};
280
Akron0e1ed242018-10-11 13:22:00 +0200281
Akronbe61f4c2018-10-20 00:52:58 +0200282# Statistics endpoint
Akron63d963b2019-07-05 15:35:51 +0200283get '/v1.0/statistics' => sub {
Akronbe61f4c2018-10-20 00:52:58 +0200284 my $c = shift;
285 my $v = $c->validation;
Akron5fa61e92019-07-15 11:56:11 +0200286 $v->optional('cq');
Akronbe61f4c2018-10-20 00:52:58 +0200287
288 my @list = 'corpusinfo';
Akron5fa61e92019-07-15 11:56:11 +0200289 if ($v->param('cq')) {
290 push @list, $v->param('cq');
Akronbe61f4c2018-10-20 00:52:58 +0200291 };
292 my $slug = slugify(join('_', @list));
293
294 # Get response based on query parameter
295 my $response = $c->load_response($slug);
296 return $c->render(%$response);
297};
298
Akron0e1ed242018-10-11 13:22:00 +0200299############
300# Auth API #
301############
302
303# Request API token
Akron63d963b2019-07-05 15:35:51 +0200304get '/v1.0/auth/logout' => sub {
Akron0e1ed242018-10-11 13:22:00 +0200305 my $c = shift;
306
307 if (my $auth = $c->req->headers->header('Authorization')) {
Akroncdfd9d52019-07-23 11:35:00 +0200308
309 if ($auth =~ /^Bearer/) {
310 $c->app->log->debug('Server-Logout: ' . $auth);
311 return $c->render(json => { msg => [[0, 'Fine!']]});
Akron0e1ed242018-10-11 13:22:00 +0200312 };
313 };
314
315 return $c->render(status => 400, json => { error => [[0, 'No!']]});
316};
317
318
319# Request API token
Akron63d963b2019-07-05 15:35:51 +0200320get '/v1.0/auth/apiToken' => sub {
Akron0e1ed242018-10-11 13:22:00 +0200321 my $c = shift;
322
323 # Get auth header
324 my $auth = $c->req->headers->authorization;
325
326 # Authorization missing or not basic
327 if (!$auth || $auth !~ s/\s*Basic\s+//gi) {
328 return $c->render(
329 json => {
330 error => [[2, 'x']]
331 }
332 );
333 };
334
335 # Decode header
336 my ($username, $pwd) = @{b($auth)->b64_decode->split(':')->to_array};
337
338 # the password is 'pass'
339 if ($pwd) {
Akrona205b142022-11-28 13:35:19 +0100340 if ($pwd eq 'ldaperr') {
Akron3d673062019-01-29 15:54:16 +0100341 return $c->render(
342 format => 'html',
343 status => 401,
344 json => {
345 "errors" => [[2022,"LDAP Authentication failed due to unknown user or password!"]]
346 }
347 );
Akron0e1ed242018-10-11 13:22:00 +0200348 };
349
350 return $c->render(
351 json => {
352 error => [[2004, undef]]
353 }
354 );
355 };
356
357 return $c->render(
358 json => {
359 error => [[2004, undef]]
360 }
361 );
362};
363
Akron33f5c672019-06-24 19:40:47 +0200364
365# Request API token
Akron63d963b2019-07-05 15:35:51 +0200366post '/v1.0/oauth2/token' => sub {
Akron33f5c672019-06-24 19:40:47 +0200367 my $c = shift;
368
Akron63d963b2019-07-05 15:35:51 +0200369 my $grant_type = $c->param('grant_type') // 'undefined';
370
371 if ($grant_type eq 'password') {
Akron33f5c672019-06-24 19:40:47 +0200372
Akron8bbbecf2019-07-01 18:57:30 +0200373 # Check for wrong client id
374 if ($c->param('client_id') ne '2') {
375 return $c->render(
376 json => {
377 "error_description" => "Unknown client with " . $_->{client_id},
378 "error" => "invalid_client"
379 },
380 status => 401
381 );
382 }
Akron33f5c672019-06-24 19:40:47 +0200383
Akron8bbbecf2019-07-01 18:57:30 +0200384 # Check for wrong client secret
385 elsif ($c->param('client_secret') ne 'k414m4r-s3cr3t') {
386 return $c->render(
387 json => {
388 "error_description" => "Invalid client credentials",
389 "error" => "invalid_client"
390 },
391 status => 401
392 );
393 }
Akron33f5c672019-06-24 19:40:47 +0200394
Akron8bbbecf2019-07-01 18:57:30 +0200395 # Check for wrong user name
Akron6a228db2021-10-14 15:57:00 +0200396 elsif ($c->param('username') !~ /^t.st$/) {
Akron8bbbecf2019-07-01 18:57:30 +0200397 return $c->render(json => {
398 error => [[2004, undef]]
399 });
400 }
401
402 # Check for ldap error
403 elsif ($c->param('password') eq 'ldaperr') {
404 return $c->render(
405 format => 'html',
406 status => 401,
407 json => {
408 "errors" => [
409 [
410 2022,
411 "LDAP Authentication failed due to unknown user or password!"
412 ]
413 ]
414 }
415 );
416 }
417
418 # Check for wrong password
419 elsif ($c->param('password') ne 'pass') {
420 return $c->render(json => {
421 format => 'html',
422 status => 401,
Akron33f5c672019-06-24 19:40:47 +0200423 "errors" => [[2022,"LDAP Authentication failed due to unknown user or password!"]]
Akron8bbbecf2019-07-01 18:57:30 +0200424 });
425 }
426
427 # Return fine access
428 return $c->render(
429 json => {
Akroncdfd9d52019-07-23 11:35:00 +0200430 "access_token" => $c->get_token('access_token'),
431 "refresh_token" => $c->get_token('refresh_token'),
Akron8bbbecf2019-07-01 18:57:30 +0200432 "scope" => "all",
433 "token_type" => "Bearer",
434 "expires_in" => 86400
435 });
436 }
437
438 # Refresh token
Akron63d963b2019-07-05 15:35:51 +0200439 elsif ($grant_type eq 'refresh_token') {
Akroncdfd9d52019-07-23 11:35:00 +0200440
441 if ($c->param('refresh_token') eq 'inv4lid') {
442 return $c->render(
443 status => 400,
444 json => {
445 "error_description" => "Refresh token is expired",
446 "error" => "invalid_grant"
447 }
448 );
449 };
450
451 $c->app->log->debug("Refresh the token in the mock server!");
452
Akron8bbbecf2019-07-01 18:57:30 +0200453 return $c->render(
454 status => 200,
455 json => {
Akroncdfd9d52019-07-23 11:35:00 +0200456 "access_token" => $c->get_token("access_token_2"),
457 "refresh_token" => $c->get_token("refresh_token_2"),
Akron8bbbecf2019-07-01 18:57:30 +0200458 "token_type" => "Bearer",
459 "expires_in" => 86400
Akron33f5c672019-06-24 19:40:47 +0200460 }
461 );
462 }
463
Akron83209f72021-01-29 17:54:15 +0100464 # Get auth_token_1
465 elsif ($grant_type eq 'authorization_code') {
Akrone3daaeb2023-05-08 09:44:18 +0200466 if ($c->param('code') && $c->param('code') eq $tokens{auth_token_1}) {
Akron83209f72021-01-29 17:54:15 +0100467 return $c->render(
468 status => 200,
469 json => {
470 "access_token" => $tokens{access_token_3},
471 "expires_in" => 31536000,
472 "scope" => 'match_info search openid',
473 "token_type" => "Bearer"
474 }
475 );
476 };
477 }
478
Akron8bbbecf2019-07-01 18:57:30 +0200479 # Unknown token grant
480 else {
481 return $c->render(
Akron63d963b2019-07-05 15:35:51 +0200482 status => 400,
Akron8bbbecf2019-07-01 18:57:30 +0200483 json => {
484 "errors" => [
485 [
Akron63d963b2019-07-05 15:35:51 +0200486 0, "Grant Type unknown", $grant_type
Akron8bbbecf2019-07-01 18:57:30 +0200487 ]
488 ]
489 }
490 )
Akron33f5c672019-06-24 19:40:47 +0200491 }
Akron33f5c672019-06-24 19:40:47 +0200492};
493
Akron4cefe1f2019-09-04 10:11:28 +0200494# Revoke API token
495post '/v1.0/oauth2/revoke' => sub {
496 my $c = shift;
497
498 my $refresh_token = $c->param('token');
499
500 if ($c->param('client_secret') ne 'k414m4r-s3cr3t') {
501 return $c->render(
502 json => {
503 "error_description" => "Invalid client credentials",
504 "error" => "invalid_client"
505 },
506 status => 401
507 );
508 };
509
510 return $c->render(
511 text => ''
512 )
513};
Akron33f5c672019-06-24 19:40:47 +0200514
Akron59992122019-10-29 11:28:45 +0100515# Register a client
516post '/v1.0/oauth2/client/register' => sub {
517 my $c = shift;
518 my $json = $c->req->json;
519
Akrondc50c892021-05-05 18:12:02 +0200520 if ($json->{redirectURI}) {
521 return $c->render(
522 status => 400,
523 json => {
524 errors => [
525 [
526 201,
527 "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\"])"
528 ]
529 ]
530 }
531 );
532 };
533
Akron59992122019-10-29 11:28:45 +0100534 my $name = $json->{name};
Akron1a9d5be2020-03-19 17:28:33 +0100535 my $desc = $json->{description};
Akron59992122019-10-29 11:28:45 +0100536 my $type = $json->{type};
537 my $url = $json->{url};
Akron9f2ad342022-05-04 16:16:40 +0200538 my $src = $json->{source};
Akronb6b156e2022-03-31 14:57:49 +0200539 my $redirect_uri = $json->{redirect_uri};
Akron59992122019-10-29 11:28:45 +0100540
Akron1a9d5be2020-03-19 17:28:33 +0100541 my $list = $c->app->defaults('oauth.client_list');
542
Akron6b75d122022-05-12 17:39:05 +0200543 my $obj = {
Akrondc50c892021-05-05 18:12:02 +0200544 "client_id" => $tokens{new_client_id},
545 "client_name" => $name,
Akronbc94a9c2021-04-15 00:07:35 +0200546 "client_description" => $desc,
Akronb6b156e2022-03-31 14:57:49 +0200547 "client_url" => $url,
Akron9f2ad342022-05-04 16:16:40 +0200548 "client_redirect_uri" => $redirect_uri,
Akron6b75d122022-05-12 17:39:05 +0200549 "client_type" => $type
Akronb6b156e2022-03-31 14:57:49 +0200550 };
551
Akron6b75d122022-05-12 17:39:05 +0200552 # Plugin!
553 if ($src) {
554 $obj->{source} = $src;
555 $obj->{client_id} = $tokens{new_client_id_3};
556 };
557
558 push @$list, $obj;
559
Akronb6b156e2022-03-31 14:57:49 +0200560 if ($redirect_uri && $redirect_uri =~ /FAIL$/) {
561 return $c->render(
562 status => 400,
563 json => {
564 "error_description" => $redirect_uri . " is invalid.",
565 "error" => "invalid_request"
566 }
567 )
Akron1a9d5be2020-03-19 17:28:33 +0100568 };
569
Akron59992122019-10-29 11:28:45 +0100570 # Confidential server application
571 if ($type eq 'CONFIDENTIAL') {
Akronb6b156e2022-03-31 14:57:49 +0200572
Akron59992122019-10-29 11:28:45 +0100573 return $c->render(json => {
Akronb6b156e2022-03-31 14:57:49 +0200574 client_id => $tokens{new_client_id_2},
Akron59992122019-10-29 11:28:45 +0100575 client_secret => $tokens{new_client_secret}
576 });
577 };
578
579 # Desktop application
580 return $c->render(json => {
581 client_id => $tokens{new_client_id}
582 });
583};
584
Helgedb720ea2023-03-20 09:39:36 +0100585# Mock API list plugins
Helge278fbca2022-11-29 18:49:15 +0100586post '/v1.0/plugins' => sub {
Akron0f1b93b2020-03-17 11:37:19 +0100587 my $c = shift;
Akron276afc02021-06-14 11:00:21 +0200588 my $v = $c->validation;
Helgedb720ea2023-03-20 09:39:36 +0100589 $v->required('super_client_id');
590 $v->required('super_client_secret');
591 if ($v->has_error) {
592 return $c->render(
593 json => [],
594 status => 400
595 );
596 };
Akron276afc02021-06-14 11:00:21 +0200597
Helgedb720ea2023-03-20 09:39:36 +0100598 return $c->render(
599 json => $c->stash('oauth.plugin_list'),
600 status => 200
601 );
602};
603
604# Mock API list installed plugins
605post '/v1.0/plugins/installed' => sub {
606 my $c = shift;
607 my $v = $c->validation;
Akron276afc02021-06-14 11:00:21 +0200608 $v->required('super_client_id');
609 $v->required('super_client_secret');
610
611 if ($v->has_error) {
612 return $c->render(
613 json => [],
614 status => 400
615 );
616 };
Helge278fbca2022-11-29 18:49:15 +0100617
Helgedb720ea2023-03-20 09:39:36 +0100618 return $c->render(
619 json => $c->stash('oauth.pluginin_list'),
620 status => 200
621 );
622};
623
624
625# Mock API plugin installation
626post '/v1.0/plugins/install' => sub {
627 my $c = shift;
628 my $v = $c->validation;
629 $v->required('super_client_id');
630 $v->required('super_client_secret');
631 $v->required('client_id');
632 my $cl_id = $c->param('client_id');
633 if ($v->has_error) {
Helge278fbca2022-11-29 18:49:15 +0100634 return $c->render(
Helgedb720ea2023-03-20 09:39:36 +0100635 json => [],
636 status => 400
637 );
638 };
639
640 my $date = "2022-12-13T16:33:27.621+01:00[Europe/Berlin]";
641 my $pl_list = $c->app->defaults('oauth.plugin_list');
642 my $cl_name = (grep{($_->{client_id} eq $cl_id)}@$pl_list)[0]->{client_name};
643
644 if (length $cl_name){
645
646 my %inst_plugin = (
647 "name" => $cl_name,
648 "client_id" => $cl_id,
649 "installed_date" => $date,
650 );
651
652 $c->add_instplugin(\%inst_plugin);
653
654 return $c->render(
655 json => %inst_plugin,
Helge278fbca2022-11-29 18:49:15 +0100656 status => 200
657 );
658 }
Helge278fbca2022-11-29 18:49:15 +0100659
Helgedb720ea2023-03-20 09:39:36 +0100660 return $c->render(
661 json => [],
662 status => 400
663 );
Helge278fbca2022-11-29 18:49:15 +0100664};
665
Helged36478d2023-06-08 17:43:01 +0200666# Mock API plugin uninstallation
667post '/v1.0/plugins/uninstall' => sub {
668 my $c = shift;
669 my $v = $c->validation;
670 $v->required('super_client_id');
671 $v->required('super_client_secret');
672 $v->required('client_id');
673 if ($v->has_error) {
674 return $c->render(
675 json => [],
676 status => 400
677 );
678 };
679 my $cl_id = $c->param('client_id');
680
681 my $plin_list = $c->app->defaults('oauth.pluginin_list');
682 my @new_list = grep{!($_->{client_id} eq $cl_id)}@$plin_list;
683 $c->app->defaults('oauth.pluginin_list' => \@new_list);
684
685 if(scalar @new_list eq scalar @$plin_list){
686 return $c->render(
687 status => 404
688 );
689 }
690 return $c->render(
691 json => $c->stash('oauth.pluginin_list'),
692 status => 200
693 );
694 };
Helgedb720ea2023-03-20 09:39:36 +0100695
Helge278fbca2022-11-29 18:49:15 +0100696# Register a client
697post '/v1.0/oauth2/client/list' => sub {
698 my $c = shift;
699
700 my $v = $c->validation;
701 $v->required('super_client_id');
702 $v->required('super_client_secret');
703
Helge05436702024-08-05 17:08:44 +0200704 $v->optional('filter_by' );
705 $v->optional('authorized_only' );
706
Helge278fbca2022-11-29 18:49:15 +0100707 if ($v->has_error) {
708 return $c->render(
709 json => [],
710 status => 400
711 );
712 };
713
Akron276afc02021-06-14 11:00:21 +0200714
Akron0f1b93b2020-03-17 11:37:19 +0100715 # $c->param('client_secret');
Akron1a9d5be2020-03-19 17:28:33 +0100716
717 # Is empty [] when nothing registered
718
Akron0f1b93b2020-03-17 11:37:19 +0100719 return $c->render(
Akron1a9d5be2020-03-19 17:28:33 +0100720 json => $c->stash('oauth.client_list'),
721 status => 200
722 );
723};
724
Akrondb1f4672023-01-24 12:05:07 +0100725# Get client info
726post '/v1.0/oauth2/client/:client_id' => sub {
727 my $c = shift;
728
729 # Validate input
730 my $v = $c->validation;
731 $v->required('super_client_id');
732 $v->required('super_client_secret');
733
734 if ($v->has_error) {
735 return $c->render(
736 status => 400,
737 json => {
738 error_description => "No super client",
739 error => "no_superclient"
740 }
741 );
742 };
743
744 my $client_id = $c->stash('client_id');
745
746 my $list = $c->stash('oauth.client_list');
747
748 foreach (@$list) {
749 if ($_->{client_id} eq $client_id) {
750 return $c->render(
751 json => $_,
752 status => 200
753 );
754 };
755 };
756
757 return $c->render(
758 json => {
759 error_description => "Unknown client with $client_id.",
760 error => "invalid_client"
761 },
762 status => 401
763 );
764};
765
Akronbc94a9c2021-04-15 00:07:35 +0200766
767# Get token list
768post '/v1.0/oauth2/token/list' => sub {
769 my $c = shift;
770 return $c->render(json => [
771 {
772 "client_description" => "Nur ein Beispiel",
773 "client_id" => $tokens{new_client_id},
774 "client_name" => "Beispiel",
775 "client_url" => "",
776 "created_date" => "2021-04-14T19:40:26.742+02:00[Europe\/Berlin]",
777 "expires_in" => "31533851",
778 "scope" => [
779 "match_info",
780 "search",
781 "openid"
782 ],
783 "token" => "jhkhkjhk_hjgjsfz67i",
784 "user_authentication_time" => "2021-04-14T19:39:41.81+02:00[Europe\/Berlin]"
785 }
786 ]);
787};
788
Akron1a9d5be2020-03-19 17:28:33 +0100789del '/v1.0/oauth2/client/deregister/:client_id' => sub {
790 my $c = shift;
791 my $client_id = $c->stash('client_id');
792
793 my $list = $c->app->defaults('oauth.client_list');
794
795 my $break = -1;
796 for (my $i = 0; $i < @$list; $i++) {
Akrondc50c892021-05-05 18:12:02 +0200797 if ($list->[$i]->{client_id} eq $client_id) {
Akron1a9d5be2020-03-19 17:28:33 +0100798 $break = $i;
799 last;
800 };
801 };
802
803 if ($break != -1) {
804 splice @$list, $break, 1;
805 }
806
807 else {
808 return $c->render(
809 json => {
810 error_description => "Unknown client with $client_id.",
811 error => "invalid_client"
Akron0f1b93b2020-03-17 11:37:19 +0100812 },
Akron1a9d5be2020-03-19 17:28:33 +0100813 status => 401
814 );
815 };
816
817 return $c->render(
818 json => $c->stash('oauth.client_list'),
Akron0f1b93b2020-03-17 11:37:19 +0100819 status => 200
820 );
821};
822
Akron83209f72021-01-29 17:54:15 +0100823post '/v1.0/oauth2/authorize' => sub {
824 my $c = shift;
825 my $type = $c->param('response_type');
826 my $client_id = $c->param('client_id');
Akrona8efaa92022-04-09 14:45:43 +0200827 my $scope = $c->param('scope');
828 my $state = $c->param('state');
829 my $redirect_uri = $c->param('redirect_uri') // 'NO';
Akron83209f72021-01-29 17:54:15 +0100830
Akrona8efaa92022-04-09 14:45:43 +0200831 if ($type eq 'code' && $client_id eq 'xyz') {
832
833 if ($state eq 'fail') {
834 $c->res->headers->location(
835 Mojo::URL->new($redirect_uri)->query({
836 error_description => 'FAIL'
837 })
838 );
839 $c->res->code(400);
840 return $c->rendered;
841 };
842
Akron9ccf69a2023-01-31 14:21:37 +0100843 if (index($redirect_uri,'http://wrong') >= 0) {
844 return $c->render(
845 code => 400,
846 content_type => 'text/plain',
847 text => '{"error_description":"Invalid redirect URI","state":"ZMwDGTZ2RY","error":"invalid_request"}'
848 );
849 };
850
Akrona8efaa92022-04-09 14:45:43 +0200851 return $c->redirect_to(
852 Mojo::URL->new($redirect_uri)->query({
853 code => $tokens{auth_token_1},
854 scope => $scope,
855 })
856 );
857 }
858
859 elsif ($type eq 'code') {
Akrone3daaeb2023-05-08 09:44:18 +0200860 my $loc = Mojo::URL->new($redirect_uri)->query({
861 code => $tokens{auth_token_1},
862 scope => 'match_info search openid'
863 });
Akron83209f72021-01-29 17:54:15 +0100864
Akrone3daaeb2023-05-08 09:44:18 +0200865 my $res = $c->res;
866 $res->headers->location($loc);
867 return $c->rendered($client_id eq '307' ? 307 : 302);
868 # return $c->rendered(302);
Akron9ccf69a2023-01-31 14:21:37 +0100869 };
870
871 return $c->render(
872 code => 400,
873 content_type => 'text/plain',
874 content => 'Unknown'
875 );
Akron83209f72021-01-29 17:54:15 +0100876};
877
Akron0f1b93b2020-03-17 11:37:19 +0100878
Akronabdf9a92021-01-12 19:06:57 +0100879#######################
880# Query Reference API #
881#######################
882
883use CHI;
884my $chi = CHI->new(
885 driver => 'Memory',
886 global => 1
887);
888
889# Store query
890put '/v1.0/query/~:user/:query_name' => sub {
891 my $c = shift;
892 my $user = $c->stash('user');
893 my $qname = $c->stash('query_name');
894
895 if ($chi->is_valid($qname)) {
896 return $c->render(
897 json => {
898 errors => [
899 {
900 message => 'Unable to store query reference'
901 }
902 ]
903 }, status => 400
904 );
905 };
906
907 my $json = $c->req->json;
908
909 my $store = {
910 name => $qname,
911 koralQuery => { '@type' => 'Okay' },
912 query => $json->{query},
913 queryType => $json->{queryType},
914 type => $json->{type},
915 queryLanguage => $json->{queryLanguage},
916 };
917
918 if (exists $json->{description}) {
919 $store->{description} = $json->{description}
920 };
921
922 # Set query reference
923 $chi->set($qname => $store);
924
925 my $queries = $chi->get('~queries') // [];
926 push @$queries, $qname;
927 $chi->set('~queries' => $queries);
928
929 return $c->render(
930 status => 201,
931 text => ''
932 );
933};
934
935# Get query
936get '/v1.0/query/~:user/:query_name' => sub {
937 my $c = shift;
938
939 my $user = $c->stash('user');
940 my $qname = $c->stash('query_name');
941
942 my $json = $chi->get($qname);
943
944 if ($json) {
945 return $c->render(
946 json => $json
947 );
948 };
949
950 return $c->render(
951 json => {
952 errors => [
953 {
954 message => 'Query reference not found'
955 }
956 ]
957 }, status => 404
958 );
959};
960
961
962# Get all queries
963get '/v1.0/query/~:user' => sub {
964 my $c = shift;
965 my $user = $c->stash('user');
966 my $qs = $chi->get('~queries') // [];
967 my @queries = ();
968 foreach (@$qs) {
969 push @queries, $chi->get($_);
970 };
971 return $c->render(json => { refs => \@queries });
972};
973
974
975# Store query
976del '/v1.0/query/~:user/:query_name' => sub {
977 my $c = shift;
978 my $user = $c->stash('user');
979 my $qname = $c->stash('query_name');
980
981 $chi->remove($qname);
982
983 my $queries = $chi->get('~queries') // [];
984
985 my @clean = ();
986 foreach (@$queries) {
987 push @clean, $_ unless $_ eq $qname
988 };
989
990 $chi->set('~queries' => \@clean);
991
992 return $c->render(
993 status => 200,
994 text => ''
995 );
996};
997
Akronc1aaf932021-06-09 12:19:15 +0200998post '/v1.0/oauth2/revoke/super' => sub {
999 my $c = shift;
1000
1001 my $s_client_id = $c->param('super_client_id');
1002 my $s_client_secret = $c->param('super_client_secret');
1003 my $token = $c->param('token');
1004
1005 return $c->render(text => 'SUCCESS');
1006};
1007
Akrona8efaa92022-04-09 14:45:43 +02001008get '/fakeclient/return' => sub {
1009 my $c = shift;
1010 $c->render(
1011 text => 'welcome back! [' . $c->param('code') . ']'
1012 );
1013} => 'return_uri';
1014
Akron0f1b93b2020-03-17 11:37:19 +01001015
Akron0e1ed242018-10-11 13:22:00 +02001016app->start;
1017
1018
1019__END__
1020
1021
1022 # Temporary:
1023 my $collection_query = {
1024 '@type' => "koral:docGroup",
1025 "operation" => "operation:or",
1026 "operands" => [
1027 {
1028 '@type' => "koral:docGroup",
1029 "operation" => "operation:and",
1030 "operands" => [
1031 {
1032 '@type' => "koral:doc",
1033 "key" => "title",
1034 "match" => "match:eq",
1035 "value" => "Der Birnbaum",
1036 "type" => "type:string"
1037 },
1038 {
1039 '@type' => "koral:doc",
1040 "key" => "pubPlace",
1041 "match" => "match:eq",
1042 "value" => "Mannheim",
1043 "type" => "type:string"
1044 },
1045 {
1046 '@type' => "koral:docGroup",
1047 "operation" => "operation:or",
1048 "operands" => [
1049 {
1050 '@type' => "koral:doc",
1051 "key" => "subTitle",
1052 "match" => "match:eq",
1053 "value" => "Aufzucht oder Pflege",
1054 "type" => "type:string"
1055 },
1056 {
1057 '@type' => "koral:doc",
1058 "key" => "subTitle",
1059 "match" => "match:eq",
1060 "value" => "Gedichte",
1061 "type" => "type:string"
1062 }
1063 ]
1064 }
1065 ]
1066 },
1067 {
1068 '@type' => "koral:doc",
1069 "key" => "pubDate",
1070 "match" => "match:geq",
1071 "value" => "2015-03-05",
1072 "type" => "type:date"
1073 }
1074 ]
1075 };