diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 43f9cef5a3..dafebb735d 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -188,13 +188,14 @@ App::init() ->inject('request') ->inject('dbForPlatform') ->inject('dbForProject') + ->inject('queueForAudits') ->inject('project') ->inject('user') ->inject('session') ->inject('servers') ->inject('mode') ->inject('team') - ->action(function (App $utopia, Request $request, Database $dbForPlatform, Database $dbForProject, Document $project, Document $user, ?Document $session, array $servers, string $mode, Document $team) { + ->action(function (App $utopia, Request $request, Database $dbForPlatform, Database $dbForProject, Audit $queueForAudits, Document $project, Document $user, ?Document $session, array $servers, string $mode, Document $team) { $route = $utopia->getRoute(); if ($project->isEmpty()) { @@ -246,9 +247,10 @@ App::init() $user = new Document([ '$id' => '', 'status' => true, + 'type' => Auth::ACTIVITY_TYPE_APP, 'email' => 'app.' . $project->getId() . '@service.' . $request->getHostname(), 'password' => '', - 'name' => $project->getAttribute('name', 'Untitled'), + 'name' => 'Dynamic Key', ]); $role = Auth::USER_ROLE_APPS; @@ -256,6 +258,8 @@ App::init() Authorization::setRole(Auth::USER_ROLE_APPS); Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys. + + $queueForAudits->setUser($user); } } elseif ($keyType === API_KEY_STANDARD) { // No underline means no prefix. Backwards compatibility. @@ -267,9 +271,10 @@ App::init() $user = new Document([ '$id' => '', 'status' => true, + 'type' => Auth::ACTIVITY_TYPE_APP, 'email' => 'app.' . $project->getId() . '@service.' . $request->getHostname(), 'password' => '', - 'name' => $project->getAttribute('name', 'Untitled'), + 'name' => $key->getAttribute('name', 'UNKNOWN'), ]); $role = Auth::USER_ROLE_APPS; @@ -304,6 +309,8 @@ App::init() $dbForPlatform->purgeCachedDocument('projects', $project->getId()); } } + + $queueForAudits->setUser($user); } } } @@ -511,8 +518,15 @@ App::init() ->setIP($request->getIP()) ->setHostname($request->getHostname()) ->setEvent($route->getLabel('audits.event', '')) - ->setProject($project) - ->setUser($user); + ->setProject($project); + + /* If a session exists, use the user associated with the session */ + if (!$user->isEmpty()) { + $userClone = clone $user; + // $user doesn't support `type` and can cause unintended effects. + $userClone->setAttribute('type', Auth::ACTIVITY_TYPE_USER); + $queueForAudits->setUser($userClone); + } $queueForDeletes->setProject($project); $queueForDatabase->setProject($project); @@ -720,10 +734,32 @@ App::shutdown() } if (!$user->isEmpty()) { + $userClone = clone $user; + // $user doesn't support `type` and can cause unintended effects. + $userClone->setAttribute('type', Auth::ACTIVITY_TYPE_USER); + $queueForAudits->setUser($userClone); + } elseif ($queueForAudits->getUser() === null || $queueForAudits->getUser()->isEmpty()) { + /** + * User in the request is empty, and no user was set for auditing previously. + * This indicates: + * - No API Key was used. + * - No active session exists. + * + * Therefore, we consider this an anonymous request and create a relevant user. + */ + $user = new Document([ + '$id' => '', + 'status' => true, + 'type' => Auth::ACTIVITY_TYPE_GUEST, + 'email' => 'guest.' . $project->getId() . '@service.' . $request->getHostname(), + 'password' => '', + 'name' => 'Guest', + ]); + $queueForAudits->setUser($user); } - if (!empty($queueForAudits->getResource()) && !empty($queueForAudits->getUser()->getId())) { + if (!empty($queueForAudits->getResource()) && !$queueForAudits->getUser()->isEmpty()) { /** * audits.payload is switched to default true * in order to auto audit payload for all endpoints diff --git a/src/Appwrite/Auth/Auth.php b/src/Appwrite/Auth/Auth.php index 1e8109622e..8555d5cb00 100644 --- a/src/Appwrite/Auth/Auth.php +++ b/src/Appwrite/Auth/Auth.php @@ -43,6 +43,13 @@ class Auth public const USER_ROLE_APPS = 'apps'; public const USER_ROLE_SYSTEM = 'system'; + /** + * Activity associated with user or the app. + */ + public const ACTIVITY_TYPE_APP = 'app'; + public const ACTIVITY_TYPE_USER = 'user'; + public const ACTIVITY_TYPE_GUEST = 'guest'; + /** * Token Types. */ diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index 86ca59d3fd..c0bcab1c3a 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Workers; +use Appwrite\Auth\Auth; use Exception; use Throwable; use Utopia\Audit\Audit; @@ -60,6 +61,7 @@ class Audits extends Action $userName = $user->getAttribute('name', ''); $userEmail = $user->getAttribute('email', ''); + $userType = $user->getAttribute('type', Auth::ACTIVITY_TYPE_USER); $audit = new Audit($dbForProject); $audit->log( @@ -74,6 +76,7 @@ class Audits extends Action 'userId' => $user->getId(), 'userName' => $userName, 'userEmail' => $userEmail, + 'userType' => $userType, 'mode' => $mode, 'data' => $auditPayload, ]