From d3856637cf1c8c054f0b89e003bfed5bff1b60f9 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Fri, 5 Jan 2024 15:51:59 +0100 Subject: [PATCH] API: clean up error codes Error codes are now extracted from API core and printed on the OpenAPI overview page. This makes it easier to see what is in use. Error messages have been cleaned up, some new codes have been assigned. Some errors have been removed. Eg. it is fine to iterate a media namespace you don't have read access to. The result will either be empty or contain files from lower namespaces that you *do* have access to. --- inc/Remote/ApiCore.php | 31 ++++++++----------- inc/Remote/OpenApiDoc/OpenAPIGenerator.php | 48 ++++++++++++++++++++++++++++++ lib/exe/openapi.php | 25 ++++++++++++++-- 3 files changed, 84 insertions(+), 20 deletions(-) diff --git a/inc/Remote/ApiCore.php b/inc/Remote/ApiCore.php index 6b25c34a0..d22d9a384 100644 --- a/inc/Remote/ApiCore.php +++ b/inc/Remote/ApiCore.php @@ -60,7 +60,6 @@ class ApiCore 'core.appendPage' => new ApiCall([$this, 'appendPage'], 'pages'), 'core.listMedia' => new ApiCall([$this, 'listMedia'], 'media'), - // todo: implement searchMedia 'core.getRecentMediaChanges' => new ApiCall([$this, 'getRecentMediaChanges'], 'media'), 'core.getMedia' => new ApiCall([$this, 'getMedia'], 'media'), @@ -679,7 +678,7 @@ class ApiCore // SPAM check if (checkwordblock()) { - throw new RemoteException('Positive wordblock check', 134); + throw new RemoteException('The page content was blocked', 134); } // autoset summary on new pages @@ -759,10 +758,6 @@ class ApiCore $namespace = cleanID($namespace); - if (auth_quickaclcheck($namespace . ':*') < AUTH_READ) { - throw new AccessDeniedException('You are not allowed to list media files.', 215); - } - $options = [ 'skipacl' => 0, 'depth' => $depth, @@ -830,12 +825,12 @@ class ApiCore { $media = cleanID($media); if (auth_quickaclcheck($media) < AUTH_READ) { - throw new AccessDeniedException('You are not allowed to read this file', 211); + throw new AccessDeniedException('You are not allowed to read this media file', 211); } $file = mediaFN($media, $rev); if (!@ file_exists($file)) { - throw new RemoteException('The requested file does not exist', 221); + throw new RemoteException('The requested media file does not exist', 221); } $data = io_readFile($file, false); @@ -861,7 +856,7 @@ class ApiCore { $media = cleanID($media); if (auth_quickaclcheck($media) < AUTH_READ) { - throw new AccessDeniedException('You are not allowed to read this file', 211); + throw new AccessDeniedException('You are not allowed to read this media file', 211); } if (!media_exists($media, $rev)) { throw new RemoteException('The requested media file does not exist', 221); @@ -898,7 +893,7 @@ class ApiCore $auth = auth_quickaclcheck(getNS($media) . ':*'); if ($media === '') { - throw new RemoteException('Media ID not given.', 231); + throw new RemoteException('Empty or invalid media ID given', 231); } // clean up base64 encoded data @@ -912,7 +907,7 @@ class ApiCore $data = base64_decode($base64, true); if ($data === false) { - throw new RemoteException('Invalid base64 encoded data.', 231); // FIXME adjust code + throw new RemoteException('Invalid base64 encoded data', 234); } // save temporary file @@ -923,7 +918,7 @@ class ApiCore $res = media_save(['name' => $ftmp], $media, $overwrite, $auth, 'rename'); if (is_array($res)) { - throw new RemoteException($res[0], -$res[1]); // FIXME adjust code -1 * -1 = 1, we want a 23x code + throw new RemoteException('Failed to save media: ' . $res[0], 235); } return (bool)$res; // should always be true at this point } @@ -948,11 +943,11 @@ class ApiCore if ($res & DOKU_MEDIA_DELETED) { return true; } elseif ($res & DOKU_MEDIA_NOT_AUTH) { - throw new AccessDeniedException('You don\'t have permissions to delete files.', 212); + throw new AccessDeniedException('You are not allowed to delete this media file', 212); } elseif ($res & DOKU_MEDIA_INUSE) { - throw new RemoteException('File is still referenced', 232); + throw new RemoteException('Media file is still referenced', 232); } else { - throw new RemoteException('Could not delete file', 233); + throw new RemoteException('Failed to delete media file', 233); } } @@ -978,15 +973,15 @@ class ApiCore { $id = cleanID($id); if ($id === '') { - throw new RemoteException('Empty or invalid page ID given', 131); // FIXME check code + throw new RemoteException('Empty or invalid page ID given', 131); } if ($existCheck && !page_exists($id)) { - throw new RemoteException('The requested page does not exist', 121); // FIXME check code + throw new RemoteException('The requested page does not exist', 121); } if ($minAccess && auth_quickaclcheck($id) < $minAccess) { - throw new AccessDeniedException('You are not allowed to read this page', 111); // FIXME check code + throw new AccessDeniedException('You are not allowed to read this page', 111); } return $id; diff --git a/inc/Remote/OpenApiDoc/OpenAPIGenerator.php b/inc/Remote/OpenApiDoc/OpenAPIGenerator.php index 38283c445..922dfcffc 100644 --- a/inc/Remote/OpenApiDoc/OpenAPIGenerator.php +++ b/inc/Remote/OpenApiDoc/OpenAPIGenerator.php @@ -53,6 +53,54 @@ class OpenAPIGenerator } /** + * Read all error codes used in ApiCore.php + * + * This is useful for the documentation, but also for checking if the error codes are unique + * + * @return array + * @todo Getting all classes/methods registered with the API and reading their error codes would be even better + * @todo This is super crude. Using the PHP Tokenizer would be more sensible + */ + public function getErrorCodes() + { + $lines = file(DOKU_INC . 'inc/Remote/ApiCore.php'); + + $codes = []; + $method = ''; + + foreach ($lines as $no => $line) { + if (preg_match('/ *function (\w+)/', $line, $match)) { + $method = $match[1]; + } + if (preg_match('/^ *throw new RemoteException\(\'([^\']+)\'.*?, (\d+)/', $line, $match)) { + $codes[] = [ + 'line' => $no, + 'exception' => 'RemoteException', + 'method' => $method, + 'code' => $match[2], + 'message' => $match[1], + ]; + } + if (preg_match('/^ *throw new AccessDeniedException\(\'([^\']+)\'.*?, (\d+)/', $line, $match)) { + $codes[] = [ + 'line' => $no, + 'exception' => 'AccessDeniedException', + 'method' => $method, + 'code' => $match[2], + 'message' => $match[1], + ]; + } + } + + usort($codes, function ($a, $b) { + return $a['code'] <=> $b['code']; + }); + + return $codes; + } + + + /** * Add the current DokuWiki instance as a server * * @return void diff --git a/lib/exe/openapi.php b/lib/exe/openapi.php index 5eb7eaebd..5a3480cf3 100644 --- a/lib/exe/openapi.php +++ b/lib/exe/openapi.php @@ -1,13 +1,14 @@ has('spec')) { header('Content-Type: application/json'); - $apigen = new \dokuwiki\Remote\OpenApiDoc\OpenAPIGenerator(); + $apigen = new OpenAPIGenerator(); echo $apigen->generate(); exit(); } @@ -43,6 +44,26 @@ if ($INPUT->has('spec')) { the API Spec

+

Error Codes

+ +

+ The following error codes are currently used in the core methods. This list may be incomplete + or change in the future. +

+ + + + + getErrorCodes() as $code) { + // duplicate codes are only shown with debug + if($code['code'] === $last && !$INPUT->has('debug')) continue; + echo ''; + } + ?> +
CodeMessage
0Success
' . $code['code'] . '' . hsc($code['message']) . '
-- 2.11.4.GIT