Parcourir la source

refactor: replace IsSuperadmin checks with fine-grained permission checks

Replace ~48 hardcoded IsSuperadmin() calls with proper RBAC permission
checks using AuthorizationService.HasPermission() and collection-level
methods like CanCreateDocument(), CanReadDocument(), etc.

- User handlers: use kUsersRead/Update/Delete permissions
- Workspace handlers: use kWorkspacesCreate/Read/Update/Delete permissions
- Group handlers: use kGroupsCreate/Read/Update/Delete OR CanManageWorkspaceGroups()
- Membership handlers: use kMembershipsCreate/Read/Update/Delete OR CanManageWorkspaceMembers()
- API key handlers: use kApiKeysCreate/Read/Delete permissions (own OR system)
- Collection handlers: use kCollectionsCreate/Read/Update/Delete permissions
- Document handlers: use CanCreateDocument/ReadDocument/WriteDocument/DeleteDocument with ownership
- View handlers: use kViewsCreate/Read/Update/Delete permissions

Kept IsSuperadmin in /auth/me response for UI compatibility.
Fszontagh il y a 6 mois
Parent
commit
61dd0c252e
1 fichiers modifiés avec 277 ajouts et 330 suppressions
  1. 277 330
      webserver/src/http_server.cpp

+ 277 - 330
webserver/src/http_server.cpp

@@ -1229,10 +1229,10 @@ void HttpServer::HandleListUsers(const httplib::Request& req, httplib::Response&
         return;
     }
 
