From 40022decb4280df133ae382bd6cc2d64081c6870 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Fri, 22 Jul 2022 15:38:06 +0100 Subject: [PATCH 01/20] Start working on simplifying exceptions --- app/config/errors.php | 103 +++++++++++++++++++++++++++++- app/controllers/api/account.php | 42 ++++++------ app/controllers/general.php | 13 ++++ app/controllers/mock.php | 42 ++++++------ app/init.php | 4 +- src/Appwrite/Extend/Exception.php | 33 +++++++++- 6 files changed, 190 insertions(+), 47 deletions(-) diff --git a/app/config/errors.php b/app/config/errors.php index 8420ddbb74..43356f4b94 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -89,6 +89,13 @@ return [ 'code' => 500, ], + /** Account Errors */ + Exception::ACCOUNT_OAUTH_FAILED_TO_OBTAIN_TOKEN => [ + 'name' => Exception::ACCOUNT_OAUTH_FAILED_TO_OBTAIN_TOKEN, + 'description' => 'Failed to obtain access token.', + 'code' => 500, + ], + /** User Errors */ Exception::USER_COUNT_EXCEEDED => [ 'name' => Exception::USER_COUNT_EXCEEDED, @@ -511,5 +518,99 @@ return [ 'name' => Exception::DOMAIN_VERIFICATION_FAILED, 'description' => 'Domain verification for the requested domain has failed.', 'code' => 401, - ] + ], + + /* Registry Errors */ + Exception::LOGGER_NOT_SUPPORTED => [ + 'name' => Exception::LOGGER_NOT_SUPPORTED, + 'description' => 'Logging provider not supported. Logging disabled.', + 'code' => 500, + ], + + /** Mocks */ + Exception::MOCK_INVALID_CONTENT_RANGE_HEADER => [ + 'name' => Exception::MOCK_INVALID_CONTENT_RANGE_HEADER, + 'description' => 'Invalid content-range header', + 'code' => 400, + ], + Exception::MOCK_FIRST_CHUNK_CANNOT_HAVE_ID => [ + 'name' => Exception::MOCK_FIRST_CHUNK_CANNOT_HAVE_ID, + 'description' => 'First chunked request cannot have id header', + 'code' => 400, + ], + Exception::MOCK_CHUNK_MISSING_ID => [ + 'name' => Exception::MOCK_CHUNK_MISSING_ID, + 'description' => 'All chunked request must have id header (except first)', + 'code' => 400, + ], + Exception::MOCK_CHUNK_INVALID_SIZE => [ + 'name' => Exception::MOCK_CHUNK_INVALID_SIZE, + 'description' => 'Chunk size must be 5MB (except last chunk)', + 'code' => 400, + ], + Exception::MOCK_INVALID_FILE_NAME => [ + 'name' => Exception::MOCK_INVALID_FILE_NAME, + 'description' => 'Wrong file name', + 'code' => 400, + ], + Exception::MOCK_INVALID_FILE_SIZE => [ + 'name' => Exception::MOCK_INVALID_FILE_SIZE, + 'description' => 'Wrong file size', + 'code' => 400, + ], + Exception::MOCK_WRONG_FILE_UPLOADED => [ + 'name' => Exception::MOCK_WRONG_FILE_UPLOADED, + 'description' => 'Wrong file uploaded', + 'code' => 400, + ], + Exception::MOCK_MISSING_COOKIE => [ + 'name' => Exception::MOCK_MISSING_COOKIE, + 'description' => 'Missing cookie value', + 'code' => 400, + ], + Exception::MOCK_INVALID_CLIENT_ID => [ + 'name' => Exception::MOCK_INVALID_CLIENT_ID, + 'description' => 'Invalid client ID', + 'code' => 400, + ], + Exception::MOCK_INVALID_CLIENT_SECRET => [ + 'name' => Exception::MOCK_INVALID_CLIENT_SECRET, + 'description' => 'Invalid client secret', + 'code' => 400, + ], + Exception::MOCK_INVALID_TOKEN => [ + 'name' => Exception::MOCK_INVALID_TOKEN, + 'description' => 'Invalid token', + 'code' => 400, + ], + Exception::MOCK_INVALID_REFRESH_TOKEN => [ + 'name' => Exception::MOCK_INVALID_REFRESH_TOKEN, + 'description' => 'Invalid refresh token', + 'code' => 400, + ], + Exception::MOCK_INVALID_GRANT_TYPE => [ + 'name' => Exception::MOCK_INVALID_GRANT_TYPE, + 'description' => 'Invalid grant type', + 'code' => 400, + ], + Exception::MOCK_FAILED_TO_READ_RESULTS => [ + 'name' => Exception::MOCK_FAILED_TO_READ_RESULTS, + 'description' => 'Failed to read results', + 'code' => 500, + ], + Exception::MOCK_FAILED_TO_SAVE_RESULTS => [ + 'name' => Exception::MOCK_FAILED_TO_SAVE_RESULTS, + 'description' => 'Failed to save results', + 'code' => 500, + ], + Exception::MOCK_400 => [ + 'name' => Exception::MOCK_400, + 'description' => 'Mock 400 error', + 'code' => 400, + ], + Exception::MOCK_500 => [ + 'name' => Exception::MOCK_500, + 'description' => 'Mock 500 error', + 'code' => 500, + ], ]; diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 6c90441afb..cb06f6725b 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -74,11 +74,11 @@ App::post('/v1/account') $whitelistIPs = $project->getAttribute('authWhitelistIPs'); if (!empty($whitelistEmails) && !\in_array($email, $whitelistEmails)) { - throw new Exception('Console registration is restricted to specific emails. Contact your administrator for more information.', 401, Exception::USER_EMAIL_NOT_WHITELISTED); + throw new Exception(Exception::USER_EMAIL_NOT_WHITELISTED); } if (!empty($whitelistIPs) && !\in_array($request->getIP(), $whitelistIPs)) { - throw new Exception('Console registration is restricted to specific IPs. Contact your administrator for more information.', 401, Exception::USER_IP_NOT_WHITELISTED); + throw new Exception(Exception::USER_IP_NOT_WHITELISTED); } } @@ -88,7 +88,7 @@ App::post('/v1/account') $total = $dbForProject->count('users', max: APP_LIMIT_USERS); if ($total >= $limit) { - throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501, Exception::USER_COUNT_EXCEEDED); + throw new Exception(Exception::USER_COUNT_EXCEEDED); } } @@ -113,7 +113,7 @@ App::post('/v1/account') 'search' => implode(' ', [$userId, $email, $name]) ]))); } catch (Duplicate $th) { - throw new Exception('Account already exists', 409, Exception::USER_ALREADY_EXISTS); + throw new Exception(Exception::USER_ALREADY_EXISTS); } Authorization::unsetRole('role:' . Auth::USER_ROLE_GUEST); @@ -167,11 +167,11 @@ App::post('/v1/account/sessions/email') new Query('email', Query::TYPE_EQUAL, [$email])]); if (!$profile || !Auth::passwordVerify($password, $profile->getAttribute('password'))) { - throw new Exception('Invalid credentials', 401, Exception::USER_INVALID_CREDENTIALS); // Wrong password or username + throw new Exception(Exception::USER_INVALID_CREDENTIALS); // Wrong password or username } if (false === $profile->getAttribute('status')) { // Account is blocked - throw new Exception('Invalid credentials. User is blocked', 401, Exception::USER_BLOCKED); // User is in status blocked + throw new Exception(Exception::USER_BLOCKED); // User is in status blocked } $detector = new Detector($request->getUserAgent('UNKNOWN')); @@ -276,13 +276,13 @@ App::get('/v1/account/sessions/oauth2/:provider') } if (empty($appId) || empty($appSecret)) { - throw new Exception('This provider is disabled. Please configure the provider app ID and app secret key from your ' . APP_NAME . ' console to continue.', 412, Exception::PROJECT_PROVIDER_DISABLED); + throw new Exception(Exception::PROJECT_PROVIDER_DISABLED); } $className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider); if (!\class_exists($className)) { - throw new Exception('Provider is not supported', 501, Exception::PROJECT_PROVIDER_UNSUPPORTED); + throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED); } if (empty($success)) { @@ -388,7 +388,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider); if (!\class_exists($className)) { - throw new Exception('Provider is not supported', 501, Exception::PROJECT_PROVIDER_UNSUPPORTED); + throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED); } $oauth2 = new $className($appId, $appSecret, $callback); @@ -397,18 +397,18 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') try { $state = \array_merge($defaultState, $oauth2->parseState($state)); } catch (\Exception$exception) { - throw new Exception('Failed to parse login state params as passed from OAuth2 provider', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR); } } else { $state = $defaultState; } if (!$validateURL->isValid($state['success'])) { - throw new Exception('Invalid redirect URL for success login', 400, Exception::PROJECT_INVALID_SUCCESS_URL); + throw new Exception(Exception::PROJECT_INVALID_SUCCESS_URL); } if (!empty($state['failure']) && !$validateURL->isValid($state['failure'])) { - throw new Exception('Invalid redirect URL for failure login', 400, Exception::PROJECT_INVALID_FAILURE_URL); + throw new Exception(Exception::PROJECT_INVALID_FAILURE_URL); } $state['failure'] = null; @@ -422,7 +422,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $response->redirect($state['failure'], 301, 0); } - throw new Exception('Failed to obtain access token', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::ACCOUNT_OAUTH_FAILED_TO_OBTAIN_TOKEN); } $oauth2ID = $oauth2->getUserID($accessToken); @@ -432,7 +432,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $response->redirect($state['failure'], 301, 0); } - throw new Exception('Missing ID from OAuth2 provider', 400, Exception::PROJECT_MISSING_USER_ID); + throw new Exception(Exception::ACCOUNT_OAUTH_MISSING_ID); } $sessions = $user->getAttribute('sessions', []); @@ -470,7 +470,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $total = $dbForProject->count('users', max: APP_LIMIT_USERS); if ($total >= $limit) { - throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501, Exception::USER_COUNT_EXCEEDED); + throw new Exception(Exception::USER_COUNT_EXCEEDED); } } @@ -495,13 +495,13 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') 'search' => implode(' ', [$userId, $email, $name]) ]))); } catch (Duplicate $th) { - throw new Exception('Account already exists', 409, Exception::USER_ALREADY_EXISTS); + throw new Exception(Exception::USER_ALREADY_EXISTS); } } } if (false === $user->getAttribute('status')) { // Account is blocked - throw new Exception('Invalid credentials. User is blocked', 401, Exception::USER_BLOCKED); // User is in status blocked + throw new Exception(Exception::USER_BLOCKED); // User is in status blocked } // Create session token, verify user account and update OAuth2 ID and Access Token @@ -619,7 +619,7 @@ App::post('/v1/account/sessions/magic-url') ->action(function (string $userId, string $email, string $url, Request $request, Response $response, Document $project, Database $dbForProject, Locale $locale, Audit $audits, Event $events, Mail $mails) { if (empty(App::getEnv('_APP_SMTP_HOST'))) { - throw new Exception('SMTP Disabled', 503, Exception::GENERAL_SMTP_DISABLED); + throw new Exception(Exception::GENERAL_SMTP_DISABLED); } $roles = Authorization::getRoles(); @@ -635,7 +635,7 @@ App::post('/v1/account/sessions/magic-url') $total = $dbForProject->count('users', max: APP_LIMIT_USERS); if ($total >= $limit) { - throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501, Exception::USER_COUNT_EXCEEDED); + throw new Exception(Exception::USER_COUNT_EXCEEDED); } } @@ -750,13 +750,13 @@ App::put('/v1/account/sessions/magic-url') $user = Authorization::skip(fn() => $dbForProject->getDocument('users', $userId)); if ($user->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $token = Auth::tokenVerify($user->getAttribute('tokens', []), Auth::TOKEN_TYPE_MAGIC_URL, $secret); if (!$token) { - throw new Exception('Invalid login token', 401, Exception::USER_INVALID_TOKEN); + throw new Exception(Exception::USER_INVALID_TOKEN); } $detector = new Detector($request->getUserAgent('UNKNOWN')); diff --git a/app/controllers/general.php b/app/controllers/general.php index 620f16f1c1..d7b1a075c0 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -352,6 +352,19 @@ App::error(function (Throwable $error, App $utopia, Request $request, Response $ throw $error; } + if ($error instanceof Appwrite\Extend\Exception) { + // Find error code and error message + + $errors = Config::getParam('errors', []); + $errorData = $errors[$error->getType()]; + + $error = new Appwrite\Extend\Exception( + $errorData['description'] ?? $error->getMessage(), + $errorData['code'] ?? $error->getCode(), + $errorData['name'] ?? $error->getType() + ); + } + if ($logger) { if ($error->getCode() >= 500 || $error->getCode() === 0) { try { diff --git a/app/controllers/mock.php b/app/controllers/mock.php index f681cb2482..bf9d00c4f0 100644 --- a/app/controllers/mock.php +++ b/app/controllers/mock.php @@ -253,31 +253,31 @@ App::post('/v1/mock/tests/general/upload') $file['size'] = (\is_array($file['size'])) ? $file['size'][0] : $file['size']; if (is_null($start) || is_null($end) || is_null($size)) { - throw new Exception('Invalid content-range header', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_CONTENT_RANGE_HEADER); } if ($start > $end || $end > $size) { - throw new Exception('Invalid content-range header', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_CONTENT_RANGE_HEADER); } if ($start === 0 && !empty($id)) { - throw new Exception('First chunked request cannot have id header', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_FIRST_CHUNK_CANNOT_HAVE_ID); } if ($start !== 0 && $id !== 'newfileid') { - throw new Exception('All chunked request must have id header (except first)', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_CHUNK_MISSING_ID); } if ($end !== $size && $end - $start + 1 !== $chunkSize) { - throw new Exception('Chunk size must be 5MB (except last chunk)', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_CHUNK_INVALID_SIZE); } if ($end !== $size && $file['size'] !== $chunkSize) { - throw new Exception('Wrong chunk size', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_CHUNK_INVALID_SIZE); } if ($file['size'] > $chunkSize) { - throw new Exception('Chunk size must be 5MB or less', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_CHUNK_INVALID_SIZE); } if ($end !== $size) { @@ -293,15 +293,15 @@ App::post('/v1/mock/tests/general/upload') $file['size'] = (\is_array($file['size'])) ? $file['size'][0] : $file['size']; if ($file['name'] !== 'file.png') { - throw new Exception('Wrong file name', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_FILE_NAME); } if ($file['size'] !== 38756) { - throw new Exception('Wrong file size', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_FILE_SIZE); } if (\md5(\file_get_contents($file['tmp_name'])) !== 'd80e7e6999a3eb2ae0d631a96fe135a4') { - throw new Exception('Wrong file uploaded', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_WRONG_FILE_UPLOADED); } } }); @@ -374,7 +374,7 @@ App::get('/v1/mock/tests/general/get-cookie') ->action(function (Request $request) { if ($request->getCookie('cookieName', '') !== 'cookieValue') { - throw new Exception('Missing cookie value', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_MISSING_COOKIE); } }); @@ -408,7 +408,7 @@ App::get('/v1/mock/tests/general/400-error') ->label('sdk.response.model', Response::MODEL_ERROR) ->label('sdk.mock', true) ->action(function () { - throw new Exception('Mock 400 error', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_400); }); App::get('/v1/mock/tests/general/500-error') @@ -424,7 +424,7 @@ App::get('/v1/mock/tests/general/500-error') ->label('sdk.response.model', Response::MODEL_ERROR) ->label('sdk.mock', true) ->action(function () { - throw new Exception('Mock 500 error', 500, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_500); }); App::get('/v1/mock/tests/general/502-error') @@ -480,11 +480,11 @@ App::get('/v1/mock/tests/general/oauth2/token') ->action(function (string $client_id, string $client_secret, string $grantType, string $redirectURI, string $code, string $refreshToken, Response $response) { if ($client_id != '1') { - throw new Exception('Invalid client ID', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_CLIENT_ID); } if ($client_secret != '123456') { - throw new Exception('Invalid client secret', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_CLIENT_SECRET); } $responseJson = [ @@ -495,18 +495,18 @@ App::get('/v1/mock/tests/general/oauth2/token') if ($grantType === 'authorization_code') { if ($code !== 'abcdef') { - throw new Exception('Invalid token', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_TOKEN); } $response->json($responseJson); } elseif ($grantType === 'refresh_token') { if ($refreshToken !== 'tuvwxyz') { - throw new Exception('Invalid refresh token', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_REFRESH_TOKEN); } $response->json($responseJson); } else { - throw new Exception('Invalid grant type', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_GRANT_TYPE); } }); @@ -520,7 +520,7 @@ App::get('/v1/mock/tests/general/oauth2/user') ->action(function (string $token, Response $response) { if ($token != '123456') { - throw new Exception('Invalid token', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_TOKEN); } $response->json([ @@ -566,7 +566,7 @@ App::shutdown(function (App $utopia, Response $response, Request $request) { $tests = (\file_exists($path)) ? \json_decode(\file_get_contents($path), true) : []; if (!\is_array($tests)) { - throw new Exception('Failed to read results', 500, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_FAILED_TO_READ_RESULTS); } $result[$route->getMethod() . ':' . $route->getPath()] = true; @@ -574,7 +574,7 @@ App::shutdown(function (App $utopia, Response $response, Request $request) { $tests = \array_merge($tests, $result); if (!\file_put_contents($path, \json_encode($tests), LOCK_EX)) { - throw new Exception('Failed to save results', 500, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_FAILED_TO_SAVE_RESULTS); } $response->dynamic(new Document(['result' => $route->getMethod() . ':' . $route->getPath() . ':passed']), Response::MODEL_MOCK); diff --git a/app/init.php b/app/init.php index 71407699b8..915cfe90c9 100644 --- a/app/init.php +++ b/app/init.php @@ -448,7 +448,7 @@ $register->set('logger', function () { } if (!Logger::hasProvider($providerName)) { - throw new Exception("Logging provider not supported. Logging disabled.", 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::LOGGER_NOT_SUPPORTED); } $classname = '\\Utopia\\Logger\\Adapter\\' . \ucfirst($providerName); @@ -818,7 +818,7 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons try { $payload = $jwt->decode($authJWT); } catch (JWTException $error) { - throw new Exception('Failed to verify JWT. ' . $error->getMessage(), 401, Exception::USER_JWT_INVALID); + throw new Exception(Exception::USER_JWT_INVALID); } $jwtUserId = $payload['userId'] ?? ''; diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 69cc32b715..34480d6bdc 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -12,6 +12,7 @@ class Exception extends \Exception * * Appwrite has the follwing entities: * - General + * - Account * - Users * - Teams * - Memberships @@ -29,6 +30,8 @@ class Exception extends \Exception * - Keys * - Platform * - Domain + * - Logger + * - Mocks */ /** General */ @@ -47,7 +50,11 @@ class Exception extends \Exception public const GENERAL_ROUTE_NOT_FOUND = 'general_route_not_found'; public const GENERAL_CURSOR_NOT_FOUND = 'general_cursor_not_found'; public const GENERAL_SERVER_ERROR = 'general_server_error'; - public const GENERAL_PROTOCOL_UNSUPPORTED = 'general_protocol_unsupported'; + public const GENERAL_PROTOCOL_UNSUPPORTED = 'general_protocol_unsupported'; + + /** Account */ + public const ACCOUNT_OAUTH_FAILED_TO_OBTAIN_TOKEN = 'account_oauth_failed_to_obtain_token'; + public const ACCOUNT_OAUTH_MISSING_ID = 'account_oauth_missing_id'; /** Users */ public const USER_COUNT_EXCEEDED = 'user_count_exceeded'; @@ -170,10 +177,32 @@ class Exception extends \Exception public const DOMAIN_ALREADY_EXISTS = 'domain_already_exists'; public const DOMAIN_VERIFICATION_FAILED = 'domain_verification_failed'; + /** Logger */ + public const LOGGER_NOT_SUPPORTED = 'logger_not_supported'; + + /** Mocks */ + public const MOCK_INVALID_CONTENT_RANGE_HEADER = 'mock_invalid_content_range_header'; + public const MOCK_FIRST_CHUNK_CANNOT_HAVE_ID = 'mock_first_chunk_cannot_have_id'; + public const MOCK_CHUNK_MISSING_ID = 'mock_chunk_missing_id'; + public const MOCK_CHUNK_INVALID_SIZE = 'mock_chunk_invalid_size'; + public const MOCK_INVALID_FILE_NAME = 'mock_invalid_file_name'; + public const MOCK_INVALID_FILE_SIZE = 'mock_invalid_file_size'; + public const MOCK_WRONG_FILE_UPLOADED = 'mock_wrong_file_uploaded'; + public const MOCK_MISSING_COOKIE = 'mock_missing_cookie'; + public const MOCK_INVALID_CLIENT_ID = 'mock_invalid_client_id'; + public const MOCK_INVALID_CLIENT_SECRET = 'mock_invalid_client_secret'; + public const MOCK_INVALID_TOKEN = 'mock_invalid_token'; + public const MOCK_INVALID_REFRESH_TOKEN = 'mock_invalid_refresh_token'; + public const MOCK_INVALID_GRANT_TYPE = 'mock_invalid_grant_type'; + public const MOCK_FAILED_TO_READ_RESULTS = 'mock_failed_to_read_results'; + public const MOCK_FAILED_TO_SAVE_RESULTS = 'mock_failed_to_save_results'; + public const MOCK_400 = 'mock_400'; + public const MOCK_500 = 'mock_500'; + private $type = ''; - public function __construct(string $message, int $code = 0, string $type = Exception::GENERAL_UNKNOWN, \Throwable $previous = null) + public function __construct(string $type = Exception::GENERAL_UNKNOWN, string $message = '', int $code = 0, \Throwable $previous = null) { $this->type = $type; From 4da9273b289a78ed577c17807cf7730d32cc538b Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Mon, 25 Jul 2022 23:54:08 +0100 Subject: [PATCH 02/20] Move default message and code handling into exception --- app/controllers/general.php | 13 ------------- src/Appwrite/Extend/Exception.php | 19 +++++++++++++++++-- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index d7b1a075c0..620f16f1c1 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -352,19 +352,6 @@ App::error(function (Throwable $error, App $utopia, Request $request, Response $ throw $error; } - if ($error instanceof Appwrite\Extend\Exception) { - // Find error code and error message - - $errors = Config::getParam('errors', []); - $errorData = $errors[$error->getType()]; - - $error = new Appwrite\Extend\Exception( - $errorData['description'] ?? $error->getMessage(), - $errorData['code'] ?? $error->getCode(), - $errorData['name'] ?? $error->getType() - ); - } - if ($logger) { if ($error->getCode() >= 500 || $error->getCode() === 0) { try { diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 34480d6bdc..b3984ba6c6 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -2,6 +2,8 @@ namespace Appwrite\Extend; +use Utopia\Config\Config; + class Exception extends \Exception { /** @@ -202,11 +204,24 @@ class Exception extends \Exception private $type = ''; - public function __construct(string $type = Exception::GENERAL_UNKNOWN, string $message = '', int $code = 0, \Throwable $previous = null) + private string $message = ''; + + private int $code = 0; + + protected array $errors = Config::getParam('errors'); + + public function __construct(string $type = Exception::GENERAL_UNKNOWN, string $message = '', \Throwable $previous = null) { $this->type = $type; - parent::__construct($message, $code, $previous); + if (isset($this->errors[$type])) { + $this->message = $this->errors[$type]['description']; + $this->code = $this->errors[$type]['code']; + } + + $this->message = $message ?? $this->message; + + parent::__construct($this->message, $this->code, $previous); } /** From 96c6c0ff006c7c556a9275b66a261952b8fb093a Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 26 Jul 2022 15:24:32 +0100 Subject: [PATCH 03/20] Migrate all exceptions to new signature --- app/config/errors.php | 17 ++- app/controllers/api/account.php | 66 +++++------ app/controllers/api/avatars.php | 22 ++-- app/controllers/api/databases.php | 190 +++++++++++++++--------------- app/controllers/api/functions.php | 76 ++++++------ app/controllers/api/graphql.php | 2 +- app/controllers/api/health.php | 12 +- app/controllers/api/projects.php | 102 ++++++++-------- app/controllers/api/storage.php | 108 ++++++++--------- app/controllers/api/teams.php | 66 +++++------ app/controllers/api/users.php | 40 +++---- app/controllers/shared/api.php | 16 +-- app/controllers/web/console.php | 4 +- src/Appwrite/Extend/Exception.php | 2 + 14 files changed, 368 insertions(+), 355 deletions(-) diff --git a/app/config/errors.php b/app/config/errors.php index 43356f4b94..9f9eb7832d 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -129,12 +129,12 @@ return [ ], Exception::USER_EMAIL_NOT_WHITELISTED => [ 'name' => Exception::USER_EMAIL_NOT_WHITELISTED, - 'description' => 'The user\'s email is not part of the whitelist. Please check the _APP_CONSOLE_WHITELIST_EMAILS environment variable of your Appwrite server.', + 'description' => 'Console registration is restricted to specific emails. Contact your administrator for more information.', 'code' => 401, ], Exception::USER_IP_NOT_WHITELISTED => [ 'name' => Exception::USER_IP_NOT_WHITELISTED, - 'description' => 'The user\'s IP address is not part of the whitelist. Please check the _APP_CONSOLE_WHITELIST_IPS environment variable of your Appwrite server.', + 'description' => 'Console registration is restricted to specific IPs. Contact your administrator for more information.', 'code' => 401, ], Exception::USER_INVALID_CREDENTIALS => [ @@ -201,7 +201,7 @@ return [ ], Exception::TEAM_INVITE_ALREADY_EXISTS => [ 'name' => Exception::TEAM_INVITE_ALREADY_EXISTS, - 'description' => 'The current user has already received an invitation to join the team.', + 'description' => 'User has already been invited or is already a member of this team', 'code' => 409, ], Exception::TEAM_INVITE_NOT_FOUND => [ @@ -224,6 +224,12 @@ return [ 'description' => 'The invite does not belong to the current user.', 'code' => 401, ], + Exception::TEAM_ID_MISMATCH => [ + 'name' => Exception::TEAM_ID_MISMATCH, + 'description' => 'Team IDs don\'t match', + 'code' => 404, + ], + /** Membership */ @@ -232,6 +238,11 @@ return [ 'description' => 'Membership with the requested ID could not be found.', 'code' => 404, ], + Exception::MEMBERSHIP_ALREADY_CONFIRMED => [ + 'name' => Exception::MEMBERSHIP_ALREADY_CONFIRMED, + 'description' => 'Membership already confirmed', + 'code' => 409, + ], /** Avatars */ Exception::AVATAR_SET_NOT_FOUND => [ diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index cb06f6725b..1b0437087c 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -276,7 +276,7 @@ App::get('/v1/account/sessions/oauth2/:provider') } if (empty($appId) || empty($appSecret)) { - throw new Exception(Exception::PROJECT_PROVIDER_DISABLED); + throw new Exception(Exception::PROJECT_PROVIDER_DISABLED, 'This provider is disabled. Please configure the provider app ID and app secret key from your ' . APP_NAME . ' console to continue.'); } $className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider); @@ -802,7 +802,7 @@ App::put('/v1/account/sessions/magic-url') $user = $dbForProject->updateDocument('users', $user->getId(), $user); if (false === $user) { - throw new Exception('Failed saving user to DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed saving user to DB'); } $audits->setResource('user/' . $user->getId()); @@ -860,7 +860,7 @@ App::post('/v1/account/sessions/phone') ->inject('phone') ->action(function (string $userId, string $number, Request $request, Response $response, Document $project, Database $dbForProject, Audit $audits, Event $events, EventPhone $messaging, Phone $phone) { if (empty(App::getEnv('_APP_PHONE_PROVIDER'))) { - throw new Exception('Phone provider not configured', 503, Exception::GENERAL_PHONE_DISABLED); + throw new Exception(Exception::GENERAL_PHONE_DISABLED); } $roles = Authorization::getRoles(); @@ -876,7 +876,7 @@ App::post('/v1/account/sessions/phone') $total = $dbForProject->count('users', max: APP_LIMIT_USERS); if ($total >= $limit) { - throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501, Exception::USER_COUNT_EXCEEDED); + throw new Exception(Exception::USER_COUNT_EXCEEDED); } } @@ -980,13 +980,13 @@ App::put('/v1/account/sessions/phone') $user = Authorization::skip(fn() => $dbForProject->getDocument('users', $userId)); if ($user->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $token = Auth::phoneTokenVerify($user->getAttribute('tokens', []), $secret); if (!$token) { - throw new Exception('Invalid login token', 401, Exception::USER_INVALID_TOKEN); + throw new Exception(Exception::USER_INVALID_TOKEN); } $detector = new Detector($request->getUserAgent('UNKNOWN')); @@ -1030,7 +1030,7 @@ App::put('/v1/account/sessions/phone') $user = $dbForProject->updateDocument('users', $user->getId(), $user); if (false === $user) { - throw new Exception('Failed saving user to DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed saving user to DB'); } $audits->setResource('user/' . $user->getId()); @@ -1092,11 +1092,11 @@ App::post('/v1/account/sessions/anonymous') $protocol = $request->getProtocol(); if ('console' === $project->getId()) { - throw new Exception('Failed to create anonymous user.', 401, Exception::USER_ANONYMOUS_CONSOLE_PROHIBITED); + throw new Exception(Exception::USER_ANONYMOUS_CONSOLE_PROHIBITED); } if (!$user->isEmpty()) { - throw new Exception('Cannot create an anonymous user when logged in.', 401, Exception::USER_SESSION_ALREADY_EXISTS); + throw new Exception(Exception::USER_SESSION_ALREADY_EXISTS); } $limit = $project->getAttribute('auths', [])['limit'] ?? 0; @@ -1105,7 +1105,7 @@ App::post('/v1/account/sessions/anonymous') $total = $dbForProject->count('users', max: APP_LIMIT_USERS); if ($total >= $limit) { - throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501, Exception::USER_COUNT_EXCEEDED); + throw new Exception(Exception::USER_COUNT_EXCEEDED); } } @@ -1222,7 +1222,7 @@ App::post('/v1/account/jwt') } if ($current->isEmpty()) { - throw new Exception('No valid session found', 404, Exception::USER_SESSION_NOT_FOUND); + throw new Exception(Exception::USER_SESSION_NOT_FOUND); } $jwt = new JWT(App::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway. @@ -1417,7 +1417,7 @@ App::get('/v1/account/sessions/:sessionId') } } - throw new Exception('Session not found', 404, Exception::USER_SESSION_NOT_FOUND); + throw new Exception(Exception::USER_SESSION_NOT_FOUND); }); App::patch('/v1/account/name') @@ -1480,7 +1480,7 @@ App::patch('/v1/account/password') // Check old password only if its an existing user. if ($user->getAttribute('passwordUpdate') !== 0 && !Auth::passwordVerify($oldPassword, $user->getAttribute('password'))) { // Double check user password - throw new Exception('Invalid credentials', 401, Exception::USER_INVALID_CREDENTIALS); + throw new Exception(Exception::USER_INVALID_CREDENTIALS); } $user = $dbForProject->updateDocument( @@ -1530,7 +1530,7 @@ App::patch('/v1/account/email') !$isAnonymousUser && !Auth::passwordVerify($password, $user->getAttribute('password')) ) { // Double check user password - throw new Exception('Invalid credentials', 401, Exception::USER_INVALID_CREDENTIALS); + throw new Exception(Exception::USER_INVALID_CREDENTIALS); } $email = \strtolower($email); @@ -1544,7 +1544,7 @@ App::patch('/v1/account/email') try { $user = $dbForProject->updateDocument('users', $user->getId(), $user); } catch (Duplicate $th) { - throw new Exception('Email already exists', 409, Exception::USER_EMAIL_ALREADY_EXISTS); + throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS); } $audits @@ -1586,7 +1586,7 @@ App::patch('/v1/account/phone') !$isAnonymousUser && !Auth::passwordVerify($password, $user->getAttribute('password')) ) { // Double check user password - throw new Exception('Invalid credentials', 401, Exception::USER_INVALID_CREDENTIALS); + throw new Exception(Exception::USER_INVALID_CREDENTIALS); } $user @@ -1597,7 +1597,7 @@ App::patch('/v1/account/phone') try { $user = $dbForProject->updateDocument('users', $user->getId(), $user); } catch (Duplicate $th) { - throw new Exception('Phone number already exists', 409, Exception::USER_PHONE_ALREADY_EXISTS); + throw new Exception(Exception::USER_PHONE_ALREADY_EXISTS); } $audits @@ -1755,7 +1755,7 @@ App::delete('/v1/account/sessions/:sessionId') } } - throw new Exception('Session not found', 404, Exception::USER_SESSION_NOT_FOUND); + throw new Exception(Exception::USER_SESSION_NOT_FOUND); }); App::patch('/v1/account/sessions/:sessionId') @@ -1809,7 +1809,7 @@ App::patch('/v1/account/sessions/:sessionId') $className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider); if (!\class_exists($className)) { - throw new Exception('Provider is not supported', 501, Exception::PROJECT_PROVIDER_UNSUPPORTED); + throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED); } $oauth2 = new $className($appId, $appSecret, '', [], []); @@ -1842,7 +1842,7 @@ App::patch('/v1/account/sessions/:sessionId') } } - throw new Exception('Session not found', 404, Exception::USER_SESSION_NOT_FOUND); + throw new Exception(Exception::USER_SESSION_NOT_FOUND); }); App::delete('/v1/account/sessions') @@ -1941,7 +1941,7 @@ App::post('/v1/account/recovery') ->action(function (string $email, string $url, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Mail $mails, Audit $audits, Event $events, Stats $usage) { if (empty(App::getEnv('_APP_SMTP_HOST'))) { - throw new Exception('SMTP Disabled', 503, Exception::GENERAL_SMTP_DISABLED); + throw new Exception(Exception::GENERAL_SMTP_DISABLED); } $roles = Authorization::getRoles(); @@ -1955,11 +1955,11 @@ App::post('/v1/account/recovery') ]); if (!$profile) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } if (false === $profile->getAttribute('status')) { // Account is blocked - throw new Exception('Invalid credentials. User is blocked', 401, Exception::USER_BLOCKED); + throw new Exception(Exception::USER_BLOCKED); } $expire = \time() + Auth::TOKEN_EXPIRATION_RECOVERY; @@ -2043,20 +2043,20 @@ App::put('/v1/account/recovery') ->action(function (string $userId, string $secret, string $password, string $passwordAgain, Response $response, Database $dbForProject, Audit $audits, Stats $usage, Event $events) { if ($password !== $passwordAgain) { - throw new Exception('Passwords must match', 400, Exception::USER_PASSWORD_MISMATCH); + throw new Exception(Exception::USER_PASSWORD_MISMATCH); } $profile = $dbForProject->getDocument('users', $userId); if ($profile->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $tokens = $profile->getAttribute('tokens', []); $recovery = Auth::tokenVerify($tokens, Auth::TOKEN_TYPE_RECOVERY, $secret); if (!$recovery) { - throw new Exception('Invalid recovery token', 401, Exception::USER_INVALID_TOKEN); + throw new Exception(Exception::USER_INVALID_TOKEN); } Authorization::setRole('user:' . $profile->getId()); @@ -2115,7 +2115,7 @@ App::post('/v1/account/verification') ->action(function (string $url, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Audit $audits, Event $events, Mail $mails, Stats $usage) { if (empty(App::getEnv('_APP_SMTP_HOST'))) { - throw new Exception('SMTP Disabled', 503, Exception::GENERAL_SMTP_DISABLED); + throw new Exception(Exception::GENERAL_SMTP_DISABLED); } $roles = Authorization::getRoles(); @@ -2204,14 +2204,14 @@ App::put('/v1/account/verification') $profile = Authorization::skip(fn() => $dbForProject->getDocument('users', $userId)); if ($profile->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $tokens = $profile->getAttribute('tokens', []); $verification = Auth::tokenVerify($tokens, Auth::TOKEN_TYPE_VERIFICATION, $secret); if (!$verification) { - throw new Exception('Invalid verification token', 401, Exception::USER_INVALID_TOKEN); + throw new Exception(Exception::USER_INVALID_TOKEN); } Authorization::setRole('user:' . $profile->getId()); @@ -2265,11 +2265,11 @@ App::post('/v1/account/verification/phone') ->action(function (Request $request, Response $response, Phone $phone, Document $user, Database $dbForProject, Audit $audits, Event $events, Stats $usage, EventPhone $messaging) { if (empty(App::getEnv('_APP_PHONE_PROVIDER'))) { - throw new Exception('Phone provider not configured', 503, Exception::GENERAL_PHONE_DISABLED); + throw new Exception(Exception::GENERAL_PHONE_DISABLED); } if (empty($user->getAttribute('phone'))) { - throw new Exception('User has no phone number.', 400, Exception::USER_PHONE_NOT_FOUND); + throw new Exception(Exception::USER_PHONE_NOT_FOUND); } $roles = Authorization::getRoles(); @@ -2352,13 +2352,13 @@ App::put('/v1/account/verification/phone') $profile = Authorization::skip(fn() => $dbForProject->getDocument('users', $userId)); if ($profile->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $verification = Auth::phoneTokenVerify($user->getAttribute('tokens', []), $secret); if (!$verification) { - throw new Exception('Invalid verification token', 401, Exception::USER_INVALID_TOKEN); + throw new Exception(Exception::USER_INVALID_TOKEN); } Authorization::setRole('user:' . $profile->getId()); diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index f31d02f8d4..ab61ffdd5a 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -25,15 +25,15 @@ $avatarCallback = function (string $type, string $code, int $width, int $height, $set = Config::getParam('avatar-' . $type, []); if (empty($set)) { - throw new Exception('Avatar set not found', 404, Exception::AVATAR_SET_NOT_FOUND); + throw new Exception(Exception::AVATAR_SET_NOT_FOUND); } if (!\array_key_exists($code, $set)) { - throw new Exception('Avatar not found', 404, Exception::AVATAR_NOT_FOUND); + throw new Exception(Exception::AVATAR_NOT_FOUND); } if (!\extension_loaded('imagick')) { - throw new Exception('Imagick extension is missing', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing'); } $output = 'png'; @@ -43,7 +43,7 @@ $avatarCallback = function (string $type, string $code, int $width, int $height, $type = 'png'; if (!\is_readable($path)) { - throw new Exception('File not readable in ' . $path, 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'File not readable in ' . $path); } $cache = new Cache(new Filesystem(APP_STORAGE_CACHE . '/app-0')); // Limit file number or size @@ -166,19 +166,19 @@ App::get('/v1/avatars/image') } if (!\extension_loaded('imagick')) { - throw new Exception('Imagick extension is missing', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing'); } $fetch = @\file_get_contents($url, false); if (!$fetch) { - throw new Exception('Image not found', 404, Exception::AVATAR_IMAGE_NOT_FOUND); + throw new Exception(Exception::AVATAR_IMAGE_NOT_FOUND); } try { $image = new Image($fetch); } catch (\Exception $exception) { - throw new Exception('Unable to parse image', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Unable to parse image'); } $image->crop((int) $width, (int) $height); @@ -232,7 +232,7 @@ App::get('/v1/avatars/favicon') } if (!\extension_loaded('imagick')) { - throw new Exception('Imagick extension is missing', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing'); } $curl = \curl_init(); @@ -254,7 +254,7 @@ App::get('/v1/avatars/favicon') \curl_close($curl); if (!$html) { - throw new Exception('Failed to fetch remote URL', 404, Exception::AVATAR_REMOTE_URL_FAILED); + throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED); } $doc = new DOMDocument(); @@ -312,7 +312,7 @@ App::get('/v1/avatars/favicon') $data = @\file_get_contents($outputHref, false); if (empty($data) || (\mb_substr($data, 0, 5) === 'save($key, $data); @@ -327,7 +327,7 @@ App::get('/v1/avatars/favicon') $fetch = @\file_get_contents($outputHref, false); if (!$fetch) { - throw new Exception('Icon not found', 404, Exception::AVATAR_ICON_NOT_FOUND); + throw new Exception(Exception::AVATAR_ICON_NOT_FOUND); } $image = new Image($fetch); diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 24304a126b..9872c635d7 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -67,28 +67,28 @@ function createAttribute(string $databaseId, string $collectionId, Document $att $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $dbForProject->getDocument('database_' . $db->getInternalId(), $collectionId); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } if (!empty($format)) { if (!Structure::hasFormat($format, $type)) { - throw new Exception("Format {$format} not available for {$type} attributes.", 400, Exception::ATTRIBUTE_FORMAT_UNSUPPORTED); + throw new Exception(Exception::ATTRIBUTE_FORMAT_UNSUPPORTED, "Format {$format} not available for {$type} attributes."); } } // Must throw here since dbForProject->createAttribute is performed by db worker if ($required && $default) { - throw new Exception('Cannot set default value for required attribute', 400, Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED); + throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED); } if ($array && $default) { - throw new Exception('Cannot set default value for array attributes', 400, Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED); + throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED); } try { @@ -114,9 +114,9 @@ function createAttribute(string $databaseId, string $collectionId, Document $att $dbForProject->checkAttribute($collection, $attribute); $attribute = $dbForProject->createDocument('attributes', $attribute); } catch (DuplicateException $exception) { - throw new Exception('Attribute already exists', 409, Exception::ATTRIBUTE_ALREADY_EXISTS); + throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS); } catch (LimitException $exception) { - throw new Exception('Attribute limit exceeded', 400, Exception::ATTRIBUTE_LIMIT_EXCEEDED); + throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED); } $dbForProject->deleteCachedDocument('database_' . $db->getInternalId(), $collectionId); @@ -184,7 +184,7 @@ App::post('/v1/databases') $collections = Config::getParam('collections', [])['collections'] ?? []; if (empty($collections)) { - throw new Exception('Collections collection is not configured.', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Collections collection is not configured.'); } $attributes = []; @@ -215,7 +215,7 @@ App::post('/v1/databases') } $dbForProject->createCollection('database_' . $database->getInternalId(), $attributes, $indexes); } catch (DuplicateException $th) { - throw new Exception('Database already exists', 409, Exception::DATABASE_ALREADY_EXISTS); + throw new Exception(Exception::DATABASE_ALREADY_EXISTS); } $audits @@ -256,7 +256,7 @@ App::get('/v1/databases') $cursorDocument = $dbForProject->getDocument('databases', $cursor); if ($cursorDocument->isEmpty()) { - throw new Exception("Collection '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Collection '{$cursor}' for the 'cursor' value not found."); } } @@ -294,7 +294,7 @@ App::get('/v1/databases/:databaseId') $database = $dbForProject->getDocument('databases', $databaseId); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $usage->setParam('databases.read', 1); @@ -325,7 +325,7 @@ App::get('/v1/databases/:databaseId/logs') $database = $dbForProject->getDocument('databases', $databaseId); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $audit = new Audit($dbForProject); @@ -405,7 +405,7 @@ App::put('/v1/databases/:databaseId') $database = $dbForProject->getDocument('databases', $databaseId); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } try { @@ -413,9 +413,9 @@ App::put('/v1/databases/:databaseId') ->setAttribute('name', $name) ->setAttribute('search', implode(' ', [$databaseId, $name]))); } catch (AuthorizationException $exception) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } catch (StructureException $exception) { - throw new Exception('Bad structure. ' . $exception->getMessage(), 400, Exception::DOCUMENT_INVALID_STRUCTURE); + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, 'Bad structure. ' . $exception->getMessage()); } $audits @@ -452,11 +452,11 @@ App::delete('/v1/databases/:databaseId') $database = $dbForProject->getDocument('databases', $databaseId); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } if (!$dbForProject->deleteDocument('databases', $databaseId)) { - throw new Exception('Failed to remove collection from DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove collection from DB'); } $dbForProject->deleteCachedCollection('databases' . $database->getInternalId()); @@ -510,7 +510,7 @@ App::post('/v1/databases/:databaseId/collections') $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collectionId = $collectionId == 'unique()' ? $dbForProject->getId() : $collectionId; @@ -531,9 +531,9 @@ App::post('/v1/databases/:databaseId/collections') $dbForProject->createCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); } catch (DuplicateException $th) { - throw new Exception('Collection already exists', 409, Exception::COLLECTION_ALREADY_EXISTS); + throw new Exception(Exception::COLLECTION_ALREADY_EXISTS); } catch (LimitException $th) { - throw new Exception('Collection limit exceeded', 400, Exception::COLLECTION_LIMIT_EXCEEDED); + throw new Exception(Exception::COLLECTION_LIMIT_EXCEEDED); } $audits @@ -581,7 +581,7 @@ App::get('/v1/databases/:databaseId/collections') $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } if (!empty($cursor)) { @@ -630,12 +630,12 @@ App::get('/v1/databases/:databaseId/collections/:collectionId') $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $usage @@ -670,13 +670,13 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/logs') $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collectionDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); $collection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $collectionDocument->getInternalId()); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $audit = new Audit($dbForProject); @@ -765,12 +765,12 @@ App::put('/v1/databases/:databaseId/collections/:collectionId') $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $read ??= $collection->getRead() ?? []; // By default inherit read permissions @@ -786,9 +786,9 @@ App::put('/v1/databases/:databaseId/collections/:collectionId') ->setAttribute('enabled', $enabled) ->setAttribute('search', implode(' ', [$collectionId, $name]))); } catch (AuthorizationException $exception) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } catch (StructureException $exception) { - throw new Exception('Bad structure. ' . $exception->getMessage(), 400, Exception::DOCUMENT_INVALID_STRUCTURE); + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, 'Bad structure. ' . $exception->getMessage()); } $audits @@ -833,17 +833,17 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId') $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } if (!$dbForProject->deleteDocument('database_' . $database->getInternalId(), $collectionId)) { - throw new Exception('Failed to remove collection from DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove collection from DB'); } $dbForProject->deleteCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); @@ -991,13 +991,13 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum') foreach ($elements as $element) { $length = \strlen($element); if ($length === 0) { - throw new Exception('Each enum element must not be empty', 400, Exception::ATTRIBUTE_VALUE_INVALID); + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Each enum element must not be empty'); } $size = ($length > $size) ? $length : $size; } if (!is_null($default) && !in_array($default, $elements)) { - throw new Exception('Default value not found in elements', 400, Exception::ATTRIBUTE_VALUE_INVALID); + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Default value not found in elements'); } $attribute = createAttribute($databaseId, $collectionId, new Document([ @@ -1128,13 +1128,13 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/intege $max = (is_null($max)) ? PHP_INT_MAX : \intval($max); if ($min > $max) { - throw new Exception('Minimum value must be lesser than maximum value', 400, Exception::ATTRIBUTE_VALUE_INVALID); + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum value must be lesser than maximum value'); } $validator = new Range($min, $max, Database::VAR_INTEGER); if (!is_null($default) && !$validator->isValid($default)) { - throw new Exception($validator->getDescription(), 400, Exception::ATTRIBUTE_VALUE_INVALID); + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription()); } $size = $max > 2147483647 ? 8 : 4; // Automatically create BigInt depending on max value @@ -1197,7 +1197,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float' $max = (is_null($max)) ? PHP_FLOAT_MAX : \floatval($max); if ($min > $max) { - throw new Exception('Minimum value must be lesser than maximum value', 400, Exception::ATTRIBUTE_VALUE_INVALID); + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum value must be lesser than maximum value'); } // Ensure default value is a float @@ -1208,7 +1208,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float' $validator = new Range($min, $max, Database::VAR_FLOAT); if (!is_null($default) && !$validator->isValid($default)) { - throw new Exception($validator->getDescription(), 400, Exception::ATTRIBUTE_VALUE_INVALID); + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription()); } $attribute = createAttribute($databaseId, $collectionId, new Document([ @@ -1296,12 +1296,12 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes') $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $attributes = $collection->getAttribute('attributes'); @@ -1347,19 +1347,19 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $attribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $collection->getInternalId() . '_' . $key); if ($attribute->isEmpty()) { - throw new Exception('Attribute not found', 404, Exception::ATTRIBUTE_NOT_FOUND); + throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); } // Select response model based on type and format @@ -1413,18 +1413,18 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $dbForProject->getDocument('database_' . $db->getInternalId(), $collectionId); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $attribute = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key); if ($attribute->isEmpty()) { - throw new Exception('Attribute not found', 404, Exception::ATTRIBUTE_NOT_FOUND); + throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); } // Only update status if removing available attribute @@ -1511,12 +1511,12 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $dbForProject->getDocument('database_' . $db->getInternalId(), $collectionId); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $count = $dbForProject->count('indexes', [ @@ -1527,7 +1527,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') $limit = 64 - MariaDB::getNumberOfDefaultIndexes(); if ($count >= $limit) { - throw new Exception('Index limit exceeded', 400, Exception::INDEX_LIMIT_EXCEEDED); + throw new Exception(Exception::INDEX_LIMIT_EXCEEDED); } // Convert Document[] to array of attribute metadata @@ -1573,7 +1573,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') $attributeIndex = \array_search($attribute, array_column($oldAttributes, 'key')); if ($attributeIndex === false) { - throw new Exception('Unknown attribute: ' . $attribute, 400, Exception::ATTRIBUTE_UNKNOWN); + throw new Exception(Exception::ATTRIBUTE_UNKNOWN, 'Unknown attribute: ' . $attribute); } $attributeStatus = $oldAttributes[$attributeIndex]['status']; @@ -1582,7 +1582,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') // ensure attribute is available if ($attributeStatus !== 'available') { - throw new Exception('Attribute not available: ' . $oldAttributes[$attributeIndex]['key'], 400, Exception::ATTRIBUTE_NOT_AVAILABLE); + throw new Exception(Exception::ATTRIBUTE_NOT_AVAILABLE, 'Attribute not available: ' . $oldAttributes[$attributeIndex]['key']); } // set attribute size as index length only for strings @@ -1604,7 +1604,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') 'orders' => $orders, ])); } catch (DuplicateException $th) { - throw new Exception('Index already exists', 409, Exception::INDEX_ALREADY_EXISTS); + throw new Exception(Exception::INDEX_ALREADY_EXISTS); } $dbForProject->deleteCachedDocument('database_' . $db->getInternalId(), $collectionId); @@ -1659,12 +1659,12 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes') $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $indexes = $collection->getAttribute('indexes'); @@ -1702,12 +1702,12 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $indexes = $collection->getAttribute('indexes'); @@ -1716,7 +1716,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') $indexIndex = array_search($key, array_column($indexes, 'key')); if ($indexIndex === false) { - throw new Exception('Index not found', 404, Exception::INDEX_NOT_FOUND); + throw new Exception(Exception::INDEX_NOT_FOUND); } $index = new Document([\array_merge($indexes[$indexIndex], [ @@ -1756,18 +1756,18 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $dbForProject->getDocument('database_' . $db->getInternalId(), $collectionId); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $index = $dbForProject->getDocument('indexes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key); if (empty($index->getId())) { - throw new Exception('Index not found', 404, Exception::INDEX_NOT_FOUND); + throw new Exception(Exception::INDEX_NOT_FOUND); } // Only update status if removing available index @@ -1836,16 +1836,16 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array if (empty($data)) { - throw new Exception('Missing payload', 400, Exception::DOCUMENT_MISSING_PAYLOAD); + throw new Exception(Exception::DOCUMENT_MISSING_PAYLOAD); } if (isset($data['$id'])) { - throw new Exception('$id is not allowed for creating new documents, try update instead', 400, Exception::DOCUMENT_INVALID_STRUCTURE); + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, '$id is not allowed for creating new documents, try update instead'); } /** @@ -1857,7 +1857,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } } @@ -1865,7 +1865,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') if ($collection->getAttribute('permission') === 'collection') { $validator = new Authorization('write'); if (!$validator->isValid($collection->getWrite())) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } } @@ -1881,12 +1881,12 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') foreach ($data['$read'] as $read) { if (!Authorization::isRole($read)) { // TODO: Isn't this a 401: Unauthorized Error ? - throw new Exception('Read permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED, 'Read permissions must be one of: (' . \implode(', ', $roles) . ')'); } } foreach ($data['$write'] as $write) { if (!Authorization::isRole($write)) { - throw new Exception('Write permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED, 'Write permissions must be one of: (' . \implode(', ', $roles) . ')'); } } } @@ -1900,9 +1900,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') } $document->setAttribute('$collection', $collectionId); } catch (StructureException $exception) { - throw new Exception($exception->getMessage(), 400, Exception::DOCUMENT_INVALID_STRUCTURE); + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $exception->getMessage()); } catch (DuplicateException $exception) { - throw new Exception('Document already exists', 409, Exception::DOCUMENT_ALREADY_EXISTS); + throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); } $events @@ -1958,7 +1958,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } /** * Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions. @@ -1969,7 +1969,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } } @@ -1977,7 +1977,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') if ($collection->getAttribute('permission') === 'collection') { $validator = new Authorization('read'); if (!$validator->isValid($collection->getRead())) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } } @@ -2066,7 +2066,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } /** * Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions. @@ -2075,7 +2075,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } } @@ -2083,7 +2083,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen if ($collection->getAttribute('permission') === 'collection') { $validator = new Authorization('read'); if (!$validator->isValid($collection->getRead())) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } } @@ -2100,7 +2100,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen $document->setAttribute('$collection', $collectionId); if ($document->isEmpty()) { - throw new Exception('No document found', 404, Exception::DOCUMENT_NOT_FOUND); + throw new Exception(Exception::DOCUMENT_NOT_FOUND); } $usage @@ -2138,18 +2138,18 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $document = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId); if ($document->isEmpty()) { - throw new Exception('No document found', 404, Exception::DOCUMENT_NOT_FOUND); + throw new Exception(Exception::DOCUMENT_NOT_FOUND); } $audit = new Audit($dbForProject); @@ -2236,7 +2236,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } /** * Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions. @@ -2245,7 +2245,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } } @@ -2253,7 +2253,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum if ($collection->getAttribute('permission') === 'collection') { $validator = new Authorization('write'); if (!$validator->isValid($collection->getWrite())) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } $document = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); @@ -2263,17 +2263,17 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum if ($document->isEmpty()) { - throw new Exception('Document not found', 404, Exception::DOCUMENT_NOT_FOUND); + throw new Exception(Exception::DOCUMENT_NOT_FOUND); } $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array if (empty($data) && empty($read) && empty($write)) { - throw new Exception('Missing payload or read/write permissions', 400, Exception::DOCUMENT_MISSING_PAYLOAD); + throw new Exception(Exception::DOCUMENT_MISSING_PAYLOAD, 'Missing payload or read/write permissions'); } if (!\is_array($data)) { - throw new Exception('Data param should be a valid JSON object', 400, Exception::DOCUMENT_INVALID_STRUCTURE); + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, 'Data param should be a valid JSON object'); } $data = \array_merge($document->getArrayCopy(), $data); @@ -2291,14 +2291,14 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum if (!is_null($read)) { foreach ($data['$read'] as $read) { if (!Authorization::isRole($read)) { - throw new Exception('Read permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED, 'Read permissions must be one of: (' . \implode(', ', $roles) . ')'); } } } if (!is_null($write)) { foreach ($data['$write'] as $write) { if (!Authorization::isRole($write)) { - throw new Exception('Write permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED, 'Write permissions must be one of: (' . \implode(', ', $roles) . ')'); } } } @@ -2316,11 +2316,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum */ $document->setAttribute('$collection', $collectionId); } catch (AuthorizationException $exception) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } catch (DuplicateException $exception) { - throw new Exception('Document already exists', 409, Exception::DOCUMENT_ALREADY_EXISTS); + throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); } catch (StructureException $exception) { - throw new Exception($exception->getMessage(), 400, Exception::DOCUMENT_INVALID_STRUCTURE); + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $exception->getMessage()); } $events @@ -2372,7 +2372,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } /** * Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions. @@ -2381,7 +2381,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } } @@ -2389,7 +2389,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu if ($collection->getAttribute('permission') === 'collection') { $validator = new Authorization('write'); if (!$validator->isValid($collection->getWrite())) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } } @@ -2401,7 +2401,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu } if ($document->isEmpty()) { - throw new Exception('No document found', 404, Exception::DOCUMENT_NOT_FOUND); + throw new Exception(Exception::DOCUMENT_NOT_FOUND); } if ($collection->getAttribute('permission') === 'collection') { @@ -2692,7 +2692,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage') $collection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $collectionDocument->getInternalId()); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $usage = []; diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 21b827042d..626b91a748 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -172,7 +172,7 @@ App::get('/v1/functions/:functionId') $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } $response->dynamic($function, Response::MODEL_FUNCTION); @@ -197,7 +197,7 @@ App::get('/v1/functions/:functionId/usage') $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } $usage = []; @@ -305,7 +305,7 @@ App::put('/v1/functions/:functionId') $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } $original = $function->getAttribute('schedule', ''); @@ -365,19 +365,19 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId') $build = $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', '')); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } if ($deployment->isEmpty()) { - throw new Exception('Deployment not found', 404, Exception::DEPLOYMENT_NOT_FOUND); + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); } if ($build->isEmpty()) { - throw new Exception('Build not found', 404, Exception::BUILD_NOT_FOUND); + throw new Exception(Exception::BUILD_NOT_FOUND); } if ($build->getAttribute('status') !== 'ready') { - throw new Exception('Build not ready', 400, Exception::BUILD_NOT_READY); + throw new Exception(Exception::BUILD_NOT_READY); } $schedule = $function->getAttribute('schedule', ''); @@ -426,11 +426,11 @@ App::delete('/v1/functions/:functionId') $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } if (!$dbForProject->deleteDocument('functions', $function->getId())) { - throw new Exception('Failed to remove function from DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove function from DB'); } $deletes @@ -473,7 +473,7 @@ App::post('/v1/functions/:functionId/deployments') $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } $file = $request->getFiles('code'); @@ -482,7 +482,7 @@ App::post('/v1/functions/:functionId/deployments') $upload = new Upload(); if (empty($file)) { - throw new Exception('No file sent', 400, Exception::STORAGE_FILE_EMPTY); + throw new Exception(Exception::STORAGE_FILE_EMPTY, 'No file sent'); } // Make sure we handle a single file and multiple files the same way @@ -491,7 +491,7 @@ App::post('/v1/functions/:functionId/deployments') $fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size']; if (!$fileExt->isValid($file['name'])) { // Check if file type is allowed - throw new Exception('File type not allowed', 400, Exception::STORAGE_FILE_TYPE_UNSUPPORTED); + throw new Exception(Exception::STORAGE_FILE_TYPE_UNSUPPORTED); } $contentRange = $request->getHeader('content-range'); @@ -505,7 +505,7 @@ App::post('/v1/functions/:functionId/deployments') $fileSize = $request->getContentRangeSize(); $deploymentId = $request->getHeader('x-appwrite-id', $deploymentId); if (is_null($start) || is_null($end) || is_null($fileSize)) { - throw new Exception('Invalid content-range header', 400, Exception::STORAGE_INVALID_CONTENT_RANGE); + throw new Exception(Exception::STORAGE_INVALID_CONTENT_RANGE); } if ($end === $fileSize) { @@ -519,11 +519,11 @@ App::post('/v1/functions/:functionId/deployments') } if (!$fileSizeValidator->isValid($fileSize)) { // Check if file size is exceeding allowed limit - throw new Exception('File size not allowed', 400, Exception::STORAGE_INVALID_FILE_SIZE); + throw new Exception(Exception::STORAGE_INVALID_FILE_SIZE); } if (!$upload->isValid($fileTmpName)) { - throw new Exception('Invalid file', 403, Exception::STORAGE_INVALID_FILE); + throw new Exception(Exception::STORAGE_INVALID_FILE); } // Save to storage @@ -544,7 +544,7 @@ App::post('/v1/functions/:functionId/deployments') $chunksUploaded = $deviceFunctions->upload($fileTmpName, $path, $chunk, $chunks, $metadata); if (empty($chunksUploaded)) { - throw new Exception('Failed moving file', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed moving file'); } $activate = (bool) filter_var($activate, FILTER_VALIDATE_BOOLEAN); @@ -651,7 +651,7 @@ App::get('/v1/functions/:functionId/deployments') $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } if (!empty($cursor)) { @@ -707,17 +707,17 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId') $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } $deployment = $dbForProject->getDocument('deployments', $deploymentId); if ($deployment->getAttribute('resourceId') !== $function->getId()) { - throw new Exception('Deployment not found', 404, Exception::DEPLOYMENT_NOT_FOUND); + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); } if ($deployment->isEmpty()) { - throw new Exception('Deployment not found', 404, Exception::DEPLOYMENT_NOT_FOUND); + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); } $response->dynamic($deployment, Response::MODEL_DEPLOYMENT); @@ -746,21 +746,21 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId') $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } $deployment = $dbForProject->getDocument('deployments', $deploymentId); if ($deployment->isEmpty()) { - throw new Exception('Deployment not found', 404, Exception::DEPLOYMENT_NOT_FOUND); + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); } if ($deployment->getAttribute('resourceId') !== $function->getId()) { - throw new Exception('Deployment not found', 404, Exception::DEPLOYMENT_NOT_FOUND); + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); } if ($deviceFunctions->delete($deployment->getAttribute('path', ''))) { if (!$dbForProject->deleteDocument('deployments', $deployment->getId())) { - throw new Exception('Failed to remove deployment from DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove deployment from DB'); } } @@ -812,7 +812,7 @@ App::post('/v1/functions/:functionId/executions') $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } $runtimes = Config::getParam('runtimes', []); @@ -820,27 +820,27 @@ App::post('/v1/functions/:functionId/executions') $runtime = (isset($runtimes[$function->getAttribute('runtime', '')])) ? $runtimes[$function->getAttribute('runtime', '')] : null; if (\is_null($runtime)) { - throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported', 400, Exception::FUNCTION_RUNTIME_UNSUPPORTED); + throw new Exception(Exception::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); } $deployment = Authorization::skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', ''))); if ($deployment->getAttribute('resourceId') !== $function->getId()) { - throw new Exception('Deployment not found. Create a deployment before trying to execute a function', 404, Exception::DEPLOYMENT_NOT_FOUND); + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); } if ($deployment->isEmpty()) { - throw new Exception('Deployment not found. Create a deployment before trying to execute a function', 404, Exception::DEPLOYMENT_NOT_FOUND); + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); } /** Check if build has completed */ $build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''))); if ($build->isEmpty()) { - throw new Exception('Build not found', 404, Exception::BUILD_NOT_FOUND); + throw new Exception(Exception::BUILD_NOT_FOUND); } if ($build->getAttribute('status') !== 'ready') { - throw new Exception('Build not ready', 400, Exception::BUILD_NOT_READY); + throw new Exception(Exception::BUILD_NOT_READY); } $validator = new Authorization('execute'); @@ -994,7 +994,7 @@ App::get('/v1/functions/:functionId/executions') $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } if (!empty($cursor)) { @@ -1042,17 +1042,17 @@ App::get('/v1/functions/:functionId/executions/:executionId') $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } $execution = $dbForProject->getDocument('executions', $executionId); if ($execution->getAttribute('functionId') !== $function->getId()) { - throw new Exception('Execution not found', 404, Exception::EXECUTION_NOT_FOUND); + throw new Exception(Exception::EXECUTION_NOT_FOUND); } if ($execution->isEmpty()) { - throw new Exception('Execution not found', 404, Exception::EXECUTION_NOT_FOUND); + throw new Exception(Exception::EXECUTION_NOT_FOUND); } $response->dynamic($execution, Response::MODEL_EXECUTION); @@ -1082,21 +1082,21 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId') $deployment = $dbForProject->getDocument('deployments', $deploymentId); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } if ($deployment->isEmpty()) { - throw new Exception('Deployment not found', 404, Exception::DEPLOYMENT_NOT_FOUND); + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); } $build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $buildId)); if ($build->isEmpty()) { - throw new Exception('Build not found', 404, Exception::BUILD_NOT_FOUND); + throw new Exception(Exception::BUILD_NOT_FOUND); } if ($build->getAttribute('status') !== 'failed') { - throw new Exception('Build not failed', 400, Exception::BUILD_IN_PROGRESS); + throw new Exception(Exception::BUILD_IN_PROGRESS, 'Build not failed'); } $events diff --git a/app/controllers/api/graphql.php b/app/controllers/api/graphql.php index e89ae17961..a048a77666 100644 --- a/app/controllers/api/graphql.php +++ b/app/controllers/api/graphql.php @@ -19,6 +19,6 @@ App::post('/v1/graphql') ->label('scope', 'public') ->action( function () { - throw new Exception('GraphQL support is coming soon!', 502, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'GraphQL support is coming soon!'); } ); diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index 9eb9ca580e..4384fcbcd5 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -73,7 +73,7 @@ App::get('/v1/health/db') $statement->execute(); } catch (Exception $_e) { - throw new Exception('Database is not available', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Database is not available'); } $output = [ @@ -104,7 +104,7 @@ App::get('/v1/health/cache') $redis = $utopia->getResource('cache'); if (!$redis->ping(true)) { - throw new Exception('Cache is not available', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Cache is not available'); } $output = [ @@ -160,7 +160,7 @@ App::get('/v1/health/time') $diff = ($timestamp - \time()); if ($diff > $gap || $diff < ($gap * -1)) { - throw new Exception('Server time gaps detected', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Server time gaps detected'); } $output = [ @@ -267,11 +267,11 @@ App::get('/v1/health/storage/local') $device = new Local($volume); if (!\is_readable($device->getRoot())) { - throw new Exception('Device ' . $key . ' dir is not readable', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Device ' . $key . ' dir is not readable'); } if (!\is_writable($device->getRoot())) { - throw new Exception('Device ' . $key . ' dir is not writable', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Device ' . $key . ' dir is not writable'); } } @@ -315,7 +315,7 @@ App::get('/v1/health/anti-virus') $output['version'] = @$antivirus->version(); $output['status'] = (@$antivirus->ping()) ? 'pass' : 'fail'; } catch (\Exception $e) { - throw new Exception('Antivirus is not available', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Antivirus is not available'); } } diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 86996f4a58..c96ddbaa41 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -34,7 +34,7 @@ use Utopia\Validator\WhiteList; App::init(function (Document $project) { if ($project->getId() !== 'console') { - throw new Exception('Access to this API is forbidden.', 401, Exception::GENERAL_ACCESS_FORBIDDEN); + throw new Exception(Exception::GENERAL_ACCESS_FORBIDDEN); } }, ['project'], 'projects'); @@ -68,7 +68,7 @@ App::post('/v1/projects') $team = $dbForConsole->getDocument('teams', $teamId); if ($team->isEmpty()) { - throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND); + throw new Exception(Exception::TEAM_NOT_FOUND); } $auth = Config::getParam('auth', []); @@ -220,7 +220,7 @@ App::get('/v1/projects/:projectId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $response->dynamic($project, Response::MODEL_PROJECT); @@ -247,7 +247,7 @@ App::get('/v1/projects/:projectId/usage') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $usage = []; @@ -364,7 +364,7 @@ App::patch('/v1/projects/:projectId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $project = $dbForConsole->updateDocument('projects', $project->getId(), $project @@ -403,7 +403,7 @@ App::patch('/v1/projects/:projectId/service') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $services = $project->getAttribute('services', []); @@ -435,7 +435,7 @@ App::patch('/v1/projects/:projectId/oauth2') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $providers = $project->getAttribute('authProviders', []); @@ -466,7 +466,7 @@ App::patch('/v1/projects/:projectId/auth/limit') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $auths = $project->getAttribute('auths', []); @@ -501,7 +501,7 @@ App::patch('/v1/projects/:projectId/auth/:method') $status = ($status === '1' || $status === 'true' || $status === 1 || $status === true); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $auths = $project->getAttribute('auths', []); @@ -530,13 +530,13 @@ App::delete('/v1/projects/:projectId') ->action(function (string $projectId, string $password, Response $response, Document $user, Database $dbForConsole, Delete $deletes) { if (!Auth::passwordVerify($password, $user->getAttribute('password'))) { // Double check user password - throw new Exception('Invalid credentials', 401, Exception::USER_INVALID_CREDENTIALS); + throw new Exception(Exception::USER_INVALID_CREDENTIALS); } $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $deletes @@ -545,11 +545,11 @@ App::delete('/v1/projects/:projectId') ; if (!$dbForConsole->deleteDocument('teams', $project->getAttribute('teamId', null))) { - throw new Exception('Failed to remove project team from DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove project team from DB'); } if (!$dbForConsole->deleteDocument('projects', $projectId)) { - throw new Exception('Failed to remove project from DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove project from DB'); } $response->noContent(); @@ -581,7 +581,7 @@ App::post('/v1/projects/:projectId/webhooks') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $security = (bool) filter_var($security, FILTER_VALIDATE_BOOLEAN); @@ -627,7 +627,7 @@ App::get('/v1/projects/:projectId/webhooks') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $webhooks = $dbForConsole->find('webhooks', [ @@ -659,7 +659,7 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $webhook = $dbForConsole->findOne('webhooks', [ @@ -668,7 +668,7 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId') ]); if ($webhook === false || $webhook->isEmpty()) { - throw new Exception('Webhook not found', 404, Exception::WEBHOOK_NOT_FOUND); + throw new Exception(Exception::WEBHOOK_NOT_FOUND); } $response->dynamic($webhook, Response::MODEL_WEBHOOK); @@ -699,7 +699,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $security = ($security === '1' || $security === 'true' || $security === 1 || $security === true); @@ -710,7 +710,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId') ]); if ($webhook === false || $webhook->isEmpty()) { - throw new Exception('Webhook not found', 404, Exception::WEBHOOK_NOT_FOUND); + throw new Exception(Exception::WEBHOOK_NOT_FOUND); } $webhook @@ -747,7 +747,7 @@ App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $webhook = $dbForConsole->findOne('webhooks', [ @@ -756,7 +756,7 @@ App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature') ]); if ($webhook === false || $webhook->isEmpty()) { - throw new Exception('Webhook not found', 404, Exception::WEBHOOK_NOT_FOUND); + throw new Exception(Exception::WEBHOOK_NOT_FOUND); } $webhook->setAttribute('signatureKey', \bin2hex(\random_bytes(64))); @@ -785,7 +785,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $webhook = $dbForConsole->findOne('webhooks', [ @@ -794,7 +794,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId') ]); if ($webhook === false || $webhook->isEmpty()) { - throw new Exception('Webhook not found', 404, Exception::WEBHOOK_NOT_FOUND); + throw new Exception(Exception::WEBHOOK_NOT_FOUND); } $dbForConsole->deleteDocument('webhooks', $webhook->getId()); @@ -827,7 +827,7 @@ App::post('/v1/projects/:projectId/keys') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $key = new Document([ @@ -868,7 +868,7 @@ App::get('/v1/projects/:projectId/keys') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $keys = $dbForConsole->find('keys', [ @@ -900,7 +900,7 @@ App::get('/v1/projects/:projectId/keys/:keyId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $key = $dbForConsole->findOne('keys', [ @@ -909,7 +909,7 @@ App::get('/v1/projects/:projectId/keys/:keyId') ]); if ($key === false || $key->isEmpty()) { - throw new Exception('Key not found', 404, Exception::KEY_NOT_FOUND); + throw new Exception(Exception::KEY_NOT_FOUND); } $response->dynamic($key, Response::MODEL_KEY); @@ -937,7 +937,7 @@ App::put('/v1/projects/:projectId/keys/:keyId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $key = $dbForConsole->findOne('keys', [ @@ -946,7 +946,7 @@ App::put('/v1/projects/:projectId/keys/:keyId') ]); if ($key === false || $key->isEmpty()) { - throw new Exception('Key not found', 404, Exception::KEY_NOT_FOUND); + throw new Exception(Exception::KEY_NOT_FOUND); } $key @@ -980,7 +980,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $key = $dbForConsole->findOne('keys', [ @@ -989,7 +989,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId') ]); if ($key === false || $key->isEmpty()) { - throw new Exception('Key not found', 404, Exception::KEY_NOT_FOUND); + throw new Exception(Exception::KEY_NOT_FOUND); } $dbForConsole->deleteDocument('keys', $key->getId()); @@ -1023,7 +1023,7 @@ App::post('/v1/projects/:projectId/platforms') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $platform = new Document([ @@ -1065,7 +1065,7 @@ App::get('/v1/projects/:projectId/platforms') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $platforms = $dbForConsole->find('platforms', [ @@ -1097,7 +1097,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $platform = $dbForConsole->findOne('platforms', [ @@ -1106,7 +1106,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId') ]); if ($platform === false || $platform->isEmpty()) { - throw new Exception('Platform not found', 404, Exception::PLATFORM_NOT_FOUND); + throw new Exception(Exception::PLATFORM_NOT_FOUND); } $response->dynamic($platform, Response::MODEL_PLATFORM); @@ -1134,7 +1134,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $platform = $dbForConsole->findOne('platforms', [ @@ -1143,7 +1143,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId') ]); if ($platform === false || $platform->isEmpty()) { - throw new Exception('Platform not found', 404, Exception::PLATFORM_NOT_FOUND); + throw new Exception(Exception::PLATFORM_NOT_FOUND); } $platform @@ -1178,7 +1178,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $platform = $dbForConsole->findOne('platforms', [ @@ -1187,7 +1187,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId') ]); if ($platform === false || $platform->isEmpty()) { - throw new Exception('Platform not found', 404, Exception::PLATFORM_NOT_FOUND); + throw new Exception(Exception::PLATFORM_NOT_FOUND); } $dbForConsole->deleteDocument('platforms', $platformId); @@ -1218,7 +1218,7 @@ App::post('/v1/projects/:projectId/domains') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $document = $dbForConsole->findOne('domains', [ @@ -1227,13 +1227,13 @@ App::post('/v1/projects/:projectId/domains') ]); if ($document && !$document->isEmpty()) { - throw new Exception('Domain already exists', 409, Exception::DOMAIN_ALREADY_EXISTS); + throw new Exception(Exception::DOMAIN_ALREADY_EXISTS); } $target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', '')); if (!$target->isKnown() || $target->isTest()) { - throw new Exception('Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.'); } $domain = new Domain($domain); @@ -1278,7 +1278,7 @@ App::get('/v1/projects/:projectId/domains') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $domains = $dbForConsole->find('domains', [ @@ -1310,7 +1310,7 @@ App::get('/v1/projects/:projectId/domains/:domainId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $domain = $dbForConsole->findOne('domains', [ @@ -1319,7 +1319,7 @@ App::get('/v1/projects/:projectId/domains/:domainId') ]); if ($domain === false || $domain->isEmpty()) { - throw new Exception('Domain not found', 404, Exception::DOMAIN_NOT_FOUND); + throw new Exception(Exception::DOMAIN_NOT_FOUND); } $response->dynamic($domain, Response::MODEL_DOMAIN); @@ -1344,7 +1344,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $domain = $dbForConsole->findOne('domains', [ @@ -1353,13 +1353,13 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') ]); if ($domain === false || $domain->isEmpty()) { - throw new Exception('Domain not found', 404, Exception::DOMAIN_NOT_FOUND); + throw new Exception(Exception::DOMAIN_NOT_FOUND); } $target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', '')); if (!$target->isKnown() || $target->isTest()) { - throw new Exception('Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.'); } if ($domain->getAttribute('verification') === true) { @@ -1369,7 +1369,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') $validator = new CNAME($target->get()); // Verify Domain with DNS records if (!$validator->isValid($domain->getAttribute('domain', ''))) { - throw new Exception('Failed to verify domain', 401, Exception::DOMAIN_VERIFICATION_FAILED); + throw new Exception(Exception::DOMAIN_VERIFICATION_FAILED); } @@ -1404,7 +1404,7 @@ App::delete('/v1/projects/:projectId/domains/:domainId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $domain = $dbForConsole->findOne('domains', [ @@ -1413,7 +1413,7 @@ App::delete('/v1/projects/:projectId/domains/:domainId') ]); if ($domain === false || $domain->isEmpty()) { - throw new Exception('Domain not found', 404, Exception::DOMAIN_NOT_FOUND); + throw new Exception(Exception::DOMAIN_NOT_FOUND); } $dbForConsole->deleteDocument('domains', $domain->getId()); diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index b62aa750db..c2e13b683d 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -74,7 +74,7 @@ App::post('/v1/storage/buckets') try { $files = Config::getParam('collections', [])['files'] ?? []; if (empty($files)) { - throw new Exception('Files collection is not configured.', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Files collection is not configured.'); } $attributes = []; @@ -123,7 +123,7 @@ App::post('/v1/storage/buckets') $dbForProject->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes); } catch (Duplicate $th) { - throw new Exception('Bucket already exists', 409, Exception::STORAGE_BUCKET_ALREADY_EXISTS); + throw new Exception(Exception::STORAGE_BUCKET_ALREADY_EXISTS); } $audits @@ -201,7 +201,7 @@ App::get('/v1/storage/buckets/:bucketId') $bucket = $dbForProject->getDocument('buckets', $bucketId); if ($bucket->isEmpty()) { - throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $usage->setParam('storage.buckets.read', 1); @@ -240,7 +240,7 @@ App::put('/v1/storage/buckets/:bucketId') $bucket = $dbForProject->getDocument('buckets', $bucketId); if ($bucket->isEmpty()) { - throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $read ??= $bucket->getAttribute('$read', []); // By default inherit read permissions @@ -298,11 +298,11 @@ App::delete('/v1/storage/buckets/:bucketId') $bucket = $dbForProject->getDocument('buckets', $bucketId); if ($bucket->isEmpty()) { - throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } if (!$dbForProject->deleteDocument('buckets', $bucketId)) { - throw new Exception('Failed to remove project from DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove project from DB'); } $deletes @@ -361,7 +361,7 @@ App::post('/v1/storage/buckets/:bucketId/files') $bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN) ) { - throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } // Check bucket permissions when enforced @@ -369,7 +369,7 @@ App::post('/v1/storage/buckets/:bucketId/files') if ($permissionBucket) { $validator = new Authorization('write'); if (!$validator->isValid($bucket->getWrite())) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } } @@ -382,12 +382,12 @@ App::post('/v1/storage/buckets/:bucketId/files') if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) { foreach ($read as $role) { if (!Authorization::isRole($role)) { - throw new Exception('Read permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED, 'Read permissions must be one of: (' . \implode(', ', $roles) . ')'); } } foreach ($write as $role) { if (!Authorization::isRole($role)) { - throw new Exception('Write permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED, 'Write permissions must be one of: (' . \implode(', ', $roles) . ')'); } } } @@ -402,12 +402,12 @@ App::post('/v1/storage/buckets/:bucketId/files') $maximumFileSize = $bucket->getAttribute('maximumFileSize', 0); if ($maximumFileSize > (int) App::getEnv('_APP_STORAGE_LIMIT', 0)) { - throw new Exception('Maximum bucket file size is larger than _APP_STORAGE_LIMIT', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Maximum bucket file size is larger than _APP_STORAGE_LIMIT'); } $file = $request->getFiles('file'); if (empty($file)) { - throw new Exception('No file sent', 400, Exception::STORAGE_FILE_EMPTY); + throw new Exception(Exception::STORAGE_FILE_EMPTY); } // Make sure we handle a single file and multiple files the same way @@ -426,7 +426,7 @@ App::post('/v1/storage/buckets/:bucketId/files') $fileSize = $request->getContentRangeSize(); $fileId = $request->getHeader('x-appwrite-id', $fileId); if (is_null($start) || is_null($end) || is_null($fileSize)) { - throw new Exception('Invalid content-range header', 400, Exception::STORAGE_INVALID_CONTENT_RANGE); + throw new Exception(Exception::STORAGE_INVALID_CONTENT_RANGE); } if ($end === $fileSize) { @@ -446,18 +446,18 @@ App::post('/v1/storage/buckets/:bucketId/files') $allowedFileExtensions = $bucket->getAttribute('allowedFileExtensions', []); $fileExt = new FileExt($allowedFileExtensions); if (!empty($allowedFileExtensions) && !$fileExt->isValid($fileName)) { - throw new Exception('File extension not allowed', 400, Exception::STORAGE_FILE_TYPE_UNSUPPORTED); + throw new Exception(Exception::STORAGE_FILE_TYPE_UNSUPPORTED); } // Check if file size is exceeding allowed limit $fileSizeValidator = new FileSize($maximumFileSize); if (!$fileSizeValidator->isValid($fileSize)) { - throw new Exception('File size not allowed', 400, Exception::STORAGE_INVALID_FILE_SIZE); + throw new Exception(Exception::STORAGE_INVALID_FILE_SIZE); } $upload = new Upload(); if (!$upload->isValid($fileTmpName)) { - throw new Exception('Invalid file', 403, Exception::STORAGE_INVALID_FILE); + throw new Exception(Exception::STORAGE_INVALID_FILE); } // Save to storage @@ -484,7 +484,7 @@ App::post('/v1/storage/buckets/:bucketId/files') $chunksUploaded = $deviceFiles->upload($fileTmpName, $path, $chunk, $chunks, $metadata); if (empty($chunksUploaded)) { - throw new Exception('Failed uploading file', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed uploading file'); } $read = (is_null($read) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $read ?? []; @@ -498,7 +498,7 @@ App::post('/v1/storage/buckets/:bucketId/files') if (!$antivirus->fileScan($path)) { $deviceFiles->delete($path); - throw new Exception('Invalid file', 400, Exception::STORAGE_INVALID_FILE); + throw new Exception(Exception::STORAGE_INVALID_FILE); } } @@ -522,7 +522,7 @@ App::post('/v1/storage/buckets/:bucketId/files') if (!empty($data)) { if (!$deviceFiles->write($path, $data, $mimeType)) { - throw new Exception('Failed to save file', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to save file'); } } @@ -589,9 +589,9 @@ App::post('/v1/storage/buckets/:bucketId/files') } } } catch (StructureException $exception) { - throw new Exception($exception->getMessage(), 400, Exception::DOCUMENT_INVALID_STRUCTURE); + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $exception->getMessage()); } catch (DuplicateException $exception) { - throw new Exception('Document already exists', 409, Exception::DOCUMENT_ALREADY_EXISTS); + throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); } $audits @@ -641,9 +641,9 @@ App::post('/v1/storage/buckets/:bucketId/files') } } } catch (StructureException $exception) { - throw new Exception($exception->getMessage(), 400, Exception::DOCUMENT_INVALID_STRUCTURE); + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $exception->getMessage()); } catch (DuplicateException $exception) { - throw new Exception('Document already exists', 409, Exception::DOCUMENT_ALREADY_EXISTS); + throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); } } @@ -690,14 +690,14 @@ App::get('/v1/storage/buckets/:bucketId/files') $bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN) ) { - throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } // Check bucket permissions when enforced if ($bucket->getAttribute('permission') === 'bucket') { $validator = new Authorization('read'); if (!$validator->isValid($bucket->getRead())) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } } @@ -768,14 +768,14 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId') $bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN) ) { - throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } // Check bucket permissions when enforced if ($bucket->getAttribute('permission') === 'bucket') { $validator = new Authorization('read'); if (!$validator->isValid($bucket->getRead())) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } } @@ -786,7 +786,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId') } if ($file->isEmpty() || $file->getAttribute('bucketId') !== $bucketId) { - throw new Exception('File not found', 404, Exception::STORAGE_FILE_NOT_FOUND); + throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); } $usage @@ -833,7 +833,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') ->action(function (string $bucketId, string $fileId, int $width, int $height, string $gravity, int $quality, int $borderWidth, string $borderColor, int $borderRadius, float $opacity, int $rotation, string $background, string $output, Request $request, Response $response, Document $project, Database $dbForProject, Stats $usage, string $mode, Device $deviceFiles, Device $deviceLocal) { if (!\extension_loaded('imagick')) { - throw new Exception('Imagick extension is missing', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing'); } $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); @@ -842,14 +842,14 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') $bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN) ) { - throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } // Check bucket permissions when enforced if ($bucket->getAttribute('permission') === 'bucket') { $validator = new Authorization('read'); if (!$validator->isValid($bucket->getRead())) { - throw new Exception('Unauthorized permissions', 401, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND, 'Unauthorized permissions'); } } @@ -872,7 +872,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') } if ($file->isEmpty() || $file->getAttribute('bucketId') !== $bucketId) { - throw new Exception('File not found', 404, Exception::STORAGE_FILE_NOT_FOUND); + throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); } $path = $file->getAttribute('path'); @@ -901,7 +901,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') $compressor = new GZIP(); if (!$deviceFiles->exists($path)) { - throw new Exception('File not found', 404, Exception::STORAGE_FILE_NOT_FOUND); + throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); } $cache = new Cache(new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId() . DIRECTORY_SEPARATOR . $bucketId . DIRECTORY_SEPARATOR . $fileId)); // Limit file number or size @@ -1010,14 +1010,14 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') $bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN) ) { - throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } // Check bucket permissions when enforced if ($bucket->getAttribute('permission') === 'bucket') { $validator = new Authorization('read'); if (!$validator->isValid($bucket->getRead())) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } } @@ -1028,13 +1028,13 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') } if ($file->isEmpty() || $file->getAttribute('bucketId') !== $bucketId) { - throw new Exception('File not found', 404, Exception::STORAGE_FILE_NOT_FOUND); + throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); } $path = $file->getAttribute('path', ''); if (!$deviceFiles->exists($path)) { - throw new Exception('File not found in ' . $path, 404, Exception::STORAGE_FILE_NOT_FOUND); + throw new Exception(Exception::STORAGE_FILE_NOT_FOUND, 'File not found in ' . $path); } $usage @@ -1062,7 +1062,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') } if ($unit !== 'bytes' || $start >= $end || $end >= $size) { - throw new Exception('Invalid range', 416, Exception::STORAGE_INVALID_RANGE); + throw new Exception(Exception::STORAGE_INVALID_RANGE); } $response @@ -1149,14 +1149,14 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') $bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN) ) { - throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } // Check bucket permissions when enforced if ($bucket->getAttribute('permission') === 'bucket') { $validator = new Authorization('read'); if (!$validator->isValid($bucket->getRead())) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } } @@ -1169,13 +1169,13 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') $mimes = Config::getParam('storage-mimes'); if ($file->isEmpty() || $file->getAttribute('bucketId') !== $bucketId) { - throw new Exception('File not found', 404, Exception::STORAGE_FILE_NOT_FOUND); + throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); } $path = $file->getAttribute('path', ''); if (!$deviceFiles->exists($path)) { - throw new Exception('File not found in ' . $path, 404, Exception::STORAGE_FILE_NOT_FOUND); + throw new Exception(Exception::STORAGE_FILE_NOT_FOUND, 'File not found in ' . $path); } $compressor = new GZIP(); @@ -1208,7 +1208,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') } if ($unit != 'bytes' || $start >= $end || $end >= $size) { - throw new Exception('Invalid range', 416, Exception::STORAGE_INVALID_RANGE); + throw new Exception(Exception::STORAGE_INVALID_RANGE); } $response @@ -1308,12 +1308,12 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) { foreach ($read as $role) { if (!Authorization::isRole($role)) { - throw new Exception('Read permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED, 'Read permissions must be one of: (' . \implode(', ', $roles) . ')'); } } foreach ($write as $role) { if (!Authorization::isRole($role)) { - throw new Exception('Write permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED, 'Write permissions must be one of: (' . \implode(', ', $roles) . ')'); } } } @@ -1322,14 +1322,14 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') $bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN) ) { - throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } // Check bucket permissions when enforced if ($bucket->getAttribute('permission') === 'bucket') { $validator = new Authorization('write'); if (!$validator->isValid($bucket->getWrite())) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } } @@ -1340,7 +1340,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') } if ($file->isEmpty() || $file->getAttribute('bucketId') !== $bucketId) { - throw new Exception('File not found', 404, Exception::STORAGE_FILE_NOT_FOUND); + throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); } $file @@ -1399,14 +1399,14 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') $bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN) ) { - throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } // Check bucket permissions when enforced if ($bucket->getAttribute('permission') === 'bucket') { $validator = new Authorization('write'); if (!$validator->isValid($bucket->getWrite())) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } } @@ -1417,7 +1417,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') } if ($file->isEmpty() || $file->getAttribute('bucketId') !== $bucketId) { - throw new Exception('File not found', 404, Exception::STORAGE_FILE_NOT_FOUND); + throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); } $deviceDeleted = false; @@ -1442,10 +1442,10 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') $deleted = $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId); } if (!$deleted) { - throw new Exception('Failed to remove file from DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove file from DB'); } } else { - throw new Exception('Failed to delete file from device', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to delete file from device'); } $audits->setResource('file/' . $file->getId()); @@ -1594,7 +1594,7 @@ App::get('/v1/storage/:bucketId/usage') $bucket = $dbForProject->getDocument('buckets', $bucketId); if ($bucket->isEmpty()) { - throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $usage = []; diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 383bcac149..2aa7c81e63 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -167,7 +167,7 @@ App::get('/v1/teams/:teamId') $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { - throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND); + throw new Exception(Exception::TEAM_NOT_FOUND); } $response->dynamic($team, Response::MODEL_TEAM); @@ -196,7 +196,7 @@ App::put('/v1/teams/:teamId') $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { - throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND); + throw new Exception(Exception::TEAM_NOT_FOUND); } $team = $dbForProject->updateDocument('teams', $team->getId(), $team @@ -231,7 +231,7 @@ App::delete('/v1/teams/:teamId') $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { - throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND); + throw new Exception(Exception::TEAM_NOT_FOUND); } $memberships = $dbForProject->find('memberships', [ @@ -241,12 +241,12 @@ App::delete('/v1/teams/:teamId') // TODO delete all members individually from the user object foreach ($memberships as $membership) { if (!$dbForProject->deleteDocument('memberships', $membership->getId())) { - throw new Exception('Failed to remove membership for team from DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove membership for team from DB'); } } if (!$dbForProject->deleteDocument('teams', $teamId)) { - throw new Exception('Failed to remove team from DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove team from DB'); } $deletes @@ -300,7 +300,7 @@ App::post('/v1/teams/:teamId/memberships') $isAppUser = Auth::isAppUser(Authorization::getRoles()); if (!$isPrivilegedUser && !$isAppUser && empty(App::getEnv('_APP_SMTP_HOST'))) { - throw new Exception('SMTP Disabled', 503, Exception::GENERAL_SMTP_DISABLED); + throw new Exception(Exception::GENERAL_SMTP_DISABLED); } $email = \strtolower($email); @@ -308,7 +308,7 @@ App::post('/v1/teams/:teamId/memberships') $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { - throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND); + throw new Exception(Exception::TEAM_NOT_FOUND); } $invitee = $dbForProject->findOne('users', [new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address @@ -320,7 +320,7 @@ App::post('/v1/teams/:teamId/memberships') $total = $dbForProject->count('users', [], APP_LIMIT_USERS); if ($total >= $limit) { - throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501, Exception::USER_COUNT_EXCEEDED); + throw new Exception(Exception::USER_COUNT_EXCEEDED); } } @@ -350,14 +350,14 @@ App::post('/v1/teams/:teamId/memberships') 'search' => implode(' ', [$userId, $email, $name]) ]))); } catch (Duplicate $th) { - throw new Exception('Account already exists', 409, Exception::USER_ALREADY_EXISTS); + throw new Exception(Exception::USER_ALREADY_EXISTS); } } $isOwner = Authorization::isRole('team:' . $team->getId() . '/owner'); if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server) - throw new Exception('User is not allowed to send invitations for this team', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED, 'User is not allowed to send invitations for this team'); } $secret = Auth::tokenGenerator(); @@ -383,7 +383,7 @@ App::post('/v1/teams/:teamId/memberships') try { $membership = Authorization::skip(fn() => $dbForProject->createDocument('memberships', $membership)); } catch (Duplicate $th) { - throw new Exception('User is already a member of this team', 409, Exception::TEAM_INVITE_ALREADY_EXISTS); + throw new Exception(Exception::TEAM_INVITE_ALREADY_EXISTS); } $team->setAttribute('total', $team->getAttribute('total', 0) + 1); $team = Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team)); @@ -393,7 +393,7 @@ App::post('/v1/teams/:teamId/memberships') try { $membership = $dbForProject->createDocument('memberships', $membership); } catch (Duplicate $th) { - throw new Exception('User has already been invited or is already a member of this team', 409, Exception::TEAM_INVITE_ALREADY_EXISTS); + throw new Exception(Exception::TEAM_INVITE_ALREADY_EXISTS); } } @@ -458,7 +458,7 @@ App::get('/v1/teams/:teamId/memberships') $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { - throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND); + throw new Exception(Exception::TEAM_NOT_FOUND); } if (!empty($cursor)) { @@ -531,13 +531,13 @@ App::get('/v1/teams/:teamId/memberships/:membershipId') $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { - throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND); + throw new Exception(Exception::TEAM_NOT_FOUND); } $membership = $dbForProject->getDocument('memberships', $membershipId); if ($membership->isEmpty() || empty($membership->getAttribute('userId'))) { - throw new Exception('Membership not found', 404, Exception::MEMBERSHIP_NOT_FOUND); + throw new Exception(Exception::MEMBERSHIP_NOT_FOUND); } $user = $dbForProject->getDocument('users', $membership->getAttribute('userId')); @@ -576,17 +576,17 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId') $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { - throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND); + throw new Exception(Exception::TEAM_NOT_FOUND); } $membership = $dbForProject->getDocument('memberships', $membershipId); if ($membership->isEmpty()) { - throw new Exception('Membership not found', 404, Exception::MEMBERSHIP_NOT_FOUND); + throw new Exception(Exception::MEMBERSHIP_NOT_FOUND); } $profile = $dbForProject->getDocument('users', $membership->getAttribute('userId')); if ($profile->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); @@ -594,7 +594,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId') $isOwner = Authorization::isRole('team:' . $team->getId() . '/owner'); if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server) - throw new Exception('User is not allowed to modify roles', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } /** @@ -652,25 +652,25 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') $membership = $dbForProject->getDocument('memberships', $membershipId); if ($membership->isEmpty()) { - throw new Exception('Membership not found', 404, Exception::MEMBERSHIP_NOT_FOUND); + throw new Exception(Exception::MEMBERSHIP_NOT_FOUND); } if ($membership->getAttribute('teamId') !== $teamId) { - throw new Exception('Team IDs don\'t match', 404, Exception::TEAM_MEMBERSHIP_MISMATCH); + throw new Exception(Exception::TEAM_MEMBERSHIP_MISMATCH); } $team = Authorization::skip(fn() => $dbForProject->getDocument('teams', $teamId)); if ($team->isEmpty()) { - throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND); + throw new Exception(Exception::TEAM_NOT_FOUND); } if (Auth::hash($secret) !== $membership->getAttribute('secret')) { - throw new Exception('Secret key not valid', 401, Exception::TEAM_INVALID_SECRET); + throw new Exception(Exception::TEAM_INVALID_SECRET); } if ($userId !== $membership->getAttribute('userId')) { - throw new Exception('Invite does not belong to current user (' . $user->getAttribute('email') . ')', 401, Exception::TEAM_INVITE_MISMATCH); + throw new Exception(Exception::TEAM_INVITE_MISMATCH, 'Invite does not belong to current user (' . $user->getAttribute('email') . ')'); } if ($user->isEmpty()) { @@ -678,11 +678,11 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') } if ($membership->getAttribute('userId') !== $user->getId()) { - throw new Exception('Invite does not belong to current user (' . $user->getAttribute('email') . ')', 401, Exception::TEAM_INVITE_MISMATCH); + throw new Exception(Exception::TEAM_INVITE_MISMATCH, 'Invite does not belong to current user (' . $user->getAttribute('email') . ')'); } if ($membership->getAttribute('confirm') === true) { - throw new Exception('Membership already confirmed', 409); + throw new Exception(Exception::MEMBERSHIP_ALREADY_CONFIRMED); } $membership // Attach user to team @@ -778,31 +778,31 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId') $membership = $dbForProject->getDocument('memberships', $membershipId); if ($membership->isEmpty()) { - throw new Exception('Invite not found', 404, Exception::TEAM_INVITE_NOT_FOUND); + throw new Exception(Exception::TEAM_INVITE_NOT_FOUND); } if ($membership->getAttribute('teamId') !== $teamId) { - throw new Exception('Team IDs don\'t match', 404); + throw new Exception(Exception::TEAM_ID_MISMATCH); } $user = $dbForProject->getDocument('users', $membership->getAttribute('userId')); if ($user->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { - throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND); + throw new Exception(Exception::TEAM_NOT_FOUND); } try { $dbForProject->deleteDocument('memberships', $membership->getId()); } catch (AuthorizationException $exception) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } catch (\Exception $exception) { - throw new Exception('Failed to remove membership from DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove membership from DB'); } $dbForProject->deleteCachedDocument('users', $user->getId()); @@ -851,7 +851,7 @@ App::get('/v1/teams/:teamId/logs') $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { - throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND); + throw new Exception(Exception::TEAM_NOT_FOUND); } $audit = new Audit($dbForProject); diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 8845db4810..321abcf632 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -74,7 +74,7 @@ App::post('/v1/users') 'search' => implode(' ', [$userId, $email, $name]) ])); } catch (Duplicate $th) { - throw new Exception('Account already exists', 409, Exception::USER_ALREADY_EXISTS); + throw new Exception(Exception::USER_ALREADY_EXISTS); } $usage @@ -155,7 +155,7 @@ App::get('/v1/users/:userId') $user = $dbForProject->getDocument('users', $userId); if ($user->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $usage @@ -184,7 +184,7 @@ App::get('/v1/users/:userId/prefs') $user = $dbForProject->getDocument('users', $userId); if ($user->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $prefs = $user->getAttribute('prefs', new \stdClass()); @@ -216,7 +216,7 @@ App::get('/v1/users/:userId/sessions') $user = $dbForProject->getDocument('users', $userId); if ($user->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $sessions = $user->getAttribute('sessions', []); @@ -259,7 +259,7 @@ App::get('/v1/users/:userId/memberships') $user = $dbForProject->getDocument('users', $userId); if ($user->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $memberships = array_map(function ($membership) use ($dbForProject, $user) { @@ -303,7 +303,7 @@ App::get('/v1/users/:userId/logs') $user = $dbForProject->getDocument('users', $userId); if ($user->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $audit = new Audit($dbForProject); @@ -384,7 +384,7 @@ App::patch('/v1/users/:userId/status') $user = $dbForProject->getDocument('users', $userId); if ($user->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('status', (bool) $status)); @@ -423,7 +423,7 @@ App::patch('/v1/users/:userId/verification') $user = $dbForProject->getDocument('users', $userId); if ($user->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', $emailVerification)); @@ -462,7 +462,7 @@ App::patch('/v1/users/:userId/verification/phone') $user = $dbForProject->getDocument('users', $userId); if ($user->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('phoneVerification', $phoneVerification)); @@ -501,7 +501,7 @@ App::patch('/v1/users/:userId/name') $user = $dbForProject->getDocument('users', $userId); if ($user->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $user @@ -545,7 +545,7 @@ App::patch('/v1/users/:userId/password') $user = $dbForProject->getDocument('users', $userId); if ($user->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $user @@ -588,7 +588,7 @@ App::patch('/v1/users/:userId/email') $user = $dbForProject->getDocument('users', $userId); if ($user->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $email = \strtolower($email); @@ -602,7 +602,7 @@ App::patch('/v1/users/:userId/email') try { $user = $dbForProject->updateDocument('users', $user->getId(), $user); } catch (Duplicate $th) { - throw new Exception('Email already exists', 409, Exception::USER_EMAIL_ALREADY_EXISTS); + throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS); } @@ -640,7 +640,7 @@ App::patch('/v1/users/:userId/phone') $user = $dbForProject->getDocument('users', $userId); if ($user->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $user @@ -651,7 +651,7 @@ App::patch('/v1/users/:userId/phone') try { $user = $dbForProject->updateDocument('users', $user->getId(), $user); } catch (Duplicate $th) { - throw new Exception('Email already exists', 409, Exception::USER_EMAIL_ALREADY_EXISTS); + throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS); } @@ -689,7 +689,7 @@ App::patch('/v1/users/:userId/prefs') $user = $dbForProject->getDocument('users', $userId); if ($user->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('prefs', $prefs)); @@ -727,13 +727,13 @@ App::delete('/v1/users/:userId/sessions/:sessionId') $user = $dbForProject->getDocument('users', $userId); if ($user->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $session = $dbForProject->getDocument('sessions', $sessionId); if ($session->isEmpty()) { - throw new Exception('Session not found', 404, Exception::USER_SESSION_NOT_FOUND); + throw new Exception(Exception::USER_SESSION_NOT_FOUND); } $dbForProject->deleteDocument('sessions', $session->getId()); @@ -774,7 +774,7 @@ App::delete('/v1/users/:userId/sessions') $user = $dbForProject->getDocument('users', $userId); if ($user->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $sessions = $user->getAttribute('sessions', []); @@ -821,7 +821,7 @@ App::delete('/v1/users/:userId') $user = $dbForProject->getDocument('users', $userId); if ($user->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } // clone user object to send to workers diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 3f5f2277d4..79fc8b5df9 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -24,7 +24,7 @@ App::init(function (App $utopia, Request $request, Response $response, Document $route = $utopia->match($request); if ($project->isEmpty() && $route->getLabel('abuse-limit', 0) > 0) { // Abuse limit requires an active project scope - throw new Exception('Missing or unknown project ID', 400, Exception::PROJECT_UNKNOWN); + throw new Exception(Exception::PROJECT_UNKNOWN); } /* @@ -74,7 +74,7 @@ App::init(function (App $utopia, Request $request, Response $response, Document && $abuse->check()) // Abuse is not disabled && (!$isAppUser && !$isPrivilegedUser) ) { // User is not an admin or API key - throw new Exception('Too many requests', 429, Exception::GENERAL_RATE_LIMIT_EXCEEDED); + throw new Exception(Exception::GENERAL_RATE_LIMIT_EXCEEDED); } } @@ -131,36 +131,36 @@ App::init(function (App $utopia, Request $request, Document $project) { switch ($route->getLabel('auth.type', '')) { case 'emailPassword': if (($auths['emailPassword'] ?? true) === false) { - throw new Exception('Email / Password authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED); + throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Email / Password authentication is disabled for this project'); } break; case 'magic-url': if ($project->getAttribute('usersAuthMagicURL', true) === false) { - throw new Exception('Magic URL authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED); + throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Magic URL authentication is disabled for this project'); } break; case 'anonymous': if (($auths['anonymous'] ?? true) === false) { - throw new Exception('Anonymous authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED); + throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Anonymous authentication is disabled for this project'); } break; case 'invites': if (($auths['invites'] ?? true) === false) { - throw new Exception('Invites authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED); + throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Invites authentication is disabled for this project'); } break; case 'jwt': if (($auths['JWT'] ?? true) === false) { - throw new Exception('JWT authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED); + throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'JWT authentication is disabled for this project'); } break; default: - throw new Exception('Unsupported authentication route', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED); + throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Unsupported authentication route'); break; } }, ['utopia', 'request', 'project'], 'auth'); diff --git a/app/controllers/web/console.php b/app/controllers/web/console.php index 410be4d307..6ac842ea38 100644 --- a/app/controllers/web/console.php +++ b/app/controllers/web/console.php @@ -507,9 +507,9 @@ App::get('/console/version') if ($version && isset($version['version'])) { return $response->json(['version' => $version['version']]); } else { - throw new Exception('Failed to check for a newer version', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to check for a newer version'); } } catch (\Throwable $th) { - throw new Exception('Failed to check for a newer version', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to check for a newer version'); } }); diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index b3984ba6c6..cbd3feec94 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -86,9 +86,11 @@ class Exception extends \Exception public const TEAM_INVALID_SECRET = 'team_invalid_secret'; public const TEAM_MEMBERSHIP_MISMATCH = 'team_membership_mismatch'; public const TEAM_INVITE_MISMATCH = 'team_invite_mismatch'; + public const TEAM_ID_MISMATCH = 'team_id_mismatch'; /** Membership */ public const MEMBERSHIP_NOT_FOUND = 'membership_not_found'; + public const MEMBERSHIP_ALREADY_CONFIRMED = 'membership_already_confirmed'; /** Avatars */ public const AVATAR_SET_NOT_FOUND = 'avatar_set_not_found'; From 3c808ca89577e4238d52cf9d1272066bb353835c Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 26 Jul 2022 15:56:59 +0100 Subject: [PATCH 04/20] Fix problem with Exception class --- app/controllers/api/databases.php | 12 ++++++------ app/controllers/api/functions.php | 8 ++++---- app/controllers/api/projects.php | 4 ++-- app/controllers/api/storage.php | 4 ++-- app/controllers/api/teams.php | 4 ++-- app/controllers/api/users.php | 2 +- app/controllers/general.php | 32 +++++++++++++++---------------- src/Appwrite/Extend/Exception.php | 11 ++++------- 8 files changed, 37 insertions(+), 40 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 9872c635d7..ebba0dabab 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -588,7 +588,7 @@ App::get('/v1/databases/:databaseId/collections') $cursorCollection = $dbForProject->getDocument('database_' . $database->getInternalId(), $cursor); if ($cursorCollection->isEmpty()) { - throw new Exception("Collection '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Collection '{$cursor}' for the 'cursor' value not found."); } } @@ -903,7 +903,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string // Ensure attribute default is within required size $validator = new Text($size); if (!is_null($default) && !$validator->isValid($default)) { - throw new Exception($validator->getDescription(), 400, Exception::ATTRIBUTE_VALUE_INVALID); + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription()); } $attribute = createAttribute($databaseId, $collectionId, new Document([ @@ -1985,7 +1985,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') $query = Query::parse($query); if (\count($query->getValues()) > 100) { - throw new Exception("You cannot use more than 100 query values on attribute '{$query->getAttribute()}'", 400, Exception::GENERAL_QUERY_LIMIT_EXCEEDED); + throw new Exception(Exception::GENERAL_QUERY_LIMIT_EXCEEDED, "You cannot use more than 100 query values on attribute '{$query->getAttribute()}'"); } return $query; @@ -1994,14 +1994,14 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') if (!empty($orderAttributes)) { $validator = new OrderAttributes($collection->getAttribute('attributes', []), $collection->getAttribute('indexes', []), true); if (!$validator->isValid($orderAttributes)) { - throw new Exception($validator->getDescription(), 400, Exception::GENERAL_QUERY_INVALID); + throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); } } if (!empty($queries)) { $validator = new QueriesValidator(new QueryValidator($collection->getAttribute('attributes', [])), $collection->getAttribute('indexes', []), true); if (!$validator->isValid($queries)) { - throw new Exception($validator->getDescription(), 400, Exception::GENERAL_QUERY_INVALID); + throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); } } @@ -2012,7 +2012,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') : $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $cursor); if ($cursorDocument->isEmpty()) { - throw new Exception("Document '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Document '{$cursor}' for the 'cursor' value not found."); } } diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 626b91a748..9800fd4d34 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -111,7 +111,7 @@ App::get('/v1/functions') $cursorFunction = $dbForProject->getDocument('functions', $cursor); if ($cursorFunction->isEmpty()) { - throw new Exception("Function '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Function '{$cursor}' for the 'cursor' value not found."); } } @@ -658,7 +658,7 @@ App::get('/v1/functions/:functionId/deployments') $cursorDeployment = $dbForProject->getDocument('deployments', $cursor); if ($cursorDeployment->isEmpty()) { - throw new Exception("Tag '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Tag '{$cursor}' for the 'cursor' value not found."); } } @@ -846,7 +846,7 @@ App::post('/v1/functions/:functionId/executions') $validator = new Authorization('execute'); if (!$validator->isValid($function->getAttribute('execute'))) { // Check if user has write access to execute function - throw new Exception($validator->getDescription(), 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED, $validator->getDescription()); } $executionId = $dbForProject->getId(); @@ -1001,7 +1001,7 @@ App::get('/v1/functions/:functionId/executions') $cursorExecution = $dbForProject->getDocument('executions', $cursor); if ($cursorExecution->isEmpty()) { - throw new Exception("Execution '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Execution '{$cursor}' for the 'cursor' value not found."); } } diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index c96ddbaa41..ae9b19245d 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -80,7 +80,7 @@ App::post('/v1/projects') $projectId = ($projectId == 'unique()') ? $dbForConsole->getId() : $projectId; if ($projectId === 'console') { - throw new Exception("'console' is a reserved project.", 400, Exception::PROJECT_RESERVED_PROJECT); + throw new Exception(Exception::PROJECT_RESERVED_PROJECT, "'console' is a reserved project."); } $project = $dbForConsole->createDocument('projects', new Document([ @@ -183,7 +183,7 @@ App::get('/v1/projects') $cursorProject = $dbForConsole->getDocument('projects', $cursor); if ($cursorProject->isEmpty()) { - throw new Exception("Project '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Project '{$cursor}' for the 'cursor' value not found.",); } } diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index c2e13b683d..76790d65bc 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -169,7 +169,7 @@ App::get('/v1/storage/buckets') $cursorBucket = $dbForProject->getDocument('buckets', $cursor); if ($cursorBucket->isEmpty()) { - throw new Exception("Bucket '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Bucket '{$cursor}' for the 'cursor' value not found."); } } @@ -715,7 +715,7 @@ App::get('/v1/storage/buckets/:bucketId/files') } if ($cursorFile->isEmpty()) { - throw new Exception("File '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "File '{$cursor}' for the 'cursor' value not found."); } } diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 2aa7c81e63..68676cf6a3 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -129,7 +129,7 @@ App::get('/v1/teams') $cursorTeam = $dbForProject->getDocument('teams', $cursor); if ($cursorTeam->isEmpty()) { - throw new Exception("Team '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Team '{$cursor}' for the 'cursor' value not found."); } } @@ -465,7 +465,7 @@ App::get('/v1/teams/:teamId/memberships') $cursorMembership = $dbForProject->getDocument('memberships', $cursor); if ($cursorMembership->isEmpty()) { - throw new Exception("Membership '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Membership '{$cursor}' for the 'cursor' value not found."); } } diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 321abcf632..3827cf8477 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -115,7 +115,7 @@ App::get('/v1/users') $cursorUser = $dbForProject->getDocument('users', $cursor); if ($cursorUser->isEmpty()) { - throw new Exception("User '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "User '{$cursor}' for the 'cursor' value not found."); } } diff --git a/app/controllers/general.php b/app/controllers/general.php index 620f16f1c1..0d3a6a9e05 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -122,11 +122,11 @@ App::init(function (App $utopia, Request $request, Response $response, Document } if ($project->isEmpty()) { - throw new AppwriteException('Project not found', 404, AppwriteException::PROJECT_NOT_FOUND); + throw new AppwriteException(AppwriteException::PROJECT_NOT_FOUND); } if (!empty($route->getLabel('sdk.auth', [])) && $project->isEmpty() && ($route->getLabel('scope', '') !== 'public')) { - throw new AppwriteException('Missing or unknown project ID', 400, AppwriteException::PROJECT_UNKNOWN); + throw new AppwriteException(AppwriteException::PROJECT_UNKNOWN); } $referrer = $request->getReferer(); @@ -197,7 +197,7 @@ App::init(function (App $utopia, Request $request, Response $response, Document if (App::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS if ($request->getProtocol() !== 'https') { if ($request->getMethod() !== Request::METHOD_GET) { - throw new AppwriteException('Method unsupported over HTTP.', 500, AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED); + throw new AppwriteException(AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED); } return $response->redirect('https://' . $request->getHostname() . $request->getURI()); @@ -230,7 +230,7 @@ App::init(function (App $utopia, Request $request, Response $response, Document && $route->getLabel('origin', false) !== '*' && empty($request->getHeader('x-appwrite-key', '')) ) { - throw new AppwriteException($originValidator->getDescription(), 403, AppwriteException::GENERAL_UNKNOWN_ORIGIN); + throw new AppwriteException(AppwriteException::GENERAL_UNKNOWN_ORIGIN, $originValidator->getDescription()); } /* @@ -286,7 +286,7 @@ App::init(function (App $utopia, Request $request, Response $response, Document $expire = $key->getAttribute('expire', 0); if (!empty($expire) && $expire < \time()) { - throw new AppwriteException('Project key expired', 401, AppwriteException:: PROJECT_KEY_EXPIRED); + throw new AppwriteException(AppwriteException::PROJECT_KEY_EXPIRED); } Authorization::setRole('role:' . Auth::USER_ROLE_APP); @@ -307,24 +307,24 @@ App::init(function (App $utopia, Request $request, Response $response, Document && !$project->getAttribute('services', [])[$service] && !(Auth::isPrivilegedUser(Authorization::getRoles()) || Auth::isAppUser(Authorization::getRoles())) ) { - throw new AppwriteException('Service is disabled', 503, AppwriteException::GENERAL_SERVICE_DISABLED); + throw new AppwriteException(AppwriteException::GENERAL_SERVICE_DISABLED); } } if (!\in_array($scope, $scopes)) { if ($project->isEmpty()) { // Check if permission is denied because project is missing - throw new AppwriteException('Project not found', 404, AppwriteException::PROJECT_NOT_FOUND); + throw new AppwriteException(AppwriteException::PROJECT_NOT_FOUND); } - throw new AppwriteException($user->getAttribute('email', 'User') . ' (role: ' . \strtolower($roles[$role]['label']) . ') missing scope (' . $scope . ')', 401, AppwriteException::GENERAL_UNAUTHORIZED_SCOPE); + throw new AppwriteException(AppwriteException::GENERAL_UNAUTHORIZED_SCOPE, $user->getAttribute('email', 'User') . ' (role: ' . \strtolower($roles[$role]['label']) . ') missing scope (' . $scope . ')'); } if (false === $user->getAttribute('status')) { // Account is blocked - throw new AppwriteException('Invalid credentials. User is blocked', 401, AppwriteException::USER_BLOCKED); + throw new AppwriteException(AppwriteException::USER_BLOCKED); } if ($user->getAttribute('reset')) { - throw new AppwriteException('Password reset is required', 412, AppwriteException::USER_PASSWORD_RESET_REQUIRED); + throw new AppwriteException(AppwriteException::USER_PASSWORD_RESET_REQUIRED); } }, ['utopia', 'request', 'response', 'console', 'project', 'dbForConsole', 'user', 'locale', 'clients']); @@ -580,32 +580,32 @@ App::get('/.well-known/acme-challenge') ]); if (!$validator->isValid($token) || \count($uriChunks) !== 4) { - throw new AppwriteException('Invalid challenge token.', 400); + throw new AppwriteException(AppwriteException::GENERAL_QUERY_INVALID, 'Invalid challenge token.'); } $base = \realpath(APP_STORAGE_CERTIFICATES); $absolute = \realpath($base . '/.well-known/acme-challenge/' . $token); if (!$base) { - throw new AppwriteException('Storage error', 500, AppwriteException::GENERAL_SERVER_ERROR); + throw new AppwriteException(AppwriteException::GENERAL_SERVER_ERROR, 'Storage error'); } if (!$absolute) { - throw new AppwriteException('Unknown path', 404); + throw new AppwriteException(AppwriteException::GENERAL_ROUTE_NOT_FOUND, 'Unknown path'); } if (!\substr($absolute, 0, \strlen($base)) === $base) { - throw new AppwriteException('Invalid path', 401); + throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'Unknown path'); } if (!\file_exists($absolute)) { - throw new AppwriteException('Unknown path', 404); + throw new AppwriteException(AppwriteException::GENERAL_ROUTE_NOT_FOUND, 'Unknown path'); } $content = @\file_get_contents($absolute); if (!$content) { - throw new AppwriteException('Failed to get contents', 500, AppwriteException::GENERAL_SERVER_ERROR); + throw new AppwriteException(AppwriteException::GENERAL_SERVER_ERROR, 'Failed to get contents'); } $response->text($content); diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index cbd3feec94..0a7c7b8319 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -204,16 +204,13 @@ class Exception extends \Exception public const MOCK_500 = 'mock_500'; - private $type = ''; + protected $type = ''; - private string $message = ''; + protected array $errors; - private int $code = 0; - - protected array $errors = Config::getParam('errors'); - - public function __construct(string $type = Exception::GENERAL_UNKNOWN, string $message = '', \Throwable $previous = null) + public function __construct(string $type, string $message = null, \Throwable $previous = null) { + $this->errors = Config::getParam('errors'); $this->type = $type; if (isset($this->errors[$type])) { From e3a3ca4c0d0b9670645f9421b0daba095486f6ab Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Wed, 27 Jul 2022 14:47:35 +0100 Subject: [PATCH 05/20] Translate more errors + Fixed an issue where some errors that should use 401 uses 400 error codes + Updated error messages and tests for multiple exceptions --- app/config/errors.php | 29 +++++++++++++++++-- app/controllers/api/databases.php | 1 - app/controllers/general.php | 4 +-- src/Appwrite/Extend/Exception.php | 3 +- .../Databases/DatabasesCustomServerTest.php | 4 +-- tests/e2e/Services/Storage/StorageBase.php | 2 +- .../Storage/StorageCustomClientTest.php | 4 +-- tests/e2e/Services/Teams/TeamsBaseClient.php | 2 +- 8 files changed, 36 insertions(+), 13 deletions(-) diff --git a/app/config/errors.php b/app/config/errors.php index 9f9eb7832d..a7592c331e 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -96,6 +96,12 @@ return [ 'code' => 500, ], + Exception::ACCOUNT_OAUTH_MISSING_ID => [ + 'name' => Exception::ACCOUNT_OAUTH_MISSING_ID, + 'description' => 'Missing ID in the request.', + 'code' => 400, + ], + /** User Errors */ Exception::USER_COUNT_EXCEEDED => [ 'name' => Exception::USER_COUNT_EXCEEDED, @@ -294,7 +300,7 @@ return [ ], Exception::STORAGE_INVALID_FILE_SIZE => [ 'name' => Exception::STORAGE_INVALID_FILE_SIZE, - 'description' => 'The file size is either not valid or exceeds the maximum allowed size. Please check the file or the value of the _APP_STORAGE_LIMIT environment variable.', + 'description' => 'File size not allowed', 'code' => 400, ], Exception::STORAGE_INVALID_FILE => [ @@ -343,7 +349,7 @@ return [ ], Exception::BUILD_NOT_READY => [ 'name' => Exception::BUILD_NOT_READY, - 'description' => 'Build with the requested ID is builing and not ready for execution.', + 'description' => 'Build with the requested ID is building and not ready for execution.', 'code' => 400, ], Exception::BUILD_IN_PROGRESS => [ @@ -366,6 +372,18 @@ return [ 'code' => 404, ], + Exception::DATABASE_NOT_FOUND => [ + 'name' => Exception::DATABASE_NOT_FOUND, + 'description' => 'Database not found', + 'code' => 404 + ], + + Exception::DATABASE_ALREADY_EXISTS => [ + 'name' => Exception::DATABASE_ALREADY_EXISTS, + 'description' => 'Database already exists', + 'code' => 409 + ], + /** Collections */ Exception::COLLECTION_NOT_FOUND => [ 'name' => Exception::COLLECTION_NOT_FOUND, @@ -428,7 +446,7 @@ return [ ], Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED => [ 'name' => Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, - 'description' => 'Default values cannot be set for array and required attributes.', + 'description' => 'Cannot set default value for array attributes', 'code' => 400, ], Exception::ATTRIBUTE_ALREADY_EXISTS => [ @@ -500,6 +518,11 @@ return [ 'description' => 'Failed to obtain user ID from the OAuth provider.', 'code' => 400, ], + Exception::PROJECT_KEY_EXPIRED => [ + 'name' => Exception::PROJECT_KEY_EXPIRED, + 'description' => 'The project key has expired. Please generate a new key using the Appwrite console.', + 'code' => 401, + ], Exception::WEBHOOK_NOT_FOUND => [ 'name' => Exception::WEBHOOK_NOT_FOUND, 'description' => 'Webhook with the requested ID could not be found.', diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index ebba0dabab..f89b6fda3b 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -1880,7 +1880,6 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) { foreach ($data['$read'] as $read) { if (!Authorization::isRole($read)) { - // TODO: Isn't this a 401: Unauthorized Error ? throw new Exception(Exception::USER_UNAUTHORIZED, 'Read permissions must be one of: (' . \implode(', ', $roles) . ')'); } } diff --git a/app/controllers/general.php b/app/controllers/general.php index 0d3a6a9e05..341570a8ff 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -424,7 +424,7 @@ App::error(function (Throwable $error, App $utopia, Request $request, Response $ /** Handle Utopia Errors */ if ($error instanceof Utopia\Exception) { - $error = new AppwriteException($message, $code, AppwriteException::GENERAL_UNKNOWN, $error); + $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error); switch ($code) { case 400: $error->setType(AppwriteException::GENERAL_ARGUMENT_INVALID); @@ -437,7 +437,7 @@ App::error(function (Throwable $error, App $utopia, Request $request, Response $ /** Wrap all exceptions inside Appwrite\Extend\Exception */ if (!($error instanceof AppwriteException)) { - $error = new AppwriteException($message, $code, AppwriteException::GENERAL_UNKNOWN, $error); + $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error); } switch ($code) { // Don't show 500 errors! diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 0a7c7b8319..4be19ae01c 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -208,7 +208,7 @@ class Exception extends \Exception protected array $errors; - public function __construct(string $type, string $message = null, \Throwable $previous = null) + public function __construct(string $type, string $message = null, int $code = null, \Throwable $previous = null) { $this->errors = Config::getParam('errors'); $this->type = $type; @@ -219,6 +219,7 @@ class Exception extends \Exception } $this->message = $message ?? $this->message; + $this->code = $code ?? $this->code; parent::__construct($this->message, $this->code, $previous); } diff --git a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php index cc75a13a80..d229f0ae73 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php +++ b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php @@ -996,7 +996,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $tooWide['headers']['status-code']); - $this->assertEquals('Attribute limit exceeded', $tooWide['body']['message']); + $this->assertEquals('The maximum number of attributes has been reached.', $tooWide['body']['message']); } public function testIndexLimitException() @@ -1110,7 +1110,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $tooMany['headers']['status-code']); - $this->assertEquals('Index limit exceeded', $tooMany['body']['message']); + $this->assertEquals('The maximum number of indexes has been reached.', $tooMany['body']['message']); $collection = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $collectionId, array_merge([ 'content-type' => 'application/json', diff --git a/tests/e2e/Services/Storage/StorageBase.php b/tests/e2e/Services/Storage/StorageBase.php index edcaa0f8a6..c71d212079 100644 --- a/tests/e2e/Services/Storage/StorageBase.php +++ b/tests/e2e/Services/Storage/StorageBase.php @@ -185,7 +185,7 @@ trait StorageBase ]); $this->assertEquals(400, $res['headers']['status-code']); - $this->assertEquals('File extension not allowed', $res['body']['message']); + $this->assertEquals('The file type is not supported.', $res['body']['message']); return ['bucketId' => $bucketId, 'fileId' => $file['body']['$id'], 'largeFileId' => $largeFile['body']['$id'], 'largeBucketId' => $bucket2['body']['$id']]; diff --git a/tests/e2e/Services/Storage/StorageCustomClientTest.php b/tests/e2e/Services/Storage/StorageCustomClientTest.php index fa30f6e762..43c513c37a 100644 --- a/tests/e2e/Services/Storage/StorageCustomClientTest.php +++ b/tests/e2e/Services/Storage/StorageCustomClientTest.php @@ -164,7 +164,7 @@ class StorageCustomClientTest extends Scope 'read' => ['user:notme'] ]); - $this->assertEquals(400, $file['headers']['status-code']); + $this->assertEquals(401, $file['headers']['status-code']); $this->assertStringStartsWith('Read permissions must be one of:', $file['body']['message']); $this->assertStringContainsString('role:all', $file['body']['message']); $this->assertStringContainsString('role:member', $file['body']['message']); @@ -180,7 +180,7 @@ class StorageCustomClientTest extends Scope 'write' => ['user:notme'] ]); - $this->assertEquals($file['headers']['status-code'], 400); + $this->assertEquals($file['headers']['status-code'], 401); $this->assertStringStartsWith('Write permissions must be one of:', $file['body']['message']); $this->assertStringContainsString('role:all', $file['body']['message']); $this->assertStringContainsString('role:member', $file['body']['message']); diff --git a/tests/e2e/Services/Teams/TeamsBaseClient.php b/tests/e2e/Services/Teams/TeamsBaseClient.php index bd9b9c1ae0..6b344e752d 100644 --- a/tests/e2e/Services/Teams/TeamsBaseClient.php +++ b/tests/e2e/Services/Teams/TeamsBaseClient.php @@ -438,7 +438,7 @@ trait TeamsBaseClient ]); $this->assertEquals(401, $response['headers']['status-code']); - $this->assertEquals('User is not allowed to modify roles', $response['body']['message']); + $this->assertEquals('The current user is not authorized to perform the requested action.', $response['body']['message']); return $data; } From 467d8b2cd2af969654cca0854637547e3225e7a9 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Wed, 27 Jul 2022 14:50:13 +0100 Subject: [PATCH 06/20] Fix linting errors --- app/controllers/api/projects.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index ae9b19245d..46a734a739 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -183,7 +183,7 @@ App::get('/v1/projects') $cursorProject = $dbForConsole->getDocument('projects', $cursor); if ($cursorProject->isEmpty()) { - throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Project '{$cursor}' for the 'cursor' value not found.",); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Project '{$cursor}' for the 'cursor' value not found."); } } From 7e3c6b49444cfc6f4f410d0e170a28a7af462924 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Thu, 28 Jul 2022 10:12:51 +0100 Subject: [PATCH 07/20] Fix permission errors being 400 when they should be 401 --- tests/e2e/Services/Storage/StorageCustomClientTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/e2e/Services/Storage/StorageCustomClientTest.php b/tests/e2e/Services/Storage/StorageCustomClientTest.php index 43c513c37a..f9e3c08b21 100644 --- a/tests/e2e/Services/Storage/StorageCustomClientTest.php +++ b/tests/e2e/Services/Storage/StorageCustomClientTest.php @@ -197,7 +197,7 @@ class StorageCustomClientTest extends Scope 'write' => ['user:notme'] ]); - $this->assertEquals($file['headers']['status-code'], 400); + $this->assertEquals($file['headers']['status-code'], 401); $this->assertStringStartsWith('Read permissions must be one of:', $file['body']['message']); $this->assertStringContainsString('role:all', $file['body']['message']); $this->assertStringContainsString('role:member', $file['body']['message']); @@ -219,7 +219,7 @@ class StorageCustomClientTest extends Scope 'read' => ['user:notme'] ]); - $this->assertEquals($file['headers']['status-code'], 400); + $this->assertEquals($file['headers']['status-code'], 401); $this->assertStringStartsWith('Read permissions must be one of:', $file['body']['message']); $this->assertStringContainsString('role:all', $file['body']['message']); $this->assertStringContainsString('role:member', $file['body']['message']); @@ -232,7 +232,7 @@ class StorageCustomClientTest extends Scope 'write' => ['user:notme'] ]); - $this->assertEquals($file['headers']['status-code'], 400); + $this->assertEquals($file['headers']['status-code'], 401); $this->assertStringStartsWith('Write permissions must be one of:', $file['body']['message']); $this->assertStringContainsString('role:all', $file['body']['message']); $this->assertStringContainsString('role:member', $file['body']['message']); @@ -246,7 +246,7 @@ class StorageCustomClientTest extends Scope 'write' => ['user:notme'] ]); - $this->assertEquals($file['headers']['status-code'], 400); + $this->assertEquals($file['headers']['status-code'], 401); $this->assertStringStartsWith('Read permissions must be one of:', $file['body']['message']); $this->assertStringContainsString('role:all', $file['body']['message']); $this->assertStringContainsString('role:member', $file['body']['message']); From bf050ece0fdebf6a54ea8e976377bcabeaeba556 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Mon, 8 Aug 2022 15:44:07 +0100 Subject: [PATCH 08/20] Fix Exceptions --- app/controllers/general.php | 36 ++++++++++++++--------------- app/controllers/mock.php | 42 +++++++++++++++++----------------- app/controllers/shared/api.php | 16 ++++++------- 3 files changed, 47 insertions(+), 47 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index c87d53c290..341ce18845 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -131,11 +131,11 @@ App::init() } if ($project->isEmpty()) { - throw new AppwriteException('Project not found', 404, AppwriteException::PROJECT_NOT_FOUND); + throw new AppwriteException(AppwriteException::PROJECT_NOT_FOUND); } if (!empty($route->getLabel('sdk.auth', [])) && $project->isEmpty() && ($route->getLabel('scope', '') !== 'public')) { - throw new AppwriteException('Missing or unknown project ID', 400, AppwriteException::PROJECT_UNKNOWN); + throw new AppwriteException(AppwriteException::PROJECT_UNKNOWN); } $referrer = $request->getReferer(); @@ -206,7 +206,7 @@ App::init() if (App::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS if ($request->getProtocol() !== 'https') { if ($request->getMethod() !== Request::METHOD_GET) { - throw new AppwriteException('Method unsupported over HTTP.', 500, AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED); + throw new AppwriteException(AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED, 'Method unsupported over HTTP.'); } return $response->redirect('https://' . $request->getHostname() . $request->getURI()); @@ -239,7 +239,7 @@ App::init() && $route->getLabel('origin', false) !== '*' && empty($request->getHeader('x-appwrite-key', '')) ) { - throw new AppwriteException($originValidator->getDescription(), 403, AppwriteException::GENERAL_UNKNOWN_ORIGIN); + throw new AppwriteException(AppwriteException::GENERAL_UNKNOWN_ORIGIN, $originValidator->getDescription(), 403); } /* @@ -295,7 +295,7 @@ App::init() $expire = $key->getAttribute('expire', 0); if (!empty($expire) && $expire < \time()) { - throw new AppwriteException('Project key expired', 401, AppwriteException:: PROJECT_KEY_EXPIRED); + throw new AppwriteException(AppwriteException:: PROJECT_KEY_EXPIRED); } Authorization::setRole('role:' . Auth::USER_ROLE_APP); @@ -316,24 +316,24 @@ App::init() && !$project->getAttribute('services', [])[$service] && !(Auth::isPrivilegedUser(Authorization::getRoles()) || Auth::isAppUser(Authorization::getRoles())) ) { - throw new AppwriteException('Service is disabled', 503, AppwriteException::GENERAL_SERVICE_DISABLED); + throw new AppwriteException(AppwriteException::GENERAL_SERVICE_DISABLED); } } if (!\in_array($scope, $scopes)) { if ($project->isEmpty()) { // Check if permission is denied because project is missing - throw new AppwriteException('Project not found', 404, AppwriteException::PROJECT_NOT_FOUND); + throw new AppwriteException(AppwriteException::PROJECT_NOT_FOUND); } - throw new AppwriteException($user->getAttribute('email', 'User') . ' (role: ' . \strtolower($roles[$role]['label']) . ') missing scope (' . $scope . ')', 401, AppwriteException::GENERAL_UNAUTHORIZED_SCOPE); + throw new AppwriteException(AppwriteException::GENERAL_UNAUTHORIZED_SCOPE, $user->getAttribute('email', 'User') . ' (role: ' . \strtolower($roles[$role]['label']) . ') missing scope (' . $scope . ')'); } if (false === $user->getAttribute('status')) { // Account is blocked - throw new AppwriteException('Invalid credentials. User is blocked', 401, AppwriteException::USER_BLOCKED); + throw new AppwriteException(AppwriteException::USER_BLOCKED); } if ($user->getAttribute('reset')) { - throw new AppwriteException('Password reset is required', 412, AppwriteException::USER_PASSWORD_RESET_REQUIRED); + throw new AppwriteException(AppwriteException::USER_PASSWORD_RESET_REQUIRED); } }); @@ -445,7 +445,7 @@ App::error() /** Handle Utopia Errors */ if ($error instanceof Utopia\Exception) { - $error = new AppwriteException($message, $code, AppwriteException::GENERAL_UNKNOWN, $error); + $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error); switch ($code) { case 400: $error->setType(AppwriteException::GENERAL_ARGUMENT_INVALID); @@ -458,7 +458,7 @@ App::error() /** Wrap all exceptions inside Appwrite\Extend\Exception */ if (!($error instanceof AppwriteException)) { - $error = new AppwriteException($message, $code, AppwriteException::GENERAL_UNKNOWN, $error); + $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error); } switch ($code) { // Don't show 500 errors! @@ -601,32 +601,32 @@ App::get('/.well-known/acme-challenge') ]); if (!$validator->isValid($token) || \count($uriChunks) !== 4) { - throw new AppwriteException('Invalid challenge token.', 400); + throw new AppwriteException(AppwriteException::GENERAL_ARGUMENT_INVALID, 'Invalid challenge token.'); } $base = \realpath(APP_STORAGE_CERTIFICATES); $absolute = \realpath($base . '/.well-known/acme-challenge/' . $token); if (!$base) { - throw new AppwriteException('Storage error', 500, AppwriteException::GENERAL_SERVER_ERROR); + throw new AppwriteException(AppwriteException::GENERAL_SERVER_ERROR, 'Storage error'); } if (!$absolute) { - throw new AppwriteException('Unknown path', 404); + throw new AppwriteException(AppwriteException::GENERAL_ROUTE_NOT_FOUND, 'Unknown path'); } if (!\substr($absolute, 0, \strlen($base)) === $base) { - throw new AppwriteException('Invalid path', 401); + throw new AppwriteException(AppwriteException::GENERAL_UNAUTHORIZED_SCOPE, 'Invalid path'); } if (!\file_exists($absolute)) { - throw new AppwriteException('Unknown path', 404); + throw new AppwriteException(AppwriteException::GENERAL_ROUTE_NOT_FOUND, 'Unknown path'); } $content = @\file_get_contents($absolute); if (!$content) { - throw new AppwriteException('Failed to get contents', 500, AppwriteException::GENERAL_SERVER_ERROR); + throw new AppwriteException(AppwriteException::GENERAL_SERVER_ERROR, 'Failed to get contents'); } $response->text($content); diff --git a/app/controllers/mock.php b/app/controllers/mock.php index d3b150a55f..55a15d1966 100644 --- a/app/controllers/mock.php +++ b/app/controllers/mock.php @@ -253,31 +253,31 @@ App::post('/v1/mock/tests/general/upload') $file['size'] = (\is_array($file['size'])) ? $file['size'][0] : $file['size']; if (is_null($start) || is_null($end) || is_null($size)) { - throw new Exception('Invalid content-range header', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::GENERAL_MOCK); } if ($start > $end || $end > $size) { - throw new Exception('Invalid content-range header', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::GENERAL_MOCK); } if ($start === 0 && !empty($id)) { - throw new Exception('First chunked request cannot have id header', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::GENERAL_MOCK); } if ($start !== 0 && $id !== 'newfileid') { - throw new Exception('All chunked request must have id header (except first)', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::GENERAL_MOCK); } if ($end !== $size && $end - $start + 1 !== $chunkSize) { - throw new Exception('Chunk size must be 5MB (except last chunk)', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::GENERAL_MOCK); } if ($end !== $size && $file['size'] !== $chunkSize) { - throw new Exception('Wrong chunk size', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::GENERAL_MOCK); } if ($file['size'] > $chunkSize) { - throw new Exception('Chunk size must be 5MB or less', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::GENERAL_MOCK); } if ($end !== $size) { @@ -293,15 +293,15 @@ App::post('/v1/mock/tests/general/upload') $file['size'] = (\is_array($file['size'])) ? $file['size'][0] : $file['size']; if ($file['name'] !== 'file.png') { - throw new Exception('Wrong file name', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_FILE_NAME); } if ($file['size'] !== 38756) { - throw new Exception('Wrong file size', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_FILE_SIZE); } if (\md5(\file_get_contents($file['tmp_name'])) !== 'd80e7e6999a3eb2ae0d631a96fe135a4') { - throw new Exception('Wrong file uploaded', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_WRONG_FILE_UPLOADED); } } }); @@ -374,7 +374,7 @@ App::get('/v1/mock/tests/general/get-cookie') ->action(function (Request $request) { if ($request->getCookie('cookieName', '') !== 'cookieValue') { - throw new Exception('Missing cookie value', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_MISSING_COOKIE); } }); @@ -408,7 +408,7 @@ App::get('/v1/mock/tests/general/400-error') ->label('sdk.response.model', Response::MODEL_ERROR) ->label('sdk.mock', true) ->action(function () { - throw new Exception('Mock 400 error', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_400); }); App::get('/v1/mock/tests/general/500-error') @@ -424,7 +424,7 @@ App::get('/v1/mock/tests/general/500-error') ->label('sdk.response.model', Response::MODEL_ERROR) ->label('sdk.mock', true) ->action(function () { - throw new Exception('Mock 500 error', 500, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_500); }); App::get('/v1/mock/tests/general/502-error') @@ -480,11 +480,11 @@ App::get('/v1/mock/tests/general/oauth2/token') ->action(function (string $client_id, string $client_secret, string $grantType, string $redirectURI, string $code, string $refreshToken, Response $response) { if ($client_id != '1') { - throw new Exception('Invalid client ID', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_CLIENT_ID); } if ($client_secret != '123456') { - throw new Exception('Invalid client secret', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_CLIENT_SECRET); } $responseJson = [ @@ -495,18 +495,18 @@ App::get('/v1/mock/tests/general/oauth2/token') if ($grantType === 'authorization_code') { if ($code !== 'abcdef') { - throw new Exception('Invalid token', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_TOKEN); } $response->json($responseJson); } elseif ($grantType === 'refresh_token') { if ($refreshToken !== 'tuvwxyz') { - throw new Exception('Invalid refresh token', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_REFRESH_TOKEN); } $response->json($responseJson); } else { - throw new Exception('Invalid grant type', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_GRANT_TYPE); } }); @@ -520,7 +520,7 @@ App::get('/v1/mock/tests/general/oauth2/user') ->action(function (string $token, Response $response) { if ($token != '123456') { - throw new Exception('Invalid token', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_TOKEN); } $response->json([ @@ -571,7 +571,7 @@ App::shutdown() $tests = (\file_exists($path)) ? \json_decode(\file_get_contents($path), true) : []; if (!\is_array($tests)) { - throw new Exception('Failed to read results', 500, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_FAILED_TO_READ_RESULTS); } $result[$route->getMethod() . ':' . $route->getPath()] = true; @@ -579,7 +579,7 @@ App::shutdown() $tests = \array_merge($tests, $result); if (!\file_put_contents($path, \json_encode($tests), LOCK_EX)) { - throw new Exception('Failed to save results', 500, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_FAILED_TO_SAVE_RESULTS); } $response->dynamic(new Document(['result' => $route->getMethod() . ':' . $route->getPath() . ':passed']), Response::MODEL_MOCK); diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 2d27209562..7394b611aa 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -39,7 +39,7 @@ App::init() $route = $utopia->match($request); if ($project->isEmpty() && $route->getLabel('abuse-limit', 0) > 0) { // Abuse limit requires an active project scope - throw new Exception('Missing or unknown project ID', 400, Exception::PROJECT_UNKNOWN); + throw new Exception(Exception::PROJECT_UNKNOWN); } /* @@ -89,7 +89,7 @@ App::init() && $abuse->check()) // Abuse is not disabled && (!$isAppUser && !$isPrivilegedUser) ) { // User is not an admin or API key - throw new Exception('Too many requests', 429, Exception::GENERAL_RATE_LIMIT_EXCEEDED); + throw new Exception(Exception::GENERAL_RATE_LIMIT_EXCEEDED); } } @@ -151,36 +151,36 @@ App::init() switch ($route->getLabel('auth.type', '')) { case 'emailPassword': if (($auths['emailPassword'] ?? true) === false) { - throw new Exception('Email / Password authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED); + throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Email / Password authentication is disabled for this project'); } break; case 'magic-url': if ($project->getAttribute('usersAuthMagicURL', true) === false) { - throw new Exception('Magic URL authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED); + throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Magic URL authentication is disabled for this project'); } break; case 'anonymous': if (($auths['anonymous'] ?? true) === false) { - throw new Exception('Anonymous authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED); + throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Anonymous authentication is disabled for this project'); } break; case 'invites': if (($auths['invites'] ?? true) === false) { - throw new Exception('Invites authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED); + throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Invites authentication is disabled for this project'); } break; case 'jwt': if (($auths['JWT'] ?? true) === false) { - throw new Exception('JWT authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED); + throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'JWT authentication is disabled for this project'); } break; default: - throw new Exception('Unsupported authentication route', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED); + throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Unsupported authentication route'); break; } }); From 8b23dcd69ebdddff86942810e5c897a350d48a7d Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sat, 13 Aug 2022 20:14:04 +0530 Subject: [PATCH 09/20] fix: update constructor, remove account entity, add missing ID error --- app/config/errors.php | 23 +++++------------------ app/controllers/api/account.php | 4 ++-- src/Appwrite/Extend/Exception.php | 25 +++++++++---------------- 3 files changed, 16 insertions(+), 36 deletions(-) diff --git a/app/config/errors.php b/app/config/errors.php index a7592c331e..28699ab081 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -89,19 +89,6 @@ return [ 'code' => 500, ], - /** Account Errors */ - Exception::ACCOUNT_OAUTH_FAILED_TO_OBTAIN_TOKEN => [ - 'name' => Exception::ACCOUNT_OAUTH_FAILED_TO_OBTAIN_TOKEN, - 'description' => 'Failed to obtain access token.', - 'code' => 500, - ], - - Exception::ACCOUNT_OAUTH_MISSING_ID => [ - 'name' => Exception::ACCOUNT_OAUTH_MISSING_ID, - 'description' => 'Missing ID in the request.', - 'code' => 400, - ], - /** User Errors */ Exception::USER_COUNT_EXCEEDED => [ 'name' => Exception::USER_COUNT_EXCEEDED, @@ -198,6 +185,11 @@ return [ 'description' => 'The current user does not have a phone number associated with their account.', 'code' => 400, ], + Exception::USER_MISSING_ID => [ + 'name' => Exception::USER_MISSING_ID, + 'description' => 'Missing ID from OAuth2 provider', + 'code' => 400, + ], /** Teams */ Exception::TEAM_NOT_FOUND => [ @@ -513,11 +505,6 @@ return [ 'description' => 'Invalid URL received for OAuth failure redirect.', 'code' => 400, ], - Exception::PROJECT_MISSING_USER_ID => [ - 'name' => Exception::PROJECT_MISSING_USER_ID, - 'description' => 'Failed to obtain user ID from the OAuth provider.', - 'code' => 400, - ], Exception::PROJECT_KEY_EXPIRED => [ 'name' => Exception::PROJECT_KEY_EXPIRED, 'description' => 'The project key has expired. Please generate a new key using the Appwrite console.', diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 1b0437087c..ed8f82cc50 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -422,7 +422,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $response->redirect($state['failure'], 301, 0); } - throw new Exception(Exception::ACCOUNT_OAUTH_FAILED_TO_OBTAIN_TOKEN); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to obtain access token'); } $oauth2ID = $oauth2->getUserID($accessToken); @@ -432,7 +432,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $response->redirect($state['failure'], 301, 0); } - throw new Exception(Exception::ACCOUNT_OAUTH_MISSING_ID); + throw new Exception(Exception::USER_MISSING_ID); } $sessions = $user->getAttribute('sessions', []); diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 4be19ae01c..c1170ee8fd 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -14,7 +14,6 @@ class Exception extends \Exception * * Appwrite has the follwing entities: * - General - * - Account * - Users * - Teams * - Memberships @@ -54,10 +53,6 @@ class Exception extends \Exception public const GENERAL_SERVER_ERROR = 'general_server_error'; public const GENERAL_PROTOCOL_UNSUPPORTED = 'general_protocol_unsupported'; - /** Account */ - public const ACCOUNT_OAUTH_FAILED_TO_OBTAIN_TOKEN = 'account_oauth_failed_to_obtain_token'; - public const ACCOUNT_OAUTH_MISSING_ID = 'account_oauth_missing_id'; - /** Users */ public const USER_COUNT_EXCEEDED = 'user_count_exceeded'; public const USER_JWT_INVALID = 'user_jwt_invalid'; @@ -78,6 +73,7 @@ class Exception extends \Exception public const USER_AUTH_METHOD_UNSUPPORTED = 'user_auth_method_unsupported'; public const USER_PHONE_ALREADY_EXISTS = 'user_phone_already_exists'; public const USER_PHONE_NOT_FOUND = 'user_phone_not_found'; + public const USER_MISSING_ID = 'user_missing_id'; /** Teams */ public const TEAM_NOT_FOUND = 'team_not_found'; @@ -90,7 +86,7 @@ class Exception extends \Exception /** Membership */ public const MEMBERSHIP_NOT_FOUND = 'membership_not_found'; - public const MEMBERSHIP_ALREADY_CONFIRMED = 'membership_already_confirmed'; + public const MEMBERSHIP_ALREADY_CONFIRMED = 'membership_already_confirmed'; /** Avatars */ public const AVATAR_SET_NOT_FOUND = 'avatar_set_not_found'; @@ -127,8 +123,8 @@ class Exception extends \Exception public const EXECUTION_NOT_FOUND = 'execution_not_found'; /** Databases */ - public const DATABASE_NOT_FOUND = 'database_not_found'; - public const DATABASE_ALREADY_EXISTS = 'database_already_exists'; + public const DATABASE_NOT_FOUND = 'database_not_found'; + public const DATABASE_ALREADY_EXISTS = 'database_already_exists'; /** Collections */ public const COLLECTION_NOT_FOUND = 'collection_not_found'; @@ -163,7 +159,6 @@ class Exception extends \Exception public const PROJECT_PROVIDER_UNSUPPORTED = 'project_provider_unsupported'; public const PROJECT_INVALID_SUCCESS_URL = 'project_invalid_success_url'; public const PROJECT_INVALID_FAILURE_URL = 'project_invalid_failure_url'; - public const PROJECT_MISSING_USER_ID = 'project_missing_user_id'; public const PROJECT_RESERVED_PROJECT = 'project_reserved_project'; public const PROJECT_KEY_EXPIRED = 'project_key_expired'; @@ -203,19 +198,17 @@ class Exception extends \Exception public const MOCK_400 = 'mock_400'; public const MOCK_500 = 'mock_500'; - protected $type = ''; - protected array $errors; + protected static array $errors = Config::getParam('errors'); - public function __construct(string $type, string $message = null, int $code = null, \Throwable $previous = null) + public function __construct(string $type = Exception::GENERAL_UNKNOWN, string $message = null, int $code = null, \Throwable $previous = null) { - $this->errors = Config::getParam('errors'); $this->type = $type; - if (isset($this->errors[$type])) { - $this->message = $this->errors[$type]['description']; - $this->code = $this->errors[$type]['code']; + if (isset(self::$errors[$type])) { + $this->code = self::$errors[$type]['code']; + $this->message = self::$errors[$type]['description']; } $this->message = $message ?? $this->message; From 5296e61d3557e851716c5e2df275a8b6ed86a31f Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sat, 13 Aug 2022 20:45:19 +0530 Subject: [PATCH 10/20] feat: address issues on accounts controller --- app/config/errors.php | 10 +++++----- app/controllers/api/account.php | 16 ++++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/config/errors.php b/app/config/errors.php index 28699ab081..00947e7fe0 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -102,7 +102,7 @@ return [ ], Exception::USER_ALREADY_EXISTS => [ 'name' => Exception::USER_ALREADY_EXISTS, - 'description' => 'A user with the same email ID already exists in your project.', + 'description' => 'A user with the same email already exists in your project.', 'code' => 409, ], Exception::USER_BLOCKED => [ @@ -152,7 +152,7 @@ return [ ], Exception::USER_EMAIL_ALREADY_EXISTS => [ 'name' => Exception::USER_EMAIL_ALREADY_EXISTS, - 'description' => 'Another user with the same email already exists in the current project.', + 'description' => 'A user with the same email already exists in the current project.', 'code' => 409, ], Exception::USER_PASSWORD_MISMATCH => [ @@ -187,7 +187,7 @@ return [ ], Exception::USER_MISSING_ID => [ 'name' => Exception::USER_MISSING_ID, - 'description' => 'Missing ID from OAuth2 provider', + 'description' => 'Missing ID from OAuth2 provider.', 'code' => 400, ], @@ -497,12 +497,12 @@ return [ ], Exception::PROJECT_INVALID_SUCCESS_URL => [ 'name' => Exception::PROJECT_INVALID_SUCCESS_URL, - 'description' => 'Invalid URL received for OAuth success redirect.', + 'description' => 'Invalid redirect URL for OAuth success.', 'code' => 400, ], Exception::PROJECT_INVALID_FAILURE_URL => [ 'name' => Exception::PROJECT_INVALID_FAILURE_URL, - 'description' => 'Invalid URL received for OAuth failure redirect.', + 'description' => 'Invalid redirect URL for OAuth failure.', 'code' => 400, ], Exception::PROJECT_KEY_EXPIRED => [ diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index ed8f82cc50..f1c6638853 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -397,7 +397,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') try { $state = \array_merge($defaultState, $oauth2->parseState($state)); } catch (\Exception$exception) { - throw new Exception(Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to parse login state params as passed from OAuth2 provider'); } } else { $state = $defaultState; @@ -619,7 +619,7 @@ App::post('/v1/account/sessions/magic-url') ->action(function (string $userId, string $email, string $url, Request $request, Response $response, Document $project, Database $dbForProject, Locale $locale, Audit $audits, Event $events, Mail $mails) { if (empty(App::getEnv('_APP_SMTP_HOST'))) { - throw new Exception(Exception::GENERAL_SMTP_DISABLED); + throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled'); } $roles = Authorization::getRoles(); @@ -860,7 +860,7 @@ App::post('/v1/account/sessions/phone') ->inject('phone') ->action(function (string $userId, string $number, Request $request, Response $response, Document $project, Database $dbForProject, Audit $audits, Event $events, EventPhone $messaging, Phone $phone) { if (empty(App::getEnv('_APP_PHONE_PROVIDER'))) { - throw new Exception(Exception::GENERAL_PHONE_DISABLED); + throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); } $roles = Authorization::getRoles(); @@ -1092,11 +1092,11 @@ App::post('/v1/account/sessions/anonymous') $protocol = $request->getProtocol(); if ('console' === $project->getId()) { - throw new Exception(Exception::USER_ANONYMOUS_CONSOLE_PROHIBITED); + throw new Exception(Exception::USER_ANONYMOUS_CONSOLE_PROHIBITED, 'Failed to create anonymous user'); } if (!$user->isEmpty()) { - throw new Exception(Exception::USER_SESSION_ALREADY_EXISTS); + throw new Exception(Exception::USER_SESSION_ALREADY_EXISTS, 'Cannot create an anonymous user when logged in'); } $limit = $project->getAttribute('auths', [])['limit'] ?? 0; @@ -1941,7 +1941,7 @@ App::post('/v1/account/recovery') ->action(function (string $email, string $url, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Mail $mails, Audit $audits, Event $events, Stats $usage) { if (empty(App::getEnv('_APP_SMTP_HOST'))) { - throw new Exception(Exception::GENERAL_SMTP_DISABLED); + throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled'); } $roles = Authorization::getRoles(); @@ -2115,7 +2115,7 @@ App::post('/v1/account/verification') ->action(function (string $url, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Audit $audits, Event $events, Mail $mails, Stats $usage) { if (empty(App::getEnv('_APP_SMTP_HOST'))) { - throw new Exception(Exception::GENERAL_SMTP_DISABLED); + throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled'); } $roles = Authorization::getRoles(); @@ -2265,7 +2265,7 @@ App::post('/v1/account/verification/phone') ->action(function (Request $request, Response $response, Phone $phone, Document $user, Database $dbForProject, Audit $audits, Event $events, Stats $usage, EventPhone $messaging) { if (empty(App::getEnv('_APP_PHONE_PROVIDER'))) { - throw new Exception(Exception::GENERAL_PHONE_DISABLED); + throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); } if (empty($user->getAttribute('phone'))) { From c6f0b14e925de1473a3b74dbdcd914c7e70703a1 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 14 Aug 2022 11:05:25 +0530 Subject: [PATCH 11/20] feat: address issues on init.php --- app/config/errors.php | 7 ------- app/init.php | 4 ++-- src/Appwrite/Extend/Exception.php | 3 --- 3 files changed, 2 insertions(+), 12 deletions(-) diff --git a/app/config/errors.php b/app/config/errors.php index 00947e7fe0..f8cded257a 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -541,13 +541,6 @@ return [ 'code' => 401, ], - /* Registry Errors */ - Exception::LOGGER_NOT_SUPPORTED => [ - 'name' => Exception::LOGGER_NOT_SUPPORTED, - 'description' => 'Logging provider not supported. Logging disabled.', - 'code' => 500, - ], - /** Mocks */ Exception::MOCK_INVALID_CONTENT_RANGE_HEADER => [ 'name' => Exception::MOCK_INVALID_CONTENT_RANGE_HEADER, diff --git a/app/init.php b/app/init.php index e8b69e40f2..488e30f05b 100644 --- a/app/init.php +++ b/app/init.php @@ -448,7 +448,7 @@ $register->set('logger', function () { } if (!Logger::hasProvider($providerName)) { - throw new Exception(Exception::LOGGER_NOT_SUPPORTED); + throw new Exception(Exception::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled"); } $classname = '\\Utopia\\Logger\\Adapter\\' . \ucfirst($providerName); @@ -818,7 +818,7 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons try { $payload = $jwt->decode($authJWT); } catch (JWTException $error) { - throw new Exception(Exception::USER_JWT_INVALID); + throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. ' . $error->getMessage()); } $jwtUserId = $payload['userId'] ?? ''; diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index c1170ee8fd..7ce41b3d12 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -176,9 +176,6 @@ class Exception extends \Exception public const DOMAIN_ALREADY_EXISTS = 'domain_already_exists'; public const DOMAIN_VERIFICATION_FAILED = 'domain_verification_failed'; - /** Logger */ - public const LOGGER_NOT_SUPPORTED = 'logger_not_supported'; - /** Mocks */ public const MOCK_INVALID_CONTENT_RANGE_HEADER = 'mock_invalid_content_range_header'; public const MOCK_FIRST_CHUNK_CANNOT_HAVE_ID = 'mock_first_chunk_cannot_have_id'; From c2f7cd6d191698fabcf605262b4d6784fd933f73 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 14 Aug 2022 11:06:04 +0530 Subject: [PATCH 12/20] feat: remove logger as an entity --- src/Appwrite/Extend/Exception.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 7ce41b3d12..60d11619f3 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -31,7 +31,6 @@ class Exception extends \Exception * - Keys * - Platform * - Domain - * - Logger * - Mocks */ From 662bfe0ab8791791b2524944df3806569427f15e Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 14 Aug 2022 11:39:46 +0530 Subject: [PATCH 13/20] feat: address issues on projects.php --- app/config/errors.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/config/errors.php b/app/config/errors.php index f8cded257a..95f706816a 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -505,6 +505,11 @@ return [ 'description' => 'Invalid redirect URL for OAuth failure.', 'code' => 400, ], + Exception::PROJECT_RESERVED_PROJECT => [ + 'name' => Exception::PROJECT_RESERVED_PROJECT, + 'description' => 'The project ID is reserved. Please choose another project ID.', + 'code' => 400, + ], Exception::PROJECT_KEY_EXPIRED => [ 'name' => Exception::PROJECT_KEY_EXPIRED, 'description' => 'The project key has expired. Please generate a new key using the Appwrite console.', From a5bc0a61ca0995ae353048aaa33c570d463a1aa2 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 14 Aug 2022 12:26:12 +0530 Subject: [PATCH 14/20] feat: exceptions on more files --- app/config/errors.php | 101 ++---------------------------- app/controllers/api/avatars.php | 2 +- app/controllers/api/databases.php | 6 +- app/controllers/api/functions.php | 4 +- app/controllers/api/graphql.php | 2 +- app/controllers/api/storage.php | 4 +- app/controllers/api/teams.php | 4 +- app/controllers/general.php | 2 +- app/controllers/mock.php | 42 ++++++------- src/Appwrite/Extend/Exception.php | 21 ------- 10 files changed, 37 insertions(+), 151 deletions(-) diff --git a/app/config/errors.php b/app/config/errors.php index 95f706816a..dc07689205 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -222,13 +222,6 @@ return [ 'description' => 'The invite does not belong to the current user.', 'code' => 401, ], - Exception::TEAM_ID_MISMATCH => [ - 'name' => Exception::TEAM_ID_MISMATCH, - 'description' => 'Team IDs don\'t match', - 'code' => 404, - ], - - /** Membership */ Exception::MEMBERSHIP_NOT_FOUND => [ @@ -287,12 +280,12 @@ return [ ], Exception::STORAGE_FILE_TYPE_UNSUPPORTED => [ 'name' => Exception::STORAGE_FILE_TYPE_UNSUPPORTED, - 'description' => 'The file type is not supported.', + 'description' => 'The given file extension is not supported.', 'code' => 400, ], Exception::STORAGE_INVALID_FILE_SIZE => [ 'name' => Exception::STORAGE_INVALID_FILE_SIZE, - 'description' => 'File size not allowed', + 'description' => 'The file size is either not valid or exceeds the maximum allowed size. Please check the file or the value of the _APP_STORAGE_LIMIT environment variable.', 'code' => 400, ], Exception::STORAGE_INVALID_FILE => [ @@ -364,6 +357,7 @@ return [ 'code' => 404, ], + /** Databases */ Exception::DATABASE_NOT_FOUND => [ 'name' => Exception::DATABASE_NOT_FOUND, 'description' => 'Database not found', @@ -438,7 +432,7 @@ return [ ], Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED => [ 'name' => Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, - 'description' => 'Cannot set default value for array attributes', + 'description' => 'Default values cannot be set for array and required attributes.', 'code' => 400, ], Exception::ATTRIBUTE_ALREADY_EXISTS => [ @@ -545,91 +539,4 @@ return [ 'description' => 'Domain verification for the requested domain has failed.', 'code' => 401, ], - - /** Mocks */ - Exception::MOCK_INVALID_CONTENT_RANGE_HEADER => [ - 'name' => Exception::MOCK_INVALID_CONTENT_RANGE_HEADER, - 'description' => 'Invalid content-range header', - 'code' => 400, - ], - Exception::MOCK_FIRST_CHUNK_CANNOT_HAVE_ID => [ - 'name' => Exception::MOCK_FIRST_CHUNK_CANNOT_HAVE_ID, - 'description' => 'First chunked request cannot have id header', - 'code' => 400, - ], - Exception::MOCK_CHUNK_MISSING_ID => [ - 'name' => Exception::MOCK_CHUNK_MISSING_ID, - 'description' => 'All chunked request must have id header (except first)', - 'code' => 400, - ], - Exception::MOCK_CHUNK_INVALID_SIZE => [ - 'name' => Exception::MOCK_CHUNK_INVALID_SIZE, - 'description' => 'Chunk size must be 5MB (except last chunk)', - 'code' => 400, - ], - Exception::MOCK_INVALID_FILE_NAME => [ - 'name' => Exception::MOCK_INVALID_FILE_NAME, - 'description' => 'Wrong file name', - 'code' => 400, - ], - Exception::MOCK_INVALID_FILE_SIZE => [ - 'name' => Exception::MOCK_INVALID_FILE_SIZE, - 'description' => 'Wrong file size', - 'code' => 400, - ], - Exception::MOCK_WRONG_FILE_UPLOADED => [ - 'name' => Exception::MOCK_WRONG_FILE_UPLOADED, - 'description' => 'Wrong file uploaded', - 'code' => 400, - ], - Exception::MOCK_MISSING_COOKIE => [ - 'name' => Exception::MOCK_MISSING_COOKIE, - 'description' => 'Missing cookie value', - 'code' => 400, - ], - Exception::MOCK_INVALID_CLIENT_ID => [ - 'name' => Exception::MOCK_INVALID_CLIENT_ID, - 'description' => 'Invalid client ID', - 'code' => 400, - ], - Exception::MOCK_INVALID_CLIENT_SECRET => [ - 'name' => Exception::MOCK_INVALID_CLIENT_SECRET, - 'description' => 'Invalid client secret', - 'code' => 400, - ], - Exception::MOCK_INVALID_TOKEN => [ - 'name' => Exception::MOCK_INVALID_TOKEN, - 'description' => 'Invalid token', - 'code' => 400, - ], - Exception::MOCK_INVALID_REFRESH_TOKEN => [ - 'name' => Exception::MOCK_INVALID_REFRESH_TOKEN, - 'description' => 'Invalid refresh token', - 'code' => 400, - ], - Exception::MOCK_INVALID_GRANT_TYPE => [ - 'name' => Exception::MOCK_INVALID_GRANT_TYPE, - 'description' => 'Invalid grant type', - 'code' => 400, - ], - Exception::MOCK_FAILED_TO_READ_RESULTS => [ - 'name' => Exception::MOCK_FAILED_TO_READ_RESULTS, - 'description' => 'Failed to read results', - 'code' => 500, - ], - Exception::MOCK_FAILED_TO_SAVE_RESULTS => [ - 'name' => Exception::MOCK_FAILED_TO_SAVE_RESULTS, - 'description' => 'Failed to save results', - 'code' => 500, - ], - Exception::MOCK_400 => [ - 'name' => Exception::MOCK_400, - 'description' => 'Mock 400 error', - 'code' => 400, - ], - Exception::MOCK_500 => [ - 'name' => Exception::MOCK_500, - 'description' => 'Mock 500 error', - 'code' => 500, - ], ]; diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index ab61ffdd5a..afdd7d4daf 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -312,7 +312,7 @@ App::get('/v1/avatars/favicon') $data = @\file_get_contents($outputHref, false); if (empty($data) || (\mb_substr($data, 0, 5) === 'save($key, $data); diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index a9199ff426..c186dacddc 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -84,11 +84,11 @@ function createAttribute(string $databaseId, string $collectionId, Document $att // Must throw here since dbForProject->createAttribute is performed by db worker if ($required && $default) { - throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED); + throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for required attribute'); } if ($array && $default) { - throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED); + throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for array attribute'); } try { @@ -184,7 +184,7 @@ App::post('/v1/databases') $collections = Config::getParam('collections', [])['collections'] ?? []; if (empty($collections)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Collections collection is not configured.'); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'The "collections" collection is not configured.'); } $attributes = []; diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 9800fd4d34..7f476aeeeb 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -826,11 +826,11 @@ App::post('/v1/functions/:functionId/executions') $deployment = Authorization::skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', ''))); if ($deployment->getAttribute('resourceId') !== $function->getId()) { - throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function'); } if ($deployment->isEmpty()) { - throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function'); } /** Check if build has completed */ diff --git a/app/controllers/api/graphql.php b/app/controllers/api/graphql.php index a048a77666..f4df08ce05 100644 --- a/app/controllers/api/graphql.php +++ b/app/controllers/api/graphql.php @@ -19,6 +19,6 @@ App::post('/v1/graphql') ->label('scope', 'public') ->action( function () { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'GraphQL support is coming soon!'); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'GraphQL support is coming soon!', 503); } ); diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 76790d65bc..8ddfb6d9a4 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -302,7 +302,7 @@ App::delete('/v1/storage/buckets/:bucketId') } if (!$dbForProject->deleteDocument('buckets', $bucketId)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove project from DB'); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove bucket from DB'); } $deletes @@ -452,7 +452,7 @@ App::post('/v1/storage/buckets/:bucketId/files') // Check if file size is exceeding allowed limit $fileSizeValidator = new FileSize($maximumFileSize); if (!$fileSizeValidator->isValid($fileSize)) { - throw new Exception(Exception::STORAGE_INVALID_FILE_SIZE); + throw new Exception(Exception::STORAGE_INVALID_FILE_SIZE, 'File size not allowed'); } $upload = new Upload(); diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 0aadeb5f5f..4e2dc4e6ea 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -320,7 +320,7 @@ App::post('/v1/teams/:teamId/memberships') $total = $dbForProject->count('users', [], APP_LIMIT_USERS); if ($total >= $limit) { - throw new Exception(Exception::USER_COUNT_EXCEEDED); + throw new Exception(Exception::USER_COUNT_EXCEEDED, 'Project registration is restricted. Contact your administrator for more information.'); } } @@ -782,7 +782,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId') } if ($membership->getAttribute('teamId') !== $teamId) { - throw new Exception(Exception::TEAM_ID_MISMATCH); + throw new Exception(Exception::TEAM_MEMBERSHIP_MISMATCH); } $user = $dbForProject->getDocument('users', $membership->getAttribute('userId')); diff --git a/app/controllers/general.php b/app/controllers/general.php index 341ce18845..9c2c5d3b81 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -239,7 +239,7 @@ App::init() && $route->getLabel('origin', false) !== '*' && empty($request->getHeader('x-appwrite-key', '')) ) { - throw new AppwriteException(AppwriteException::GENERAL_UNKNOWN_ORIGIN, $originValidator->getDescription(), 403); + throw new AppwriteException(AppwriteException::GENERAL_UNKNOWN_ORIGIN, $originValidator->getDescription()); } /* diff --git a/app/controllers/mock.php b/app/controllers/mock.php index 55a15d1966..0c6b986277 100644 --- a/app/controllers/mock.php +++ b/app/controllers/mock.php @@ -253,31 +253,31 @@ App::post('/v1/mock/tests/general/upload') $file['size'] = (\is_array($file['size'])) ? $file['size'][0] : $file['size']; if (is_null($start) || is_null($end) || is_null($size)) { - throw new Exception(Exception::GENERAL_MOCK); + throw new Exception(Exception::GENERAL_MOCK, 'Invalid content-range header'); } if ($start > $end || $end > $size) { - throw new Exception(Exception::GENERAL_MOCK); + throw new Exception(Exception::GENERAL_MOCK, 'Invalid content-range header'); } if ($start === 0 && !empty($id)) { - throw new Exception(Exception::GENERAL_MOCK); + throw new Exception(Exception::GENERAL_MOCK, 'First chunked request cannot have id header'); } if ($start !== 0 && $id !== 'newfileid') { - throw new Exception(Exception::GENERAL_MOCK); + throw new Exception(Exception::GENERAL_MOCK, 'All chunked request must have id header (except first)'); } if ($end !== $size && $end - $start + 1 !== $chunkSize) { - throw new Exception(Exception::GENERAL_MOCK); + throw new Exception(Exception::GENERAL_MOCK, 'Chunk size must be 5MB (except last chunk)'); } if ($end !== $size && $file['size'] !== $chunkSize) { - throw new Exception(Exception::GENERAL_MOCK); + throw new Exception(Exception::GENERAL_MOCK, 'Wrong chunk size'); } if ($file['size'] > $chunkSize) { - throw new Exception(Exception::GENERAL_MOCK); + throw new Exception(Exception::GENERAL_MOCK, 'Chunk size must be 5MB or less'); } if ($end !== $size) { @@ -293,15 +293,15 @@ App::post('/v1/mock/tests/general/upload') $file['size'] = (\is_array($file['size'])) ? $file['size'][0] : $file['size']; if ($file['name'] !== 'file.png') { - throw new Exception(Exception::MOCK_INVALID_FILE_NAME); + throw new Exception(Exception::GENERAL_MOCK, 'Wrong file name'); } if ($file['size'] !== 38756) { - throw new Exception(Exception::MOCK_INVALID_FILE_SIZE); + throw new Exception(Exception::GENERAL_MOCK, 'Wrong file size'); } if (\md5(\file_get_contents($file['tmp_name'])) !== 'd80e7e6999a3eb2ae0d631a96fe135a4') { - throw new Exception(Exception::MOCK_WRONG_FILE_UPLOADED); + throw new Exception(Exception::GENERAL_MOCK, 'Wrong file uploaded'); } } }); @@ -374,7 +374,7 @@ App::get('/v1/mock/tests/general/get-cookie') ->action(function (Request $request) { if ($request->getCookie('cookieName', '') !== 'cookieValue') { - throw new Exception(Exception::MOCK_MISSING_COOKIE); + throw new Exception(Exception::GENERAL_MOCK, 'Missing cookie value'); } }); @@ -408,7 +408,7 @@ App::get('/v1/mock/tests/general/400-error') ->label('sdk.response.model', Response::MODEL_ERROR) ->label('sdk.mock', true) ->action(function () { - throw new Exception(Exception::MOCK_400); + throw new Exception(Exception::GENERAL_MOCK, 'Mock 400 error'); }); App::get('/v1/mock/tests/general/500-error') @@ -424,7 +424,7 @@ App::get('/v1/mock/tests/general/500-error') ->label('sdk.response.model', Response::MODEL_ERROR) ->label('sdk.mock', true) ->action(function () { - throw new Exception(Exception::MOCK_500); + throw new Exception(Exception::GENERAL_MOCK, 'Mock 500 error', 500); }); App::get('/v1/mock/tests/general/502-error') @@ -480,11 +480,11 @@ App::get('/v1/mock/tests/general/oauth2/token') ->action(function (string $client_id, string $client_secret, string $grantType, string $redirectURI, string $code, string $refreshToken, Response $response) { if ($client_id != '1') { - throw new Exception(Exception::MOCK_INVALID_CLIENT_ID); + throw new Exception(Exception::GENERAL_MOCK, 'Invalid client ID'); } if ($client_secret != '123456') { - throw new Exception(Exception::MOCK_INVALID_CLIENT_SECRET); + throw new Exception(Exception::GENERAL_MOCK, 'Invalid client secret'); } $responseJson = [ @@ -495,18 +495,18 @@ App::get('/v1/mock/tests/general/oauth2/token') if ($grantType === 'authorization_code') { if ($code !== 'abcdef') { - throw new Exception(Exception::MOCK_INVALID_TOKEN); + throw new Exception(Exception::GENERAL_MOCK, 'Invalid token'); } $response->json($responseJson); } elseif ($grantType === 'refresh_token') { if ($refreshToken !== 'tuvwxyz') { - throw new Exception(Exception::MOCK_INVALID_REFRESH_TOKEN); + throw new Exception(Exception::GENERAL_MOCK, 'Invalid refresh token'); } $response->json($responseJson); } else { - throw new Exception(Exception::MOCK_INVALID_GRANT_TYPE); + throw new Exception(Exception::GENERAL_MOCK, 'Invalid grant type'); } }); @@ -520,7 +520,7 @@ App::get('/v1/mock/tests/general/oauth2/user') ->action(function (string $token, Response $response) { if ($token != '123456') { - throw new Exception(Exception::MOCK_INVALID_TOKEN); + throw new Exception(Exception::GENERAL_MOCK, 'Invalid token'); } $response->json([ @@ -571,7 +571,7 @@ App::shutdown() $tests = (\file_exists($path)) ? \json_decode(\file_get_contents($path), true) : []; if (!\is_array($tests)) { - throw new Exception(Exception::MOCK_FAILED_TO_READ_RESULTS); + throw new Exception(Exception::GENERAL_MOCK, 'Failed to read results', 500); } $result[$route->getMethod() . ':' . $route->getPath()] = true; @@ -579,7 +579,7 @@ App::shutdown() $tests = \array_merge($tests, $result); if (!\file_put_contents($path, \json_encode($tests), LOCK_EX)) { - throw new Exception(Exception::MOCK_FAILED_TO_SAVE_RESULTS); + throw new Exception(Exception::GENERAL_MOCK, 'Failed to save results', 500); } $response->dynamic(new Document(['result' => $route->getMethod() . ':' . $route->getPath() . ':passed']), Response::MODEL_MOCK); diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 60d11619f3..70f3ee8e65 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -31,7 +31,6 @@ class Exception extends \Exception * - Keys * - Platform * - Domain - * - Mocks */ /** General */ @@ -81,7 +80,6 @@ class Exception extends \Exception public const TEAM_INVALID_SECRET = 'team_invalid_secret'; public const TEAM_MEMBERSHIP_MISMATCH = 'team_membership_mismatch'; public const TEAM_INVITE_MISMATCH = 'team_invite_mismatch'; - public const TEAM_ID_MISMATCH = 'team_id_mismatch'; /** Membership */ public const MEMBERSHIP_NOT_FOUND = 'membership_not_found'; @@ -175,25 +173,6 @@ class Exception extends \Exception public const DOMAIN_ALREADY_EXISTS = 'domain_already_exists'; public const DOMAIN_VERIFICATION_FAILED = 'domain_verification_failed'; - /** Mocks */ - public const MOCK_INVALID_CONTENT_RANGE_HEADER = 'mock_invalid_content_range_header'; - public const MOCK_FIRST_CHUNK_CANNOT_HAVE_ID = 'mock_first_chunk_cannot_have_id'; - public const MOCK_CHUNK_MISSING_ID = 'mock_chunk_missing_id'; - public const MOCK_CHUNK_INVALID_SIZE = 'mock_chunk_invalid_size'; - public const MOCK_INVALID_FILE_NAME = 'mock_invalid_file_name'; - public const MOCK_INVALID_FILE_SIZE = 'mock_invalid_file_size'; - public const MOCK_WRONG_FILE_UPLOADED = 'mock_wrong_file_uploaded'; - public const MOCK_MISSING_COOKIE = 'mock_missing_cookie'; - public const MOCK_INVALID_CLIENT_ID = 'mock_invalid_client_id'; - public const MOCK_INVALID_CLIENT_SECRET = 'mock_invalid_client_secret'; - public const MOCK_INVALID_TOKEN = 'mock_invalid_token'; - public const MOCK_INVALID_REFRESH_TOKEN = 'mock_invalid_refresh_token'; - public const MOCK_INVALID_GRANT_TYPE = 'mock_invalid_grant_type'; - public const MOCK_FAILED_TO_READ_RESULTS = 'mock_failed_to_read_results'; - public const MOCK_FAILED_TO_SAVE_RESULTS = 'mock_failed_to_save_results'; - public const MOCK_400 = 'mock_400'; - public const MOCK_500 = 'mock_500'; - protected $type = ''; protected static array $errors = Config::getParam('errors'); From cfab9221f21889d22aa3ebe72f29ea84e1ce3e3f Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 14 Aug 2022 12:32:41 +0530 Subject: [PATCH 15/20] feat: exceptions on more files --- app/controllers/api/databases.php | 4 +-- app/controllers/api/storage.php | 2 +- .../Databases/DatabasesCustomServerTest.php | 26 +++++++++---------- tests/e2e/Services/Storage/StorageBase.php | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index c186dacddc..72afb1f82c 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -116,7 +116,7 @@ function createAttribute(string $databaseId, string $collectionId, Document $att } catch (DuplicateException $exception) { throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS); } catch (LimitException $exception) { - throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED); + throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED, 'Attribute limit exceeded'); } $dbForProject->deleteCachedDocument('database_' . $db->getInternalId(), $collectionId); @@ -1527,7 +1527,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') $limit = 64 - MariaDB::getNumberOfDefaultIndexes(); if ($count >= $limit) { - throw new Exception(Exception::INDEX_LIMIT_EXCEEDED); + throw new Exception(Exception::INDEX_LIMIT_EXCEEDED, 'Index limit exceeded.'); } // Convert Document[] to array of attribute metadata diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 8ddfb6d9a4..9d897b4bb1 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -446,7 +446,7 @@ App::post('/v1/storage/buckets/:bucketId/files') $allowedFileExtensions = $bucket->getAttribute('allowedFileExtensions', []); $fileExt = new FileExt($allowedFileExtensions); if (!empty($allowedFileExtensions) && !$fileExt->isValid($fileName)) { - throw new Exception(Exception::STORAGE_FILE_TYPE_UNSUPPORTED); + throw new Exception(Exception::STORAGE_FILE_TYPE_UNSUPPORTED, 'File extension not allowed'); } // Check if file size is exceeding allowed limit diff --git a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php index d229f0ae73..9f4c649fdf 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php +++ b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php @@ -618,8 +618,8 @@ class DatabasesCustomServerTest extends Scope 'required' => true, ]); - $this->assertEquals(201, $attribute1['headers']['status-code']); - $this->assertEquals(201, $attribute2['headers']['status-code']); + $this->assertEquals(202, $attribute1['headers']['status-code']); + $this->assertEquals(202, $attribute2['headers']['status-code']); $this->assertEquals('attribute1', $attribute1['body']['key']); $this->assertEquals('attribute2', $attribute2['body']['key']); @@ -646,8 +646,8 @@ class DatabasesCustomServerTest extends Scope 'attributes' => ['attribute2'], ]); - $this->assertEquals(201, $index1['headers']['status-code']); - $this->assertEquals(201, $index2['headers']['status-code']); + $this->assertEquals(202, $index1['headers']['status-code']); + $this->assertEquals(202, $index2['headers']['status-code']); $this->assertEquals('index1', $index1['body']['key']); $this->assertEquals('index2', $index2['body']['key']); @@ -742,8 +742,8 @@ class DatabasesCustomServerTest extends Scope 'required' => true, ]); - $this->assertEquals(201, $attribute1['headers']['status-code']); - $this->assertEquals(201, $attribute2['headers']['status-code']); + $this->assertEquals(202, $attribute1['headers']['status-code']); + $this->assertEquals(202, $attribute2['headers']['status-code']); $this->assertEquals('attribute1', $attribute1['body']['key']); $this->assertEquals('attribute2', $attribute2['body']['key']); @@ -770,8 +770,8 @@ class DatabasesCustomServerTest extends Scope 'attributes' => ['attribute2'], ]); - $this->assertEquals(201, $index1['headers']['status-code']); - $this->assertEquals(201, $index2['headers']['status-code']); + $this->assertEquals(202, $index1['headers']['status-code']); + $this->assertEquals(202, $index2['headers']['status-code']); $this->assertEquals('index1', $index1['body']['key']); $this->assertEquals('index2', $index2['body']['key']); @@ -980,7 +980,7 @@ class DatabasesCustomServerTest extends Scope 'required' => true, ]); - $this->assertEquals($attribute['headers']['status-code'], 201); + $this->assertEquals($attribute['headers']['status-code'], 202); } sleep(5); @@ -996,7 +996,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $tooWide['headers']['status-code']); - $this->assertEquals('The maximum number of attributes has been reached.', $tooWide['body']['message']); + $this->assertEquals('Attribute limit exceeded', $tooWide['body']['message']); } public function testIndexLimitException() @@ -1043,7 +1043,7 @@ class DatabasesCustomServerTest extends Scope 'required' => true, ]); - $this->assertEquals($attribute['headers']['status-code'], 201); + $this->assertEquals($attribute['headers']['status-code'], 202); } sleep(20); @@ -1080,7 +1080,7 @@ class DatabasesCustomServerTest extends Scope 'attributes' => ["attribute{$i}"], ]); - $this->assertEquals(201, $index['headers']['status-code']); + $this->assertEquals(202, $index['headers']['status-code']); $this->assertEquals("key_attribute{$i}", $index['body']['key']); } @@ -1110,7 +1110,7 @@ class DatabasesCustomServerTest extends Scope ]); $this->assertEquals(400, $tooMany['headers']['status-code']); - $this->assertEquals('The maximum number of indexes has been reached.', $tooMany['body']['message']); + $this->assertEquals('Index limit exceeded', $tooMany['body']['message']); $collection = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $collectionId, array_merge([ 'content-type' => 'application/json', diff --git a/tests/e2e/Services/Storage/StorageBase.php b/tests/e2e/Services/Storage/StorageBase.php index c71d212079..edcaa0f8a6 100644 --- a/tests/e2e/Services/Storage/StorageBase.php +++ b/tests/e2e/Services/Storage/StorageBase.php @@ -185,7 +185,7 @@ trait StorageBase ]); $this->assertEquals(400, $res['headers']['status-code']); - $this->assertEquals('The file type is not supported.', $res['body']['message']); + $this->assertEquals('File extension not allowed', $res['body']['message']); return ['bucketId' => $bucketId, 'fileId' => $file['body']['$id'], 'largeFileId' => $largeFile['body']['$id'], 'largeBucketId' => $bucket2['body']['$id']]; From aeaf6eee185c085c5a911e31ee709b7aa96bb623 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 14 Aug 2022 12:35:47 +0530 Subject: [PATCH 16/20] fix: revert error codes --- .../e2e/Services/Storage/StorageCustomClientTest.php | 12 ++++++------ tests/e2e/Services/Teams/TeamsBaseClient.php | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/e2e/Services/Storage/StorageCustomClientTest.php b/tests/e2e/Services/Storage/StorageCustomClientTest.php index f9e3c08b21..fa30f6e762 100644 --- a/tests/e2e/Services/Storage/StorageCustomClientTest.php +++ b/tests/e2e/Services/Storage/StorageCustomClientTest.php @@ -164,7 +164,7 @@ class StorageCustomClientTest extends Scope 'read' => ['user:notme'] ]); - $this->assertEquals(401, $file['headers']['status-code']); + $this->assertEquals(400, $file['headers']['status-code']); $this->assertStringStartsWith('Read permissions must be one of:', $file['body']['message']); $this->assertStringContainsString('role:all', $file['body']['message']); $this->assertStringContainsString('role:member', $file['body']['message']); @@ -180,7 +180,7 @@ class StorageCustomClientTest extends Scope 'write' => ['user:notme'] ]); - $this->assertEquals($file['headers']['status-code'], 401); + $this->assertEquals($file['headers']['status-code'], 400); $this->assertStringStartsWith('Write permissions must be one of:', $file['body']['message']); $this->assertStringContainsString('role:all', $file['body']['message']); $this->assertStringContainsString('role:member', $file['body']['message']); @@ -197,7 +197,7 @@ class StorageCustomClientTest extends Scope 'write' => ['user:notme'] ]); - $this->assertEquals($file['headers']['status-code'], 401); + $this->assertEquals($file['headers']['status-code'], 400); $this->assertStringStartsWith('Read permissions must be one of:', $file['body']['message']); $this->assertStringContainsString('role:all', $file['body']['message']); $this->assertStringContainsString('role:member', $file['body']['message']); @@ -219,7 +219,7 @@ class StorageCustomClientTest extends Scope 'read' => ['user:notme'] ]); - $this->assertEquals($file['headers']['status-code'], 401); + $this->assertEquals($file['headers']['status-code'], 400); $this->assertStringStartsWith('Read permissions must be one of:', $file['body']['message']); $this->assertStringContainsString('role:all', $file['body']['message']); $this->assertStringContainsString('role:member', $file['body']['message']); @@ -232,7 +232,7 @@ class StorageCustomClientTest extends Scope 'write' => ['user:notme'] ]); - $this->assertEquals($file['headers']['status-code'], 401); + $this->assertEquals($file['headers']['status-code'], 400); $this->assertStringStartsWith('Write permissions must be one of:', $file['body']['message']); $this->assertStringContainsString('role:all', $file['body']['message']); $this->assertStringContainsString('role:member', $file['body']['message']); @@ -246,7 +246,7 @@ class StorageCustomClientTest extends Scope 'write' => ['user:notme'] ]); - $this->assertEquals($file['headers']['status-code'], 401); + $this->assertEquals($file['headers']['status-code'], 400); $this->assertStringStartsWith('Read permissions must be one of:', $file['body']['message']); $this->assertStringContainsString('role:all', $file['body']['message']); $this->assertStringContainsString('role:member', $file['body']['message']); diff --git a/tests/e2e/Services/Teams/TeamsBaseClient.php b/tests/e2e/Services/Teams/TeamsBaseClient.php index b76a684f13..08e2ddc8c8 100644 --- a/tests/e2e/Services/Teams/TeamsBaseClient.php +++ b/tests/e2e/Services/Teams/TeamsBaseClient.php @@ -440,7 +440,7 @@ trait TeamsBaseClient ]); $this->assertEquals(401, $response['headers']['status-code']); - $this->assertEquals('The current user is not authorized to perform the requested action.', $response['body']['message']); + $this->assertEquals('User is not allowed to modify roles', $response['body']['message']); return $data; } From fb81627b8335e46e1ae64c7e4ebdae3cb34f7c0f Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 14 Aug 2022 12:49:19 +0530 Subject: [PATCH 17/20] feat: update exception class --- src/Appwrite/Extend/Exception.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 70f3ee8e65..7b2f2644e6 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -175,15 +175,14 @@ class Exception extends \Exception protected $type = ''; - protected static array $errors = Config::getParam('errors'); - public function __construct(string $type = Exception::GENERAL_UNKNOWN, string $message = null, int $code = null, \Throwable $previous = null) { + $this->errors = Config::getParam('errors'); $this->type = $type; - if (isset(self::$errors[$type])) { - $this->code = self::$errors[$type]['code']; - $this->message = self::$errors[$type]['description']; + if (isset($this->errors[$type])) { + $this->code = $this->errors[$type]['code']; + $this->message = $this->errors[$type]['description']; } $this->message = $message ?? $this->message; From 5725fbef701cabfb6ba844431983ebbaebcc820a Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 14 Aug 2022 12:52:20 +0530 Subject: [PATCH 18/20] feat: update exception class --- .../Databases/DatabasesCustomServerTest.php | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php index 9f4c649fdf..cc75a13a80 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomServerTest.php +++ b/tests/e2e/Services/Databases/DatabasesCustomServerTest.php @@ -618,8 +618,8 @@ class DatabasesCustomServerTest extends Scope 'required' => true, ]); - $this->assertEquals(202, $attribute1['headers']['status-code']); - $this->assertEquals(202, $attribute2['headers']['status-code']); + $this->assertEquals(201, $attribute1['headers']['status-code']); + $this->assertEquals(201, $attribute2['headers']['status-code']); $this->assertEquals('attribute1', $attribute1['body']['key']); $this->assertEquals('attribute2', $attribute2['body']['key']); @@ -646,8 +646,8 @@ class DatabasesCustomServerTest extends Scope 'attributes' => ['attribute2'], ]); - $this->assertEquals(202, $index1['headers']['status-code']); - $this->assertEquals(202, $index2['headers']['status-code']); + $this->assertEquals(201, $index1['headers']['status-code']); + $this->assertEquals(201, $index2['headers']['status-code']); $this->assertEquals('index1', $index1['body']['key']); $this->assertEquals('index2', $index2['body']['key']); @@ -742,8 +742,8 @@ class DatabasesCustomServerTest extends Scope 'required' => true, ]); - $this->assertEquals(202, $attribute1['headers']['status-code']); - $this->assertEquals(202, $attribute2['headers']['status-code']); + $this->assertEquals(201, $attribute1['headers']['status-code']); + $this->assertEquals(201, $attribute2['headers']['status-code']); $this->assertEquals('attribute1', $attribute1['body']['key']); $this->assertEquals('attribute2', $attribute2['body']['key']); @@ -770,8 +770,8 @@ class DatabasesCustomServerTest extends Scope 'attributes' => ['attribute2'], ]); - $this->assertEquals(202, $index1['headers']['status-code']); - $this->assertEquals(202, $index2['headers']['status-code']); + $this->assertEquals(201, $index1['headers']['status-code']); + $this->assertEquals(201, $index2['headers']['status-code']); $this->assertEquals('index1', $index1['body']['key']); $this->assertEquals('index2', $index2['body']['key']); @@ -980,7 +980,7 @@ class DatabasesCustomServerTest extends Scope 'required' => true, ]); - $this->assertEquals($attribute['headers']['status-code'], 202); + $this->assertEquals($attribute['headers']['status-code'], 201); } sleep(5); @@ -1043,7 +1043,7 @@ class DatabasesCustomServerTest extends Scope 'required' => true, ]); - $this->assertEquals($attribute['headers']['status-code'], 202); + $this->assertEquals($attribute['headers']['status-code'], 201); } sleep(20); @@ -1080,7 +1080,7 @@ class DatabasesCustomServerTest extends Scope 'attributes' => ["attribute{$i}"], ]); - $this->assertEquals(202, $index['headers']['status-code']); + $this->assertEquals(201, $index['headers']['status-code']); $this->assertEquals("key_attribute{$i}", $index['body']['key']); } From 8e24dbe22f21b6aaae991c407dd25b7134cdf2ad Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 14 Aug 2022 13:35:11 +0530 Subject: [PATCH 19/20] feat: update exception class --- app/controllers/api/databases.php | 4 ++-- app/controllers/api/storage.php | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 72afb1f82c..a710a799b9 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -88,7 +88,7 @@ function createAttribute(string $databaseId, string $collectionId, Document $att } if ($array && $default) { - throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for array attribute'); + throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for array attributes'); } try { @@ -1527,7 +1527,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') $limit = 64 - MariaDB::getNumberOfDefaultIndexes(); if ($count >= $limit) { - throw new Exception(Exception::INDEX_LIMIT_EXCEEDED, 'Index limit exceeded.'); + throw new Exception(Exception::INDEX_LIMIT_EXCEEDED, 'Index limit exceeded'); } // Convert Document[] to array of attribute metadata diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 9d897b4bb1..6a361720c2 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -382,12 +382,12 @@ App::post('/v1/storage/buckets/:bucketId/files') if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) { foreach ($read as $role) { if (!Authorization::isRole($role)) { - throw new Exception(Exception::USER_UNAUTHORIZED, 'Read permissions must be one of: (' . \implode(', ', $roles) . ')'); + throw new Exception(Exception::USER_UNAUTHORIZED, 'Read permissions must be one of: (' . \implode(', ', $roles) . ')', 400); } } foreach ($write as $role) { if (!Authorization::isRole($role)) { - throw new Exception(Exception::USER_UNAUTHORIZED, 'Write permissions must be one of: (' . \implode(', ', $roles) . ')'); + throw new Exception(Exception::USER_UNAUTHORIZED, 'Write permissions must be one of: (' . \implode(', ', $roles) . ')', 400); } } } @@ -1308,12 +1308,12 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) { foreach ($read as $role) { if (!Authorization::isRole($role)) { - throw new Exception(Exception::USER_UNAUTHORIZED, 'Read permissions must be one of: (' . \implode(', ', $roles) . ')'); + throw new Exception(Exception::USER_UNAUTHORIZED, 'Read permissions must be one of: (' . \implode(', ', $roles) . ')', 400); } } foreach ($write as $role) { if (!Authorization::isRole($role)) { - throw new Exception(Exception::USER_UNAUTHORIZED, 'Write permissions must be one of: (' . \implode(', ', $roles) . ')'); + throw new Exception(Exception::USER_UNAUTHORIZED, 'Write permissions must be one of: (' . \implode(', ', $roles) . ')', 400); } } } From 70d2ebf55bb9405a250e3ab041bba1261f161120 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 14 Aug 2022 14:25:59 +0530 Subject: [PATCH 20/20] feat: tests --- app/controllers/api/teams.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 4e2dc4e6ea..fc1e0be0c4 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -594,7 +594,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId') $isOwner = Authorization::isRole('team:' . $team->getId() . '/owner'); if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server) - throw new Exception(Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED, 'User is not allowed to modify roles'); } /**