blob: abc68465b20370e502c0bf2bd9d8cbf7e1b1af2d [file] [log] [blame]
Akron0e1ed242018-10-11 13:22:00 +02001#!/usr/bin/env perl
2use Mojolicious::Lite;
3use Mojo::ByteStream 'b';
4use Mojo::Date;
5use Mojo::JSON qw/true false encode_json decode_json/;
6use strict;
7use warnings;
8use Mojo::JWT;
Akron6d49c1f2018-10-11 14:22:21 +02009use Mojo::File qw/path/;
10use Mojo::Util qw/slugify/;
Akron0e1ed242018-10-11 13:22:00 +020011
12# This is an API fake server with fixtures
13
14my $secret = 's3cr3t';
Akron73f36082018-10-25 15:34:59 +020015my $fixture_path = path(Mojo::File->new(__FILE__)->dirname)->child('..', 'fixtures');
Akron0e1ed242018-10-11 13:22:00 +020016
Akroncdfd9d52019-07-23 11:35:00 +020017our %tokens = (
Akron59992122019-10-29 11:28:45 +010018 'access_token' => "4dcf8784ccfd26fac9bdb82778fe60e2",
19 'refresh_token' => "hlWci75xb8atDiq3924NUSvOdtAh7Nlf9z",
20 'access_token_2' => "abcde",
Akron83209f72021-01-29 17:54:15 +010021 'access_token_3' => 'jvgjbvjgzucgdwuiKHJK',
Akron59992122019-10-29 11:28:45 +010022 'refresh_token_2' => "fghijk",
23 'new_client_id' => 'fCBbQkA2NDA3MzM1Yw==',
Akronb6b156e2022-03-31 14:57:49 +020024 'new_client_id_2' => 'hghGHhjhFRz_gJhjrd==',
Akron6b75d122022-05-12 17:39:05 +020025 'new_client_id_3' => 'jh0gfjhjbfdsgzjghj==',
Akron59992122019-10-29 11:28:45 +010026 'new_client_secret' => 'KUMaFxs6R1WGud4HM22w3HbmYKHMnNHIiLJ2ihaWtB4N5JxGzZgyqs5GTLutrORj',
Akron83209f72021-01-29 17:54:15 +010027 'auth_token_1' => 'mscajfdghnjdfshtkjcuynxahgz5il'
Akroncdfd9d52019-07-23 11:35:00 +020028);
29
30helper get_token => sub {
31 my ($c, $token) = @_;
32 return $tokens{$token}
33};
34
Akron33f5c672019-06-24 19:40:47 +020035# Legacy:
Akron0e1ed242018-10-11 13:22:00 +020036helper jwt_encode => sub {
37 shift;
38 return Mojo::JWT->new(
39 secret => $secret,
40 token_type => 'api_token',
41 expires => time + (3 * 34 * 60 * 60),
42 claims => { @_ }
43 );
44};
45
Akron33f5c672019-06-24 19:40:47 +020046# Legacy;
Akron0e1ed242018-10-11 13:22:00 +020047helper jwt_decode => sub {
48 my ($c, $auth) = @_;
49 $auth =~ s/\s*api_token\s+//;
50 return Mojo::JWT->new(secret => $secret)->decode($auth);
51};
52
Akroncdfd9d52019-07-23 11:35:00 +020053# Expiration helper
54helper expired => sub {
55 my ($c, $auth, $set) = @_;
56
Akroncdfd9d52019-07-23 11:35:00 +020057 $auth =~ s/^[^ ]+? //;
58 if ($set) {
59 $c->app->log->debug("Set $auth for expiration");
60 $c->app->defaults('auth_' . $auth => 1);
61 return 1;
62 };
63
64 $c->app->log->debug("Check $auth for expiration: " . (
65 $c->app->defaults('auth_' . $auth) // '0'
66 ));
67
68 return $c->app->defaults('auth_' . $auth);
69};
Akron0e1ed242018-10-11 13:22:00 +020070
Akron408bc7c2022-04-28 15:46:43 +020071
72helper 'add_client' => sub {
73 my $c = shift;
74 my $client = shift;
75 my $list = $c->app->defaults('oauth.client_list');
76 push @$list, $client;
77};
78
79
Akron6d49c1f2018-10-11 14:22:21 +020080# Load fixture responses
81helper 'load_response' => sub {
82 my $c = shift;
83 my $q_name = shift;
84 my $file = $fixture_path->child("response_$q_name.json");
Akron8ea84292018-10-24 13:41:52 +020085 $c->app->log->debug("Load response from $file");
86
Akron6d49c1f2018-10-11 14:22:21 +020087 unless (-f $file) {
88 return {
89 status => 500,
90 json => {
91 errors => [[0, 'Unable to load query response from ' . $file]]
92 }
93 }
94 };
Akron8ea84292018-10-24 13:41:52 +020095
Akron6d49c1f2018-10-11 14:22:21 +020096 my $response = $file->slurp;
Akrona3c353c2019-02-14 23:50:00 +010097 my $decode = decode_json($response);
98 unless ($decode) {
99 return {
100 status => 500,
101 json => {
102 errors => [[0, 'Unable to parse JSON']]
103 }
104 }
105 };
106
107 return $decode;
Akron6d49c1f2018-10-11 14:22:21 +0200108};
109
Akron1a9d5be2020-03-19 17:28:33 +0100110app->defaults('oauth.client_list' => []);
111
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');
Akron8ea84292018-10-24 13:41:52 +0200145 $v->optional('cutoff')->in(qw/true false/);
Akron0e1ed242018-10-11 13:22:00 +0200146
Akron32396632018-10-11 17:08:37 +0200147 $c->app->log->debug('Receive request');
148
Akron0e1ed242018-10-11 13:22:00 +0200149 # Response q=x&ql=cosmas3
150 if ($v->param('ql') && $v->param('ql') eq 'cosmas3') {
151 return $c->render(
152 status => 400,
153 json => {
154 "\@context" => "http://korap.ids-mannheim.de/ns/koral/0.3/context.jsonld",
155 "errors" => [[307,"cosmas3 is not a supported query language!"]]
156 });
157 };
158
Akron6d49c1f2018-10-11 14:22:21 +0200159 if (!$v->param('q')) {
Akron8ea84292018-10-24 13:41:52 +0200160 return $c->render(%{$c->load_response('query_no_query')});
Akron0e1ed242018-10-11 13:22:00 +0200161 };
162
Akroncce055c2021-07-02 12:18:03 +0200163 if ($v->param('q') eq 'error') {
164 return $c->render(
165 status => 500,
166 inline => '<html><head>ERROR</head></html>'
167 );
168 };
169
Akron8ea84292018-10-24 13:41:52 +0200170 my @slug_base = ($v->param('q'));
171 push @slug_base, 'o' . $v->param('offset') if defined $v->param('offset');
172 push @slug_base, 'c' . $v->param('count') if defined $v->param('count');
173 push @slug_base, 'co' . $v->param('cutoff') if defined $v->param('cutoff');
Akroncd42a142019-07-12 18:55:37 +0200174 push @slug_base, 'cq' if defined $v->param('cq');
Akronc58bfc42020-10-05 12:09:45 +0200175 push @slug_base, 'p' . $v->param('pipes') if defined $v->param('pipes');
Akron8ea84292018-10-24 13:41:52 +0200176
Akron6d49c1f2018-10-11 14:22:21 +0200177 # Get response based on query parameter
Akron8ea84292018-10-24 13:41:52 +0200178 my $response = $c->load_response('query_' . slugify(join('_', @slug_base)));
Akron0e1ed242018-10-11 13:22:00 +0200179
180 # Check authentification
181 if (my $auth = $c->req->headers->header('Authorization')) {
Akron33f5c672019-06-24 19:40:47 +0200182
Akroncdfd9d52019-07-23 11:35:00 +0200183 $c->app->log->debug("There is an authorization header $auth");
Akron33f5c672019-06-24 19:40:47 +0200184 my $jwt;
185 if ($auth =~ /^Bearer/) {
186 # Username unknown in OAuth2
187 $response->{json}->{meta}->{authorized} = 'yes';
188 }
Akroncdfd9d52019-07-23 11:35:00 +0200189 elsif ($auth =~ /^api_token/ && ($jwt = $c->jwt_decode($auth))) {
Akron6d49c1f2018-10-11 14:22:21 +0200190 $response->{json}->{meta}->{authorized} = $jwt->{username} if $jwt->{username};
Akron0e1ed242018-10-11 13:22:00 +0200191 };
Akroncdfd9d52019-07-23 11:35:00 +0200192
193 # Code is expired
194 if ($c->expired($auth)) {
195
196 $c->app->log->debug("The access token has expired");
197
198 return $c->render(
199 status => 401,
200 json => {
201 errors => [[2003, 'Access token is expired']]
202 }
203 );
204 }
205
206 # Auth token is invalid
207 if ($auth =~ /^Bearer inv4lid/) {
208 $c->app->log->debug("The access token is invalid");
209
210 return $c->render(
211 status => 401,
212 json => {
213 errors => [[2011, 'Access token is invalid']]
214 }
215 );
216 }
Akron0e1ed242018-10-11 13:22:00 +0200217 };
218
Akronc58bfc42020-10-05 12:09:45 +0200219 if ($v->param('pipes')) {
220 $response->{json}->{meta}->{pipes} = $v->param('pipes');
Akron7b9a1962020-07-02 09:52:53 +0200221 };
222
Akron6d49c1f2018-10-11 14:22:21 +0200223 # Set page parameter
Akron0e1ed242018-10-11 13:22:00 +0200224 if ($v->param('page')) {
Akron6d49c1f2018-10-11 14:22:21 +0200225 $response->{json}->{meta}->{startIndex} = $v->param("startIndex");
Akron0e1ed242018-10-11 13:22:00 +0200226 };
227
Akron0e1ed242018-10-11 13:22:00 +0200228 # Simple search fixture
Akron32396632018-10-11 17:08:37 +0200229 $c->render(%$response);
230
231 $c->app->log->debug('Rendered result');
232
233 return 1;
Akron0e1ed242018-10-11 13:22:00 +0200234};
235
Akron80a84b22018-10-24 17:44:24 +0200236# Textinfo fixtures
Akron63d963b2019-07-05 15:35:51 +0200237get '/v1.0/corpus/:corpusId/:docId/:textId' => sub {
Akron80a84b22018-10-24 17:44:24 +0200238 my $c = shift;
239
240 my $file = join('_', (
241 'textinfo',
242 $c->stash('corpusId'),
243 $c->stash('docId'),
244 $c->stash('textId')
245 ));
246
247 my $slug = slugify($file);
248
249 # Get response based on query parameter
250 my $response = $c->load_response($slug);
251 return $c->render(%$response);
252};
253
Akron0e1ed242018-10-11 13:22:00 +0200254
Akronb80341d2018-10-15 19:46:23 +0200255# Matchinfo fixtures
Akron63d963b2019-07-05 15:35:51 +0200256get '/v1.0/corpus/:corpusId/:docId/:textId/:matchId/matchInfo' => sub {
Akronb80341d2018-10-15 19:46:23 +0200257 my $c = shift;
258
259 my $file = join('_', (
260 'matchinfo',
261 $c->stash('corpusId'),
262 $c->stash('docId'),
263 $c->stash('textId'),
264 $c->stash('matchId')
265 ));
266
Akronb8d0b402018-10-18 23:51:52 +0200267 my $slug = slugify($file);
268
Akronb80341d2018-10-15 19:46:23 +0200269 # Get response based on query parameter
Akronb8d0b402018-10-18 23:51:52 +0200270 my $response = $c->load_response($slug);
Akronb80341d2018-10-15 19:46:23 +0200271 return $c->render(%$response);
272};
273
Akron0e1ed242018-10-11 13:22:00 +0200274
Akronbe61f4c2018-10-20 00:52:58 +0200275# Statistics endpoint
Akron63d963b2019-07-05 15:35:51 +0200276get '/v1.0/statistics' => sub {
Akronbe61f4c2018-10-20 00:52:58 +0200277 my $c = shift;
278 my $v = $c->validation;
Akron5fa61e92019-07-15 11:56:11 +0200279 $v->optional('cq');
Akronbe61f4c2018-10-20 00:52:58 +0200280
281 my @list = 'corpusinfo';
Akron5fa61e92019-07-15 11:56:11 +0200282 if ($v->param('cq')) {
283 push @list, $v->param('cq');
Akronbe61f4c2018-10-20 00:52:58 +0200284 };
285 my $slug = slugify(join('_', @list));
286
287 # Get response based on query parameter
288 my $response = $c->load_response($slug);
289 return $c->render(%$response);
290};
291
Akron0e1ed242018-10-11 13:22:00 +0200292############
293# Auth API #
294############
295
296# Request API token
Akron63d963b2019-07-05 15:35:51 +0200297get '/v1.0/auth/logout' => sub {
Akron0e1ed242018-10-11 13:22:00 +0200298 my $c = shift;
299
300 if (my $auth = $c->req->headers->header('Authorization')) {
Akroncdfd9d52019-07-23 11:35:00 +0200301
302 if ($auth =~ /^Bearer/) {
303 $c->app->log->debug('Server-Logout: ' . $auth);
304 return $c->render(json => { msg => [[0, 'Fine!']]});
305 }
306
307 elsif (my $jwt = $c->jwt_decode($auth)) {
Akron0e1ed242018-10-11 13:22:00 +0200308 my $user = $jwt->{username} if $jwt->{username};
309
310 $c->app->log->debug('Server-Logout: ' . $user);
311 return $c->render(json => { msg => [[0, 'Fine!']]});
312 };
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) {
340
341 # the password is 'pass'
342 if ($pwd eq 'pass') {
343
344 # Render info with token
345 my $jwt = $c->jwt_encode(username => $username);
346
347 # Render in the Kustvakt fashion:
348 return $c->render(
349 format => 'html',
350 text => encode_json({
351 %{$jwt->claims},
352 expires => $jwt->expires,
353 token => $jwt->encode,
354 token_type => 'api_token'
355 })
356 );
Akron3d673062019-01-29 15:54:16 +0100357 }
358
359 elsif ($pwd eq 'ldaperr') {
360 return $c->render(
361 format => 'html',
362 status => 401,
363 json => {
364 "errors" => [[2022,"LDAP Authentication failed due to unknown user or password!"]]
365 }
366 );
Akron0e1ed242018-10-11 13:22:00 +0200367 };
368
369 return $c->render(
370 json => {
371 error => [[2004, undef]]
372 }
373 );
374 };
375
376 return $c->render(
377 json => {
378 error => [[2004, undef]]
379 }
380 );
381};
382
Akron33f5c672019-06-24 19:40:47 +0200383
384# Request API token
Akron63d963b2019-07-05 15:35:51 +0200385post '/v1.0/oauth2/token' => sub {
Akron33f5c672019-06-24 19:40:47 +0200386 my $c = shift;
387
Akron63d963b2019-07-05 15:35:51 +0200388 my $grant_type = $c->param('grant_type') // 'undefined';
389
390 if ($grant_type eq 'password') {
Akron33f5c672019-06-24 19:40:47 +0200391
Akron8bbbecf2019-07-01 18:57:30 +0200392 # Check for wrong client id
393 if ($c->param('client_id') ne '2') {
394 return $c->render(
395 json => {
396 "error_description" => "Unknown client with " . $_->{client_id},
397 "error" => "invalid_client"
398 },
399 status => 401
400 );
401 }
Akron33f5c672019-06-24 19:40:47 +0200402
Akron8bbbecf2019-07-01 18:57:30 +0200403 # Check for wrong client secret
404 elsif ($c->param('client_secret') ne 'k414m4r-s3cr3t') {
405 return $c->render(
406 json => {
407 "error_description" => "Invalid client credentials",
408 "error" => "invalid_client"
409 },
410 status => 401
411 );
412 }
Akron33f5c672019-06-24 19:40:47 +0200413
Akron8bbbecf2019-07-01 18:57:30 +0200414 # Check for wrong user name
Akron6a228db2021-10-14 15:57:00 +0200415 elsif ($c->param('username') !~ /^t.st$/) {
Akron8bbbecf2019-07-01 18:57:30 +0200416 return $c->render(json => {
417 error => [[2004, undef]]
418 });
419 }
420
421 # Check for ldap error
422 elsif ($c->param('password') eq 'ldaperr') {
423 return $c->render(
424 format => 'html',
425 status => 401,
426 json => {
427 "errors" => [
428 [
429 2022,
430 "LDAP Authentication failed due to unknown user or password!"
431 ]
432 ]
433 }
434 );
435 }
436
437 # Check for wrong password
438 elsif ($c->param('password') ne 'pass') {
439 return $c->render(json => {
440 format => 'html',
441 status => 401,
Akron33f5c672019-06-24 19:40:47 +0200442 "errors" => [[2022,"LDAP Authentication failed due to unknown user or password!"]]
Akron8bbbecf2019-07-01 18:57:30 +0200443 });
444 }
445
446 # Return fine access
447 return $c->render(
448 json => {
Akroncdfd9d52019-07-23 11:35:00 +0200449 "access_token" => $c->get_token('access_token'),
450 "refresh_token" => $c->get_token('refresh_token'),
Akron8bbbecf2019-07-01 18:57:30 +0200451 "scope" => "all",
452 "token_type" => "Bearer",
453 "expires_in" => 86400
454 });
455 }
456
457 # Refresh token
Akron63d963b2019-07-05 15:35:51 +0200458 elsif ($grant_type eq 'refresh_token') {
Akroncdfd9d52019-07-23 11:35:00 +0200459
460 if ($c->param('refresh_token') eq 'inv4lid') {
461 return $c->render(
462 status => 400,
463 json => {
464 "error_description" => "Refresh token is expired",
465 "error" => "invalid_grant"
466 }
467 );
468 };
469
470 $c->app->log->debug("Refresh the token in the mock server!");
471
Akron8bbbecf2019-07-01 18:57:30 +0200472 return $c->render(
473 status => 200,
474 json => {
Akroncdfd9d52019-07-23 11:35:00 +0200475 "access_token" => $c->get_token("access_token_2"),
476 "refresh_token" => $c->get_token("refresh_token_2"),
Akron8bbbecf2019-07-01 18:57:30 +0200477 "token_type" => "Bearer",
478 "expires_in" => 86400
Akron33f5c672019-06-24 19:40:47 +0200479 }
480 );
481 }
482
Akron83209f72021-01-29 17:54:15 +0100483 # Get auth_token_1
484 elsif ($grant_type eq 'authorization_code') {
485 if ($c->param('code') eq $tokens{auth_token_1}) {
486 return $c->render(
487 status => 200,
488 json => {
489 "access_token" => $tokens{access_token_3},
490 "expires_in" => 31536000,
491 "scope" => 'match_info search openid',
492 "token_type" => "Bearer"
493 }
494 );
495 };
496 }
497
Akron8bbbecf2019-07-01 18:57:30 +0200498 # Unknown token grant
499 else {
500 return $c->render(
Akron63d963b2019-07-05 15:35:51 +0200501 status => 400,
Akron8bbbecf2019-07-01 18:57:30 +0200502 json => {
503 "errors" => [
504 [
Akron63d963b2019-07-05 15:35:51 +0200505 0, "Grant Type unknown", $grant_type
Akron8bbbecf2019-07-01 18:57:30 +0200506 ]
507 ]
508 }
509 )
Akron33f5c672019-06-24 19:40:47 +0200510 }
Akron33f5c672019-06-24 19:40:47 +0200511};
512
Akron4cefe1f2019-09-04 10:11:28 +0200513# Revoke API token
514post '/v1.0/oauth2/revoke' => sub {
515 my $c = shift;
516
517 my $refresh_token = $c->param('token');
518
519 if ($c->param('client_secret') ne 'k414m4r-s3cr3t') {
520 return $c->render(
521 json => {
522 "error_description" => "Invalid client credentials",
523 "error" => "invalid_client"
524 },
525 status => 401
526 );
527 };
528
529 return $c->render(
530 text => ''
531 )
532};
Akron33f5c672019-06-24 19:40:47 +0200533
Akron59992122019-10-29 11:28:45 +0100534# Register a client
535post '/v1.0/oauth2/client/register' => sub {
536 my $c = shift;
537 my $json = $c->req->json;
538
Akrondc50c892021-05-05 18:12:02 +0200539 if ($json->{redirectURI}) {
540 return $c->render(
541 status => 400,
542 json => {
543 errors => [
544 [
545 201,
546 "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\"])"
547 ]
548 ]
549 }
550 );
551 };
552
Akron59992122019-10-29 11:28:45 +0100553 my $name = $json->{name};
Akron1a9d5be2020-03-19 17:28:33 +0100554 my $desc = $json->{description};
Akron59992122019-10-29 11:28:45 +0100555 my $type = $json->{type};
556 my $url = $json->{url};
Akron9f2ad342022-05-04 16:16:40 +0200557 my $src = $json->{source};
Akronb6b156e2022-03-31 14:57:49 +0200558 my $redirect_uri = $json->{redirect_uri};
Akron59992122019-10-29 11:28:45 +0100559
Akron1a9d5be2020-03-19 17:28:33 +0100560 my $list = $c->app->defaults('oauth.client_list');
561
Akron6b75d122022-05-12 17:39:05 +0200562 my $obj = {
Akrondc50c892021-05-05 18:12:02 +0200563 "client_id" => $tokens{new_client_id},
564 "client_name" => $name,
Akronbc94a9c2021-04-15 00:07:35 +0200565 "client_description" => $desc,
Akronb6b156e2022-03-31 14:57:49 +0200566 "client_url" => $url,
Akron9f2ad342022-05-04 16:16:40 +0200567 "client_redirect_uri" => $redirect_uri,
Akron6b75d122022-05-12 17:39:05 +0200568 "client_type" => $type
Akronb6b156e2022-03-31 14:57:49 +0200569 };
570
Akron6b75d122022-05-12 17:39:05 +0200571 # Plugin!
572 if ($src) {
573 $obj->{source} = $src;
574 $obj->{client_id} = $tokens{new_client_id_3};
575 };
576
577 push @$list, $obj;
578
Akronb6b156e2022-03-31 14:57:49 +0200579 if ($redirect_uri && $redirect_uri =~ /FAIL$/) {
580 return $c->render(
581 status => 400,
582 json => {
583 "error_description" => $redirect_uri . " is invalid.",
584 "error" => "invalid_request"
585 }
586 )
Akron1a9d5be2020-03-19 17:28:33 +0100587 };
588
Akron59992122019-10-29 11:28:45 +0100589 # Confidential server application
590 if ($type eq 'CONFIDENTIAL') {
Akronb6b156e2022-03-31 14:57:49 +0200591
Akron59992122019-10-29 11:28:45 +0100592 return $c->render(json => {
Akronb6b156e2022-03-31 14:57:49 +0200593 client_id => $tokens{new_client_id_2},
Akron59992122019-10-29 11:28:45 +0100594 client_secret => $tokens{new_client_secret}
595 });
596 };
597
598 # Desktop application
599 return $c->render(json => {
600 client_id => $tokens{new_client_id}
601 });
602};
603
Akron33f5c672019-06-24 19:40:47 +0200604
Akron0f1b93b2020-03-17 11:37:19 +0100605# Register a client
606post '/v1.0/oauth2/client/list' => sub {
607 my $c = shift;
608
Akron276afc02021-06-14 11:00:21 +0200609 my $v = $c->validation;
610
611 $v->required('super_client_id');
612 $v->required('super_client_secret');
613
614 if ($v->has_error) {
615 return $c->render(
616 json => [],
617 status => 400
618 );
619 };
620
Akron0f1b93b2020-03-17 11:37:19 +0100621 # $c->param('client_secret');
Akron1a9d5be2020-03-19 17:28:33 +0100622
623 # Is empty [] when nothing registered
624
Akron0f1b93b2020-03-17 11:37:19 +0100625 return $c->render(
Akron1a9d5be2020-03-19 17:28:33 +0100626 json => $c->stash('oauth.client_list'),
627 status => 200
628 );
629};
630
Akronbc94a9c2021-04-15 00:07:35 +0200631
632# Get token list
633post '/v1.0/oauth2/token/list' => sub {
634 my $c = shift;
635 return $c->render(json => [
636 {
637 "client_description" => "Nur ein Beispiel",
638 "client_id" => $tokens{new_client_id},
639 "client_name" => "Beispiel",
640 "client_url" => "",
641 "created_date" => "2021-04-14T19:40:26.742+02:00[Europe\/Berlin]",
642 "expires_in" => "31533851",
643 "scope" => [
644 "match_info",
645 "search",
646 "openid"
647 ],
648 "token" => "jhkhkjhk_hjgjsfz67i",
649 "user_authentication_time" => "2021-04-14T19:39:41.81+02:00[Europe\/Berlin]"
650 }
651 ]);
652};
653
Akron1a9d5be2020-03-19 17:28:33 +0100654del '/v1.0/oauth2/client/deregister/:client_id' => sub {
655 my $c = shift;
656 my $client_id = $c->stash('client_id');
657
658 my $list = $c->app->defaults('oauth.client_list');
659
660 my $break = -1;
661 for (my $i = 0; $i < @$list; $i++) {
Akrondc50c892021-05-05 18:12:02 +0200662 if ($list->[$i]->{client_id} eq $client_id) {
Akron1a9d5be2020-03-19 17:28:33 +0100663 $break = $i;
664 last;
665 };
666 };
667
668 if ($break != -1) {
669 splice @$list, $break, 1;
670 }
671
672 else {
673 return $c->render(
674 json => {
675 error_description => "Unknown client with $client_id.",
676 error => "invalid_client"
Akron0f1b93b2020-03-17 11:37:19 +0100677 },
Akron1a9d5be2020-03-19 17:28:33 +0100678 status => 401
679 );
680 };
681
682 return $c->render(
683 json => $c->stash('oauth.client_list'),
Akron0f1b93b2020-03-17 11:37:19 +0100684 status => 200
685 );
686};
687
Akron83209f72021-01-29 17:54:15 +0100688post '/v1.0/oauth2/authorize' => sub {
689 my $c = shift;
690 my $type = $c->param('response_type');
691 my $client_id = $c->param('client_id');
Akrona8efaa92022-04-09 14:45:43 +0200692 my $scope = $c->param('scope');
693 my $state = $c->param('state');
694 my $redirect_uri = $c->param('redirect_uri') // 'NO';
Akron83209f72021-01-29 17:54:15 +0100695
Akrona8efaa92022-04-09 14:45:43 +0200696 if ($type eq 'code' && $client_id eq 'xyz') {
697
698 if ($state eq 'fail') {
699 $c->res->headers->location(
700 Mojo::URL->new($redirect_uri)->query({
701 error_description => 'FAIL'
702 })
703 );
704 $c->res->code(400);
705 return $c->rendered;
706 };
707
708 return $c->redirect_to(
709 Mojo::URL->new($redirect_uri)->query({
710 code => $tokens{auth_token_1},
711 scope => $scope,
712 })
713 );
714 }
715
716 elsif ($type eq 'code') {
Akron83209f72021-01-29 17:54:15 +0100717
718 return $c->redirect_to(
719 Mojo::URL->new($redirect_uri)->query({
720 code => $tokens{auth_token_1},
721 scope => 'match_info search openid'
722 })
723 );
724 }
725};
726
Akron0f1b93b2020-03-17 11:37:19 +0100727
Akronabdf9a92021-01-12 19:06:57 +0100728#######################
729# Query Reference API #
730#######################
731
732use CHI;
733my $chi = CHI->new(
734 driver => 'Memory',
735 global => 1
736);
737
738# Store query
739put '/v1.0/query/~:user/:query_name' => sub {
740 my $c = shift;
741 my $user = $c->stash('user');
742 my $qname = $c->stash('query_name');
743
744 if ($chi->is_valid($qname)) {
745 return $c->render(
746 json => {
747 errors => [
748 {
749 message => 'Unable to store query reference'
750 }
751 ]
752 }, status => 400
753 );
754 };
755
756 my $json = $c->req->json;
757
758 my $store = {
759 name => $qname,
760 koralQuery => { '@type' => 'Okay' },
761 query => $json->{query},
762 queryType => $json->{queryType},
763 type => $json->{type},
764 queryLanguage => $json->{queryLanguage},
765 };
766
767 if (exists $json->{description}) {
768 $store->{description} = $json->{description}
769 };
770
771 # Set query reference
772 $chi->set($qname => $store);
773
774 my $queries = $chi->get('~queries') // [];
775 push @$queries, $qname;
776 $chi->set('~queries' => $queries);
777
778 return $c->render(
779 status => 201,
780 text => ''
781 );
782};
783
784# Get query
785get '/v1.0/query/~:user/:query_name' => sub {
786 my $c = shift;
787
788 my $user = $c->stash('user');
789 my $qname = $c->stash('query_name');
790
791 my $json = $chi->get($qname);
792
793 if ($json) {
794 return $c->render(
795 json => $json
796 );
797 };
798
799 return $c->render(
800 json => {
801 errors => [
802 {
803 message => 'Query reference not found'
804 }
805 ]
806 }, status => 404
807 );
808};
809
810
811# Get all queries
812get '/v1.0/query/~:user' => sub {
813 my $c = shift;
814 my $user = $c->stash('user');
815 my $qs = $chi->get('~queries') // [];
816 my @queries = ();
817 foreach (@$qs) {
818 push @queries, $chi->get($_);
819 };
820 return $c->render(json => { refs => \@queries });
821};
822
823
824# Store query
825del '/v1.0/query/~:user/:query_name' => sub {
826 my $c = shift;
827 my $user = $c->stash('user');
828 my $qname = $c->stash('query_name');
829
830 $chi->remove($qname);
831
832 my $queries = $chi->get('~queries') // [];
833
834 my @clean = ();
835 foreach (@$queries) {
836 push @clean, $_ unless $_ eq $qname
837 };
838
839 $chi->set('~queries' => \@clean);
840
841 return $c->render(
842 status => 200,
843 text => ''
844 );
845};
846
Akronc1aaf932021-06-09 12:19:15 +0200847post '/v1.0/oauth2/revoke/super' => sub {
848 my $c = shift;
849
850 my $s_client_id = $c->param('super_client_id');
851 my $s_client_secret = $c->param('super_client_secret');
852 my $token = $c->param('token');
853
854 return $c->render(text => 'SUCCESS');
855};
856
Akrona8efaa92022-04-09 14:45:43 +0200857get '/fakeclient/return' => sub {
858 my $c = shift;
859 $c->render(
860 text => 'welcome back! [' . $c->param('code') . ']'
861 );
862} => 'return_uri';
863
Akron0f1b93b2020-03-17 11:37:19 +0100864
Akron0e1ed242018-10-11 13:22:00 +0200865app->start;
866
867
868__END__
869
870
871 # Temporary:
872 my $collection_query = {
873 '@type' => "koral:docGroup",
874 "operation" => "operation:or",
875 "operands" => [
876 {
877 '@type' => "koral:docGroup",
878 "operation" => "operation:and",
879 "operands" => [
880 {
881 '@type' => "koral:doc",
882 "key" => "title",
883 "match" => "match:eq",
884 "value" => "Der Birnbaum",
885 "type" => "type:string"
886 },
887 {
888 '@type' => "koral:doc",
889 "key" => "pubPlace",
890 "match" => "match:eq",
891 "value" => "Mannheim",
892 "type" => "type:string"
893 },
894 {
895 '@type' => "koral:docGroup",
896 "operation" => "operation:or",
897 "operands" => [
898 {
899 '@type' => "koral:doc",
900 "key" => "subTitle",
901 "match" => "match:eq",
902 "value" => "Aufzucht oder Pflege",
903 "type" => "type:string"
904 },
905 {
906 '@type' => "koral:doc",
907 "key" => "subTitle",
908 "match" => "match:eq",
909 "value" => "Gedichte",
910 "type" => "type:string"
911 }
912 ]
913 }
914 ]
915 },
916 {
917 '@type' => "koral:doc",
918 "key" => "pubDate",
919 "match" => "match:geq",
920 "value" => "2015-03-05",
921 "type" => "type:date"
922 }
923 ]
924 };