-    // Only superadmins can list all users
-    if (!IsSuperadmin(*auth_user)) {
+    // Requires system:users:read permission
+    if (!authorizationService_->HasPermission(*auth_user, permissions::kUsersRead)) {
         res.status = 403;
-        res.set_content(R"({"error":"Forbidden - superadmin required"})", "application/json");
+        res.set_content(R"({"error":"Forbidden - requires system:users:read permission"})", "application/json");
         return;
     }
 
@@ -1328,10 +1328,11 @@ void HttpServer::HandleGetUser(const httplib::Request& req, httplib::Response& r
         // Extract user ID from path
         std::string user_id = req.matches[1].str();
 
-        // Users can only get their own info, unless superadmin
-        if (auth_user->user_id != user_id && !IsSuperadmin(*auth_user)) {
+        // Users can only get their own info, or require system:users:read permission
+        bool is_own = (auth_user->user_id == user_id);
+        if (!is_own && !authorizationService_->HasPermission(*auth_user, permissions::kUsersRead)) {
             res.status = 403;
-            res.set_content(R"({"error":"Forbidden - can only access your own user data"})", "application/json");
+            res.set_content(R"({"error":"Forbidden - can only access your own user data or requires system:users:read permission"})", "application/json");
             return;
         }
 
@@ -1382,10 +1383,11 @@ void HttpServer::HandleUpdateUser(const httplib::Request& req, httplib::Response
         // Extract user ID from path
         std::string user_id = req.matches[1].str();
 
-        // Users can only update their own info, unless superadmin
-        if (auth_user->user_id != user_id && !IsSuperadmin(*auth_user)) {
+        // Users can only update their own info, or require system:users:update permission
+        bool is_own = (auth_user->user_id == user_id);
+        if (!is_own && !authorizationService_->HasPermission(*auth_user, permissions::kUsersUpdate)) {
             res.status = 403;
-            res.set_content(R"({"error":"Forbidden - can only update your own user data"})", "application/json");
+            res.set_content(R"({"error":"Forbidden - can only update your own user data or requires system:users:update permission"})", "application/json");
             return;
         }
 
@@ -1442,10 +1444,10 @@ void HttpServer::HandleDeleteUser(const httplib::Request& req, httplib::Response
         return;
     }
 
-    // Only superadmins can delete users
-    if (!IsSuperadmin(*auth_user)) {
+    // Requires system:users:delete permission
+    if (!authorizationService_->HasPermission(*auth_user, permissions::kUsersDelete)) {
         res.status = 403;
-        res.set_content(R"({"error":"Forbidden - superadmin required"})", "application/json");
+        res.set_content(R"({"error":"Forbidden - requires system:users:delete permission"})", "application/json");
         return;
     }
 
@@ -1520,10 +1522,10 @@ void HttpServer::HandleCreateWorkspace(const httplib::Request& req, httplib::Res
         return;
     }
 
-    // Only superadmins can create workspaces
-    if (!IsSuperadmin(*auth_user)) {
+    // Requires system:workspaces:create permission
+    if (!authorizationService_->HasPermission(*auth_user, permissions::kWorkspacesCreate)) {
         res.status = 403;
-        res.set_content(R"({"error":"Forbidden - superadmin required"})", "application/json");
+        res.set_content(R"({"error":"Forbidden - requires system:workspaces:create permission"})", "application/json");
         return;
     }
 
@@ -1595,9 +1597,10 @@ void HttpServer::HandleListWorkspaces(const httplib::Request& req, httplib::Resp
 
     try {
         WorkspaceListResult result;
+        bool has_system_access = authorizationService_->HasPermission(*auth_user, permissions::kWorkspacesRead);
 
-        // Superadmins can see all workspaces, others only see their memberships
-        if (IsSuperadmin(*auth_user)) {
+        // Users with system:workspaces:read can see all workspaces, others only see their memberships
+        if (has_system_access) {
             // Parse query parameters
             int limit = 100;
             int offset = 0;
@@ -1621,7 +1624,7 @@ void HttpServer::HandleListWorkspaces(const httplib::Request& req, httplib::Resp
 
             result = workspaceService_->ListWorkspaces(limit, offset, include_deleted);
         } else {
-            // Non-superadmin: filter by user's workspace memberships
+            // Non-privileged user: filter by user's workspace memberships
             result = workspaceService_->ListWorkspacesByIds(auth_user->workspace_ids, false);
         }
 
@@ -1646,7 +1649,7 @@ void HttpServer::HandleListWorkspaces(const httplib::Request& req, httplib::Resp
                 {"created_by_name", ResolveUserName(workspace.created_by)},
                 {"updated_by_name", ResolveUserName(workspace.updated_by)}
             };
-            if (IsSuperadmin(*auth_user)) {
+            if (has_system_access) {
                 ws_json["deleted_at"] = workspace.deleted_at;
             }
             workspaces_array.push_back(ws_json);
@@ -1686,14 +1689,15 @@ void HttpServer::HandleGetWorkspace(const httplib::Request& req, httplib::Respon
         // Extract workspace ID from path
         std::string workspace_id = req.matches[1].str();
 
-        // Non-superadmins can only access workspaces they're members of
-        if (!IsSuperadmin(*auth_user)) {
-            auto it = std::find(auth_user->workspace_ids.begin(), auth_user->workspace_ids.end(), workspace_id);
-            if (it == auth_user->workspace_ids.end()) {
-                res.status = 403;
-                res.set_content(R"({"error":"Forbidden - not a member of this workspace"})", "application/json");
-                return;
-            }
+        // Check access: must be member OR have system:workspaces:read permission
+        bool is_member = (std::find(auth_user->workspace_ids.begin(),
+                                    auth_user->workspace_ids.end(),
+                                    workspace_id) != auth_user->workspace_ids.end());
+        bool has_system_access = authorizationService_->HasPermission(*auth_user, permissions::kWorkspacesRead);
+        if (!is_member && !has_system_access) {
+            res.status = 403;
+            res.set_content(R"({"error":"Forbidden - not a member of this workspace"})", "application/json");
+            return;
         }
 
         auto result = workspaceService_->GetWorkspace(workspace_id);
@@ -1732,10 +1736,15 @@ void HttpServer::HandleUpdateWorkspace(const httplib::Request& req, httplib::Res
         return;
     }
 
-    // Only superadmins can update workspaces
-    if (!IsSuperadmin(*auth_user)) {
+    // Extract workspace ID from path (needed for IsWorkspaceAdmin check)
+    std::string workspace_id = req.matches[1].str();
+
+    // Requires system:workspaces:update permission OR workspace admin
+    bool can_update = authorizationService_->HasPermission(*auth_user, permissions::kWorkspacesUpdate) ||
+                      authorizationService_->IsWorkspaceAdmin(*auth_user, workspace_id);
+    if (!can_update) {
         res.status = 403;
-        res.set_content(R"({"error":"Forbidden - superadmin required"})", "application/json");
+        res.set_content(R"({"error":"Forbidden - requires system:workspaces:update permission or workspace admin"})", "application/json");
         return;
     }
 
@@ -1747,9 +1756,6 @@ void HttpServer::HandleUpdateWorkspace(const httplib::Request& req, httplib::Res
     }
 
     try {
-        // Extract workspace ID from path
-        std::string workspace_id = req.matches[1].str();
-
         auto body = nlohmann::json::parse(req.body);
 
         UpdateWorkspaceRequest update_req;
@@ -1800,10 +1806,10 @@ void HttpServer::HandleDeleteWorkspace(const httplib::Request& req, httplib::Res
         return;
     }
 
-    // Only superadmins can delete workspaces
-    if (!IsSuperadmin(*auth_user)) {
+    // Requires system:workspaces:delete permission
+    if (!authorizationService_->HasPermission(*auth_user, permissions::kWorkspacesDelete)) {
         res.status = 403;
-        res.set_content(R"({"error":"Forbidden - superadmin required"})", "application/json");
+        res.set_content(R"({"error":"Forbidden - requires system:workspaces:delete permission"})", "application/json");
         return;
     }
 
@@ -1881,17 +1887,12 @@ void HttpServer::HandleCreateGroup(const httplib::Request& req, httplib::Respons
     // Extract workspace ID from path
     std::string workspace_id = req.matches[1].str();
 
-    // Check if user has access to this workspace (superadmin or member)
-    if (!IsSuperadmin(*auth_user)) {
-        auto it = std::find(auth_user->workspace_ids.begin(), auth_user->workspace_ids.end(), workspace_id);
-        if (it == auth_user->workspace_ids.end()) {
-            res.status = 403;
-            res.set_content(R"({"error":"Forbidden - not a member of this workspace"})", "application/json");
-            return;
-        }
-        // Only superadmins can create groups
+    // Requires system:groups:create permission OR workspace group management permission
+    bool can_create = authorizationService_->HasPermission(*auth_user, permissions::kGroupsCreate) ||
+                      authorizationService_->CanManageWorkspaceGroups(*auth_user, workspace_id);
+    if (!can_create) {
         res.status = 403;
-        res.set_content(R"({"error":"Forbidden - superadmin required to create groups"})", "application/json");
+        res.set_content(R"({"error":"Forbidden - requires system:groups:create permission or workspace group management"})", "application/json");
         return;
     }
 
@@ -1963,14 +1964,15 @@ void HttpServer::HandleListGroups(const httplib::Request& req, httplib::Response
     // Extract workspace ID from path
     std::string workspace_id = req.matches[1].str();
 
-    // Check if user has access to this workspace
-    if (!IsSuperadmin(*auth_user)) {
-        auto it = std::find(auth_user->workspace_ids.begin(), auth_user->workspace_ids.end(), workspace_id);
-        if (it == auth_user->workspace_ids.end()) {
-            res.status = 403;
-            res.set_content(R"({"error":"Forbidden - not a member of this workspace"})", "application/json");
-            return;
-        }
+    // Check access: must be member OR have system:groups:read permission
+    bool is_member = (std::find(auth_user->workspace_ids.begin(),
+                                auth_user->workspace_ids.end(),
+                                workspace_id) != auth_user->workspace_ids.end());
+    bool has_system_access = authorizationService_->HasPermission(*auth_user, permissions::kGroupsRead);
+    if (!is_member && !has_system_access) {
+        res.status = 403;
+        res.set_content(R"({"error":"Forbidden - not a member of this workspace"})", "application/json");
+        return;
     }
 
     // Check if GroupService is available
@@ -1998,10 +2000,10 @@ void HttpServer::HandleListGroups(const httplib::Request& req, httplib::Response
                 offset = 0;
             }
         }
-        if (IsSuperadmin(*auth_user) && req.has_param("include_deleted")) {
+        if (has_system_access && req.has_param("include_deleted")) {
             include_deleted = req.get_param_value("include_deleted") == "true";
         }
-        bool include_system = IsSuperadmin(*auth_user) &&
+        bool include_system = has_system_access &&
                               req.has_param("include_system") &&
                               req.get_param_value("include_system") == "true";
 
@@ -2057,7 +2059,7 @@ void HttpServer::HandleListGroups(const httplib::Request& req, httplib::Response
                 {"created_by_name", ResolveUserName(group.created_by)},
                 {"updated_by_name", ResolveUserName(group.updated_by)}
             };
-            if (IsSuperadmin(*auth_user)) {
+            if (has_system_access) {
                 grp_json["deleted_at"] = group.deleted_at;
             }
             groups_array.push_back(grp_json);
@@ -2090,14 +2092,15 @@ void HttpServer::HandleGetGroup(const httplib::Request& req, httplib::Response&
     std::string workspace_id = req.matches[1].str();
     std::string group_id = req.matches[2].str();
 
-    // Check if user has access to this workspace
-    if (!IsSuperadmin(*auth_user)) {
-        auto it = std::find(auth_user->workspace_ids.begin(), auth_user->workspace_ids.end(), workspace_id);
-        if (it == auth_user->workspace_ids.end()) {
-            res.status = 403;
-            res.set_content(R"({"error":"Forbidden - not a member of this workspace"})", "application/json");
-            return;
-        }
+    // Check access: must be member OR have system:groups:read permission
+    bool is_member = (std::find(auth_user->workspace_ids.begin(),
+                                auth_user->workspace_ids.end(),
+                                workspace_id) != auth_user->workspace_ids.end());
+    bool has_system_access = authorizationService_->HasPermission(*auth_user, permissions::kGroupsRead);
+    if (!is_member && !has_system_access) {
+        res.status = 403;
+        res.set_content(R"({"error":"Forbidden - not a member of this workspace"})", "application/json");
+        return;
     }
 
     // Check if GroupService is available
@@ -2156,10 +2159,12 @@ void HttpServer::HandleUpdateGroup(const httplib::Request& req, httplib::Respons
     std::string workspace_id = req.matches[1].str();
     std::string group_id = req.matches[2].str();
 
-    // Only superadmins can update groups
-    if (!IsSuperadmin(*auth_user)) {
+    // Requires system:groups:update permission OR workspace group management permission
+    bool can_update = authorizationService_->HasPermission(*auth_user, permissions::kGroupsUpdate) ||
+                      authorizationService_->CanManageWorkspaceGroups(*auth_user, workspace_id);
+    if (!can_update) {
         res.status = 403;
-        res.set_content(R"({"error":"Forbidden - superadmin required"})", "application/json");
+        res.set_content(R"({"error":"Forbidden - requires system:groups:update permission or workspace group management"})", "application/json");
         return;
     }
 
@@ -2247,10 +2252,12 @@ void HttpServer::HandleDeleteGroup(const httplib::Request& req, httplib::Respons
     std::string workspace_id = req.matches[1].str();
     std::string group_id = req.matches[2].str();
 
-    // Only superadmins can delete groups
-    if (!IsSuperadmin(*auth_user)) {
+    // Requires system:groups:delete permission OR workspace group management permission
+    bool can_delete = authorizationService_->HasPermission(*auth_user, permissions::kGroupsDelete) ||
+                      authorizationService_->CanManageWorkspaceGroups(*auth_user, workspace_id);
+    if (!can_delete) {
         res.status = 403;
-        res.set_content(R"({"error":"Forbidden - superadmin required"})", "application/json");
+        res.set_content(R"({"error":"Forbidden - requires system:groups:delete permission or workspace group management"})", "application/json");
         return;
     }
 
@@ -2340,10 +2347,12 @@ void HttpServer::HandleAddMember(const httplib::Request& req, httplib::Response&
     // Extract workspace ID from path
     std::string workspace_id = req.matches[1].str();
 
-    // Only superadmins can add members
-    if (!IsSuperadmin(*auth_user)) {
+    // Requires system:memberships:create permission OR workspace member management permission
+    bool can_add = authorizationService_->HasPermission(*auth_user, permissions::kMembershipsCreate) ||
+                   authorizationService_->CanManageWorkspaceMembers(*auth_user, workspace_id);
+    if (!can_add) {
         res.status = 403;
-        res.set_content(R"({"error":"Forbidden - superadmin required"})", "application/json");
+        res.set_content(R"({"error":"Forbidden - requires system:memberships:create permission or workspace member management"})", "application/json");
         return;
     }
 
@@ -2422,14 +2431,15 @@ void HttpServer::HandleListMembers(const httplib::Request& req, httplib::Respons
     // Extract workspace ID from path
     std::string workspace_id = req.matches[1].str();
 
-    // Check access to workspace (superadmin or member)
-    if (!IsSuperadmin(*auth_user)) {
-        auto it = std::find(auth_user->workspace_ids.begin(), auth_user->workspace_ids.end(), workspace_id);
-        if (it == auth_user->workspace_ids.end()) {
-            res.status = 403;
-            res.set_content(R"({"error":"Forbidden - not a member of this workspace"})", "application/json");
-            return;
-        }
+    // Check access: must be member OR have system:memberships:read permission
+    bool is_member = (std::find(auth_user->workspace_ids.begin(),
+                                auth_user->workspace_ids.end(),
+                                workspace_id) != auth_user->workspace_ids.end());
+    bool has_system_access = authorizationService_->HasPermission(*auth_user, permissions::kMembershipsRead);
+    if (!is_member && !has_system_access) {
+        res.status = 403;
+        res.set_content(R"({"error":"Forbidden - not a member of this workspace"})", "application/json");
+        return;
     }
 
     // Check if MembershipService is available
@@ -2457,7 +2467,7 @@ void HttpServer::HandleListMembers(const httplib::Request& req, httplib::Respons
                 offset = 0;
             }
         }
-        if (IsSuperadmin(*auth_user) && req.has_param("include_deleted")) {
+        if (has_system_access && req.has_param("include_deleted")) {
             include_deleted = req.get_param_value("include_deleted") == "true";
         }
 
@@ -2481,7 +2491,7 @@ void HttpServer::HandleListMembers(const httplib::Request& req, httplib::Respons
                 {"created_at", member.created_at},
                 {"updated_at", member.updated_at}
             };
-            if (IsSuperadmin(*auth_user)) {
+            if (has_system_access) {
                 member_json["deleted_at"] = member.deleted_at;
             }
 
@@ -2528,14 +2538,15 @@ void HttpServer::HandleGetMember(const httplib::Request& req, httplib::Response&
     std::string workspace_id = req.matches[1].str();
     std::string user_id = req.matches[2].str();
 
-    // Check access to workspace (superadmin or member)
-    if (!IsSuperadmin(*auth_user)) {
-        auto it = std::find(auth_user->workspace_ids.begin(), auth_user->workspace_ids.end(), workspace_id);
-        if (it == auth_user->workspace_ids.end()) {
-            res.status = 403;
-            res.set_content(R"({"error":"Forbidden - not a member of this workspace"})", "application/json");
-            return;
-        }
+    // Check access: must be member OR have system:memberships:read permission
+    bool is_member = (std::find(auth_user->workspace_ids.begin(),
+                                auth_user->workspace_ids.end(),
+                                workspace_id) != auth_user->workspace_ids.end());
+    bool has_system_access = authorizationService_->HasPermission(*auth_user, permissions::kMembershipsRead);
+    if (!is_member && !has_system_access) {
+        res.status = 403;
+        res.set_content(R"({"error":"Forbidden - not a member of this workspace"})", "application/json");
+        return;
     }
 
     // Check if MembershipService is available
@@ -2587,10 +2598,12 @@ void HttpServer::HandleUpdateMember(const httplib::Request& req, httplib::Respon
     std::string workspace_id = req.matches[1].str();
     std::string user_id = req.matches[2].str();
 
-    // Only superadmins can update memberships
-    if (!IsSuperadmin(*auth_user)) {
+    // Requires system:memberships:update permission OR workspace member management permission
+    bool can_update = authorizationService_->HasPermission(*auth_user, permissions::kMembershipsUpdate) ||
+                      authorizationService_->CanManageWorkspaceMembers(*auth_user, workspace_id);
+    if (!can_update) {
         res.status = 403;
-        res.set_content(R"({"error":"Forbidden - superadmin required"})", "application/json");
+        res.set_content(R"({"error":"Forbidden - requires system:memberships:update permission or workspace member management"})", "application/json");
         return;
     }
 
@@ -2662,10 +2675,12 @@ void HttpServer::HandleRemoveMember(const httplib::Request& req, httplib::Respon
     std::string workspace_id = req.matches[1].str();
     std::string user_id = req.matches[2].str();
 
-    // Only superadmins can remove members
-    if (!IsSuperadmin(*auth_user)) {
+    // Requires system:memberships:delete permission OR workspace member management permission
+    bool can_remove = authorizationService_->HasPermission(*auth_user, permissions::kMembershipsDelete) ||
+                      authorizationService_->CanManageWorkspaceMembers(*auth_user, workspace_id);
+    if (!can_remove) {
         res.status = 403;
-        res.set_content(R"({"error":"Forbidden - superadmin required"})", "application/json");
+        res.set_content(R"({"error":"Forbidden - requires system:memberships:delete permission or workspace member management"})", "application/json");
         return;
     }
 
@@ -2730,10 +2745,12 @@ void HttpServer::HandleCreateApiKey(const httplib::Request& req, httplib::Respon
     // Extract user ID from path
     std::string target_user_id = req.matches[1].str();
 
-    // Check permissions: superadmin can create for any user, others only for themselves
-    if (!IsSuperadmin(*auth_user) && auth_user->user_id != target_user_id) {
+    // Check permissions: own OR system:api_keys:create permission
+    bool is_own = (auth_user->user_id == target_user_id);
+    bool has_system_access = authorizationService_->HasPermission(*auth_user, permissions::kApiKeysCreate);
+    if (!is_own && !has_system_access) {
         res.status = 403;
-        res.set_content(R"({"error":"Forbidden - can only create API keys for yourself"})", "application/json");
+        res.set_content(R"({"error":"Forbidden - can only create API keys for yourself or requires system:api_keys:create permission"})", "application/json");
         return;
     }
 
@@ -2763,8 +2780,8 @@ void HttpServer::HandleCreateApiKey(const httplib::Request& req, httplib::Respon
             for (const auto& item : json["permissions"]) {
                 if (item.is_string()) {
                     std::string perm = item.get<std::string>();
-                    // Users can only grant permissions they have (unless superadmin)
-                    if (!IsSuperadmin(*auth_user)) {
+                    // Users can only grant permissions they have (unless they have system access)
+                    if (!has_system_access) {
                         // For now, allow all permissions - proper permission checking
                         // would require looking up user's actual permissions
                     }
@@ -2824,10 +2841,12 @@ void HttpServer::HandleListApiKeys(const httplib::Request& req, httplib::Respons
     // Extract user ID from path
     std::string target_user_id = req.matches[1].str();
 
-    // Check permissions: superadmin can list for any user, others only for themselves
-    if (!IsSuperadmin(*auth_user) && auth_user->user_id != target_user_id) {
+    // Check permissions: own OR system:api_keys:read permission
+    bool is_own = (auth_user->user_id == target_user_id);
+    bool has_system_access = authorizationService_->HasPermission(*auth_user, permissions::kApiKeysRead);
+    if (!is_own && !has_system_access) {
         res.status = 403;
-        res.set_content(R"({"error":"Forbidden - can only list your own API keys"})", "application/json");
+        res.set_content(R"({"error":"Forbidden - can only list your own API keys or requires system:api_keys:read permission"})", "application/json");
         return;
     }
 
@@ -2839,9 +2858,9 @@ void HttpServer::HandleListApiKeys(const httplib::Request& req, httplib::Respons
     }
 
     try {
-        // Check for include_revoked parameter (superadmin only)
+        // Check for include_revoked parameter (requires system access)
         bool include_revoked = false;
-        if (IsSuperadmin(*auth_user) && req.has_param("include_revoked")) {
+        if (has_system_access && req.has_param("include_revoked")) {
             include_revoked = req.get_param_value("include_revoked") == "true";
         }
 
@@ -2868,7 +2887,7 @@ void HttpServer::HandleListApiKeys(const httplib::Request& req, httplib::Respons
                 {"last_used_at", key.last_used_at},
                 {"expires_at", key.expires_at}
             };
-            if (IsSuperadmin(*auth_user)) {
+            if (has_system_access) {
                 key_json["deleted_at"] = key.deleted_at;
             }
             keys_array.push_back(key_json);
@@ -2901,10 +2920,12 @@ void HttpServer::HandleRevokeApiKey(const httplib::Request& req, httplib::Respon
     std::string target_user_id = req.matches[1].str();
     std::string key_id = req.matches[2].str();
 
-    // Check permissions: superadmin can revoke for any user, others only for themselves
-    if (!IsSuperadmin(*auth_user) && auth_user->user_id != target_user_id) {
+    // Check permissions: own OR system:api_keys:delete permission
+    bool is_own = (auth_user->user_id == target_user_id);
+    bool has_system_access = authorizationService_->HasPermission(*auth_user, permissions::kApiKeysDelete);
+    if (!is_own && !has_system_access) {
         res.status = 403;
-        res.set_content(R"({"error":"Forbidden - can only revoke your own API keys"})", "application/json");
+        res.set_content(R"({"error":"Forbidden - can only revoke your own API keys or requires system:api_keys:delete permission"})", "application/json");
         return;
     }
 
@@ -2987,20 +3008,15 @@ void HttpServer::HandleCreateCollection(const httplib::Request& req, httplib::Re
         return;
     }
 
-    // Check if user has access to this workspace (superadmin has access to all)
-    if (!IsSuperadmin(*auth_user)) {
-        bool has_access = false;
-        for (const auto& wid : auth_user->workspace_ids) {
-            if (wid == workspace_id) {
-                has_access = true;
-                break;
-            }
-        }
-        if (!has_access) {
-            res.status = 403;
-            res.set_content(R"({"error":"Forbidden - no access to this workspace"})", "application/json");
-            return;
-        }
+    // Requires system:collections:create permission OR workspace membership with manage_collections
+    bool is_member = (std::find(auth_user->workspace_ids.begin(),
+                                auth_user->workspace_ids.end(),
+                                workspace_id) != auth_user->workspace_ids.end());
+    bool has_system_access = authorizationService_->HasPermission(*auth_user, permissions::kCollectionsCreate);
+    if (!is_member && !has_system_access) {
+        res.status = 403;
+        res.set_content(R"({"error":"Forbidden - no access to this workspace"})", "application/json");
+        return;
     }
 
     // Check if CollectionService is available
@@ -3099,20 +3115,15 @@ void HttpServer::HandleListCollections(const httplib::Request& req, httplib::Res
         return;
     }
 
-    // Check if user has access to this workspace
-    if (!IsSuperadmin(*auth_user)) {
-        bool has_access = false;
-        for (const auto& wid : auth_user->workspace_ids) {
-            if (wid == workspace_id) {
-                has_access = true;
-                break;
-            }
-        }
-        if (!has_access) {
-            res.status = 403;
-            res.set_content(R"({"error":"Forbidden - no access to this workspace"})", "application/json");
-            return;
-        }
+    // Check access: must be member OR have system:collections:read permission
+    bool is_member = (std::find(auth_user->workspace_ids.begin(),
+                                auth_user->workspace_ids.end(),
+                                workspace_id) != auth_user->workspace_ids.end());
+    bool has_system_access = authorizationService_->HasPermission(*auth_user, permissions::kCollectionsRead);
+    if (!is_member && !has_system_access) {
+        res.status = 403;
+        res.set_content(R"({"error":"Forbidden - no access to this workspace"})", "application/json");
+        return;
     }
 
     // Check if CollectionService is available
@@ -3123,8 +3134,8 @@ void HttpServer::HandleListCollections(const httplib::Request& req, httplib::Res
     }
 
     try {
-        // Check if superadmin wants to include system collections
-        bool include_system = IsSuperadmin(*auth_user) &&
+        // Check if user with system access wants to include system collections
+        bool include_system = has_system_access &&
                               req.has_param("include_system") &&
                               req.get_param_value("include_system") == "true";
 
@@ -3222,20 +3233,15 @@ void HttpServer::HandleGetCollection(const httplib::Request& req, httplib::Respo
         return;
     }
 
-    // Check if user has access to this workspace
-    if (!IsSuperadmin(*auth_user)) {
-        bool has_access = false;
-        for (const auto& wid : auth_user->workspace_ids) {
-            if (wid == workspace_id) {
-                has_access = true;
-                break;
-            }
-        }
-        if (!has_access) {
-            res.status = 403;
-            res.set_content(R"({"error":"Forbidden - no access to this workspace"})", "application/json");
-            return;
-        }
+    // Check access: must be member OR have system:collections:read permission
+    bool is_member = (std::find(auth_user->workspace_ids.begin(),
+                                auth_user->workspace_ids.end(),
+                                workspace_id) != auth_user->workspace_ids.end());
+    bool has_system_access = authorizationService_->HasPermission(*auth_user, permissions::kCollectionsRead);
+    if (!is_member && !has_system_access) {
+        res.status = 403;
+        res.set_content(R"({"error":"Forbidden - no access to this workspace"})", "application/json");
+        return;
     }
 
     // Check if CollectionService is available
@@ -3299,20 +3305,15 @@ void HttpServer::HandleUpdateCollection(const httplib::Request& req, httplib::Re
         return;
     }
 
-    // Check if user has access to this workspace
-    if (!IsSuperadmin(*auth_user)) {
-        bool has_access = false;
-        for (const auto& wid : auth_user->workspace_ids) {
-            if (wid == workspace_id) {
-                has_access = true;
-                break;
-            }
-        }
-        if (!has_access) {
-            res.status = 403;
-            res.set_content(R"({"error":"Forbidden - no access to this workspace"})", "application/json");
-            return;
-        }
+    // Requires system:collections:update permission OR workspace membership
+    bool is_member = (std::find(auth_user->workspace_ids.begin(),
+                                auth_user->workspace_ids.end(),
+                                workspace_id) != auth_user->workspace_ids.end());
+    bool has_system_access = authorizationService_->HasPermission(*auth_user, permissions::kCollectionsUpdate);
+    if (!is_member && !has_system_access) {
+        res.status = 403;
+        res.set_content(R"({"error":"Forbidden - no access to this workspace"})", "application/json");
+        return;
     }
 
     // Check if CollectionService is available
@@ -3444,20 +3445,11 @@ void HttpServer::HandleCreateDocument(const httplib::Request& req, httplib::Resp
         return;
     }
 
-    // Check if user has access to this workspace
-    if (!IsSuperadmin(*auth_user)) {
-        bool has_access = false;
-        for (const auto& wid : auth_user->workspace_ids) {
-            if (wid == workspace_id) {
-                has_access = true;
-                break;
-            }
-        }
-        if (!has_access) {
-            res.status = 403;
-            res.set_content(R"({"error":"Forbidden - no access to this workspace"})", "application/json");
-            return;
-        }
+    // Check document create permission using AuthorizationService
+    if (!authorizationService_->CanCreateDocument(*auth_user, workspace_id, collection_name)) {
+        res.status = 403;
+        res.set_content(R"({"error":"Forbidden - no permission to create documents in this collection"})", "application/json");
+        return;
     }
 
     // Check if DocumentService is available
@@ -3546,20 +3538,11 @@ void HttpServer::HandleListDocuments(const httplib::Request& req, httplib::Respo
         return;
     }
 
-    // Check if user has access to this workspace
-    if (!IsSuperadmin(*auth_user)) {
-        bool has_access = false;
-        for (const auto& wid : auth_user->workspace_ids) {
-            if (wid == workspace_id) {
-                has_access = true;
-                break;
-            }
-        }
-        if (!has_access) {
-            res.status = 403;
-            res.set_content(R"({"error":"Forbidden - no access to this workspace"})", "application/json");
-            return;
-        }
+    // Check collection read permission using AuthorizationService
+    if (!authorizationService_->CanReadCollection(*auth_user, workspace_id, collection_name)) {
+        res.status = 403;
+        res.set_content(R"({"error":"Forbidden - no permission to read documents in this collection"})", "application/json");
+        return;
     }
 
     // Check if DocumentService is available
@@ -3661,22 +3644,6 @@ void HttpServer::HandleGetDocument(const httplib::Request& req, httplib::Respons
         return;
     }
 
-    // Check if user has access to this workspace
-    if (!IsSuperadmin(*auth_user)) {
-        bool has_access = false;
-        for (const auto& wid : auth_user->workspace_ids) {
-            if (wid == workspace_id) {
-                has_access = true;
-                break;
-            }
-        }
-        if (!has_access) {
-            res.status = 403;
-            res.set_content(R"({"error":"Forbidden - no access to this workspace"})", "application/json");
-            return;
-        }
-    }
-
     // Check if DocumentService is available
     if (!documentService_) {
         res.status = 503;
@@ -3694,6 +3661,14 @@ void HttpServer::HandleGetDocument(const httplib::Request& req, httplib::Respons
             return;
         }
 
+        // Check document read permission with ownership
+        std::string doc_owner = result.document->data.value("_created_by", "");
+        if (!authorizationService_->CanReadDocument(*auth_user, workspace_id, collection_name, doc_owner)) {
+            res.status = 403;
+            res.set_content(R"({"error":"Forbidden - no permission to read this document"})", "application/json");
+            return;
+        }
+
         nlohmann::json doc_json = {
             {"id", result.document->id},
             {"collection", result.document->collection},
@@ -3735,22 +3710,6 @@ void HttpServer::HandleUpdateDocument(const httplib::Request& req, httplib::Resp
         return;
     }
 
-    // Check if user has access to this workspace
-    if (!IsSuperadmin(*auth_user)) {
-        bool has_access = false;
-        for (const auto& wid : auth_user->workspace_ids) {
-            if (wid == workspace_id) {
-                has_access = true;
-                break;
-            }
-        }
-        if (!has_access) {
-            res.status = 403;
-            res.set_content(R"({"error":"Forbidden - no access to this workspace"})", "application/json");
-            return;
-        }
-    }
-
     // Check if DocumentService is available
     if (!documentService_) {
         res.status = 503;
@@ -3759,6 +3718,23 @@ void HttpServer::HandleUpdateDocument(const httplib::Request& req, httplib::Resp
     }
 
     try {
+        // First get the document to check ownership
+        auto existing = documentService_->GetDocument(workspace_id, collection_name, document_id);
+        if (!existing.success) {
+            res.status = 404;
+            nlohmann::json error_response = {{"error", existing.error}};
+            res.set_content(error_response.dump(), "application/json");
+            return;
+        }
+
+        // Check document write permission with ownership
+        std::string doc_owner = existing.document->data.value("_created_by", "");
+        if (!authorizationService_->CanWriteDocument(*auth_user, workspace_id, collection_name, doc_owner)) {
+            res.status = 403;
+            res.set_content(R"({"error":"Forbidden - no permission to update this document"})", "application/json");
+            return;
+        }
+
         // Parse request body
         auto json = nlohmann::json::parse(req.body);
 
@@ -3840,22 +3816,6 @@ void HttpServer::HandleDeleteDocument(const httplib::Request& req, httplib::Resp
         return;
     }
 
-    // Check if user has access to this workspace
-    if (!IsSuperadmin(*auth_user)) {
-        bool has_access = false;
-        for (const auto& wid : auth_user->workspace_ids) {
-            if (wid == workspace_id) {
-                has_access = true;
-                break;
-            }
-        }
-        if (!has_access) {
-            res.status = 403;
-            res.set_content(R"({"error":"Forbidden - no access to this workspace"})", "application/json");
-            return;
-        }
-    }
-
     // Check if DocumentService is available
     if (!documentService_) {
         res.status = 503;
@@ -3864,6 +3824,23 @@ void HttpServer::HandleDeleteDocument(const httplib::Request& req, httplib::Resp
     }
 
     try {
+        // First get the document to check ownership
+        auto existing = documentService_->GetDocument(workspace_id, collection_name, document_id);
+        if (!existing.success) {
+            res.status = 404;
+            nlohmann::json error_response = {{"error", existing.error}};
+            res.set_content(error_response.dump(), "application/json");
+            return;
+        }
+
+        // Check document delete permission with ownership
+        std::string doc_owner = existing.document->data.value("_created_by", "");
+        if (!authorizationService_->CanDeleteDocument(*auth_user, workspace_id, collection_name, doc_owner)) {
+            res.status = 403;
+            res.set_content(R"({"error":"Forbidden - no permission to delete this document"})", "application/json");
+            return;
+        }
+
         auto result = documentService_->DeleteDocument(workspace_id, collection_name, document_id);
 
         if (!result.success) {
@@ -3907,20 +3884,15 @@ void HttpServer::HandleDropCollection(const httplib::Request& req, httplib::Resp
         return;
     }
 
-    // Check if user has access to this workspace (for collections, maybe require superadmin?)
-    if (!IsSuperadmin(*auth_user)) {
-        bool has_access = false;
-        for (const auto& wid : auth_user->workspace_ids) {
-            if (wid == workspace_id) {
-                has_access = true;
-                break;
-            }
-        }
-        if (!has_access) {
-            res.status = 403;
-            res.set_content(R"({"error":"Forbidden - no access to this workspace"})", "application/json");
-            return;
-        }
+    // Requires system:collections:delete permission OR workspace membership
+    bool is_member = (std::find(auth_user->workspace_ids.begin(),
+                                auth_user->workspace_ids.end(),
+                                workspace_id) != auth_user->workspace_ids.end());
+    bool has_system_access = authorizationService_->HasPermission(*auth_user, permissions::kCollectionsDelete);
+    if (!is_member && !has_system_access) {
+        res.status = 403;
+        res.set_content(R"({"error":"Forbidden - no access to this workspace"})", "application/json");
+        return;
     }
 
     // Check if CollectionService is available
@@ -4013,20 +3985,15 @@ void HttpServer::HandleCreateView(const httplib::Request& req, httplib::Response
         return;
     }
 
-    // Check if user has access to this workspace
-    if (!IsSuperadmin(*auth_user)) {
-        bool has_access = false;
-        for (const auto& wid : auth_user->workspace_ids) {
-            if (wid == workspace_id) {
-                has_access = true;
-                break;
-            }
-        }
-        if (!has_access) {
-            res.status = 403;
-            res.set_content(R"({"error":"Forbidden - no access to this workspace"})", "application/json");
-            return;
-        }
+    // Requires system:views:create permission OR workspace membership
+    bool is_member = (std::find(auth_user->workspace_ids.begin(),
+                                auth_user->workspace_ids.end(),
+                                workspace_id) != auth_user->workspace_ids.end());
+    bool has_system_access = authorizationService_->HasPermission(*auth_user, permissions::kViewsCreate);
+    if (!is_member && !has_system_access) {
+        res.status = 403;
+        res.set_content(R"({"error":"Forbidden - no access to this workspace"})", "application/json");
+        return;
     }
 
     // Check if ViewService is available
@@ -4200,20 +4167,15 @@ void HttpServer::HandleListViews(const httplib::Request& req, httplib::Response&
         return;
     }
 
-    // Check if user has access to this workspace
-    if (!IsSuperadmin(*auth_user)) {
-        bool has_access = false;
-        for (const auto& wid : auth_user->workspace_ids) {
-            if (wid == workspace_id) {
-                has_access = true;
-                break;
-            }
-        }
-        if (!has_access) {
-            res.status = 403;
-            res.set_content(R"({"error":"Forbidden - no access to this workspace"})", "application/json");
-            return;
-        }
+    // Check access: must be member OR have system:views:read permission
+    bool is_member = (std::find(auth_user->workspace_ids.begin(),
+                                auth_user->workspace_ids.end(),
+                                workspace_id) != auth_user->workspace_ids.end());
+    bool has_system_access = authorizationService_->HasPermission(*auth_user, permissions::kViewsRead);
+    if (!is_member && !has_system_access) {
+        res.status = 403;
+        res.set_content(R"({"error":"Forbidden - no access to this workspace"})", "application/json");
+        return;
     }
 
     // Check if ViewService is available
@@ -4299,20 +4261,15 @@ void HttpServer::HandleGetView(const httplib::Request& req, httplib::Response& r
         return;
     }
 
-    // Check if user has access to this workspace
-    if (!IsSuperadmin(*auth_user)) {
-        bool has_access = false;
-        for (const auto& wid : auth_user->workspace_ids) {
-            if (wid == workspace_id) {
-                has_access = true;
-                break;
-            }
-        }
-        if (!has_access) {
-            res.status = 403;
-            res.set_content(R"({"error":"Forbidden - no access to this workspace"})", "application/json");
-            return;
-        }
+    // Check access: must be member OR have system:views:read permission
+    bool is_member = (std::find(auth_user->workspace_ids.begin(),
+                                auth_user->workspace_ids.end(),
+                                workspace_id) != auth_user->workspace_ids.end());
+    bool has_system_access = authorizationService_->HasPermission(*auth_user, permissions::kViewsRead);
+    if (!is_member && !has_system_access) {
+        res.status = 403;
+        res.set_content(R"({"error":"Forbidden - no access to this workspace"})", "application/json");
+        return;
     }
 
     // Check if ViewService is available
@@ -4416,20 +4373,15 @@ void HttpServer::HandleUpdateView(const httplib::Request& req, httplib::Response
         return;
     }
 
-    // Check if user has access to this workspace
-    if (!IsSuperadmin(*auth_user)) {
-        bool has_access = false;
-        for (const auto& wid : auth_user->workspace_ids) {
-            if (wid == workspace_id) {
-                has_access = true;
-                break;
-            }
-        }
-        if (!has_access) {
-            res.status = 403;
-            res.set_content(R"({"error":"Forbidden - no access to this workspace"})", "application/json");
-            return;
-        }
+    // Requires system:views:update permission OR workspace membership
+    bool is_member = (std::find(auth_user->workspace_ids.begin(),
+                                auth_user->workspace_ids.end(),
+                                workspace_id) != auth_user->workspace_ids.end());
+    bool has_system_access = authorizationService_->HasPermission(*auth_user, permissions::kViewsUpdate);
+    if (!is_member && !has_system_access) {
+        res.status = 403;
+        res.set_content(R"({"error":"Forbidden - no access to this workspace"})", "application/json");
+        return;
     }
 
     // Check if ViewService is available
@@ -4596,20 +4548,15 @@ void HttpServer::HandleDeleteView(const httplib::Request& req, httplib::Response
         return;
     }
 
-    // Check if user has access to this workspace
-    if (!IsSuperadmin(*auth_user)) {
-        bool has_access = false;
-        for (const auto& wid : auth_user->workspace_ids) {
-            if (wid == workspace_id) {
-                has_access = true;
-                break;
-            }
-        }
-        if (!has_access) {
-            res.status = 403;
-            res.set_content(R"({"error":"Forbidden - no access to this workspace"})", "application/json");
-            return;
-        }
+    // Requires system:views:delete permission OR workspace membership
+    bool is_member = (std::find(auth_user->workspace_ids.begin(),
+                                auth_user->workspace_ids.end(),
+                                workspace_id) != auth_user->workspace_ids.end());
+    bool has_system_access = authorizationService_->HasPermission(*auth_user, permissions::kViewsDelete);
+    if (!is_member && !has_system_access) {
+        res.status = 403;
+        res.set_content(R"({"error":"Forbidden - no access to this workspace"})", "application/json");
+        return;
     }
 
     // Check if ViewService is available