|
@@ -1229,10 +1229,10 @@ void HttpServer::HandleListUsers(const httplib::Request& req, httplib::Response&
|
|
|
return;
|
|
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.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;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1328,10 +1328,11 @@ void HttpServer::HandleGetUser(const httplib::Request& req, httplib::Response& r
|
|
|
// Extract user ID from path
|
|
// Extract user ID from path
|
|
|
std::string user_id = req.matches[1].str();
|
|
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.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;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1382,10 +1383,11 @@ void HttpServer::HandleUpdateUser(const httplib::Request& req, httplib::Response
|
|
|
// Extract user ID from path
|
|
// Extract user ID from path
|
|
|
std::string user_id = req.matches[1].str();
|
|
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.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;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1442,10 +1444,10 @@ void HttpServer::HandleDeleteUser(const httplib::Request& req, httplib::Response
|
|
|
return;
|
|
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.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;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1520,10 +1522,10 @@ void HttpServer::HandleCreateWorkspace(const httplib::Request& req, httplib::Res
|
|
|
return;
|
|
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.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;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1595,9 +1597,10 @@ void HttpServer::HandleListWorkspaces(const httplib::Request& req, httplib::Resp
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
WorkspaceListResult result;
|
|
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
|
|
// Parse query parameters
|
|
|
int limit = 100;
|
|
int limit = 100;
|
|
|
int offset = 0;
|
|
int offset = 0;
|
|
@@ -1621,7 +1624,7 @@ void HttpServer::HandleListWorkspaces(const httplib::Request& req, httplib::Resp
|
|
|
|
|
|
|
|
result = workspaceService_->ListWorkspaces(limit, offset, include_deleted);
|
|
result = workspaceService_->ListWorkspaces(limit, offset, include_deleted);
|
|
|
} else {
|
|
} 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);
|
|
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)},
|
|
{"created_by_name", ResolveUserName(workspace.created_by)},
|
|
|
{"updated_by_name", ResolveUserName(workspace.updated_by)}
|
|
{"updated_by_name", ResolveUserName(workspace.updated_by)}
|
|
|
};
|
|
};
|
|
|
- if (IsSuperadmin(*auth_user)) {
|
|
|
|
|
|
|
+ if (has_system_access) {
|
|
|
ws_json["deleted_at"] = workspace.deleted_at;
|
|
ws_json["deleted_at"] = workspace.deleted_at;
|
|
|
}
|
|
}
|
|
|
workspaces_array.push_back(ws_json);
|
|
workspaces_array.push_back(ws_json);
|
|
@@ -1686,14 +1689,15 @@ void HttpServer::HandleGetWorkspace(const httplib::Request& req, httplib::Respon
|
|
|
// Extract workspace ID from path
|
|
// Extract workspace ID from path
|
|
|
std::string workspace_id = req.matches[1].str();
|
|
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);
|
|
auto result = workspaceService_->GetWorkspace(workspace_id);
|
|
@@ -1732,10 +1736,15 @@ void HttpServer::HandleUpdateWorkspace(const httplib::Request& req, httplib::Res
|
|
|
return;
|
|
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.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;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1747,9 +1756,6 @@ void HttpServer::HandleUpdateWorkspace(const httplib::Request& req, httplib::Res
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
- // Extract workspace ID from path
|
|
|
|
|
- std::string workspace_id = req.matches[1].str();
|
|
|
|
|
-
|
|
|
|
|
auto body = nlohmann::json::parse(req.body);
|
|
auto body = nlohmann::json::parse(req.body);
|
|
|
|
|
|
|
|
UpdateWorkspaceRequest update_req;
|
|
UpdateWorkspaceRequest update_req;
|
|
@@ -1800,10 +1806,10 @@ void HttpServer::HandleDeleteWorkspace(const httplib::Request& req, httplib::Res
|
|
|
return;
|
|
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.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;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1881,17 +1887,12 @@ void HttpServer::HandleCreateGroup(const httplib::Request& req, httplib::Respons
|
|
|
// Extract workspace ID from path
|
|
// Extract workspace ID from path
|
|
|
std::string workspace_id = req.matches[1].str();
|
|
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.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;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1963,14 +1964,15 @@ void HttpServer::HandleListGroups(const httplib::Request& req, httplib::Response
|
|
|
// Extract workspace ID from path
|
|
// Extract workspace ID from path
|
|
|
std::string workspace_id = req.matches[1].str();
|
|
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
|
|
// Check if GroupService is available
|
|
@@ -1998,10 +2000,10 @@ void HttpServer::HandleListGroups(const httplib::Request& req, httplib::Response
|
|
|
offset = 0;
|
|
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";
|
|
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.has_param("include_system") &&
|
|
|
req.get_param_value("include_system") == "true";
|
|
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)},
|
|
{"created_by_name", ResolveUserName(group.created_by)},
|
|
|
{"updated_by_name", ResolveUserName(group.updated_by)}
|
|
{"updated_by_name", ResolveUserName(group.updated_by)}
|
|
|
};
|
|
};
|
|
|
- if (IsSuperadmin(*auth_user)) {
|
|
|
|
|
|
|
+ if (has_system_access) {
|
|
|
grp_json["deleted_at"] = group.deleted_at;
|
|
grp_json["deleted_at"] = group.deleted_at;
|
|
|
}
|
|
}
|
|
|
groups_array.push_back(grp_json);
|
|
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 workspace_id = req.matches[1].str();
|
|
|
std::string group_id = req.matches[2].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
|
|
// 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 workspace_id = req.matches[1].str();
|
|
|
std::string group_id = req.matches[2].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.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;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -2247,10 +2252,12 @@ void HttpServer::HandleDeleteGroup(const httplib::Request& req, httplib::Respons
|
|
|
std::string workspace_id = req.matches[1].str();
|
|
std::string workspace_id = req.matches[1].str();
|
|
|
std::string group_id = req.matches[2].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.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;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -2340,10 +2347,12 @@ void HttpServer::HandleAddMember(const httplib::Request& req, httplib::Response&
|
|
|
// Extract workspace ID from path
|
|
// Extract workspace ID from path
|
|
|
std::string workspace_id = req.matches[1].str();
|
|
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.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;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -2422,14 +2431,15 @@ void HttpServer::HandleListMembers(const httplib::Request& req, httplib::Respons
|
|
|
// Extract workspace ID from path
|
|
// Extract workspace ID from path
|
|
|
std::string workspace_id = req.matches[1].str();
|
|
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
|
|
// Check if MembershipService is available
|
|
@@ -2457,7 +2467,7 @@ void HttpServer::HandleListMembers(const httplib::Request& req, httplib::Respons
|
|
|
offset = 0;
|
|
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";
|
|
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},
|
|
{"created_at", member.created_at},
|
|
|
{"updated_at", member.updated_at}
|
|
{"updated_at", member.updated_at}
|
|
|
};
|
|
};
|
|
|
- if (IsSuperadmin(*auth_user)) {
|
|
|
|
|
|
|
+ if (has_system_access) {
|
|
|
member_json["deleted_at"] = member.deleted_at;
|
|
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 workspace_id = req.matches[1].str();
|
|
|
std::string user_id = req.matches[2].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
|
|
// 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 workspace_id = req.matches[1].str();
|
|
|
std::string user_id = req.matches[2].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.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;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -2662,10 +2675,12 @@ void HttpServer::HandleRemoveMember(const httplib::Request& req, httplib::Respon
|
|
|
std::string workspace_id = req.matches[1].str();
|
|
std::string workspace_id = req.matches[1].str();
|
|
|
std::string user_id = req.matches[2].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.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;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -2730,10 +2745,12 @@ void HttpServer::HandleCreateApiKey(const httplib::Request& req, httplib::Respon
|
|
|
// Extract user ID from path
|
|
// Extract user ID from path
|
|
|
std::string target_user_id = req.matches[1].str();
|
|
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.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;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -2763,8 +2780,8 @@ void HttpServer::HandleCreateApiKey(const httplib::Request& req, httplib::Respon
|
|
|
for (const auto& item : json["permissions"]) {
|
|
for (const auto& item : json["permissions"]) {
|
|
|
if (item.is_string()) {
|
|
if (item.is_string()) {
|
|
|
std::string perm = item.get<std::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
|
|
// For now, allow all permissions - proper permission checking
|
|
|
// would require looking up user's actual permissions
|
|
// 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
|
|
// Extract user ID from path
|
|
|
std::string target_user_id = req.matches[1].str();
|
|
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.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;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -2839,9 +2858,9 @@ void HttpServer::HandleListApiKeys(const httplib::Request& req, httplib::Respons
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
- // Check for include_revoked parameter (superadmin only)
|
|
|
|
|
|
|
+ // Check for include_revoked parameter (requires system access)
|
|
|
bool include_revoked = false;
|
|
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";
|
|
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},
|
|
{"last_used_at", key.last_used_at},
|
|
|
{"expires_at", key.expires_at}
|
|
{"expires_at", key.expires_at}
|
|
|
};
|
|
};
|
|
|
- if (IsSuperadmin(*auth_user)) {
|
|
|
|
|
|
|
+ if (has_system_access) {
|
|
|
key_json["deleted_at"] = key.deleted_at;
|
|
key_json["deleted_at"] = key.deleted_at;
|
|
|
}
|
|
}
|
|
|
keys_array.push_back(key_json);
|
|
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 target_user_id = req.matches[1].str();
|
|
|
std::string key_id = req.matches[2].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.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;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -2987,20 +3008,15 @@ void HttpServer::HandleCreateCollection(const httplib::Request& req, httplib::Re
|
|
|
return;
|
|
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
|
|
// Check if CollectionService is available
|
|
@@ -3099,20 +3115,15 @@ void HttpServer::HandleListCollections(const httplib::Request& req, httplib::Res
|
|
|
return;
|
|
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
|
|
// Check if CollectionService is available
|
|
@@ -3123,8 +3134,8 @@ void HttpServer::HandleListCollections(const httplib::Request& req, httplib::Res
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
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.has_param("include_system") &&
|
|
|
req.get_param_value("include_system") == "true";
|
|
req.get_param_value("include_system") == "true";
|
|
|
|
|
|
|
@@ -3222,20 +3233,15 @@ void HttpServer::HandleGetCollection(const httplib::Request& req, httplib::Respo
|
|
|
return;
|
|
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
|
|
// Check if CollectionService is available
|
|
@@ -3299,20 +3305,15 @@ void HttpServer::HandleUpdateCollection(const httplib::Request& req, httplib::Re
|
|
|
return;
|
|
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
|
|
// Check if CollectionService is available
|
|
@@ -3444,20 +3445,11 @@ void HttpServer::HandleCreateDocument(const httplib::Request& req, httplib::Resp
|
|
|
return;
|
|
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
|
|
// Check if DocumentService is available
|
|
@@ -3546,20 +3538,11 @@ void HttpServer::HandleListDocuments(const httplib::Request& req, httplib::Respo
|
|
|
return;
|
|
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
|
|
// Check if DocumentService is available
|
|
@@ -3661,22 +3644,6 @@ void HttpServer::HandleGetDocument(const httplib::Request& req, httplib::Respons
|
|
|
return;
|
|
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
|
|
// Check if DocumentService is available
|
|
|
if (!documentService_) {
|
|
if (!documentService_) {
|
|
|
res.status = 503;
|
|
res.status = 503;
|
|
@@ -3694,6 +3661,14 @@ void HttpServer::HandleGetDocument(const httplib::Request& req, httplib::Respons
|
|
|
return;
|
|
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 = {
|
|
nlohmann::json doc_json = {
|
|
|
{"id", result.document->id},
|
|
{"id", result.document->id},
|
|
|
{"collection", result.document->collection},
|
|
{"collection", result.document->collection},
|
|
@@ -3735,22 +3710,6 @@ void HttpServer::HandleUpdateDocument(const httplib::Request& req, httplib::Resp
|
|
|
return;
|
|
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
|
|
// Check if DocumentService is available
|
|
|
if (!documentService_) {
|
|
if (!documentService_) {
|
|
|
res.status = 503;
|
|
res.status = 503;
|
|
@@ -3759,6 +3718,23 @@ void HttpServer::HandleUpdateDocument(const httplib::Request& req, httplib::Resp
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
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
|
|
// Parse request body
|
|
|
auto json = nlohmann::json::parse(req.body);
|
|
auto json = nlohmann::json::parse(req.body);
|
|
|
|
|
|
|
@@ -3840,22 +3816,6 @@ void HttpServer::HandleDeleteDocument(const httplib::Request& req, httplib::Resp
|
|
|
return;
|
|
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
|
|
// Check if DocumentService is available
|
|
|
if (!documentService_) {
|
|
if (!documentService_) {
|
|
|
res.status = 503;
|
|
res.status = 503;
|
|
@@ -3864,6 +3824,23 @@ void HttpServer::HandleDeleteDocument(const httplib::Request& req, httplib::Resp
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
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);
|
|
auto result = documentService_->DeleteDocument(workspace_id, collection_name, document_id);
|
|
|
|
|
|
|
|
if (!result.success) {
|
|
if (!result.success) {
|
|
@@ -3907,20 +3884,15 @@ void HttpServer::HandleDropCollection(const httplib::Request& req, httplib::Resp
|
|
|
return;
|
|
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
|
|
// Check if CollectionService is available
|
|
@@ -4013,20 +3985,15 @@ void HttpServer::HandleCreateView(const httplib::Request& req, httplib::Response
|
|
|
return;
|
|
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
|
|
// Check if ViewService is available
|
|
@@ -4200,20 +4167,15 @@ void HttpServer::HandleListViews(const httplib::Request& req, httplib::Response&
|
|
|
return;
|
|
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
|
|
// Check if ViewService is available
|
|
@@ -4299,20 +4261,15 @@ void HttpServer::HandleGetView(const httplib::Request& req, httplib::Response& r
|
|
|
return;
|
|
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
|
|
// Check if ViewService is available
|
|
@@ -4416,20 +4373,15 @@ void HttpServer::HandleUpdateView(const httplib::Request& req, httplib::Response
|
|
|
return;
|
|
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
|
|
// Check if ViewService is available
|
|
@@ -4596,20 +4548,15 @@ void HttpServer::HandleDeleteView(const httplib::Request& req, httplib::Response
|
|
|
return;
|
|
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
|
|
// Check if ViewService is available
|