diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 385f8c4f4b..c7bf0097d3 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -20,7 +20,6 @@ use Appwrite\Utopia\Response; use Appwrite\Utopia\Database\Validator\CustomId; use MaxMind\Db\Reader; use Utopia\App; -use Appwrite\Event\Audit; use Appwrite\Event\Phone as EventPhone; use Utopia\Audit\Audit as EventAudit; use Utopia\Config\Config; @@ -47,6 +46,8 @@ App::post('/v1/account') ->label('event', 'users.[userId].create') ->label('scope', 'public') ->label('auth.type', 'emailPassword') + ->label('audits.resource', 'user/{response.$id}') + ->label('audits.userId', '{response.$id}') ->label('sdk.auth', []) ->label('sdk.namespace', 'account') ->label('sdk.method', 'create') @@ -63,10 +64,10 @@ App::post('/v1/account') ->inject('response') ->inject('project') ->inject('dbForProject') - ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (string $userId, string $email, string $password, string $name, Request $request, Response $response, Document $project, Database $dbForProject, Audit $audits, Stats $usage, Event $events) { + ->action(function (string $userId, string $email, string $password, string $name, Request $request, Response $response, Document $project, Database $dbForProject, Stats $usage, Event $events) { + $email = \strtolower($email); if ('console' === $project->getId()) { $whitelistEmails = $project->getAttribute('authWhitelistEmails'); @@ -121,11 +122,6 @@ App::post('/v1/account') Authorization::setRole('user:' . $user->getId()); Authorization::setRole('role:' . Auth::USER_ROLE_MEMBER); - $audits - ->setResource('user/' . $user->getId()) - ->setUser($user) - ; - $usage->setParam('users.create', 1); $events->setParam('userId', $user->getId()); @@ -140,6 +136,8 @@ App::post('/v1/account/sessions/email') ->label('event', 'users.[userId].sessions.[sessionId].create') ->label('scope', 'public') ->label('auth.type', 'emailPassword') + ->label('audits.resource', 'user/{response.userId}') + ->label('audits.userId', '{response.userId}') ->label('sdk.auth', []) ->label('sdk.namespace', 'account') ->label('sdk.method', 'createEmailSession') @@ -156,10 +154,9 @@ App::post('/v1/account/sessions/email') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (string $email, string $password, Request $request, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Audit $audits, Stats $usage, Event $events) { + ->action(function (string $email, string $password, Request $request, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Stats $usage, Event $events) { $email = \strtolower($email); $protocol = $request->getProtocol(); @@ -214,11 +211,6 @@ App::post('/v1/account/sessions/email') $dbForProject->deleteCachedDocument('users', $profile->getId()); - $audits - ->setResource('user/' . $profile->getId()) - ->setUser($profile) - ; - if (!Config::getParam('domainVerification')) { $response ->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($profile->getId(), $secret)])) @@ -366,6 +358,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ->label('error', __DIR__ . '/../../views/general/error.phtml') ->label('event', 'users.[userId].sessions.[sessionId].create') ->label('scope', 'public') + ->label('audits.resource', 'user/{user.$id}') ->label('abuse-limit', 50) ->label('abuse-key', 'ip:{ip}') ->label('docs', false) @@ -378,10 +371,9 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ->inject('user') ->inject('dbForProject') ->inject('geodb') - ->inject('audits') ->inject('events') ->inject('usage') - ->action(function (string $provider, string $code, string $state, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Audit $audits, Event $events, Stats $usage) use ($oauthDefaultSuccess) { + ->action(function (string $provider, string $code, string $state, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Event $events, Stats $usage) use ($oauthDefaultSuccess) { $protocol = $request->getProtocol(); $callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId(); @@ -560,11 +552,6 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $dbForProject->deleteCachedDocument('users', $user->getId()); - $audits - ->setResource('user/' . $user->getId()) - ->setUser($user) - ; - $usage ->setParam('users.sessions.create', 1) ->setParam('projectId', $project->getId()) @@ -602,12 +589,13 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ; }); - App::post('/v1/account/sessions/magic-url') ->desc('Create Magic URL session') ->groups(['api', 'account']) ->label('scope', 'public') ->label('auth.type', 'magic-url') + ->label('audits.resource', 'user/{response.userId}') + ->label('audits.userId', '{response.userId}') ->label('sdk.auth', []) ->label('sdk.namespace', 'account') ->label('sdk.method', 'createMagicURLSession') @@ -625,10 +613,9 @@ App::post('/v1/account/sessions/magic-url') ->inject('project') ->inject('dbForProject') ->inject('locale') - ->inject('audits') ->inject('events') ->inject('mails') - ->action(function (string $userId, string $email, string $url, Request $request, Response $response, Document $project, Database $dbForProject, Locale $locale, Audit $audits, Event $events, Mail $mails) { + ->action(function (string $userId, string $email, string $url, Request $request, Response $response, Document $project, Database $dbForProject, Locale $locale, Event $events, Mail $mails) { if (empty(App::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled'); @@ -723,11 +710,6 @@ App::post('/v1/account/sessions/magic-url') // Hide secret for clients $token->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $loginSecret : ''); - $audits - ->setResource('user/' . $user->getId()) - ->setUser($user) - ; - $response ->setStatusCode(Response::STATUS_CODE_CREATED) ->dynamic($token, Response::MODEL_TOKEN) @@ -739,6 +721,8 @@ App::put('/v1/account/sessions/magic-url') ->groups(['api', 'account']) ->label('scope', 'public') ->label('event', 'users.[userId].sessions.[sessionId].create') + ->label('audits.resource', 'user/{response.userId}') + ->label('audits.userId', '{response.userId}') ->label('sdk.auth', []) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updateMagicURLSession') @@ -755,9 +739,8 @@ App::put('/v1/account/sessions/magic-url') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->inject('audits') ->inject('events') - ->action(function (string $userId, string $secret, Request $request, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Audit $audits, Event $events) { + ->action(function (string $userId, string $secret, Request $request, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Event $events) { /** @var Utopia\Database\Document $user */ @@ -819,8 +802,6 @@ App::put('/v1/account/sessions/magic-url') throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed saving user to DB'); } - $audits->setResource('user/' . $user->getId()); - $events ->setParam('userId', $user->getId()) ->setParam('sessionId', $session->getId()) @@ -853,6 +834,8 @@ App::post('/v1/account/sessions/phone') ->groups(['api', 'account']) ->label('scope', 'public') ->label('auth.type', 'phone') + ->label('audits.resource', 'user/{response.userId}') + ->label('audits.userId', '{response.userId}') ->label('sdk.auth', []) ->label('sdk.namespace', 'account') ->label('sdk.method', 'createPhoneSession') @@ -868,10 +851,10 @@ App::post('/v1/account/sessions/phone') ->inject('response') ->inject('project') ->inject('dbForProject') - ->inject('audits') ->inject('events') ->inject('messaging') - ->action(function (string $userId, string $phone, Request $request, Response $response, Document $project, Database $dbForProject, Audit $audits, Event $events, EventPhone $messaging) { + ->action(function (string $userId, string $phone, Request $request, Response $response, Document $project, Database $dbForProject, Event $events, EventPhone $messaging) { + if (empty(App::getEnv('_APP_SMS_PROVIDER'))) { throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); } @@ -954,11 +937,6 @@ App::post('/v1/account/sessions/phone') // Hide secret for clients $token->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $secret : ''); - $audits - ->setResource('user/' . $user->getId()) - ->setUser($user) - ; - $response ->setStatusCode(Response::STATUS_CODE_CREATED) ->dynamic($token, Response::MODEL_TOKEN) @@ -966,7 +944,7 @@ App::post('/v1/account/sessions/phone') }); App::put('/v1/account/sessions/phone') - ->desc('Create Phone session (confirmation)') + ->desc('Create Phone Session (confirmation)') ->groups(['api', 'account']) ->label('scope', 'public') ->label('event', 'users.[userId].sessions.[sessionId].create') @@ -986,9 +964,8 @@ App::put('/v1/account/sessions/phone') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->inject('audits') ->inject('events') - ->action(function (string $userId, string $secret, Request $request, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Audit $audits, Event $events) { + ->action(function (string $userId, string $secret, Request $request, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Event $events) { $user = Authorization::skip(fn() => $dbForProject->getDocument('users', $userId)); @@ -1046,8 +1023,6 @@ App::put('/v1/account/sessions/phone') throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed saving user to DB'); } - $audits->setResource('user/' . $user->getId()); - $events ->setParam('userId', $user->getId()) ->setParam('sessionId', $session->getId()) @@ -1081,6 +1056,8 @@ App::post('/v1/account/sessions/anonymous') ->label('event', 'users.[userId].sessions.[sessionId].create') ->label('scope', 'public') ->label('auth.type', 'anonymous') + ->label('audits.resource', 'user/{response.userId}') + ->label('audits.userId', '{response.userId}') ->label('sdk.auth', []) ->label('sdk.namespace', 'account') ->label('sdk.method', 'createAnonymousSession') @@ -1097,10 +1074,9 @@ App::post('/v1/account/sessions/anonymous') ->inject('project') ->inject('dbForProject') ->inject('geodb') - ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Audit $audits, Stats $usage, Event $events) { + ->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Stats $usage, Event $events) { $protocol = $request->getProtocol(); @@ -1175,8 +1151,6 @@ App::post('/v1/account/sessions/anonymous') $dbForProject->deleteCachedDocument('users', $user->getId()); - $audits->setResource('user/' . $user->getId()); - $usage ->setParam('users.sessions.create', 1) ->setParam('provider', 'anonymous') @@ -1438,6 +1412,7 @@ App::patch('/v1/account/name') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.name') ->label('scope', 'account') + ->label('audits.resource', 'user/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updateName') @@ -1449,20 +1424,14 @@ App::patch('/v1/account/name') ->inject('response') ->inject('user') ->inject('dbForProject') - ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (string $name, Response $response, Document $user, Database $dbForProject, Audit $audits, Stats $usage, Event $events) { + ->action(function (string $name, Response $response, Document $user, Database $dbForProject, Stats $usage, Event $events) { $user = $dbForProject->updateDocument('users', $user->getId(), $user ->setAttribute('name', $name) ->setAttribute('search', implode(' ', [$user->getId(), $name, $user->getAttribute('email', ''), $user->getAttribute('phone', '')]))); - $audits - ->setResource('user/' . $user->getId()) - ->setUser($user) - ; - $usage->setParam('users.update', 1); $events->setParam('userId', $user->getId()); @@ -1474,6 +1443,8 @@ App::patch('/v1/account/password') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.password') ->label('scope', 'account') + ->label('audits.resource', 'user/{response.$id}') + ->label('audits.userId', '{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updatePassword') @@ -1486,10 +1457,10 @@ App::patch('/v1/account/password') ->inject('response') ->inject('user') ->inject('dbForProject') - ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (string $password, string $oldPassword, Response $response, Document $user, Database $dbForProject, Audit $audits, Stats $usage, Event $events) { + ->action(function (string $password, string $oldPassword, Response $response, Document $user, Database $dbForProject, Stats $usage, Event $events) { + // Check old password only if its an existing user. if ($user->getAttribute('passwordUpdate') !== 0 && !Auth::passwordVerify($oldPassword, $user->getAttribute('password'), $user->getAttribute('hash'), $user->getAttribute('hashOptions'))) { // Double check user password throw new Exception(Exception::USER_INVALID_CREDENTIALS); @@ -1501,11 +1472,6 @@ App::patch('/v1/account/password') ->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS) ->setAttribute('passwordUpdate', \time())); - $audits - ->setResource('user/' . $user->getId()) - ->setUser($user) - ; - $usage->setParam('users.update', 1); $events->setParam('userId', $user->getId()); @@ -1517,6 +1483,7 @@ App::patch('/v1/account/email') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.email') ->label('scope', 'account') + ->label('audits.resource', 'user/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updateEmail') @@ -1529,10 +1496,9 @@ App::patch('/v1/account/email') ->inject('response') ->inject('user') ->inject('dbForProject') - ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (string $email, string $password, Response $response, Document $user, Database $dbForProject, Audit $audits, Stats $usage, Event $events) { + ->action(function (string $email, string $password, Response $response, Document $user, Database $dbForProject, Stats $usage, Event $events) { $isAnonymousUser = Auth::isAnonymousUser($user); // Check if request is from an anonymous account for converting if ( @@ -1558,11 +1524,6 @@ App::patch('/v1/account/email') throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS); } - $audits - ->setResource('user/' . $user->getId()) - ->setUser($user) - ; - $usage->setParam('users.update', 1); $events->setParam('userId', $user->getId()); @@ -1574,6 +1535,7 @@ App::patch('/v1/account/phone') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.phone') ->label('scope', 'account') + ->label('audits.resource', 'user/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updatePhone') @@ -1586,10 +1548,9 @@ App::patch('/v1/account/phone') ->inject('response') ->inject('user') ->inject('dbForProject') - ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (string $phone, string $password, Response $response, Document $user, Database $dbForProject, Audit $audits, Stats $usage, Event $events) { + ->action(function (string $phone, string $password, Response $response, Document $user, Database $dbForProject, Stats $usage, Event $events) { $isAnonymousUser = Auth::isAnonymousUser($user); // Check if request is from an anonymous account for converting @@ -1611,11 +1572,6 @@ App::patch('/v1/account/phone') throw new Exception(Exception::USER_PHONE_ALREADY_EXISTS); } - $audits - ->setResource('user/' . $user->getId()) - ->setUser($user) - ; - $usage->setParam('users.update', 1); $events->setParam('userId', $user->getId()); @@ -1627,6 +1583,7 @@ App::patch('/v1/account/prefs') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.prefs') ->label('scope', 'account') + ->label('audits.resource', 'user/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updatePrefs') @@ -1638,14 +1595,12 @@ App::patch('/v1/account/prefs') ->inject('response') ->inject('user') ->inject('dbForProject') - ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (array $prefs, Response $response, Document $user, Database $dbForProject, Audit $audits, Stats $usage, Event $events) { + ->action(function (array $prefs, Response $response, Document $user, Database $dbForProject, Stats $usage, Event $events) { $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('prefs', $prefs)); - $audits->setResource('user/' . $user->getId()); $usage->setParam('users.update', 1); $events->setParam('userId', $user->getId()); @@ -1657,6 +1612,7 @@ App::patch('/v1/account/status') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.status') ->label('scope', 'account') + ->label('audits.resource', 'user/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updateStatus') @@ -1668,17 +1624,12 @@ App::patch('/v1/account/status') ->inject('response') ->inject('user') ->inject('dbForProject') - ->inject('audits') ->inject('events') ->inject('usage') - ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Audit $audits, Event $events, Stats $usage) { + ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $events, Stats $usage) { $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('status', false)); - $audits - ->setResource('user/' . $user->getId()) - ->setPayload($response->output($user, Response::MODEL_ACCOUNT)); - $events ->setParam('userId', $user->getId()) ->setPayload($response->output($user, Response::MODEL_ACCOUNT)); @@ -1697,6 +1648,7 @@ App::delete('/v1/account/sessions/:sessionId') ->groups(['api', 'account']) ->label('scope', 'account') ->label('event', 'users.[userId].sessions.[sessionId].delete') + ->label('audits.resource', 'user/{user.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'deleteSession') @@ -1710,10 +1662,9 @@ App::delete('/v1/account/sessions/:sessionId') ->inject('user') ->inject('dbForProject') ->inject('locale') - ->inject('audits') ->inject('events') ->inject('usage') - ->action(function (?string $sessionId, Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Audit $audits, Event $events, Stats $usage) { + ->action(function (?string $sessionId, Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Event $events, Stats $usage) { $protocol = $request->getProtocol(); $sessionId = ($sessionId === 'current') @@ -1728,8 +1679,6 @@ App::delete('/v1/account/sessions/:sessionId') $dbForProject->deleteDocument('sessions', $session->getId()); - $audits->setResource('user/' . $user->getId()); - $session->setAttribute('current', false); if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too @@ -1774,6 +1723,8 @@ App::patch('/v1/account/sessions/:sessionId') ->groups(['api', 'account']) ->label('scope', 'account') ->label('event', 'users.[userId].sessions.[sessionId].update') + ->label('audits.resource', 'user/{response.userId}') + ->label('audits.userId', '{response.userId}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updateSession') @@ -1789,10 +1740,9 @@ App::patch('/v1/account/sessions/:sessionId') ->inject('dbForProject') ->inject('project') ->inject('locale') - ->inject('audits') ->inject('events') ->inject('usage') - ->action(function (?string $sessionId, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Audit $audits, Event $events, Stats $usage) { + ->action(function (?string $sessionId, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Event $events, Stats $usage) { $sessionId = ($sessionId === 'current') ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret) @@ -1836,8 +1786,6 @@ App::patch('/v1/account/sessions/:sessionId') $dbForProject->deleteCachedDocument('users', $user->getId()); - $audits->setResource('user/' . $user->getId()); - $events ->setParam('userId', $user->getId()) ->setParam('sessionId', $session->getId()) @@ -1861,6 +1809,7 @@ App::delete('/v1/account/sessions') ->groups(['api', 'account']) ->label('scope', 'account') ->label('event', 'users.[userId].sessions.[sessionId].delete') + ->label('audits.resource', 'user/{user.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'deleteSessions') @@ -1873,10 +1822,9 @@ App::delete('/v1/account/sessions') ->inject('user') ->inject('dbForProject') ->inject('locale') - ->inject('audits') ->inject('events') ->inject('usage') - ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Audit $audits, Event $events, Stats $usage) { + ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Event $events, Stats $usage) { $protocol = $request->getProtocol(); $sessions = $user->getAttribute('sessions', []); @@ -1884,8 +1832,6 @@ App::delete('/v1/account/sessions') foreach ($sessions as $session) {/** @var Document $session */ $dbForProject->deleteDocument('sessions', $session->getId()); - $audits->setResource('user/' . $user->getId()); - if (!Config::getParam('domainVerification')) { $response->addHeader('X-Fallback-Cookies', \json_encode([])); } @@ -1929,6 +1875,8 @@ App::post('/v1/account/recovery') ->groups(['api', 'account']) ->label('scope', 'public') ->label('event', 'users.[userId].recovery.[tokenId].create') + ->label('audits.resource', 'user/{response.userId}') + ->label('audits.userId', '{response.userId}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'createRecovery') @@ -1946,10 +1894,9 @@ App::post('/v1/account/recovery') ->inject('project') ->inject('locale') ->inject('mails') - ->inject('audits') ->inject('events') ->inject('usage') - ->action(function (string $email, string $url, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Mail $mails, Audit $audits, Event $events, Stats $usage) { + ->action(function (string $email, string $url, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Mail $mails, Event $events, Stats $usage) { if (empty(App::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled'); @@ -2021,7 +1968,6 @@ App::post('/v1/account/recovery') // Hide secret for clients $recovery->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $secret : ''); - $audits->setResource('user/' . $profile->getId()); $usage->setParam('users.update', 1); $response->setStatusCode(Response::STATUS_CODE_CREATED); @@ -2033,6 +1979,8 @@ App::put('/v1/account/recovery') ->groups(['api', 'account']) ->label('scope', 'public') ->label('event', 'users.[userId].recovery.[tokenId].update') + ->label('audits.resource', 'user/{response.userId}') + ->label('audits.userId', '{response.userId}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updateRecovery') @@ -2048,10 +1996,9 @@ App::put('/v1/account/recovery') ->param('passwordAgain', '', new Password(), 'Repeat new user password. Must be at least 8 chars.') ->inject('response') ->inject('dbForProject') - ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (string $userId, string $secret, string $password, string $passwordAgain, Response $response, Database $dbForProject, Audit $audits, Stats $usage, Event $events) { + ->action(function (string $userId, string $secret, string $password, string $passwordAgain, Response $response, Database $dbForProject, Stats $usage, Event $events) { if ($password !== $passwordAgain) { throw new Exception(Exception::USER_PASSWORD_MISMATCH); } @@ -2087,8 +2034,6 @@ App::put('/v1/account/recovery') $dbForProject->deleteDocument('tokens', $recovery); $dbForProject->deleteCachedDocument('users', $profile->getId()); - $audits->setResource('user/' . $profile->getId()); - $usage->setParam('users.update', 1); $events @@ -2104,6 +2049,7 @@ App::post('/v1/account/verification') ->groups(['api', 'account']) ->label('scope', 'account') ->label('event', 'users.[userId].verification.[tokenId].create') + ->label('audits.resource', 'user/{response.userId}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'createVerification') @@ -2120,11 +2066,10 @@ App::post('/v1/account/verification') ->inject('user') ->inject('dbForProject') ->inject('locale') - ->inject('audits') ->inject('events') ->inject('mails') ->inject('usage') - ->action(function (string $url, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Audit $audits, Event $events, Mail $mails, Stats $usage) { + ->action(function (string $url, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Event $events, Mail $mails, Stats $usage) { if (empty(App::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled'); @@ -2182,7 +2127,6 @@ App::post('/v1/account/verification') // Hide secret for clients $verification->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $verificationSecret : ''); - $audits->setResource('user/' . $user->getId()); $usage->setParam('users.update', 1); $response->setStatusCode(Response::STATUS_CODE_CREATED); @@ -2194,6 +2138,7 @@ App::put('/v1/account/verification') ->groups(['api', 'account']) ->label('scope', 'public') ->label('event', 'users.[userId].verification.[tokenId].update') + ->label('audits.resource', 'user/{response.userId}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updateVerification') @@ -2208,10 +2153,9 @@ App::put('/v1/account/verification') ->inject('response') ->inject('user') ->inject('dbForProject') - ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Audit $audits, Stats $usage, Event $events) { + ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Stats $usage, Event $events) { $profile = Authorization::skip(fn() => $dbForProject->getDocument('users', $userId)); @@ -2239,8 +2183,6 @@ App::put('/v1/account/verification') $dbForProject->deleteDocument('tokens', $verification); $dbForProject->deleteCachedDocument('users', $profile->getId()); - $audits->setResource('user/' . $user->getId()); - $usage->setParam('users.update', 1); $events @@ -2256,6 +2198,7 @@ App::post('/v1/account/verification/phone') ->groups(['api', 'account']) ->label('scope', 'account') ->label('event', 'users.[userId].verification.[tokenId].create') + ->label('audits.resource', 'user/{response.userId}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'createPhoneVerification') @@ -2269,11 +2212,10 @@ App::post('/v1/account/verification/phone') ->inject('response') ->inject('user') ->inject('dbForProject') - ->inject('audits') ->inject('events') ->inject('usage') ->inject('messaging') - ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Audit $audits, Event $events, Stats $usage, EventPhone $messaging) { + ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $events, Stats $usage, EventPhone $messaging) { if (empty(App::getEnv('_APP_SMS_PROVIDER'))) { throw new Exception(Exception::GENERAL_PHONE_DISABLED); @@ -2329,7 +2271,6 @@ App::post('/v1/account/verification/phone') // Hide secret for clients $verification->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $verificationSecret : ''); - $audits->setResource('user/' . $user->getId()); $usage->setParam('users.update', 1); $response->setStatusCode(Response::STATUS_CODE_CREATED); @@ -2341,6 +2282,7 @@ App::put('/v1/account/verification/phone') ->groups(['api', 'account']) ->label('scope', 'public') ->label('event', 'users.[userId].verification.[tokenId].update') + ->label('audits.resource', 'user/{response.userId}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'updatePhoneVerification') @@ -2355,10 +2297,9 @@ App::put('/v1/account/verification/phone') ->inject('response') ->inject('user') ->inject('dbForProject') - ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Audit $audits, Stats $usage, Event $events) { + ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Stats $usage, Event $events) { $profile = Authorization::skip(fn() => $dbForProject->getDocument('users', $userId)); @@ -2384,8 +2325,6 @@ App::put('/v1/account/verification/phone') $dbForProject->deleteDocument('tokens', $verification); $dbForProject->deleteCachedDocument('users', $profile->getId()); - $audits->setResource('user/' . $user->getId()); - $usage->setParam('users.update', 1); $events diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index e9ef7e5eb6..6dc31787b5 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -301,8 +301,6 @@ App::get('/v1/avatars/qr') ->desc('Get QR Code') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') - ->label('cache', true) - ->label('cache.resource', 'avatar/qr') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'avatars') ->label('sdk.method', 'getQR') @@ -336,7 +334,7 @@ App::get('/v1/avatars/qr') $response ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache ->setContentType('image/png') - ->file($image->output('png', 9)) + ->send($image->output('png', 9)) ; }); diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 14b00b7e5c..152e5b5c68 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -36,7 +36,6 @@ use Appwrite\Utopia\Database\Validator\Queries as QueriesValidator; use Appwrite\Utopia\Database\Validator\OrderAttributes; use Appwrite\Utopia\Response; use Appwrite\Detector\Detector; -use Appwrite\Event\Audit as EventAudit; use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Event; use Appwrite\Stats\Stats; @@ -50,7 +49,7 @@ use MaxMind\Db\Reader; * @return Document Newly created attribute document * @throws Exception */ -function createAttribute(string $databaseId, string $collectionId, Document $attribute, Response $response, Database $dbForProject, EventDatabase $database, EventAudit $audits, Event $events, Stats $usage): Document +function createAttribute(string $databaseId, string $collectionId, Document $attribute, Response $response, Database $dbForProject, EventDatabase $database, Event $events, Stats $usage): Document { $key = $attribute->getAttribute('key'); $type = $attribute->getAttribute('type', ''); @@ -141,11 +140,6 @@ function createAttribute(string $databaseId, string $collectionId, Document $att ->setParam('attributeId', $attribute->getId()) ; - $audits - ->setResource('database/' . $db->getId() . '/collection/' . $collectionId) - ->setPayload($attribute->getArrayCopy()) - ; - $response->setStatusCode(Response::STATUS_CODE_CREATED); return $attribute; @@ -156,6 +150,7 @@ App::post('/v1/databases') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].create') ->label('scope', 'databases.write') + ->label('audits.resource', 'database/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'create') @@ -167,10 +162,9 @@ App::post('/v1/databases') ->param('name', '', new Text(128), 'Collection name. Max length: 128 chars.') ->inject('response') ->inject('dbForProject') - ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (string $databaseId, string $name, Response $response, Database $dbForProject, EventAudit $audits, Stats $usage, Event $events) { + ->action(function (string $databaseId, string $name, Response $response, Database $dbForProject, Stats $usage, Event $events) { $databaseId = $databaseId == 'unique()' ? $dbForProject->getId() : $databaseId; @@ -218,11 +212,6 @@ App::post('/v1/databases') throw new Exception(Exception::DATABASE_ALREADY_EXISTS); } - $audits - ->setResource('database/' . $databaseId) - ->setPayload($database->getArrayCopy()) - ; - $events->setParam('databaseId', $database->getId()); $usage->setParam('databases.create', 1); @@ -386,6 +375,7 @@ App::put('/v1/databases/:databaseId') ->groups(['api', 'database']) ->label('scope', 'databases.write') ->label('event', 'databases.[databaseId].update') + ->label('audits.resource', 'database/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'update') @@ -397,10 +387,9 @@ App::put('/v1/databases/:databaseId') ->param('name', null, new Text(128), 'Collection name. Max length: 128 chars.') ->inject('response') ->inject('dbForProject') - ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (string $databaseId, string $name, Response $response, Database $dbForProject, EventAudit $audits, Stats $usage, Event $events) { + ->action(function (string $databaseId, string $name, Response $response, Database $dbForProject, Stats $usage, Event $events) { $database = $dbForProject->getDocument('databases', $databaseId); @@ -418,11 +407,6 @@ App::put('/v1/databases/:databaseId') throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, 'Bad structure. ' . $exception->getMessage()); } - $audits - ->setResource('database/' . $databaseId) - ->setPayload($database->getArrayCopy()) - ; - $usage->setParam('databases.update', 1); $events->setParam('databaseId', $database->getId()); @@ -434,6 +418,7 @@ App::delete('/v1/databases/:databaseId') ->groups(['api', 'database']) ->label('scope', 'databases.write') ->label('event', 'databases.[databaseId].delete') + ->label('audits.resource', 'database/{request.databaseId}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'delete') @@ -444,10 +429,9 @@ App::delete('/v1/databases/:databaseId') ->inject('response') ->inject('dbForProject') ->inject('events') - ->inject('audits') ->inject('deletes') ->inject('usage') - ->action(function (string $databaseId, Response $response, Database $dbForProject, Event $events, EventAudit $audits, Delete $deletes, Stats $usage) { + ->action(function (string $databaseId, Response $response, Database $dbForProject, Event $events, Delete $deletes, Stats $usage) { $database = $dbForProject->getDocument('databases', $databaseId); @@ -471,11 +455,6 @@ App::delete('/v1/databases/:databaseId') ->setPayload($response->output($database, Response::MODEL_DATABASE)) ; - $audits - ->setResource('database/' . $databaseId) - ->setPayload($database->getArrayCopy()) - ; - $usage->setParam('databases.delete', 1); $response->noContent(); @@ -487,6 +466,7 @@ App::post('/v1/databases/:databaseId/collections') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].collections.[collectionId].create') ->label('scope', 'collections.write') + ->label('audits.resource', 'database/{request.databaseId}/collection/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'createCollection') @@ -502,10 +482,9 @@ App::post('/v1/databases/:databaseId/collections') ->param('write', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.') ->inject('response') ->inject('dbForProject') - ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $name, ?string $permission, ?array $read, ?array $write, Response $response, Database $dbForProject, EventAudit $audits, Stats $usage, Event $events) { + ->action(function (string $databaseId, string $collectionId, string $name, ?string $permission, ?array $read, ?array $write, Response $response, Database $dbForProject, Stats $usage, Event $events) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -536,11 +515,6 @@ App::post('/v1/databases/:databaseId/collections') throw new Exception(Exception::COLLECTION_LIMIT_EXCEEDED); } - $audits - ->setResource('database/' . $databaseId . '/collection/' . $collectionId) - ->setPayload($collection->getArrayCopy()) - ; - $events ->setContext('database', $database) ->setParam('databaseId', $databaseId) @@ -741,6 +715,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId') ->groups(['api', 'database']) ->label('scope', 'collections.write') ->label('event', 'databases.[databaseId].collections.[collectionId].update') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'updateCollection') @@ -757,10 +732,9 @@ App::put('/v1/databases/:databaseId/collections/:collectionId') ->param('enabled', true, new Boolean(), 'Is collection enabled?', true) ->inject('response') ->inject('dbForProject') - ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $name, string $permission, ?array $read, ?array $write, bool $enabled, Response $response, Database $dbForProject, EventAudit $audits, Stats $usage, Event $events) { + ->action(function (string $databaseId, string $collectionId, string $name, string $permission, ?array $read, ?array $write, bool $enabled, Response $response, Database $dbForProject, Stats $usage, Event $events) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -791,11 +765,6 @@ App::put('/v1/databases/:databaseId/collections/:collectionId') throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, 'Bad structure. ' . $exception->getMessage()); } - $audits - ->setResource('database/' . $databaseId . '/collection/' . $collectionId) - ->setPayload($collection->getArrayCopy()) - ; - $usage ->setParam('databaseId', $databaseId) ->setParam('databases.collections.update', 1); @@ -814,6 +783,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId') ->groups(['api', 'database']) ->label('scope', 'collections.write') ->label('event', 'databases.[databaseId].collections.[collectionId].delete') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'deleteCollection') @@ -825,10 +795,9 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId') ->inject('response') ->inject('dbForProject') ->inject('events') - ->inject('audits') ->inject('deletes') ->inject('usage') - ->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, Event $events, EventAudit $audits, Delete $deletes, Stats $usage) { + ->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, Event $events, Delete $deletes, Stats $usage) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -860,11 +829,6 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId') ->setPayload($response->output($collection, Response::MODEL_COLLECTION)) ; - $audits - ->setResource('database/' . $databaseId . '/collection/' . $collectionId) - ->setPayload($collection->getArrayCopy()) - ; - $usage ->setParam('databaseId', $databaseId) ->setParam('databases.collections.delete', 1); @@ -878,6 +842,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') ->label('scope', 'collections.write') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'createStringAttribute') @@ -886,7 +851,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_ATTRIBUTE_STRING) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).') ->param('key', '', new Key(), 'Attribute Key.') ->param('size', null, new Range(1, APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH, Range::TYPE_INTEGER), 'Attribute size for text attributes, in number of characters.') ->param('required', null, new Boolean(), 'Is attribute required?') @@ -895,10 +860,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string ->inject('response') ->inject('dbForProject') ->inject('database') - ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, ?int $size, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, EventAudit $audits, Stats $usage, Event $events) { + ->action(function (string $databaseId, string $collectionId, string $key, ?int $size, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Stats $usage, Event $events) { // Ensure attribute default is within required size $validator = new Text($size); @@ -913,7 +877,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string 'required' => $required, 'default' => $default, 'array' => $array, - ]), $response, $dbForProject, $database, $audits, $events, $usage); + ]), $response, $dbForProject, $database, $events, $usage); $response->setStatusCode(Response::STATUS_CODE_ACCEPTED); $response->dynamic($attribute, Response::MODEL_ATTRIBUTE_STRING); @@ -925,6 +889,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email' ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') ->label('scope', 'collections.write') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk.namespace', 'databases') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.method', 'createEmailAttribute') @@ -933,7 +898,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email' ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_ATTRIBUTE_EMAIL) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).') ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') ->param('default', null, new Email(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) @@ -941,10 +906,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email' ->inject('response') ->inject('dbForProject') ->inject('database') - ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, EventAudit $audits, Stats $usage, Event $events) { + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Stats $usage, Event $events) { $attribute = createAttribute($databaseId, $collectionId, new Document([ 'key' => $key, @@ -954,7 +918,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email' 'default' => $default, 'array' => $array, 'format' => APP_DATABASE_ATTRIBUTE_EMAIL, - ]), $response, $dbForProject, $database, $audits, $events, $usage); + ]), $response, $dbForProject, $database, $events, $usage); $response->setStatusCode(Response::STATUS_CODE_ACCEPTED); $response->dynamic($attribute, Response::MODEL_ATTRIBUTE_EMAIL); @@ -966,6 +930,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') ->label('scope', 'collections.write') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk.namespace', 'databases') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.method', 'createEnumAttribute') @@ -974,7 +939,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_ATTRIBUTE_ENUM) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).') ->param('key', '', new Key(), 'Attribute Key.') ->param('elements', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of elements in enumerated type. Uses length of longest element to determine size. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' elements are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.') ->param('required', null, new Boolean(), 'Is attribute required?') @@ -983,10 +948,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum') ->inject('response') ->inject('dbForProject') ->inject('database') - ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, array $elements, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, EventAudit $audits, Stats $usage, Event $events) { + ->action(function (string $databaseId, string $collectionId, string $key, array $elements, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Stats $usage, Event $events) { // use length of longest string as attribute size $size = 0; @@ -1011,7 +975,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum') 'array' => $array, 'format' => APP_DATABASE_ATTRIBUTE_ENUM, 'formatOptions' => ['elements' => $elements], - ]), $response, $dbForProject, $database, $audits, $events, $usage); + ]), $response, $dbForProject, $database, $events, $usage); $response->setStatusCode(Response::STATUS_CODE_ACCEPTED); $response->dynamic($attribute, Response::MODEL_ATTRIBUTE_ENUM); @@ -1023,6 +987,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') ->label('scope', 'collections.write') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk.namespace', 'databases') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.method', 'createIpAttribute') @@ -1031,7 +996,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_ATTRIBUTE_IP) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).') ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') ->param('default', null, new IP(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) @@ -1039,10 +1004,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') ->inject('response') ->inject('dbForProject') ->inject('database') - ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, EventAudit $audits, Stats $usage, Event $events) { + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Stats $usage, Event $events) { $attribute = createAttribute($databaseId, $collectionId, new Document([ 'key' => $key, @@ -1052,7 +1016,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') 'default' => $default, 'array' => $array, 'format' => APP_DATABASE_ATTRIBUTE_IP, - ]), $response, $dbForProject, $database, $audits, $events, $usage); + ]), $response, $dbForProject, $database, $events, $usage); $response->setStatusCode(Response::STATUS_CODE_ACCEPTED); $response->dynamic($attribute, Response::MODEL_ATTRIBUTE_IP); @@ -1064,6 +1028,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') ->label('scope', 'collections.write') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk.namespace', 'databases') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.method', 'createUrlAttribute') @@ -1072,7 +1037,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_ATTRIBUTE_URL) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).') ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') ->param('default', null, new URL(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) @@ -1080,10 +1045,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url') ->inject('response') ->inject('dbForProject') ->inject('database') - ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, EventAudit $audits, Stats $usage, Event $events) { + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Stats $usage, Event $events) { $attribute = createAttribute($databaseId, $collectionId, new Document([ 'key' => $key, @@ -1093,7 +1057,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url') 'default' => $default, 'array' => $array, 'format' => APP_DATABASE_ATTRIBUTE_URL, - ]), $response, $dbForProject, $database, $audits, $events, $usage); + ]), $response, $dbForProject, $database, $events, $usage); $response->setStatusCode(Response::STATUS_CODE_ACCEPTED); $response->dynamic($attribute, Response::MODEL_ATTRIBUTE_URL); @@ -1105,6 +1069,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/intege ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') ->label('scope', 'collections.write') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk.namespace', 'databases') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.method', 'createIntegerAttribute') @@ -1113,7 +1078,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/intege ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_ATTRIBUTE_INTEGER) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).') ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') ->param('min', null, new Integer(), 'Minimum value to enforce on new documents', true) @@ -1123,10 +1088,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/intege ->inject('response') ->inject('dbForProject') ->inject('database') - ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, EventAudit $audits, Stats $usage, Event $events) { + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Stats $usage, Event $events) { // Ensure attribute default is within range $min = (is_null($min)) ? PHP_INT_MIN : \intval($min); @@ -1156,7 +1120,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/intege 'min' => $min, 'max' => $max, ], - ]), $response, $dbForProject, $database, $audits, $events, $usage); + ]), $response, $dbForProject, $database, $events, $usage); $formatOptions = $attribute->getAttribute('formatOptions', []); @@ -1175,6 +1139,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float' ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') ->label('scope', 'collections.write') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk.namespace', 'databases') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.method', 'createFloatAttribute') @@ -1183,7 +1148,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float' ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_ATTRIBUTE_FLOAT) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).') ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') ->param('min', null, new FloatValidator(), 'Minimum value to enforce on new documents', true) @@ -1193,10 +1158,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float' ->inject('response') ->inject('dbForProject') ->inject('database') - ->inject('audits') ->inject('events') ->inject('usage') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, EventAudit $audits, Event $events, Stats $usage) { + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Event $events, Stats $usage) { // Ensure attribute default is within range $min = (is_null($min)) ? -PHP_FLOAT_MAX : \floatval($min); @@ -1229,7 +1193,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float' 'min' => $min, 'max' => $max, ], - ]), $response, $dbForProject, $database, $audits, $events, $usage); + ]), $response, $dbForProject, $database, $events, $usage); $formatOptions = $attribute->getAttribute('formatOptions', []); @@ -1248,6 +1212,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boolea ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create') ->label('scope', 'collections.write') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk.namespace', 'databases') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.method', 'createBooleanAttribute') @@ -1256,7 +1221,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boolea ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_ATTRIBUTE_BOOLEAN) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).') ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') ->param('default', null, new Boolean(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) @@ -1264,10 +1229,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boolea ->inject('response') ->inject('dbForProject') ->inject('database') - ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, EventAudit $audits, Stats $usage, Event $events) { + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Stats $usage, Event $events) { $attribute = createAttribute($databaseId, $collectionId, new Document([ 'key' => $key, @@ -1276,7 +1240,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boolea 'required' => $required, 'default' => $default, 'array' => $array, - ]), $response, $dbForProject, $database, $audits, $events, $usage); + ]), $response, $dbForProject, $database, $events, $usage); $response->setStatusCode(Response::STATUS_CODE_ACCEPTED); $response->dynamic($attribute, Response::MODEL_ATTRIBUTE_BOOLEAN); @@ -1295,7 +1259,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_ATTRIBUTE_LIST) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).') ->inject('response') ->inject('dbForProject') ->inject('usage') @@ -1345,7 +1309,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') Response::MODEL_ATTRIBUTE_IP, Response::MODEL_ATTRIBUTE_STRING,])// needs to be last, since its condition would dominate any other string attribute ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).') ->param('key', '', new Key(), 'Attribute Key.') ->inject('response') ->inject('dbForProject') @@ -1401,6 +1365,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key ->groups(['api', 'database']) ->label('scope', 'collections.write') ->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].delete') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'deleteAttribute') @@ -1408,15 +1373,14 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.model', Response::MODEL_NONE) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).') ->param('key', '', new Key(), 'Attribute Key.') ->inject('response') ->inject('dbForProject') ->inject('database') ->inject('events') - ->inject('audits') ->inject('usage') - ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $database, Event $events, EventAudit $audits, Stats $usage) { + ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $database, Event $events, Stats $usage) { $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -1481,11 +1445,6 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key ->setPayload($response->output($attribute, $model)) ; - $audits - ->setResource('database/' . $databaseId . '/collection/' . $collectionId) - ->setPayload($attribute->getArrayCopy()) - ; - $response->noContent(); }); @@ -1495,6 +1454,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].collections.[collectionId].indexes.[indexId].create') ->label('scope', 'collections.write') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'createIndex') @@ -1503,7 +1463,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_INDEX) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).') ->param('key', null, new Key(), 'Index Key.') ->param('type', null, new WhiteList([Database::INDEX_KEY, Database::INDEX_FULLTEXT, Database::INDEX_UNIQUE, Database::INDEX_SPATIAL, Database::INDEX_ARRAY]), 'Index type.') ->param('attributes', null, new ArrayList(new Key(true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of attributes to index. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' attributes are allowed, each 32 characters long.') @@ -1511,10 +1471,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') ->inject('response') ->inject('dbForProject') ->inject('database') - ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (string $databaseId, string $collectionId, string $key, string $type, array $attributes, array $orders, Response $response, Database $dbForProject, EventDatabase $database, EventAudit $audits, Stats $usage, Event $events) { + ->action(function (string $databaseId, string $collectionId, string $key, string $type, array $attributes, array $orders, Response $response, Database $dbForProject, EventDatabase $database, Stats $usage, Event $events) { $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -1636,11 +1595,6 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') ->setContext('database', $db) ; - $audits - ->setResource('database/' . $databaseId . '/collection/' . $collection->getId()) - ->setPayload($index->getArrayCopy()) - ; - $response->setStatusCode(Response::STATUS_CODE_ACCEPTED); $response->dynamic($index, Response::MODEL_INDEX); }); @@ -1658,7 +1612,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_INDEX_LIST) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).') ->inject('response') ->inject('dbForProject') ->inject('usage') @@ -1700,7 +1654,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_INDEX) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).') ->param('key', null, new Key(), 'Index Key.') ->inject('response') ->inject('dbForProject') @@ -1744,6 +1698,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->groups(['api', 'database']) ->label('scope', 'collections.write') ->label('event', 'databases.[databaseId].collections.[collectionId].indexes.[indexId].delete') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'deleteIndex') @@ -1751,15 +1706,14 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.model', Response::MODEL_NONE) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', null, new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('collectionId', null, new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).') ->param('key', '', new Key(), 'Index Key.') ->inject('response') ->inject('dbForProject') ->inject('database') ->inject('events') - ->inject('audits') ->inject('usage') - ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $database, Event $events, EventAudit $audits, Stats $usage) { + ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $database, Event $events, Stats $usage) { $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -1805,11 +1759,6 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->setPayload($response->output($index, Response::MODEL_INDEX)) ; - $audits - ->setResource('database/' . $databaseId . '/collection/' . $collection->getId()) - ->setPayload($index->getArrayCopy()) - ; - $response->noContent(); }); @@ -1819,6 +1768,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].create') ->label('scope', 'documents.write') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'createDocument') @@ -1828,18 +1778,17 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') ->label('sdk.response.model', Response::MODEL_DOCUMENT) ->param('databaseId', '', new UID(), 'Database ID.') ->param('documentId', '', new CustomId(), 'Document ID. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') - ->param('collectionId', null, new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). Make sure to define attributes before creating documents.') + ->param('collectionId', null, new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection). Make sure to define attributes before creating documents.') ->param('data', [], new JSON(), 'Document data as JSON object.') ->param('read', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with read permissions. By default only the current user is granted with read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true) ->param('write', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with write permissions. By default only the current user is granted with write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true) ->inject('response') ->inject('dbForProject') ->inject('user') - ->inject('audits') ->inject('usage') ->inject('events') ->inject('mode') - ->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $read, ?array $write, Response $response, Database $dbForProject, Document $user, EventAudit $audits, Stats $usage, Event $events, string $mode) { + ->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $read, ?array $write, Response $response, Database $dbForProject, Document $user, Stats $usage, Event $events, string $mode) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -1926,11 +1875,6 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') ->setParam('collectionId', $collectionId) ; - $audits - ->setResource('database/' . $databaseId . '/collection/' . $collectionId . '/document/' . $document->getId()) - ->setPayload($document->getArrayCopy()) - ; - $response->setStatusCode(Response::STATUS_CODE_CREATED); $response->dynamic($document, Response::MODEL_DOCUMENT); }); @@ -1948,8 +1892,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_DOCUMENT_LIST) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).') + ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/database#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) ->param('limit', 25, new Range(0, 100), 'Maximum number of documents to return in response. By default will return maximum 25 results. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' results allowed per request.', true) ->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this value to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true) ->param('cursor', '', new UID(), 'ID of the document used as the starting point for the query, excluding the document itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true) @@ -2062,7 +2006,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_DOCUMENT) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', null, new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('collectionId', null, new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).') ->param('documentId', null, new UID(), 'Document ID.') ->inject('response') ->inject('dbForProject') @@ -2219,6 +2163,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].update') ->label('scope', 'documents.write') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}/document/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'updateDocument') @@ -2234,11 +2179,10 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum ->param('write', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with write permissions. By default inherits the existing write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.', true) ->inject('response') ->inject('dbForProject') - ->inject('audits') ->inject('usage') ->inject('events') ->inject('mode') - ->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $read, ?array $write, Response $response, Database $dbForProject, EventAudit $audits, Stats $usage, Event $events, string $mode) { + ->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $read, ?array $write, Response $response, Database $dbForProject, Stats $usage, Event $events, string $mode) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -2344,11 +2288,6 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum ->setParam('collectionId', $collectionId) ; - $audits - ->setResource('database/' . $databaseId . '/collection/' . $collectionId . '/document/' . $document->getId()) - ->setPayload($document->getArrayCopy()) - ; - $response->dynamic($document, Response::MODEL_DOCUMENT); }); @@ -2358,6 +2297,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu ->groups(['api', 'database']) ->label('scope', 'documents.write') ->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].delete') + ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}/document/{request.documentId}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'deleteDocument') @@ -2365,16 +2305,15 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.model', Response::MODEL_NONE) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', null, new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('collectionId', null, new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).') ->param('documentId', null, new UID(), 'Document ID.') ->inject('response') ->inject('dbForProject') ->inject('events') - ->inject('audits') ->inject('deletes') ->inject('usage') ->inject('mode') - ->action(function (string $databaseId, string $collectionId, string $documentId, Response $response, Database $dbForProject, Event $events, EventAudit $audits, Delete $deletes, Stats $usage, string $mode) { + ->action(function (string $databaseId, string $collectionId, string $documentId, Response $response, Database $dbForProject, Event $events, Delete $deletes, Stats $usage, string $mode) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -2444,11 +2383,6 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu ->setPayload($response->output($document, Response::MODEL_DOCUMENT)) ; - $audits - ->setResource('database/' . $databaseId . '/collection/' . $collectionId . '/document/' . $document->getId()) - ->setPayload($document->getArrayCopy()) - ; - $response->noContent(); }); diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 2133fa1b49..07dbfbc18a 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -43,6 +43,7 @@ App::post('/v1/functions') ->desc('Create Function') ->label('scope', 'functions.write') ->label('event', 'functions.[functionId].create') + ->label('audits.resource', 'function/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'functions') ->label('sdk.method', 'create') @@ -281,6 +282,7 @@ App::put('/v1/functions/:functionId') ->desc('Update Function') ->label('scope', 'functions.write') ->label('event', 'functions.[functionId].update') + ->label('audits.resource', 'function/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'functions') ->label('sdk.method', 'update') @@ -345,6 +347,7 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId') ->desc('Update Function Deployment') ->label('scope', 'functions.write') ->label('event', 'functions.[functionId].deployments.[deploymentId].update') + ->label('audits.resource', 'function/{request.functionId}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'functions') ->label('sdk.method', 'updateDeployment') @@ -410,6 +413,7 @@ App::delete('/v1/functions/:functionId') ->desc('Delete Function') ->label('scope', 'functions.write') ->label('event', 'functions.[functionId].delete') + ->label('audits.resource', 'function/{request.functionId}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'functions') ->label('sdk.method', 'delete') @@ -447,6 +451,7 @@ App::post('/v1/functions/:functionId/deployments') ->desc('Create Deployment') ->label('scope', 'functions.write') ->label('event', 'functions.[functionId].deployments.[deploymentId].create') + ->label('audits.resource', 'function/{request.functionId}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'functions') ->label('sdk.method', 'createDeployment') @@ -728,6 +733,7 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId') ->desc('Delete Deployment') ->label('scope', 'functions.write') ->label('event', 'functions.[functionId].deployments.[deploymentId].delete') + ->label('audits.resource', 'function/{request.functionId}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'functions') ->label('sdk.method', 'deleteDeployment') @@ -1091,6 +1097,7 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId') ->desc('Retry Build') ->label('scope', 'functions.write') ->label('event', 'functions.[functionId].deployments.[deploymentId].update') + ->label('audits.resource', 'function/{request.functionId}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'functions') ->label('sdk.method', 'retryBuild') diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 9ac78a0eae..5ed7756067 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -2,7 +2,6 @@ use Appwrite\Auth\Auth; use Appwrite\ClamAV\Network; -use Appwrite\Event\Audit; use Appwrite\Event\Delete; use Appwrite\Event\Event; use Appwrite\Utopia\Database\Validator\CustomId; @@ -44,6 +43,7 @@ App::post('/v1/storage/buckets') ->groups(['api', 'storage']) ->label('scope', 'buckets.write') ->label('event', 'buckets.[bucketId].create') + ->label('audits.resource', 'buckets/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'storage') ->label('sdk.method', 'createBucket') @@ -63,10 +63,9 @@ App::post('/v1/storage/buckets') ->param('antivirus', true, new Boolean(true), 'Is virus scanning enabled? For file size above ' . Storage::human(APP_LIMIT_ANTIVIRUS, 0) . ' AntiVirus scanning is skipped even if it\'s enabled', true) ->inject('response') ->inject('dbForProject') - ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (string $bucketId, string $name, string $permission, ?array $read, ?array $write, bool $enabled, int $maximumFileSize, array $allowedFileExtensions, bool $encryption, bool $antivirus, Response $response, Database $dbForProject, Audit $audits, Stats $usage, Event $events) { + ->action(function (string $bucketId, string $name, string $permission, ?array $read, ?array $write, bool $enabled, int $maximumFileSize, array $allowedFileExtensions, bool $encryption, bool $antivirus, Response $response, Database $dbForProject, Stats $usage, Event $events) { $bucketId = $bucketId === 'unique()' ? $dbForProject->getId() : $bucketId; try { @@ -124,11 +123,6 @@ App::post('/v1/storage/buckets') throw new Exception(Exception::STORAGE_BUCKET_ALREADY_EXISTS); } - $audits - ->setResource('storage/buckets/' . $bucket->getId()) - ->setPayload($bucket->getArrayCopy()) - ; - $events ->setParam('bucketId', $bucket->getId()) ; @@ -212,6 +206,7 @@ App::put('/v1/storage/buckets/:bucketId') ->groups(['api', 'storage']) ->label('scope', 'buckets.write') ->label('event', 'buckets.[bucketId].update') + ->label('audits.resource', 'buckets/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'storage') ->label('sdk.method', 'updateBucket') @@ -231,10 +226,9 @@ App::put('/v1/storage/buckets/:bucketId') ->param('antivirus', true, new Boolean(true), 'Is virus scanning enabled? For file size above ' . Storage::human(APP_LIMIT_ANTIVIRUS, 0) . ' AntiVirus scanning is skipped even if it\'s enabled', true) ->inject('response') ->inject('dbForProject') - ->inject('audits') ->inject('usage') ->inject('events') - ->action(function (string $bucketId, string $name, string $permission, ?array $read, ?array $write, bool $enabled, ?int $maximumFileSize, array $allowedFileExtensions, bool $encryption, bool $antivirus, Response $response, Database $dbForProject, Audit $audits, Stats $usage, Event $events) { + ->action(function (string $bucketId, string $name, string $permission, ?array $read, ?array $write, bool $enabled, ?int $maximumFileSize, array $allowedFileExtensions, bool $encryption, bool $antivirus, Response $response, Database $dbForProject, Stats $usage, Event $events) { $bucket = $dbForProject->getDocument('buckets', $bucketId); if ($bucket->isEmpty()) { @@ -260,11 +254,6 @@ App::put('/v1/storage/buckets/:bucketId') ->setAttribute('permission', $permission) ->setAttribute('antivirus', (bool) filter_var($antivirus, FILTER_VALIDATE_BOOLEAN))); - $audits - ->setResource('storage/buckets/' . $bucket->getId()) - ->setPayload($bucket->getArrayCopy()) - ; - $events ->setParam('bucketId', $bucket->getId()) ; @@ -279,6 +268,7 @@ App::delete('/v1/storage/buckets/:bucketId') ->groups(['api', 'storage']) ->label('scope', 'buckets.write') ->label('event', 'buckets.[bucketId].delete') + ->label('audits.resource', 'buckets/{request.bucketId}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'storage') ->label('sdk.method', 'deleteBucket') @@ -288,11 +278,10 @@ App::delete('/v1/storage/buckets/:bucketId') ->param('bucketId', '', new UID(), 'Bucket unique ID.') ->inject('response') ->inject('dbForProject') - ->inject('audits') ->inject('deletes') ->inject('events') ->inject('usage') - ->action(function (string $bucketId, Response $response, Database $dbForProject, Audit $audits, Delete $deletes, Event $events, Stats $usage) { + ->action(function (string $bucketId, Response $response, Database $dbForProject, Delete $deletes, Event $events, Stats $usage) { $bucket = $dbForProject->getDocument('buckets', $bucketId); if ($bucket->isEmpty()) { @@ -312,11 +301,6 @@ App::delete('/v1/storage/buckets/:bucketId') ->setPayload($response->output($bucket, Response::MODEL_BUCKET)) ; - $audits - ->setResource('storage/buckets/' . $bucket->getId()) - ->setPayload($bucket->getArrayCopy()) - ; - $usage->setParam('storage.buckets.delete', 1); $response->noContent(); @@ -328,6 +312,7 @@ App::post('/v1/storage/buckets/:bucketId/files') ->groups(['api', 'storage']) ->label('scope', 'files.write') ->label('event', 'buckets.[bucketId].files.[fileId].create') + ->label('audits.resource', 'files/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'storage') ->label('sdk.method', 'createFile') @@ -346,14 +331,14 @@ App::post('/v1/storage/buckets/:bucketId/files') ->inject('response') ->inject('dbForProject') ->inject('user') - ->inject('audits') ->inject('usage') ->inject('events') ->inject('mode') ->inject('deviceFiles') ->inject('deviceLocal') ->inject('deletes') - ->action(function (string $bucketId, string $fileId, mixed $file, ?array $read, ?array $write, Request $request, Response $response, Database $dbForProject, Document $user, Audit $audits, Stats $usage, Event $events, string $mode, Device $deviceFiles, Device $deviceLocal, Delete $deletes) { + ->action(function (string $bucketId, string $fileId, mixed $file, ?array $read, ?array $write, Request $request, Response $response, Database $dbForProject, Document $user, Stats $usage, Event $events, string $mode, Device $deviceFiles, Device $deviceLocal, Delete $deletes) { + $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); if ( @@ -439,8 +424,8 @@ App::post('/v1/storage/buckets/:bucketId/files') } /** - * Validators - */ + * Validators + */ // Check if file type is allowed $allowedFileExtensions = $bucket->getAttribute('allowedFileExtensions', []); $fileExt = new FileExt($allowedFileExtensions); @@ -593,10 +578,6 @@ App::post('/v1/storage/buckets/:bucketId/files') throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); } - $audits - ->setResource('storage/files/' . $file->getId()) - ; - $usage ->setParam('storage', $sizeActual ?? 0) ->setParam('storage.files.create', 1) @@ -1267,6 +1248,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') ->groups(['api', 'storage']) ->label('scope', 'files.write') ->label('event', 'buckets.[bucketId].files.[fileId].update') + ->label('audits.resource', 'files/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'storage') ->label('sdk.method', 'updateFile') @@ -1281,11 +1263,10 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') ->inject('response') ->inject('dbForProject') ->inject('user') - ->inject('audits') ->inject('usage') ->inject('mode') ->inject('events') - ->action(function (string $bucketId, string $fileId, ?array $read, ?array $write, Response $response, Database $dbForProject, Document $user, Audit $audits, Stats $usage, string $mode, Event $events) { + ->action(function (string $bucketId, string $fileId, ?array $read, ?array $write, Response $response, Database $dbForProject, Document $user, Stats $usage, string $mode, Event $events) { $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); $read = (is_null($read) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $read ?? []; // By default set read permissions for user $write = (is_null($write) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $write ?? []; @@ -1348,8 +1329,6 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') ->setContext('bucket', $bucket) ; - $audits->setResource('file/' . $file->getId()); - $usage ->setParam('storage.files.update', 1) ->setParam('bucketId', $bucketId) @@ -1364,6 +1343,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') ->groups(['api', 'storage']) ->label('scope', 'files.write') ->label('event', 'buckets.[bucketId].files.[fileId].delete') + ->label('audits.resource', 'file/{request.fileId}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'storage') ->label('sdk.method', 'deleteFile') @@ -1373,15 +1353,13 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') ->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).') ->param('fileId', '', new UID(), 'File ID.') ->inject('response') - ->inject('request') ->inject('dbForProject') ->inject('events') - ->inject('audits') ->inject('usage') ->inject('mode') ->inject('deviceFiles') ->inject('deletes') - ->action(function (string $bucketId, string $fileId, Response $response, Request $request, Database $dbForProject, Event $events, Audit $audits, Stats $usage, string $mode, Device $deviceFiles, Delete $deletes) { + ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $events, Stats $usage, string $mode, Device $deviceFiles, Delete $deletes) { $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); if ( @@ -1437,8 +1415,6 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to delete file from device'); } - $audits->setResource('file/' . $file->getId()); - $usage ->setParam('storage', $file->getAttribute('size', 0) * -1) ->setParam('storage.files.delete', 1) diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index e78b47cdd9..aee8efb951 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -2,7 +2,6 @@ use Appwrite\Auth\Auth; use Appwrite\Detector\Detector; -use Appwrite\Event\Audit as EventAudit; use Appwrite\Event\Delete; use Appwrite\Event\Event; use Appwrite\Event\Mail; @@ -36,6 +35,7 @@ App::post('/v1/teams') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].create') ->label('scope', 'teams.write') + ->label('audits.resource', 'team/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'teams') ->label('sdk.method', 'create') @@ -50,8 +50,7 @@ App::post('/v1/teams') ->inject('user') ->inject('dbForProject') ->inject('events') - ->inject('audits') - ->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Event $events, Event $audits) { + ->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Event $events) { $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); $isAppUser = Auth::isAppUser(Authorization::getRoles()); @@ -94,12 +93,6 @@ App::post('/v1/teams') $events->setParam('userId', $user->getId()); } - $audits - ->setParam('event', 'teams.create') - ->setParam('resource', 'team/' . $teamId) - ->setParam('data', $team->getArrayCopy()) - ; - $response->setStatusCode(Response::STATUS_CODE_CREATED); $response->dynamic($team, Response::MODEL_TEAM); }); @@ -178,6 +171,7 @@ App::put('/v1/teams/:teamId') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].update') ->label('scope', 'teams.write') + ->label('audits.resource', 'team/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'teams') ->label('sdk.method', 'update') @@ -190,8 +184,7 @@ App::put('/v1/teams/:teamId') ->inject('response') ->inject('dbForProject') ->inject('events') - ->inject('audits') - ->action(function (string $teamId, string $name, Response $response, Database $dbForProject, Event $events, EventAudit $audits) { + ->action(function (string $teamId, string $name, Response $response, Database $dbForProject, Event $events) { $team = $dbForProject->getDocument('teams', $teamId); @@ -204,7 +197,6 @@ App::put('/v1/teams/:teamId') ->setAttribute('search', implode(' ', [$teamId, $name]))); $events->setParam('teamId', $team->getId()); - $audits->setResource('team/' . $team->getId()); $response->dynamic($team, Response::MODEL_TEAM); }); @@ -214,6 +206,7 @@ App::delete('/v1/teams/:teamId') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].delete') ->label('scope', 'teams.write') + ->label('audits.resource', 'team/{request.teamId}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'teams') ->label('sdk.method', 'delete') @@ -225,8 +218,7 @@ App::delete('/v1/teams/:teamId') ->inject('dbForProject') ->inject('events') ->inject('deletes') - ->inject('audits') - ->action(function (string $teamId, Response $response, Database $dbForProject, Event $events, Delete $deletes, EventAudit $audits) { + ->action(function (string $teamId, Response $response, Database $dbForProject, Event $events, Delete $deletes) { $team = $dbForProject->getDocument('teams', $teamId); @@ -258,12 +250,6 @@ App::delete('/v1/teams/:teamId') ->setPayload($response->output($team, Response::MODEL_TEAM)) ; - $audits - ->setParam('event', 'teams.delete') - ->setParam('resource', 'team/' . $teamId) - ->setParam('data', $team->getArrayCopy()) - ; - $response->noContent(); }); @@ -273,6 +259,8 @@ App::post('/v1/teams/:teamId/memberships') ->label('event', 'teams.[teamId].memberships.[membershipId].create') ->label('scope', 'teams.write') ->label('auth.type', 'invites') + ->label('audits.resource', 'team/{request.teamId}') + ->label('audits.userId', '{request.userId}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'teams') ->label('sdk.method', 'createMembership') @@ -291,10 +279,9 @@ App::post('/v1/teams/:teamId/memberships') ->inject('user') ->inject('dbForProject') ->inject('locale') - ->inject('audits') ->inject('mails') ->inject('events') - ->action(function (string $teamId, string $email, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, EventAudit $audits, Mail $mails, Event $events) { + ->action(function (string $teamId, string $email, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $mails, Event $events) { $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); $isAppUser = Auth::isAppUser(Authorization::getRoles()); @@ -416,10 +403,6 @@ App::post('/v1/teams/:teamId/memberships') ; } - $audits - ->setResource('team/' . $teamId) - ; - $events ->setParam('teamId', $team->getId()) ->setParam('membershipId', $membership->getId()) @@ -558,6 +541,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].memberships.[membershipId].update') ->label('scope', 'teams.write') + ->label('audits.resource', 'team/{request.teamId}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'teams') ->label('sdk.method', 'updateMembershipRoles') @@ -572,9 +556,8 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId') ->inject('response') ->inject('user') ->inject('dbForProject') - ->inject('audits') ->inject('events') - ->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Database $dbForProject, EventAudit $audits, Event $events) { + ->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Database $dbForProject, Event $events) { $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { @@ -610,8 +593,6 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId') */ $dbForProject->deleteCachedDocument('users', $profile->getId()); - $audits->setResource('team/' . $teamId); - $events ->setParam('teamId', $team->getId()) ->setParam('membershipId', $membership->getId()); @@ -630,6 +611,8 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].memberships.[membershipId].update.status') ->label('scope', 'public') + ->label('audits.resource', 'team/{request.teamId}') + ->label('audits.userId', '{request.userId}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'teams') ->label('sdk.method', 'updateMembershipStatus') @@ -646,9 +629,8 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') ->inject('user') ->inject('dbForProject') ->inject('geodb') - ->inject('audits') ->inject('events') - ->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Reader $geodb, EventAudit $audits, Event $events) { + ->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Reader $geodb, Event $events) { $protocol = $request->getProtocol(); $membership = $dbForProject->getDocument('memberships', $membershipId); @@ -692,9 +674,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') ->setAttribute('confirm', true) ; - $user - ->setAttribute('emailVerification', true) - ; + $user->setAttribute('emailVerification', true); // Log user in @@ -731,8 +711,6 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') $team = Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team->setAttribute('total', $team->getAttribute('total', 0) + 1))); - $audits->setResource('team/' . $teamId); - $events ->setParam('teamId', $team->getId()) ->setParam('membershipId', $membership->getId()) @@ -763,6 +741,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].memberships.[membershipId].delete') ->label('scope', 'teams.write') + ->label('audits.resource', 'team/{request.teamId}') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'teams') ->label('sdk.method', 'deleteMembership') @@ -773,9 +752,8 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId') ->param('membershipId', '', new UID(), 'Membership ID.') ->inject('response') ->inject('dbForProject') - ->inject('audits') ->inject('events') - ->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject, EventAudit $audits, Event $events) { + ->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject, Event $events) { $membership = $dbForProject->getDocument('memberships', $membershipId); @@ -814,8 +792,6 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId') Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team)); } - $audits->setResource('team/' . $teamId); - $events ->setParam('teamId', $team->getId()) ->setParam('membershipId', $membership->getId()) diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 64e5f81615..de04cdb1fb 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -6,7 +6,6 @@ use Appwrite\Auth\Validator\Phone; use Appwrite\Detector\Detector; use Appwrite\Event\Delete; use Appwrite\Event\Event; -use Appwrite\Event\Audit as EventAudit; use Appwrite\Network\Validator\Email; use Appwrite\Stats\Stats; use Appwrite\Utopia\Database\Validator\CustomId; @@ -68,13 +67,9 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e throw new Exception(Exception::USER_ALREADY_EXISTS); } - $usage - ->setParam('users.create', 1) - ; + $usage->setParam('users.create', 1); - $events - ->setParam('userId', $user->getId()) - ; + $events->setParam('userId', $user->getId()); return $user; } @@ -84,6 +79,7 @@ App::post('/v1/users') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') ->label('scope', 'users.write') + ->label('audits.resource', 'user/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'create') @@ -112,6 +108,7 @@ App::post('/v1/users/bcrypt') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') ->label('scope', 'users.write') + ->label('audits.resource', 'user/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'createBcryptUser') @@ -139,6 +136,7 @@ App::post('/v1/users/md5') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') ->label('scope', 'users.write') + ->label('audits.resource', 'user/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'createMD5User') @@ -166,6 +164,7 @@ App::post('/v1/users/argon2') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') ->label('scope', 'users.write') + ->label('audits.resource', 'user/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'createArgon2User') @@ -193,6 +192,7 @@ App::post('/v1/users/sha') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') ->label('scope', 'users.write') + ->label('audits.resource', 'user/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'createSHAUser') @@ -227,6 +227,7 @@ App::post('/v1/users/phpass') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') ->label('scope', 'users.write') + ->label('audits.resource', 'user/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'createPHPassUser') @@ -254,6 +255,7 @@ App::post('/v1/users/scrypt') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') ->label('scope', 'users.write') + ->label('audits.resource', 'user/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'createScryptUser') @@ -294,6 +296,7 @@ App::post('/v1/users/scrypt-modified') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') ->label('scope', 'users.write') + ->label('audits.resource', 'user/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'createScryptModifiedUser') @@ -355,9 +358,7 @@ App::get('/v1/users') $queries[] = new Query('search', Query::TYPE_SEARCH, [$search]); } - $usage - ->setParam('users.read', 1) - ; + $usage->setParam('users.read', 1); $response->dynamic(new Document([ 'users' => $dbForProject->find('users', $queries, $limit, $offset, [], [$orderType], $cursorUser ?? null, $cursorDirection), @@ -388,12 +389,48 @@ App::get('/v1/users/:userId') throw new Exception(Exception::USER_NOT_FOUND); } - $usage - ->setParam('users.read', 1) - ; + $usage->setParam('users.read', 1); + $response->dynamic($user, Response::MODEL_USER); }); +App::patch('/v1/users/:userId/prefs') + ->desc('Update User Preferences') + ->groups(['api', 'users']) + ->label('event', 'users.[userId].update.prefs') + ->label('scope', 'users.write') + ->label('audits.resource', 'user/{request.userId}') + ->label('audits.userId', '{request.userId}') + ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) + ->label('sdk.namespace', 'users') + ->label('sdk.method', 'updatePrefs') + ->label('sdk.description', '/docs/references/users/update-user-prefs.md') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_PREFERENCES) + ->param('userId', '', new UID(), 'User ID.') + ->param('prefs', '', new Assoc(), 'Prefs key-value JSON object.') + ->inject('response') + ->inject('dbForProject') + ->inject('usage') + ->inject('events') + ->action(function (string $userId, array $prefs, Response $response, Database $dbForProject, Stats $usage, Event $events) { + + $user = $dbForProject->getDocument('users', $userId); + + if ($user->isEmpty()) { + throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + } + + $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('prefs', $prefs)); + + $usage->setParam('users.update', 1); + + $events->setParam('userId', $user->getId()); + + $response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES); + }); + App::get('/v1/users/:userId/prefs') ->desc('Get User Preferences') ->groups(['api', 'users']) @@ -419,9 +456,8 @@ App::get('/v1/users/:userId/prefs') $prefs = $user->getAttribute('prefs', new \stdClass()); - $usage - ->setParam('users.read', 1) - ; + $usage->setParam('users.read', 1); + $response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES); }); @@ -461,9 +497,8 @@ App::get('/v1/users/:userId/sessions') $sessions[$key] = $session; } - $usage - ->setParam('users.read', 1) - ; + $usage->setParam('users.read', 1); + $response->dynamic(new Document([ 'sessions' => $sessions, 'total' => count($sessions), @@ -581,9 +616,7 @@ App::get('/v1/users/:userId/logs') } } - $usage - ->setParam('users.read', 1) - ; + $usage->setParam('users.read', 1); $response->dynamic(new Document([ 'total' => $audit->countLogsByUser($user->getId()), @@ -596,6 +629,8 @@ App::patch('/v1/users/:userId/status') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.status') ->label('scope', 'users.write') + ->label('audits.resource', 'user/{response.$id}') + ->label('audits.userId', '{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'updateStatus') @@ -619,13 +654,9 @@ App::patch('/v1/users/:userId/status') $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('status', (bool) $status)); - $usage - ->setParam('users.update', 1) - ; + $usage->setParam('users.update', 1); - $events - ->setParam('userId', $user->getId()) - ; + $events->setParam('userId', $user->getId()); $response->dynamic($user, Response::MODEL_USER); }); @@ -635,6 +666,7 @@ App::patch('/v1/users/:userId/verification') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.verification') ->label('scope', 'users.write') + ->label('audits.resource', 'user/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'updateEmailVerification') @@ -658,13 +690,9 @@ App::patch('/v1/users/:userId/verification') $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', $emailVerification)); - $usage - ->setParam('users.update', 1) - ; + $usage->setParam('users.update', 1); - $events - ->setParam('userId', $user->getId()) - ; + $events->setParam('userId', $user->getId()); $response->dynamic($user, Response::MODEL_USER); }); @@ -674,6 +702,7 @@ App::patch('/v1/users/:userId/verification/phone') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.verification') ->label('scope', 'users.write') + ->label('audits.resource', 'user/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'updatePhoneVerification') @@ -697,13 +726,9 @@ App::patch('/v1/users/:userId/verification/phone') $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('phoneVerification', $phoneVerification)); - $usage - ->setParam('users.update', 1) - ; + $usage->setParam('users.update', 1); - $events - ->setParam('userId', $user->getId()) - ; + $events->setParam('userId', $user->getId()); $response->dynamic($user, Response::MODEL_USER); }); @@ -713,6 +738,8 @@ App::patch('/v1/users/:userId/name') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.name') ->label('scope', 'users.write') + ->label('audits.resource', 'user/{response.$id}') + ->label('audits.userId', '{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'updateName') @@ -724,9 +751,8 @@ App::patch('/v1/users/:userId/name') ->param('name', '', new Text(128), 'User name. Max length: 128 chars.') ->inject('response') ->inject('dbForProject') - ->inject('audits') ->inject('events') - ->action(function (string $userId, string $name, Response $response, Database $dbForProject, EventAudit $audits, Event $events) { + ->action(function (string $userId, string $name, Response $response, Database $dbForProject, Event $events) { $user = $dbForProject->getDocument('users', $userId); @@ -741,13 +767,7 @@ App::patch('/v1/users/:userId/name') $user = $dbForProject->updateDocument('users', $user->getId(), $user); - $audits - ->setResource('user/' . $user->getId()) - ; - - $events - ->setParam('userId', $user->getId()) - ; + $events->setParam('userId', $user->getId()); $response->dynamic($user, Response::MODEL_USER); }); @@ -757,6 +777,8 @@ App::patch('/v1/users/:userId/password') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.password') ->label('scope', 'users.write') + ->label('audits.resource', 'user/{response.$id}') + ->label('audits.userId', '{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'updatePassword') @@ -768,9 +790,9 @@ App::patch('/v1/users/:userId/password') ->param('password', '', new Password(), 'New user password. Must be at least 8 chars.') ->inject('response') ->inject('dbForProject') - ->inject('audits') ->inject('events') - ->action(function (string $userId, string $password, Response $response, Database $dbForProject, EventAudit $audits, Event $events) { + ->action(function (string $userId, string $password, Response $response, Database $dbForProject, Event $events) { + $user = $dbForProject->getDocument('users', $userId); if ($user->isEmpty()) { @@ -785,13 +807,7 @@ App::patch('/v1/users/:userId/password') $user = $dbForProject->updateDocument('users', $user->getId(), $user); - $audits - ->setResource('user/' . $user->getId()) - ; - - $events - ->setParam('userId', $user->getId()) - ; + $events->setParam('userId', $user->getId()); $response->dynamic($user, Response::MODEL_USER); }); @@ -801,6 +817,8 @@ App::patch('/v1/users/:userId/email') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.email') ->label('scope', 'users.write') + ->label('audits.resource', 'user/{response.$id}') + ->label('audits.userId', '{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'updateEmail') @@ -812,9 +830,8 @@ App::patch('/v1/users/:userId/email') ->param('email', '', new Email(), 'User email.') ->inject('response') ->inject('dbForProject') - ->inject('audits') ->inject('events') - ->action(function (string $userId, string $email, Response $response, Database $dbForProject, EventAudit $audits, Event $events) { + ->action(function (string $userId, string $email, Response $response, Database $dbForProject, Event $events) { $user = $dbForProject->getDocument('users', $userId); @@ -836,14 +853,7 @@ App::patch('/v1/users/:userId/email') throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS); } - - $audits - ->setResource('user/' . $user->getId()) - ; - - $events - ->setParam('userId', $user->getId()) - ; + $events->setParam('userId', $user->getId()); $response->dynamic($user, Response::MODEL_USER); }); @@ -853,6 +863,7 @@ App::patch('/v1/users/:userId/phone') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.phone') ->label('scope', 'users.write') + ->label('audits.resource', 'user/{response.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'updatePhone') @@ -864,9 +875,8 @@ App::patch('/v1/users/:userId/phone') ->param('number', '', new Phone(), 'User phone number.') ->inject('response') ->inject('dbForProject') - ->inject('audits') ->inject('events') - ->action(function (string $userId, string $number, Response $response, Database $dbForProject, EventAudit $audits, Event $events) { + ->action(function (string $userId, string $number, Response $response, Database $dbForProject, Event $events) { $user = $dbForProject->getDocument('users', $userId); @@ -886,14 +896,44 @@ App::patch('/v1/users/:userId/phone') throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS); } + $events->setParam('userId', $user->getId()); - $audits - ->setResource('user/' . $user->getId()) - ; + $response->dynamic($user, Response::MODEL_USER); + }); - $events - ->setParam('userId', $user->getId()) - ; +App::patch('/v1/users/:userId/verification') + ->desc('Update Email Verification') + ->groups(['api', 'users']) + ->label('event', 'users.[userId].update.verification') + ->label('scope', 'users.write') + ->label('audits.resource', 'user/{request.userId}') + ->label('audits.userId', '{request.userId}') + ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) + ->label('sdk.namespace', 'users') + ->label('sdk.method', 'updateEmailVerification') + ->label('sdk.description', '/docs/references/users/update-user-email-verification.md') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_USER) + ->param('userId', '', new UID(), 'User ID.') + ->param('emailVerification', false, new Boolean(), 'User email verification status.') + ->inject('response') + ->inject('dbForProject') + ->inject('usage') + ->inject('events') + ->action(function (string $userId, bool $emailVerification, Response $response, Database $dbForProject, Stats $usage, Event $events) { + + $user = $dbForProject->getDocument('users', $userId); + + if ($user->isEmpty()) { + throw new Exception(Exception::USER_NOT_FOUND); + } + + $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', $emailVerification)); + + $usage->setParam('users.update', 1); + + $events->setParam('userId', $user->getId()); $response->dynamic($user, Response::MODEL_USER); }); @@ -926,13 +966,9 @@ App::patch('/v1/users/:userId/prefs') $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('prefs', $prefs)); - $usage - ->setParam('users.update', 1) - ; + $usage->setParam('users.update', 1); - $events - ->setParam('userId', $user->getId()) - ; + $events->setParam('userId', $user->getId()); $response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES); }); @@ -942,6 +978,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId') ->groups(['api', 'users']) ->label('event', 'users.[userId].sessions.[sessionId].delete') ->label('scope', 'users.write') + ->label('audits.resource', 'user/{request.userId}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'deleteSession') @@ -990,6 +1027,7 @@ App::delete('/v1/users/:userId/sessions') ->groups(['api', 'users']) ->label('event', 'users.[userId].sessions.[sessionId].delete') ->label('scope', 'users.write') + ->label('audits.resource', 'user/{user.$id}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'deleteSessions') @@ -1036,6 +1074,7 @@ App::delete('/v1/users/:userId') ->groups(['api', 'users']) ->label('event', 'users.[userId].delete') ->label('scope', 'users.write') + ->label('audits.resource', 'user/{request.userId}') ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'users') ->label('sdk.method', 'delete') @@ -1071,9 +1110,7 @@ App::delete('/v1/users/:userId') ->setPayload($response->output($clone, Response::MODEL_USER)) ; - $usage - ->setParam('users.delete', 1) - ; + $usage->setParam('users.delete', 1); $response->noContent(); }); diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index de93416712..5e77424e30 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -43,7 +43,6 @@ $parseLabel = function (string $label, array $responsePayload, array $requestPar $label = \str_replace($find, $params[$replace], $label); } } - return $label; }; @@ -70,7 +69,7 @@ App::init() throw new Exception(Exception::PROJECT_UNKNOWN); } - /* + /** * Abuse Check */ $abuseKeyLabel = $route->getLabel('abuse-key', 'url:{url},ip:{ip}'); @@ -282,7 +281,7 @@ App::shutdown() $bucket = $events->getContext('bucket'); $target = Realtime::fromPayload( - // Pass first, most verbose event pattern + // Pass first, most verbose event pattern event: $allEvents[0], payload: $payload, project: $project, @@ -305,7 +304,38 @@ App::shutdown() } } - if (!empty($audits->getResource())) { + $route = $utopia->match($request); + $requestParams = $route->getParamsValues(); + $user = $audits->getUser(); + + /** + * Audit labels + */ + $pattern = $route->getLabel('audits.resource', null); + if (!empty($pattern)) { + $resource = $parseLabel($pattern, $responsePayload, $requestParams, $user); + if (!empty($resource) && $resource !== $pattern) { + $audits->setResource($resource); + } + } + + $pattern = $route->getLabel('audits.userId', null); + if (!empty($pattern)) { + $userId = $parseLabel($pattern, $responsePayload, $requestParams, $user); + $user = $dbForProject->getDocument('users', $userId); + $audits->setUser($user); + } + + if (!empty($audits->getResource()) && !empty($audits->getUser()->getId())) { + /** + * audits.payload is switched to default true + * in order to auto audit payload for all endpoints + */ + $pattern = $route->getLabel('audits.payload', true); + if (!empty($pattern)) { + $audits->setPayload($responsePayload); + } + foreach ($events->getParams() as $key => $value) { $audits->setParam($key, $value); } @@ -320,10 +350,9 @@ App::shutdown() $database->trigger(); } - $route = $utopia->match($request); - $requestParams = $route->getParamsValues(); - $user = $audits->getUser(); - + /** + * Cache label + */ $useCache = $route->getLabel('cache', false); if ($useCache) { $resource = null; diff --git a/app/workers/deletes.php b/app/workers/deletes.php index 3d77cc3236..15b5b555e7 100644 --- a/app/workers/deletes.php +++ b/app/workers/deletes.php @@ -138,7 +138,7 @@ class DeletesV1 extends Worker protected function deleteCacheByTimestamp(): void { $this->deleteCacheFiles([ - new Query('accessedAt', Query::TYPE_LESSER, [$this->args['timestamp']]) + new Query('accessedAt', Query::TYPE_LESSER, [$this->args['timestamp']]) ]); }