diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index 6efabab7d6..9792f351d3 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -1178,6 +1178,7 @@ Http::get('/v1/messaging/providers/:providerId/logs') 'userEmail' => $log['data']['userEmail'] ?? null, 'userName' => $log['data']['userName'] ?? null, 'mode' => $log['data']['mode'] ?? null, + 'userType' => $log['data']['userType'] ?? null, 'ip' => $log['ip'], 'time' => $log['time'], 'osCode' => $os['osCode'], @@ -2584,6 +2585,7 @@ Http::get('/v1/messaging/topics/:topicId/logs') 'userEmail' => $log['data']['userEmail'] ?? null, 'userName' => $log['data']['userName'] ?? null, 'mode' => $log['data']['mode'] ?? null, + 'userType' => $log['data']['userType'] ?? null, 'ip' => $log['ip'], 'time' => $log['time'], 'osCode' => $os['osCode'], @@ -2999,6 +3001,7 @@ Http::get('/v1/messaging/subscribers/:subscriberId/logs') 'userEmail' => $log['data']['userEmail'] ?? null, 'userName' => $log['data']['userName'] ?? null, 'mode' => $log['data']['mode'] ?? null, + 'userType' => $log['data']['userType'] ?? null, 'ip' => $log['ip'], 'time' => $log['time'], 'osCode' => $os['osCode'], @@ -3812,6 +3815,7 @@ Http::get('/v1/messaging/messages/:messageId/logs') 'userEmail' => $log['data']['userEmail'] ?? null, 'userName' => $log['data']['userName'] ?? null, 'mode' => $log['data']['mode'] ?? null, + 'userType' => $log['data']['userType'] ?? null, 'ip' => $log['ip'], 'time' => $log['time'], 'osCode' => $os['osCode'], diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index eca953c8b3..e699b61423 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -1010,6 +1010,8 @@ Http::get('/v1/users/:userId/logs') 'userId' => ID::custom($log['data']['userId']), 'userEmail' => $log['data']['userEmail'] ?? null, 'userName' => $log['data']['userName'] ?? null, + 'mode' => $log['data']['mode'] ?? null, + 'userType' => $log['data']['userType'] ?? null, 'ip' => $log['ip'], 'time' => $log['time'], 'osCode' => $os['osCode'], diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 8254a22ac0..11ac345fca 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -186,7 +186,7 @@ Http::init() $user = new User([ '$id' => '', 'status' => true, - 'type' => ACTIVITY_TYPE_APP, + 'type' => ACTIVITY_TYPE_KEY_PROJECT, 'email' => 'app.' . $project->getId() . '@service.' . $request->getHostname(), 'password' => '', 'name' => $apiKey->getName(), @@ -256,7 +256,14 @@ Http::init() } } - $queueForAudits->setUser($user); + $userClone = clone $user; + $userClone->setAttribute('type', match ($apiKey->getType()) { + API_KEY_STANDARD => ACTIVITY_TYPE_KEY_PROJECT, + API_KEY_ACCOUNT => ACTIVITY_TYPE_KEY_ACCOUNT, + API_KEY_ORGANIZATION => ACTIVITY_TYPE_KEY_ORGANIZATION, + default => ACTIVITY_TYPE_KEY_PROJECT, + }); + $queueForAudits->setUser($userClone); } // Apply permission @@ -599,7 +606,9 @@ Http::init() if (! $user->isEmpty()) { $userClone = clone $user; // $user doesn't support `type` and can cause unintended effects. - $userClone->setAttribute('type', ACTIVITY_TYPE_USER); + if (empty($user->getAttribute('type'))) { + $userClone->setAttribute('type', $mode === APP_MODE_ADMIN ? ACTIVITY_TYPE_ADMIN : ACTIVITY_TYPE_USER); + } $queueForAudits->setUser($userClone); } @@ -781,7 +790,8 @@ Http::shutdown() ->inject('eventProcessor') ->inject('bus') ->inject('apiKey') - ->action(function (Http $utopia, Request $request, Response $response, Document $project, User $user, Event $queueForEvents, Audit $queueForAudits, Context $usage, UsagePublisher $publisherForUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Func $queueForFunctions, Event $queueForWebhooks, Realtime $queueForRealtime, Database $dbForProject, Authorization $authorization, callable $timelimit, EventProcessor $eventProcessor, Bus $bus, ?Key $apiKey) use ($parseLabel) { + ->inject('mode') + ->action(function (Http $utopia, Request $request, Response $response, Document $project, User $user, Event $queueForEvents, Audit $queueForAudits, Context $usage, UsagePublisher $publisherForUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Func $queueForFunctions, Event $queueForWebhooks, Realtime $queueForRealtime, Database $dbForProject, Authorization $authorization, callable $timelimit, EventProcessor $eventProcessor, Bus $bus, ?Key $apiKey, string $mode) use ($parseLabel) { $responsePayload = $response->getPayload(); @@ -883,7 +893,9 @@ Http::shutdown() if (! $user->isEmpty()) { $userClone = clone $user; // $user doesn't support `type` and can cause unintended effects. - $userClone->setAttribute('type', ACTIVITY_TYPE_USER); + if (empty($user->getAttribute('type'))) { + $userClone->setAttribute('type', $mode === APP_MODE_ADMIN ? ACTIVITY_TYPE_ADMIN : ACTIVITY_TYPE_USER); + } $queueForAudits->setUser($userClone); } elseif ($queueForAudits->getUser() === null || $queueForAudits->getUser()->isEmpty()) { /** diff --git a/app/init/constants.php b/app/init/constants.php index 89a50ad811..a737a9b9e1 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -156,9 +156,12 @@ const SESSION_PROVIDER_SERVER = 'server'; /** * Activity associated with user or the app. */ -const ACTIVITY_TYPE_APP = 'app'; const ACTIVITY_TYPE_USER = 'user'; +const ACTIVITY_TYPE_ADMIN = 'admin'; const ACTIVITY_TYPE_GUEST = 'guest'; +const ACTIVITY_TYPE_KEY_PROJECT = 'keyProject'; +const ACTIVITY_TYPE_KEY_ACCOUNT = 'keyAccount'; +const ACTIVITY_TYPE_KEY_ORGANIZATION = 'keyOrganization'; /** * MFA diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php index f177003a2c..9a51751508 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Logs/XList.php @@ -129,6 +129,7 @@ class XList extends Action 'userEmail' => $log['data']['userEmail'] ?? null, 'userName' => $log['data']['userName'] ?? null, 'mode' => $log['data']['mode'] ?? null, + 'userType' => $log['data']['userType'] ?? null, 'ip' => $log['ip'], 'time' => $log['time'], 'osCode' => $os['osCode'], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php index 79a626af19..5766e9e0bf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Logs/XList.php @@ -121,6 +121,7 @@ class XList extends Action 'userEmail' => $log['data']['userEmail'] ?? null, 'userName' => $log['data']['userName'] ?? null, 'mode' => $log['data']['mode'] ?? null, + 'userType' => $log['data']['userType'] ?? null, 'ip' => $log['ip'] ?? null, 'time' => $log['time'] ?? null, 'osCode' => $os['osCode'] ?? null, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Logs/XList.php index a6956a760e..421c7401bc 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Logs/XList.php @@ -111,6 +111,7 @@ class XList extends Action 'userEmail' => $log['data']['userEmail'] ?? null, 'userName' => $log['data']['userName'] ?? null, 'mode' => $log['data']['mode'] ?? null, + 'userType' => $log['data']['userType'] ?? null, 'ip' => $log['ip'], 'time' => $log['time'], 'osCode' => $os['osCode'], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Logs/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Logs/XList.php index b3739a7ab3..b877686c47 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Logs/XList.php @@ -105,6 +105,7 @@ class XList extends Action 'userEmail' => $log['data']['userEmail'] ?? null, 'userName' => $log['data']['userName'] ?? null, 'mode' => $log['data']['mode'] ?? null, + 'userType' => $log['data']['userType'] ?? null, 'ip' => $log['ip'], 'time' => $log['time'], 'osCode' => $os['osCode'], diff --git a/src/Appwrite/Platform/Modules/Project/Http/Project/Variables/Create.php b/src/Appwrite/Platform/Modules/Project/Http/Project/Variables/Create.php index 8dbc720045..7b6dc67b4f 100644 --- a/src/Appwrite/Platform/Modules/Project/Http/Project/Variables/Create.php +++ b/src/Appwrite/Platform/Modules/Project/Http/Project/Variables/Create.php @@ -53,7 +53,7 @@ class Create extends Action ) ], )) - ->param('variableId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Variable ID. Choose a custom ID or generate a random ID with `ID.unique()`. 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.', false, ['dbForProject']) + ->param('variableId', 'unique()', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Variable ID. Choose a custom ID or generate a random ID with `ID.unique()`. 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.', true, ['dbForProject']) ->param('key', null, new Text(Database::LENGTH_KEY), 'Variable key. Max length: ' . Database::LENGTH_KEY . ' chars.') ->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.') ->param('secret', true, new Boolean(), 'Secret variables can be updated or deleted, but only projects can read them during build and runtime.', true) diff --git a/src/Appwrite/Platform/Modules/Teams/Http/Logs/XList.php b/src/Appwrite/Platform/Modules/Teams/Http/Logs/XList.php index 05cb935375..6c6f5fd639 100644 --- a/src/Appwrite/Platform/Modules/Teams/Http/Logs/XList.php +++ b/src/Appwrite/Platform/Modules/Teams/Http/Logs/XList.php @@ -101,6 +101,7 @@ class XList extends Action 'userEmail' => $log['data']['userEmail'] ?? null, 'userName' => $log['data']['userName'] ?? null, 'mode' => $log['data']['mode'] ?? null, + 'userType' => $log['data']['userType'] ?? null, 'ip' => $log['ip'], 'time' => $log['time'], 'osCode' => $os['osCode'], diff --git a/src/Appwrite/Utopia/Response/Model/Log.php b/src/Appwrite/Utopia/Response/Model/Log.php index a8c00280d3..1e48a6bc89 100644 --- a/src/Appwrite/Utopia/Response/Model/Log.php +++ b/src/Appwrite/Utopia/Response/Model/Log.php @@ -40,6 +40,12 @@ class Log extends Model 'default' => '', 'example' => 'admin', ]) + ->addRule('userType', [ + 'type' => self::TYPE_STRING, + 'description' => 'User type who triggered the audit log. Possible values: user, admin, guest, keyProject, keyAccount, keyOrganization.', + 'default' => '', + 'example' => 'user', + ]) ->addRule('ip', [ 'type' => self::TYPE_STRING, 'description' => 'IP session in use when the session was created.', diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 853aa32b9b..ed273b0e04 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -4826,6 +4826,22 @@ class ProjectsConsoleClientTest extends Scope $this->assertEquals('APP_TEST_CREATE_1', $variable['body']['key']); $this->assertEmpty($variable['body']['value']); + // test for variable without variableId (auto-generated) + $variable = $this->client->call(Client::METHOD_POST, '/project/variables', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $data['projectId'], + 'x-appwrite-mode' => 'admin', + ], $this->getHeaders()), [ + 'key' => 'APP_TEST_CREATE_AUTO_ID', + 'value' => 'AUTOIDVALUE', + 'secret' => false + ]); + + $this->assertEquals(201, $variable['headers']['status-code']); + $this->assertNotEmpty($variable['body']['$id']); + $this->assertEquals('APP_TEST_CREATE_AUTO_ID', $variable['body']['key']); + $this->assertEquals('AUTOIDVALUE', $variable['body']['value']); + /** * Test for FAILURE */