fixed udb rbac
This commit is contained in:
parent
091a3672de
commit
1b51ee3e1c
3 changed files with 35 additions and 34 deletions
|
|
@ -956,7 +956,7 @@ class ComponentObjects:
|
||||||
mid = file.get("mandateId")
|
mid = file.get("mandateId")
|
||||||
return not mid or (isinstance(mid, str) and not mid.strip())
|
return not mid or (isinstance(mid, str) and not mid.strip())
|
||||||
|
|
||||||
def getAllFiles(self, pagination: Optional[PaginationParams] = None) -> Union[List[FileItem], PaginatedResult]:
|
def getAllFiles(self, pagination: Optional[PaginationParams] = None, recordFilter: Dict[str, Any] = None) -> Union[List[FileItem], PaginatedResult]:
|
||||||
"""
|
"""
|
||||||
Returns files visible to the current user based on RBAC scope rules.
|
Returns files visible to the current user based on RBAC scope rules.
|
||||||
|
|
||||||
|
|
@ -999,44 +999,31 @@ class ComponentObjects:
|
||||||
continue
|
continue
|
||||||
return fileItems
|
return fileItems
|
||||||
|
|
||||||
folderFilter = None
|
|
||||||
hasFolderFilter = False
|
|
||||||
if pagination and pagination.filters and "folderId" in pagination.filters:
|
|
||||||
folderFilter = pagination.filters.pop("folderId")
|
|
||||||
hasFolderFilter = True
|
|
||||||
|
|
||||||
def _applyFolderFilter(files):
|
|
||||||
if not hasFolderFilter:
|
|
||||||
return files
|
|
||||||
if folderFilter is None:
|
|
||||||
return [f for f in files if not (f.get("folderId") if isinstance(f, dict) else getattr(f, "folderId", None))]
|
|
||||||
return [f for f in files if (f.get("folderId") if isinstance(f, dict) else getattr(f, "folderId", None)) == folderFilter]
|
|
||||||
|
|
||||||
if pagination is None:
|
if pagination is None:
|
||||||
allFiles = getRecordsetWithRBAC(
|
allFiles = getRecordsetWithRBAC(
|
||||||
self.db, FileItem, self.currentUser,
|
self.db, FileItem, self.currentUser,
|
||||||
|
recordFilter=recordFilter,
|
||||||
mandateId=self.mandateId,
|
mandateId=self.mandateId,
|
||||||
featureInstanceId=self.featureInstanceId,
|
featureInstanceId=self.featureInstanceId,
|
||||||
)
|
)
|
||||||
return _convertFileItems(_applyFolderFilter(allFiles))
|
return _convertFileItems(allFiles)
|
||||||
|
|
||||||
result = getRecordsetPaginatedWithRBAC(
|
result = getRecordsetPaginatedWithRBAC(
|
||||||
self.db, FileItem, self.currentUser,
|
self.db, FileItem, self.currentUser,
|
||||||
pagination=pagination,
|
pagination=pagination,
|
||||||
|
recordFilter=recordFilter,
|
||||||
mandateId=self.mandateId,
|
mandateId=self.mandateId,
|
||||||
featureInstanceId=self.featureInstanceId,
|
featureInstanceId=self.featureInstanceId,
|
||||||
)
|
)
|
||||||
|
|
||||||
if isinstance(result, PaginatedResult):
|
if isinstance(result, PaginatedResult):
|
||||||
filtered = _applyFolderFilter(result.items)
|
|
||||||
return PaginatedResult(
|
return PaginatedResult(
|
||||||
items=_convertFileItems(filtered),
|
items=_convertFileItems(result.items),
|
||||||
totalItems=len(filtered),
|
totalItems=result.totalItems,
|
||||||
totalPages=max(1, -(-len(filtered) // (pagination.pageSize or 20))),
|
totalPages=result.totalPages,
|
||||||
)
|
)
|
||||||
|
|
||||||
raw = result if isinstance(result, list) else []
|
return _convertFileItems(result if isinstance(result, list) else [])
|
||||||
return _convertFileItems(_applyFolderFilter(raw))
|
|
||||||
|
|
||||||
def getFile(self, fileId: str) -> Optional[FileItem]:
|
def getFile(self, fileId: str) -> Optional[FileItem]:
|
||||||
"""Returns a file by ID if the current user has RBAC access (scope-based)."""
|
"""Returns a file by ID if the current user has RBAC access (scope-based)."""
|
||||||
|
|
@ -1337,13 +1324,14 @@ class ComponentObjects:
|
||||||
return folders[0] if folders else None
|
return folders[0] if folders else None
|
||||||
|
|
||||||
def listFolders(self, parentId: Optional[str] = None) -> List[Dict[str, Any]]:
|
def listFolders(self, parentId: Optional[str] = None) -> List[Dict[str, Any]]:
|
||||||
"""List folders for current user, optionally filtered by parentId.
|
"""List folders visible to the current user.
|
||||||
Each folder is enriched with ``fileCount`` (number of direct files
|
Own folders are always returned. Other users' folders are only
|
||||||
visible to this user via RBAC scope rules)."""
|
returned when they contain files visible to the current user.
|
||||||
recordFilter = {"sysCreatedBy": self.userId or ""}
|
Each folder is enriched with ``fileCount``."""
|
||||||
|
recordFilter = {}
|
||||||
if parentId is not None:
|
if parentId is not None:
|
||||||
recordFilter["parentId"] = parentId
|
recordFilter["parentId"] = parentId
|
||||||
folders = self.db.getRecordset(FileFolder, recordFilter=recordFilter)
|
folders = self.db.getRecordset(FileFolder, recordFilter=recordFilter if recordFilter else None)
|
||||||
|
|
||||||
if not folders:
|
if not folders:
|
||||||
return folders
|
return folders
|
||||||
|
|
@ -1351,11 +1339,7 @@ class ComponentObjects:
|
||||||
folderIds = [f["id"] for f in folders if f.get("id")]
|
folderIds = [f["id"] for f in folders if f.get("id")]
|
||||||
fileCounts: Dict[str, int] = {}
|
fileCounts: Dict[str, int] = {}
|
||||||
try:
|
try:
|
||||||
# Count files per folder that the user can see (RBAC scope-aware).
|
|
||||||
# Own files are always counted; shared files (global/mandate/featureInstance)
|
|
||||||
# that happen to be in one of the user's folders are also counted.
|
|
||||||
from modules.interfaces.interfaceRbac import _buildFilesScopeWhereClause
|
from modules.interfaces.interfaceRbac import _buildFilesScopeWhereClause
|
||||||
from modules.datamodels.datamodelUam import User as UserModel
|
|
||||||
scopeClause = _buildFilesScopeWhereClause(
|
scopeClause = _buildFilesScopeWhereClause(
|
||||||
self.currentUser, "FileItem", self.db,
|
self.currentUser, "FileItem", self.db,
|
||||||
self.mandateId, self.featureInstanceId,
|
self.mandateId, self.featureInstanceId,
|
||||||
|
|
@ -1382,10 +1366,16 @@ class ComponentObjects:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Could not count files per folder: {e}")
|
logger.warning(f"Could not count files per folder: {e}")
|
||||||
|
|
||||||
|
userId = self.userId or ""
|
||||||
|
result = []
|
||||||
for folder in folders:
|
for folder in folders:
|
||||||
folder["fileCount"] = fileCounts.get(folder.get("id", ""), 0)
|
fc = fileCounts.get(folder.get("id", ""), 0)
|
||||||
|
folder["fileCount"] = fc
|
||||||
|
isOwn = folder.get("sysCreatedBy") == userId
|
||||||
|
if isOwn or fc > 0:
|
||||||
|
result.append(folder)
|
||||||
|
|
||||||
return folders
|
return result
|
||||||
|
|
||||||
def createFolder(self, name: str, parentId: Optional[str] = None) -> Dict[str, Any]:
|
def createFolder(self, name: str, parentId: Optional[str] = None) -> Dict[str, Any]:
|
||||||
"""Create a new folder with unique name validation."""
|
"""Create a new folder with unique name validation."""
|
||||||
|
|
|
||||||
|
|
@ -740,9 +740,15 @@ def buildRbacWhereClause(
|
||||||
|
|
||||||
# All records within the feature instance - only featureInstanceId filtering
|
# All records within the feature instance - only featureInstanceId filtering
|
||||||
if readLevel == AccessLevel.ALL:
|
if readLevel == AccessLevel.ALL:
|
||||||
|
namespaceAll = TABLE_NAMESPACE.get(table, "system")
|
||||||
|
# Files: scope-based context filtering applies even with ALL access
|
||||||
|
if namespaceAll == "files":
|
||||||
|
return _buildFilesScopeWhereClause(
|
||||||
|
currentUser, table, connector, mandateId, featureInstanceId,
|
||||||
|
baseConditions, baseValues,
|
||||||
|
)
|
||||||
# Chat / AI Workspace: even DATA read ALL must not list other users' rows in a
|
# Chat / AI Workspace: even DATA read ALL must not list other users' rows in a
|
||||||
# shared featureInstance (stale RBAC rules or merged roles). Same as MY.
|
# shared featureInstance (stale RBAC rules or merged roles). Same as MY.
|
||||||
namespaceAll = TABLE_NAMESPACE.get(table, "system")
|
|
||||||
if featureInstanceId and namespaceAll == "chat":
|
if featureInstanceId and namespaceAll == "chat":
|
||||||
userIdFieldAll = "sysCreatedBy"
|
userIdFieldAll = "sysCreatedBy"
|
||||||
if table == "UserInDB":
|
if table == "UserInDB":
|
||||||
|
|
|
||||||
|
|
@ -207,12 +207,17 @@ def get_files(
|
||||||
detail=f"Invalid pagination parameter: {str(e)}"
|
detail=f"Invalid pagination parameter: {str(e)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
recordFilter = None
|
||||||
|
if paginationParams and paginationParams.filters and "folderId" in paginationParams.filters:
|
||||||
|
fVal = paginationParams.filters.pop("folderId")
|
||||||
|
recordFilter = {"folderId": fVal}
|
||||||
|
|
||||||
managementInterface = interfaceDbManagement.getInterface(
|
managementInterface = interfaceDbManagement.getInterface(
|
||||||
currentUser,
|
currentUser,
|
||||||
mandateId=str(context.mandateId) if context.mandateId else None,
|
mandateId=str(context.mandateId) if context.mandateId else None,
|
||||||
featureInstanceId=str(context.featureInstanceId) if context.featureInstanceId else None
|
featureInstanceId=str(context.featureInstanceId) if context.featureInstanceId else None
|
||||||
)
|
)
|
||||||
result = managementInterface.getAllFiles(pagination=paginationParams)
|
result = managementInterface.getAllFiles(pagination=paginationParams, recordFilter=recordFilter)
|
||||||
|
|
||||||
# If pagination was requested, result is PaginatedResult
|
# If pagination was requested, result is PaginatedResult
|
||||||
# If no pagination, result is List[FileItem]
|
# If no pagination, result is List[FileItem]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue