blob: 3b0959ff5cc8a3f6295c760e2e2e4df8f19b20cb [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/;
Akron0e1ed242018-10-11 13:22:00 +020010
11# This is an API fake server with fixtures
12
13my $secret = 's3cr3t';
Akron73f36082018-10-25 15:34:59 +020014my $fixture_path = path(Mojo::File->new(__FILE__)->dirname)->child('..', 'fixtures');
Akron0e1ed242018-10-11 13:22:00 +020015
Akroncdfd9d52019-07-23 11:35:00 +020016our %tokens = (
Akron59992122019-10-29 11:28:45 +010017 'access_token' => "4dcf8784ccfd26fac9bdb82778fe60e2",
18 'refresh_token' => "hlWci75xb8atDiq3924NUSvOdtAh7Nlf9z",
19 'access_token_2' => "abcde",
Akron83209f72021-01-29 17:54:15 +010020 'access_token_3' => 'jvgjbvjgzucgdwuiKHJK',
Akron59992122019-10-29 11:28:45 +010021 'refresh_token_2' => "fghijk",
22 'new_client_id' => 'fCBbQkA2NDA3MzM1Yw==',
Akronb6b156e2022-03-31 14:57:49 +020023 'new_client_id_2' => 'hghGHhjhFRz_gJhjrd==',
Akron6b75d122022-05-12 17:39:05 +020024 'new_client_id_3' => 'jh0gfjhjbfdsgzjghj==',
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
Akroncdfd9d52019-07-23 11:35:00 +020034# Expiration helper
35helper expired => sub {
36 my ($c, $auth, $set) = @_;
37
Akroncdfd9d52019-07-23 11:35:00 +020038 $auth =~ s/^[^ ]+? //;
39 if ($set) {
40 $c->app->log->debug("Set $auth for expiration");
41 $c->app->defaults('auth_' . $auth => 1);
42 return 1;
43 };
44
45 $c->app->log->debug("Check $auth for expiration: " . (
46 $c->app->defaults('auth_' . $auth) // '0'
47 ));
48
49 return $c->app->defaults('auth_' . $auth);
50};
Akron0e1ed242018-10-11 13:22:00 +020051
Akron408bc7c2022-04-28 15:46:43 +020052
53helper 'add_client' => sub {
54 my $c = shift;
55 my $client = shift;
Akrondb1f4672023-01-24 12:05:07 +010056 my $list = $c->stash('oauth.client_list');
Akron408bc7c2022-04-28 15:46:43 +020057 push @$list, $client;
58};
59
Helge278fbca2022-11-29 18:49:15 +010060# Add plugin to plugin list for marketplace
61helper 'add_plugin' => sub {
62 my $c = shift;
63 my $cplugin = shift;
64 my $pl_list = $c->app->defaults('oauth.plugin_list');
65 push @$pl_list, $cplugin;
66};
Akron408bc7c2022-04-28 15:46:43 +020067
Helgedb720ea2023-03-20 09:39:36 +010068helper 'add_instplugin' => sub {
69 my $c = shift;
70 my $cplugin = shift;
71 my $pl_list = $c->app->defaults('oauth.pluginin_list');
72 push @$pl_list, $cplugin;
73};
74
75
Akron6d49c1f2018-10-11 14:22:21 +020076# Load fixture responses
77helper 'load_response' => sub {
78 my $c = shift;
79 my $q_name = shift;
80 my $file = $fixture_path->child("response_$q_name.json");
Akron8ea84292018-10-24 13:41:52 +020081 $c->app->log->debug("Load response from $file");
82
Akron6d49c1f2018-10-11 14:22:21 +020083 unless (-f $file) {
84 return {
85 status => 500,
86 json => {
87 errors => [[0, 'Unable to load query response from ' . $file]]
88 }
89 }
90 };
Akron8ea84292018-10-24 13:41:52 +020091
Akron6d49c1f2018-10-11 14:22:21 +020092 my $response = $file->slurp;
Akrona3c353c2019-02-14 23:50:00 +010093 my $decode = decode_json($response);
94 unless ($decode) {
95 return {
96 status => 500,
97 json => {
98 errors => [[0, 'Unable to parse JSON']]
99 }
100 }
101 };
102
103 return $decode;
Akron6d49c1f2018-10-11 14:22:21 +0200104};
105
Akron1a9d5be2020-03-19 17:28:33 +0100106app->defaults('oauth.client_list' => []);
Helge278fbca2022-11-29 18:49:15 +0100107app->defaults('oauth.plugin_list' => []);
Helgedb720ea2023-03-20 09:39:36 +0100108app->defaults('oauth.pluginin_list' => []);
Akron6d49c1f2018-10-11 14:22:21 +0200109
Akron0e1ed242018-10-11 13:22:00 +0200110# Base page
Akron63d963b2019-07-05 15:35:51 +0200111get '/v1.0/' => sub {
Akron6d49c1f2018-10-11 14:22:21 +0200112 shift->render(text => 'Fake server available');
Akron0e1ed242018-10-11 13:22:00 +0200113};
114
Akron32396632018-10-11 17:08:37 +0200115
Akrond00b4272020-02-05 17:00:33 +0100116get '/v1.0/redirect-target-a' => sub {
117 shift->render(text => 'Redirect Target!');
118} => 'redirect-target';
119
120
121# Base page
122get '/v1.0/redirect' => sub {
123 my $c = shift;
124 $c->res->code(308);
125 $c->res->headers->location($c->url_for('redirect-target')->to_abs);
126 return $c->render(text => '');
127};
128
129
Akron0e1ed242018-10-11 13:22:00 +0200130# Search fixtures
Akron63d963b2019-07-05 15:35:51 +0200131get '/v1.0/search' => sub {
Akron0e1ed242018-10-11 13:22:00 +0200132 my $c = shift;
133 my $v = $c->validation;
134 $v->optional('q');
135 $v->optional('page');
136 $v->optional('ql');
Akroncd42a142019-07-12 18:55:37 +0200137 $v->optional('cq');
Akron0e1ed242018-10-11 13:22:00 +0200138 $v->optional('count');
139 $v->optional('context');
Akron8ea84292018-10-24 13:41:52 +0200140 $v->optional('offset');
Akronc58bfc42020-10-05 12:09:45 +0200141 $v->optional('pipes');
Akron8ea84292018-10-24 13:41:52 +0200142 $v->optional('cutoff')->in(qw/true false/);
Akron0e1ed242018-10-11 13:22:00 +0200143
Akron32396632018-10-11 17:08:37 +0200144 $c->app->log->debug('Receive request');
145
Akron0e1ed242018-10-11 13:22:00 +0200146 # Response q=x&ql=cosmas3
147 if ($v->param('ql') && $v->param('ql') eq 'cosmas3') {
148 return $c->render(
149 status => 400,
150 json => {
151 "\@context" => "http://korap.ids-mannheim.de/ns/koral/0.3/context.jsonld",
152 "errors" => [[307,"cosmas3 is not a supported query language!"]]
153 });
154 };
155
Akron6d49c1f2018-10-11 14:22:21 +0200156 if (!$v->param('q')) {
Akron8ea84292018-10-24 13:41:52 +0200157 return $c->render(%{$c->load_response('query_no_query')});
Akron0e1ed242018-10-11 13:22:00 +0200158 };
159
Akroncce055c2021-07-02 12:18:03 +0200160 if ($v->param('q') eq 'error') {
161 return $c->render(
162 status => 500,
163 inline => '<html><head>ERROR</head></html>'
164 );
165 };
166
Akron8ea84292018-10-24 13:41:52 +0200167 my @slug_base = ($v->param('q'));
168 push @slug_base, 'o' . $v->param('offset') if defined $v->param('offset');
169 push @slug_base, 'c' . $v->param('count') if defined $v->param('count');
170 push @slug_base, 'co' . $v->param('cutoff') if defined $v->param('cutoff');
Akroncd42a142019-07-12 18:55:37 +0200171 push @slug_base, 'cq' if defined $v->param('cq');
Akronc58bfc42020-10-05 12:09:45 +0200172 push @slug_base, 'p' . $v->param('pipes') if defined $v->param('pipes');
Akron8ea84292018-10-24 13:41:52 +0200173
Akron6d49c1f2018-10-11 14:22:21 +0200174 # Get response based on query parameter
Akron8ea84292018-10-24 13:41:52 +0200175 my $response = $c->load_response('query_' . slugify(join('_', @slug_base)));
Akron0e1ed242018-10-11 13:22:00 +0200176
177 # Check authentification
178 if (my $auth = $c->req->headers->header('Authorization')) {
Akron33f5c672019-06-24 19:40:47 +0200179
Akroncdfd9d52019-07-23 11:35:00 +0200180 $c->app->log->debug("There is an authorization header $auth");
Akron33f5c672019-06-24 19:40:47 +0200181 if ($auth =~ /^Bearer/) {
182 # Username unknown in OAuth2
183 $response->{json}->{meta}->{authorized} = 'yes';
Akron0e1ed242018-10-11 13:22:00 +0200184 };
Akroncdfd9d52019-07-23 11:35:00 +0200185
186 # Code is expired
187 if ($c->expired($auth)) {
188
189 $c->app->log->debug("The access token has expired");
190
191 return $c->render(
192 status => 401,
193 json => {
194 errors => [[2003, 'Access token is expired']]
195 }
196 );
197 }
198
199 # Auth token is invalid
200 if ($auth =~ /^Bearer inv4lid/) {
201 $c->app->log->debug("The access token is invalid");
202
203 return $c->render(
204 status => 401,
205 json => {
206 errors => [[2011, 'Access token is invalid']]
207 }
208 );
209 }
Akron0e1ed242018-10-11 13:22:00 +0200210 };
211
Akronc58bfc42020-10-05 12:09:45 +0200212 if ($v->param('pipes')) {
213 $response->{json}->{meta}->{pipes} = $v->param('pipes');
Akron7b9a1962020-07-02 09:52:53 +0200214 };
215
Akron6d49c1f2018-10-11 14:22:21 +0200216 # Set page parameter
Akron0e1ed242018-10-11 13:22:00 +0200217 if ($v->param('page')) {
Akron6d49c1f2018-10-11 14:22:21 +0200218 $response->{json}->{meta}->{startIndex} = $v->param("startIndex");
Akron0e1ed242018-10-11 13:22:00 +0200219 };
220
Akron0e1ed242018-10-11 13:22:00 +0200221 # Simple search fixture
Akron32396632018-10-11 17:08:37 +0200222 $c->render(%$response);
223
224 $c->app->log->debug('Rendered result');
225
226 return 1;
Akron0e1ed242018-10-11 13:22:00 +0200227};
228
Akron80a84b22018-10-24 17:44:24 +0200229# Textinfo fixtures
Akron4e413fb2023-09-26 13:11:11 +0200230get '/v1.0/corpus/#corpusId/#docId/#textId' => sub {
Akron80a84b22018-10-24 17:44:24 +0200231 my $c = shift;
232
233 my $file = join('_', (
234 'textinfo',
235 $c->stash('corpusId'),
236 $c->stash('docId'),
237 $c->stash('textId')
238 ));
239
240 my $slug = slugify($file);
241
242 # Get response based on query parameter
243 my $response = $c->load_response($slug);
244 return $c->render(%$response);
245};
246
Akron0e1ed242018-10-11 13:22:00 +0200247
Akronb80341d2018-10-15 19:46:23 +0200248# Matchinfo fixtures
Akron4e413fb2023-09-26 13:11:11 +0200249get '/v1.0/corpus/#corpusId/#docId/#textId/#matchId/matchInfo' => sub {
Akronb80341d2018-10-15 19:46:23 +0200250 my $c = shift;
251
252 my $file = join('_', (
253 'matchinfo',
254 $c->stash('corpusId'),
255 $c->stash('docId'),
256 $c->stash('textId'),
257 $c->stash('matchId')
258 ));
259
Akronb8d0b402018-10-18 23:51:52 +0200260 my $slug = slugify($file);
261
Akronb80341d2018-10-15 19:46:23 +0200262 # Get response based on query parameter
Akronb8d0b402018-10-18 23:51:52 +0200263 my $response = $c->load_response($slug);
Akronb80341d2018-10-15 19:46:23 +0200264 return $c->render(%$response);
265};
266
Akron0e1ed242018-10-11 13:22:00 +0200267
Akronbe61f4c2018-10-20 00:52:58 +0200268# Statistics endpoint
Akron63d963b2019-07-05 15:35:51 +0200269get '/v1.0/statistics' => sub {
Akronbe61f4c2018-10-20 00:52:58 +0200270 my $c = shift;
271 my $v = $c->validation;
Akron5fa61e92019-07-15 11:56:11 +0200272 $v->optional('cq');
Akronbe61f4c2018-10-20 00:52:58 +0200273
274 my @list = 'corpusinfo';
Akron5fa61e92019-07-15 11:56:11 +0200275 if ($v->param('cq')) {
276 push @list, $v->param('cq');
Akronbe61f4c2018-10-20 00:52:58 +0200277 };
278 my $slug = slugify(join('_', @list));
279
280 # Get response based on query parameter
281 my $response = $c->load_response($slug);
282 return $c->render(%$response);
283};
284
Akron0e1ed242018-10-11 13:22:00 +0200285############
286# Auth API #
287############
288
289# Request API token
Akron63d963b2019-07-05 15:35:51 +0200290get '/v1.0/auth/logout' => sub {
Akron0e1ed242018-10-11 13:22:00 +0200291 my $c = shift;
292
293 if (my $auth = $c->req->headers->header('Authorization')) {
Akroncdfd9d52019-07-23 11:35:00 +0200294
295 if ($auth =~ /^Bearer/) {
296 $c->app->log->debug('Server-Logout: ' . $auth);
297 return $c->render(json => { msg => [[0, 'Fine!']]});
Akron0e1ed242018-10-11 13:22:00 +0200298 };
299 };
300
301 return $c->render(status => 400, json => { error => [[0, 'No!']]});
302};
303
304
305# Request API token
Akron63d963b2019-07-05 15:35:51 +0200306get '/v1.0/auth/apiToken' => sub {
Akron0e1ed242018-10-11 13:22:00 +0200307 my $c = shift;
308
309 # Get auth header
310 my $auth = $c->req->headers->authorization;
311
312 # Authorization missing or not basic
313 if (!$auth || $auth !~ s/\s*Basic\s+//gi) {
314 return $c->render(
315 json => {
316 error => [[2, 'x']]
317 }
318 );
319 };
320
321 # Decode header
322 my ($username, $pwd) = @{b($auth)->b64_decode->split(':')->to_array};
323
324 # the password is 'pass'
325 if ($pwd) {
Akrona205b142022-11-28 13:35:19 +0100326 if ($pwd eq 'ldaperr') {
Akron3d673062019-01-29 15:54:16 +0100327 return $c->render(
328 format => 'html',
329 status => 401,
330 json => {
331 "errors" => [[2022,"LDAP Authentication failed due to unknown user or password!"]]
332 }
333 );
Akron0e1ed242018-10-11 13:22:00 +0200334 };
335
336 return $c->render(
337 json => {
338 error => [[2004, undef]]
339 }
340 );
341 };
342
343 return $c->render(
344 json => {
345 error => [[2004, undef]]
346 }
347 );
348};
349
Akron33f5c672019-06-24 19:40:47 +0200350
351# Request API token
Akron63d963b2019-07-05 15:35:51 +0200352post '/v1.0/oauth2/token' => sub {
Akron33f5c672019-06-24 19:40:47 +0200353 my $c = shift;
354
Akron63d963b2019-07-05 15:35:51 +0200355 my $grant_type = $c->param('grant_type') // 'undefined';
356
357 if ($grant_type eq 'password') {
Akron33f5c672019-06-24 19:40:47 +0200358
Akron8bbbecf2019-07-01 18:57:30 +0200359 # Check for wrong client id
360 if ($c->param('client_id') ne '2') {
361 return $c->render(
362 json => {
363 "error_description" => "Unknown client with " . $_->{client_id},
364 "error" => "invalid_client"
365 },
366 status => 401
367 );
368 }
Akron33f5c672019-06-24 19:40:47 +0200369
Akron8bbbecf2019-07-01 18:57:30 +0200370 # Check for wrong client secret
371 elsif ($c->param('client_secret') ne 'k414m4r-s3cr3t') {
372 return $c->render(
373 json => {
374 "error_description" => "Invalid client credentials",
375 "error" => "invalid_client"
376 },
377 status => 401
378 );
379 }
Akron33f5c672019-06-24 19:40:47 +0200380
Akron8bbbecf2019-07-01 18:57:30 +0200381 # Check for wrong user name
Akron6a228db2021-10-14 15:57:00 +0200382 elsif ($c->param('username') !~ /^t.st$/) {
Akron8bbbecf2019-07-01 18:57:30 +0200383 return $c->render(json => {
384 error => [[2004, undef]]
385 });
386 }
387
388 # Check for ldap error
389 elsif ($c->param('password') eq 'ldaperr') {
390 return $c->render(
391 format => 'html',
392 status => 401,
393 json => {
394 "errors" => [
395 [
396 2022,
397 "LDAP Authentication failed due to unknown user or password!"
398 ]
399 ]
400 }
401 );
402 }
403
404 # Check for wrong password
405 elsif ($c->param('password') ne 'pass') {
406 return $c->render(json => {
407 format => 'html',
408 status => 401,
Akron33f5c672019-06-24 19:40:47 +0200409 "errors" => [[2022,"LDAP Authentication failed due to unknown user or password!"]]
Akron8bbbecf2019-07-01 18:57:30 +0200410 });
411 }
412
413 # Return fine access
414 return $c->render(
415 json => {
Akroncdfd9d52019-07-23 11:35:00 +0200416 "access_token" => $c->get_token('access_token'),
417 "refresh_token" => $c->get_token('refresh_token'),
Akron8bbbecf2019-07-01 18:57:30 +0200418 "scope" => "all",
419 "token_type" => "Bearer",
420 "expires_in" => 86400
421 });
422 }
423
424 # Refresh token
Akron63d963b2019-07-05 15:35:51 +0200425 elsif ($grant_type eq 'refresh_token') {
Akroncdfd9d52019-07-23 11:35:00 +0200426
427 if ($c->param('refresh_token') eq 'inv4lid') {
428 return $c->render(
429 status => 400,
430 json => {
431 "error_description" => "Refresh token is expired",
432 "error" => "invalid_grant"
433 }
434 );
435 };
436
437 $c->app->log->debug("Refresh the token in the mock server!");
438
Akron8bbbecf2019-07-01 18:57:30 +0200439 return $c->render(
440 status => 200,
441 json => {
Akroncdfd9d52019-07-23 11:35:00 +0200442 "access_token" => $c->get_token("access_token_2"),
443 "refresh_token" => $c->get_token("refresh_token_2"),
Akron8bbbecf2019-07-01 18:57:30 +0200444 "token_type" => "Bearer",
445 "expires_in" => 86400
Akron33f5c672019-06-24 19:40:47 +0200446 }
447 );
448 }
449
Akron83209f72021-01-29 17:54:15 +0100450 # Get auth_token_1
451 elsif ($grant_type eq 'authorization_code') {
Akrone3daaeb2023-05-08 09:44:18 +0200452 if ($c->param('code') && $c->param('code') eq $tokens{auth_token_1}) {
Akron83209f72021-01-29 17:54:15 +0100453 return $c->render(
454 status => 200,
455 json => {
456 "access_token" => $tokens{access_token_3},
457 "expires_in" => 31536000,
458 "scope" => 'match_info search openid',
459 "token_type" => "Bearer"
460 }
461 );
462 };
463 }
464
Akron8bbbecf2019-07-01 18:57:30 +0200465 # Unknown token grant
466 else {
467 return $c->render(
Akron63d963b2019-07-05 15:35:51 +0200468 status => 400,
Akron8bbbecf2019-07-01 18:57:30 +0200469 json => {
470 "errors" => [
471 [
Akron63d963b2019-07-05 15:35:51 +0200472 0, "Grant Type unknown", $grant_type
Akron8bbbecf2019-07-01 18:57:30 +0200473 ]
474 ]
475 }
476 )
Akron33f5c672019-06-24 19:40:47 +0200477 }
Akron33f5c672019-06-24 19:40:47 +0200478};
479
Akron4cefe1f2019-09-04 10:11:28 +0200480# Revoke API token
481post '/v1.0/oauth2/revoke' => sub {
482 my $c = shift;
483
484 my $refresh_token = $c->param('token');
485
486 if ($c->param('client_secret') ne 'k414m4r-s3cr3t') {
487 return $c->render(
488 json => {
489 "error_description" => "Invalid client credentials",
490 "error" => "invalid_client"
491 },
492 status => 401
493 );
494 };
495
496 return $c->render(
497 text => ''
498 )
499};
Akron33f5c672019-06-24 19:40:47 +0200500
Akron59992122019-10-29 11:28:45 +0100501# Register a client
502post '/v1.0/oauth2/client/register' => sub {
503 my $c = shift;
504 my $json = $c->req->json;
505
Akrondc50c892021-05-05 18:12:02 +0200506 if ($json->{redirectURI}) {
507 return $c->render(
508 status => 400,
509 json => {
510 errors => [
511 [
512 201,
513 "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\"])"
514 ]
515 ]
516 }
517 );
518 };
519
Akron59992122019-10-29 11:28:45 +0100520 my $name = $json->{name};
Akron1a9d5be2020-03-19 17:28:33 +0100521 my $desc = $json->{description};
Akron59992122019-10-29 11:28:45 +0100522 my $type = $json->{type};
523 my $url = $json->{url};
Akron9f2ad342022-05-04 16:16:40 +0200524 my $src = $json->{source};
Akronb6b156e2022-03-31 14:57:49 +0200525 my $redirect_uri = $json->{redirect_uri};
Akron59992122019-10-29 11:28:45 +0100526
Akron1a9d5be2020-03-19 17:28:33 +0100527 my $list = $c->app->defaults('oauth.client_list');
528
Akron6b75d122022-05-12 17:39:05 +0200529 my $obj = {
Akrondc50c892021-05-05 18:12:02 +0200530 "client_id" => $tokens{new_client_id},
531 "client_name" => $name,
Akronbc94a9c2021-04-15 00:07:35 +0200532 "client_description" => $desc,
Akronb6b156e2022-03-31 14:57:49 +0200533 "client_url" => $url,
Akron9f2ad342022-05-04 16:16:40 +0200534 "client_redirect_uri" => $redirect_uri,
Akron6b75d122022-05-12 17:39:05 +0200535 "client_type" => $type
Akronb6b156e2022-03-31 14:57:49 +0200536 };
537
Akron6b75d122022-05-12 17:39:05 +0200538 # Plugin!
539 if ($src) {
540 $obj->{source} = $src;
541 $obj->{client_id} = $tokens{new_client_id_3};
542 };
543
544 push @$list, $obj;
545
Akronb6b156e2022-03-31 14:57:49 +0200546 if ($redirect_uri && $redirect_uri =~ /FAIL$/) {
547 return $c->render(
548 status => 400,
549 json => {
550 "error_description" => $redirect_uri . " is invalid.",
551 "error" => "invalid_request"
552 }
553 )
Akron1a9d5be2020-03-19 17:28:33 +0100554 };
555
Akron59992122019-10-29 11:28:45 +0100556 # Confidential server application
557 if ($type eq 'CONFIDENTIAL') {
Akronb6b156e2022-03-31 14:57:49 +0200558
Akron59992122019-10-29 11:28:45 +0100559 return $c->render(json => {
Akronb6b156e2022-03-31 14:57:49 +0200560 client_id => $tokens{new_client_id_2},
Akron59992122019-10-29 11:28:45 +0100561 client_secret => $tokens{new_client_secret}
562 });
563 };
564
565 # Desktop application
566 return $c->render(json => {
567 client_id => $tokens{new_client_id}
568 });
569};
570
Helgedb720ea2023-03-20 09:39:36 +0100571# Mock API list plugins
Helge278fbca2022-11-29 18:49:15 +0100572post '/v1.0/plugins' => sub {
Akron0f1b93b2020-03-17 11:37:19 +0100573 my $c = shift;
Akron276afc02021-06-14 11:00:21 +0200574 my $v = $c->validation;
Helgedb720ea2023-03-20 09:39:36 +0100575 $v->required('super_client_id');
576 $v->required('super_client_secret');
577 if ($v->has_error) {
578 return $c->render(
579 json => [],
580 status => 400
581 );
582 };
Akron276afc02021-06-14 11:00:21 +0200583
Helgedb720ea2023-03-20 09:39:36 +0100584 return $c->render(
585 json => $c->stash('oauth.plugin_list'),
586 status => 200
587 );
588};
589
590# Mock API list installed plugins
591post '/v1.0/plugins/installed' => sub {
592 my $c = shift;
593 my $v = $c->validation;
Akron276afc02021-06-14 11:00:21 +0200594 $v->required('super_client_id');
595 $v->required('super_client_secret');
596
597 if ($v->has_error) {
598 return $c->render(
599 json => [],
600 status => 400
601 );
602 };
Helge278fbca2022-11-29 18:49:15 +0100603
Helgedb720ea2023-03-20 09:39:36 +0100604 return $c->render(
605 json => $c->stash('oauth.pluginin_list'),
606 status => 200
607 );
608};
609
610
611# Mock API plugin installation
612post '/v1.0/plugins/install' => sub {
613 my $c = shift;
614 my $v = $c->validation;
615 $v->required('super_client_id');
616 $v->required('super_client_secret');
617 $v->required('client_id');
618 my $cl_id = $c->param('client_id');
619 if ($v->has_error) {
Helge278fbca2022-11-29 18:49:15 +0100620 return $c->render(
Helgedb720ea2023-03-20 09:39:36 +0100621 json => [],
622 status => 400
623 );
624 };
625
626 my $date = "2022-12-13T16:33:27.621+01:00[Europe/Berlin]";
627 my $pl_list = $c->app->defaults('oauth.plugin_list');
628 my $cl_name = (grep{($_->{client_id} eq $cl_id)}@$pl_list)[0]->{client_name};
629
630 if (length $cl_name){
631
632 my %inst_plugin = (
633 "name" => $cl_name,
634 "client_id" => $cl_id,
635 "installed_date" => $date,
636 );
637
638 $c->add_instplugin(\%inst_plugin);
639
640 return $c->render(
641 json => %inst_plugin,
Helge278fbca2022-11-29 18:49:15 +0100642 status => 200
643 );
644 }
Helge278fbca2022-11-29 18:49:15 +0100645
Helgedb720ea2023-03-20 09:39:36 +0100646 return $c->render(
647 json => [],
648 status => 400
649 );
Helge278fbca2022-11-29 18:49:15 +0100650};
651
Helged36478d2023-06-08 17:43:01 +0200652# Mock API plugin uninstallation
653post '/v1.0/plugins/uninstall' => sub {
654 my $c = shift;
655 my $v = $c->validation;
656 $v->required('super_client_id');
657 $v->required('super_client_secret');
658 $v->required('client_id');
659 if ($v->has_error) {
660 return $c->render(
661 json => [],
662 status => 400
663 );
664 };
665 my $cl_id = $c->param('client_id');
666
667 my $plin_list = $c->app->defaults('oauth.pluginin_list');
668 my @new_list = grep{!($_->{client_id} eq $cl_id)}@$plin_list;
669 $c->app->defaults('oauth.pluginin_list' => \@new_list);
670
671 if(scalar @new_list eq scalar @$plin_list){
672 return $c->render(
673 status => 404
674 );
675 }
676 return $c->render(
677 json => $c->stash('oauth.pluginin_list'),
678 status => 200
679 );
680 };
Helgedb720ea2023-03-20 09:39:36 +0100681
Helge278fbca2022-11-29 18:49:15 +0100682# Register a client
683post '/v1.0/oauth2/client/list' => sub {
684 my $c = shift;
685
686 my $v = $c->validation;
687 $v->required('super_client_id');
688 $v->required('super_client_secret');
689
690 if ($v->has_error) {
691 return $c->render(
692 json => [],
693 status => 400
694 );
695 };
696
Akron276afc02021-06-14 11:00:21 +0200697
Akron0f1b93b2020-03-17 11:37:19 +0100698 # $c->param('client_secret');
Akron1a9d5be2020-03-19 17:28:33 +0100699
700 # Is empty [] when nothing registered
701
Akron0f1b93b2020-03-17 11:37:19 +0100702 return $c->render(
Akron1a9d5be2020-03-19 17:28:33 +0100703 json => $c->stash('oauth.client_list'),
704 status => 200
705 );
706};
707
Akrondb1f4672023-01-24 12:05:07 +0100708# Get client info
709post '/v1.0/oauth2/client/:client_id' => sub {
710 my $c = shift;
711
712 # Validate input
713 my $v = $c->validation;
714 $v->required('super_client_id');
715 $v->required('super_client_secret');
716
717 if ($v->has_error) {
718 return $c->render(
719 status => 400,
720 json => {
721 error_description => "No super client",
722 error => "no_superclient"
723 }
724 );
725 };
726
727 my $client_id = $c->stash('client_id');
728
729 my $list = $c->stash('oauth.client_list');
730
731 foreach (@$list) {
732 if ($_->{client_id} eq $client_id) {
733 return $c->render(
734 json => $_,
735 status => 200
736 );
737 };
738 };
739
740 return $c->render(
741 json => {
742 error_description => "Unknown client with $client_id.",
743 error => "invalid_client"
744 },
745 status => 401
746 );
747};
748
Akronbc94a9c2021-04-15 00:07:35 +0200749
750# Get token list
751post '/v1.0/oauth2/token/list' => sub {
752 my $c = shift;
753 return $c->render(json => [
754 {
755 "client_description" => "Nur ein Beispiel",
756 "client_id" => $tokens{new_client_id},
757 "client_name" => "Beispiel",
758 "client_url" => "",
759 "created_date" => "2021-04-14T19:40:26.742+02:00[Europe\/Berlin]",
760 "expires_in" => "31533851",
761 "scope" => [
762 "match_info",
763 "search",
764 "openid"
765 ],
766 "token" => "jhkhkjhk_hjgjsfz67i",
767 "user_authentication_time" => "2021-04-14T19:39:41.81+02:00[Europe\/Berlin]"
768 }
769 ]);
770};
771
Akron1a9d5be2020-03-19 17:28:33 +0100772del '/v1.0/oauth2/client/deregister/:client_id' => sub {
773 my $c = shift;
774 my $client_id = $c->stash('client_id');
775
776 my $list = $c->app->defaults('oauth.client_list');
777
778 my $break = -1;
779 for (my $i = 0; $i < @$list; $i++) {
Akrondc50c892021-05-05 18:12:02 +0200780 if ($list->[$i]->{client_id} eq $client_id) {
Akron1a9d5be2020-03-19 17:28:33 +0100781 $break = $i;
782 last;
783 };
784 };
785
786 if ($break != -1) {
787 splice @$list, $break, 1;
788 }
789
790 else {
791 return $c->render(
792 json => {
793 error_description => "Unknown client with $client_id.",
794 error => "invalid_client"
Akron0f1b93b2020-03-17 11:37:19 +0100795 },
Akron1a9d5be2020-03-19 17:28:33 +0100796 status => 401
797 );
798 };
799
800 return $c->render(
801 json => $c->stash('oauth.client_list'),
Akron0f1b93b2020-03-17 11:37:19 +0100802 status => 200
803 );
804};
805
Akron83209f72021-01-29 17:54:15 +0100806post '/v1.0/oauth2/authorize' => sub {
807 my $c = shift;
808 my $type = $c->param('response_type');
809 my $client_id = $c->param('client_id');
Akrona8efaa92022-04-09 14:45:43 +0200810 my $scope = $c->param('scope');
811 my $state = $c->param('state');
812 my $redirect_uri = $c->param('redirect_uri') // 'NO';
Akron83209f72021-01-29 17:54:15 +0100813
Akrona8efaa92022-04-09 14:45:43 +0200814 if ($type eq 'code' && $client_id eq 'xyz') {
815
816 if ($state eq 'fail') {
817 $c->res->headers->location(
818 Mojo::URL->new($redirect_uri)->query({
819 error_description => 'FAIL'
820 })
821 );
822 $c->res->code(400);
823 return $c->rendered;
824 };
825
Akron9ccf69a2023-01-31 14:21:37 +0100826 if (index($redirect_uri,'http://wrong') >= 0) {
827 return $c->render(
828 code => 400,
829 content_type => 'text/plain',
830 text => '{"error_description":"Invalid redirect URI","state":"ZMwDGTZ2RY","error":"invalid_request"}'
831 );
832 };
833
Akrona8efaa92022-04-09 14:45:43 +0200834 return $c->redirect_to(
835 Mojo::URL->new($redirect_uri)->query({
836 code => $tokens{auth_token_1},
837 scope => $scope,
838 })
839 );
840 }
841
842 elsif ($type eq 'code') {
Akrone3daaeb2023-05-08 09:44:18 +0200843 my $loc = Mojo::URL->new($redirect_uri)->query({
844 code => $tokens{auth_token_1},
845 scope => 'match_info search openid'
846 });
Akron83209f72021-01-29 17:54:15 +0100847
Akrone3daaeb2023-05-08 09:44:18 +0200848 my $res = $c->res;
849 $res->headers->location($loc);
850 return $c->rendered($client_id eq '307' ? 307 : 302);
851 # return $c->rendered(302);
Akron9ccf69a2023-01-31 14:21:37 +0100852 };
853
854 return $c->render(
855 code => 400,
856 content_type => 'text/plain',
857 content => 'Unknown'
858 );
Akron83209f72021-01-29 17:54:15 +0100859};
860
Akron0f1b93b2020-03-17 11:37:19 +0100861
Akronabdf9a92021-01-12 19:06:57 +0100862#######################
863# Query Reference API #
864#######################
865
866use CHI;
867my $chi = CHI->new(
868 driver => 'Memory',
869 global => 1
870);
871
872# Store query
873put '/v1.0/query/~:user/:query_name' => sub {
874 my $c = shift;
875 my $user = $c->stash('user');
876 my $qname = $c->stash('query_name');
877
878 if ($chi->is_valid($qname)) {
879 return $c->render(
880 json => {
881 errors => [
882 {
883 message => 'Unable to store query reference'
884 }
885 ]
886 }, status => 400
887 );
888 };
889
890 my $json = $c->req->json;
891
892 my $store = {
893 name => $qname,
894 koralQuery => { '@type' => 'Okay' },
895 query => $json->{query},
896 queryType => $json->{queryType},
897 type => $json->{type},
898 queryLanguage => $json->{queryLanguage},
899 };
900
901 if (exists $json->{description}) {
902 $store->{description} = $json->{description}
903 };
904
905 # Set query reference
906 $chi->set($qname => $store);
907
908 my $queries = $chi->get('~queries') // [];
909 push @$queries, $qname;
910 $chi->set('~queries' => $queries);
911
912 return $c->render(
913 status => 201,
914 text => ''
915 );
916};
917
918# Get query
919get '/v1.0/query/~:user/:query_name' => sub {
920 my $c = shift;
921
922 my $user = $c->stash('user');
923 my $qname = $c->stash('query_name');
924
925 my $json = $chi->get($qname);
926
927 if ($json) {
928 return $c->render(
929 json => $json
930 );
931 };
932
933 return $c->render(
934 json => {
935 errors => [
936 {
937 message => 'Query reference not found'
938 }
939 ]
940 }, status => 404
941 );
942};
943
944
945# Get all queries
946get '/v1.0/query/~:user' => sub {
947 my $c = shift;
948 my $user = $c->stash('user');
949 my $qs = $chi->get('~queries') // [];
950 my @queries = ();
951 foreach (@$qs) {
952 push @queries, $chi->get($_);
953 };
954 return $c->render(json => { refs => \@queries });
955};
956
957
958# Store query
959del '/v1.0/query/~:user/:query_name' => sub {
960 my $c = shift;
961 my $user = $c->stash('user');
962 my $qname = $c->stash('query_name');
963
964 $chi->remove($qname);
965
966 my $queries = $chi->get('~queries') // [];
967
968 my @clean = ();
969 foreach (@$queries) {
970 push @clean, $_ unless $_ eq $qname
971 };
972
973 $chi->set('~queries' => \@clean);
974
975 return $c->render(
976 status => 200,
977 text => ''
978 );
979};
980
Akronc1aaf932021-06-09 12:19:15 +0200981post '/v1.0/oauth2/revoke/super' => sub {
982 my $c = shift;
983
984 my $s_client_id = $c->param('super_client_id');
985 my $s_client_secret = $c->param('super_client_secret');
986 my $token = $c->param('token');
987
988 return $c->render(text => 'SUCCESS');
989};
990
Akrona8efaa92022-04-09 14:45:43 +0200991get '/fakeclient/return' => sub {
992 my $c = shift;
993 $c->render(
994 text => 'welcome back! [' . $c->param('code') . ']'
995 );
996} => 'return_uri';
997
Akron0f1b93b2020-03-17 11:37:19 +0100998
Akron0e1ed242018-10-11 13:22:00 +0200999app->start;
1000
1001
1002__END__
1003
1004
1005 # Temporary:
1006 my $collection_query = {
1007 '@type' => "koral:docGroup",
1008 "operation" => "operation:or",
1009 "operands" => [
1010 {
1011 '@type' => "koral:docGroup",
1012 "operation" => "operation:and",
1013 "operands" => [
1014 {
1015 '@type' => "koral:doc",
1016 "key" => "title",
1017 "match" => "match:eq",
1018 "value" => "Der Birnbaum",
1019 "type" => "type:string"
1020 },
1021 {
1022 '@type' => "koral:doc",
1023 "key" => "pubPlace",
1024 "match" => "match:eq",
1025 "value" => "Mannheim",
1026 "type" => "type:string"
1027 },
1028 {
1029 '@type' => "koral:docGroup",
1030 "operation" => "operation:or",
1031 "operands" => [
1032 {
1033 '@type' => "koral:doc",
1034 "key" => "subTitle",
1035 "match" => "match:eq",
1036 "value" => "Aufzucht oder Pflege",
1037 "type" => "type:string"
1038 },
1039 {
1040 '@type' => "koral:doc",
1041 "key" => "subTitle",
1042 "match" => "match:eq",
1043 "value" => "Gedichte",
1044 "type" => "type:string"
1045 }
1046 ]
1047 }
1048 ]
1049 },
1050 {
1051 '@type' => "koral:doc",
1052 "key" => "pubDate",
1053 "match" => "match:geq",
1054 "value" => "2015-03-05",
1055 "type" => "type:date"
1056 }
1057 ]
1058 };