$tenant]; if (isset($filters['enabled'])) { $filter['enabled'] = (bool)$filters['enabled']; } if (isset($filters['role'])) { $filter['roles'] = $filters['role']; } // Fetch users with aggregated role data $pipeline = [ ['$match' => $filter], [ '$lookup' => [ 'from' => 'user_roles', 'localField' => 'roles', 'foreignField' => 'rid', 'as' => 'role_details' ] ], [ '$addFields' => [ 'permissions' => [ '$reduce' => [ 'input' => [ '$map' => [ 'input' => '$role_details', 'as' => 'r', 'in' => ['$ifNull' => ['$$r.permissions', []]] ] ], 'initialValue' => [], 'in' => ['$setUnion' => ['$$value', '$$this']] ] ] ] ], ['$unset' => 'role_details'], ['$sort' => ['label' => 1]] ]; $cursor = $this->store->selectCollection('user_accounts')->aggregate($pipeline); $users = []; foreach ($cursor as $entry) { $users[] = (array)$entry; } return $users; } public function fetchByIdentity(string $tenant, string $identity): array | null { $pipeline = [ [ '$match' => [ 'tid' => $tenant, 'identity' => $identity ] ], [ '$lookup' => [ 'from' => 'user_roles', 'localField' => 'roles', // Array field in `users` 'foreignField' => 'rid', // Scalar field in `user_roles` 'as' => 'role_details' ] ], // Add flattened, deduplicated permissions while preserving all original user fields [ '$addFields' => [ 'permissions' => [ '$reduce' => [ 'input' => [ '$map' => [ 'input' => '$role_details', 'as' => 'r', 'in' => [ '$ifNull' => ['$$r.permissions', []] ] ] ], 'initialValue' => [], 'in' => [ '$setUnion' => ['$$value', '$$this'] ] ] ] ] ], // Optionally remove expanded role documents from output [ '$unset' => 'role_details' ] ]; $entry = $this->store->selectCollection('user_accounts')->aggregate($pipeline)->toArray()[0] ?? null; if (!$entry) { return null; } return (array)$entry; } public function fetchByIdentifier(string $tenant, string $identifier): array | null { $pipeline = [ [ '$match' => [ 'tid' => $tenant, 'uid' => $identifier ] ], [ '$lookup' => [ 'from' => 'user_roles', 'localField' => 'roles', 'foreignField' => 'rid', 'as' => 'role_details' ] ], [ '$addFields' => [ 'permissions' => [ '$reduce' => [ 'input' => [ '$map' => [ 'input' => '$role_details', 'as' => 'r', 'in' => [ '$ifNull' => ['$$r.permissions', []] ] ] ], 'initialValue' => [], 'in' => [ '$setUnion' => ['$$value', '$$this'] ] ] ] ] ], [ '$unset' => 'role_details' ] ]; $entry = $this->store->selectCollection('user_accounts')->aggregate($pipeline)->toArray()[0] ?? null; if (!$entry) { return null; } return (array)$entry; } public function fetchByProviderSubject(string $tenant, string $provider, string $subject): array | null { $entry = $this->store->selectCollection('user_accounts')->findOne([ 'tid' => $tenant, 'provider' => $provider, 'provider_subject' => $subject ]); if (!$entry) { return null; } return (array)$entry; } public function createUser(string $tenant, array $userData): array { $userData['tid'] = $tenant; $userData['uid'] = $userData['uid'] ?? UUID::v4(); $userData['enabled'] = $userData['enabled'] ?? true; $userData['roles'] = $userData['roles'] ?? []; $userData['profile'] = $userData['profile'] ?? []; $userData['settings'] = $userData['settings'] ?? []; $this->store->selectCollection('user_accounts')->insertOne($userData); return $this->fetchByIdentifier($tenant, $userData['uid']); } public function updateUser(string $tenant, string $uid, array $updates): bool { $result = $this->store->selectCollection('user_accounts')->updateOne( ['tid' => $tenant, 'uid' => $uid], ['$set' => $updates] ); return $result->getModifiedCount() > 0; } public function deleteUser(string $tenant, string $uid): bool { $result = $this->store->selectCollection('user_accounts')->deleteOne([ 'tid' => $tenant, 'uid' => $uid ]); return $result->getDeletedCount() > 0; } // ========================================================================= // Profile Operations // ========================================================================= public function fetchProfile(string $tenant, string $uid): ?array { $user = $this->store->selectCollection('user_accounts')->findOne( ['tid' => $tenant, 'uid' => $uid], ['projection' => ['profile' => 1, 'provider_managed_fields' => 1]] ); if (!$user) { return null; } return [ 'profile' => $user['profile'] ?? [], 'provider_managed_fields' => $user['provider_managed_fields'] ?? [], ]; } public function storeProfile(string $tenant, string $uid, array $profileFields): bool { if (empty($profileFields)) { return false; } $updates = []; foreach ($profileFields as $key => $value) { $updates["profile.{$key}"] = $value; } $result = $this->store->selectCollection('user_accounts')->updateOne( ['tid' => $tenant, 'uid' => $uid], ['$set' => $updates] ); return $result->getModifiedCount() > 0; } // ========================================================================= // Settings Operations // ========================================================================= public function fetchSettings(string $tenant, string $uid, array $settings = []): ?array { // Only fetch the settings field from the database $user = $this->store->selectCollection('user_accounts')->findOne( ['tid' => $tenant, 'uid' => $uid], ['projection' => ['settings' => 1]] ); if (!$user) { return null; } $userSettings = $user['settings'] ?? []; if (empty($settings)) { return $userSettings; } $result = []; foreach ($settings as $key) { $result[$key] = $userSettings[$key] ?? null; } return $result; } public function storeSettings(string $tenant, string $uid, array $settings): bool { if (empty($settings)) { return false; } $updates = []; foreach ($settings as $key => $value) { $updates["settings.{$key}"] = $value; } $result = $this->store->selectCollection('user_accounts')->updateOne( ['tid' => $tenant, 'uid' => $uid], ['$set' => $updates] ); // Return true if document was matched (exists), even if not modified return $result->getMatchedCount() > 0; } }