diff --git a/app.py b/app.py index 63c430c..e31c270 100644 --- a/app.py +++ b/app.py @@ -255,11 +255,16 @@ async def createUser( user: CreateUserDefinition, access_token: str | None = Header(default=None), ): - is_admin = check_if_admin_access_token(connector, access_token) - if not is_admin: + try: + _user, _ = get_user_by_access_token(connector, access_token) + if not _user: + raise Exception("Not allowed") + acl = get_user_permissions_for_table(connector, "users", _user) + if acl != AccessType.READ and acl != AccessType.READ_WRITE: + raise Exception("Not allowed") + except Exception as e: return JSONResponse( - ErrorResponse(error="Not allowed").dict(), - status_code=status.HTTP_403_FORBIDDEN, + ErrorResponse(error=str(e)).dict(), status_code=status.HTTP_403_FORBIDDEN ) try: @@ -300,11 +305,16 @@ async def updateUser( user: UserUpdateDefinition, access_token: str | None = Header(default=None), ): - is_admin = check_if_admin_access_token(connector, access_token) - if not is_admin: + try: + _user, _ = get_user_by_access_token(connector, access_token) + if not _user: + raise Exception("Not allowed") + acl = get_user_permissions_for_table(connector, "users", _user) + if acl != AccessType.WRITE and acl != AccessType.READ_WRITE: + raise Exception("Not allowed") + except Exception as e: return JSONResponse( - ErrorResponse(error="Not allowed").dict(), - status_code=status.HTTP_403_FORBIDDEN, + ErrorResponse(error=str(e)).dict(), status_code=status.HTTP_403_FORBIDDEN ) try: @@ -344,11 +354,16 @@ async def removeUser( user_id: int, access_token: str | None = Header(default=None), ): - is_admin = check_if_admin_access_token(connector, access_token) - if not is_admin: + try: + _user, _ = get_user_by_access_token(connector, access_token) + if not _user: + raise Exception("Not allowed") + acl = get_user_permissions_for_table(connector, "users", _user) + if acl != AccessType.WRITE and acl != AccessType.READ_WRITE: + raise Exception("Not allowed") + except Exception as e: return JSONResponse( - ErrorResponse(error="Not allowed").dict(), - status_code=status.HTTP_403_FORBIDDEN, + ErrorResponse(error=str(e)).dict(), status_code=status.HTTP_403_FORBIDDEN ) try: @@ -408,11 +423,20 @@ async def items( status_code=status.HTTP_404_NOT_FOUND, ) - is_admin = check_if_admin_access_token(connector, access_token) - if table_info["system"] and not is_admin: + try: + is_admin = check_if_admin_access_token(connector, access_token) + if table_info["system"] and not is_admin: + raise Exception("Not allowed") + + user, group = get_user_by_access_token(connector, access_token) + if not user: + raise Exception("Not allowed") + acl = get_user_permissions_for_table(connector, tableName, user) + if acl != AccessType.READ and acl != AccessType.READ_WRITE: + raise Exception("Not allowed") + except Exception as e: return JSONResponse( - ErrorResponse(error="Not allowed").dict(), - status_code=status.HTTP_403_FORBIDDEN, + ErrorResponse(error=str(e)).dict(), status_code=status.HTTP_403_FORBIDDEN ) columns = parse_columns_from_definition(table_info["columns"]) @@ -514,14 +538,22 @@ async def itemsCreate( status_code=status.HTTP_404_NOT_FOUND, ) - is_admin = check_if_admin_access_token(connector, access_token) - if table_info["system"] and not is_admin: + try: + is_admin = check_if_admin_access_token(connector, access_token) + if table_info["system"] and not is_admin: + raise Exception("Not allowed") + + user, group = get_user_by_access_token(connector, access_token) + if not user: + raise Exception("Not allowed") + acl = get_user_permissions_for_table(connector, tableName, user) + if acl != AccessType.WRITE and acl != AccessType.READ_WRITE: + raise Exception("Not allowed") + except Exception as e: return JSONResponse( - ErrorResponse(error="Not allowed").dict(), - status_code=status.HTTP_403_FORBIDDEN, + ErrorResponse(error=str(e)).dict(), status_code=status.HTTP_403_FORBIDDEN ) - user, group = get_user_by_access_token(connector, access_token) if not is_admin: allowedColumns = get_allowed_columns_for_group( connector, tableName, group.id if group else -1 @@ -597,14 +629,22 @@ async def itemsUpdate( status_code=status.HTTP_404_NOT_FOUND, ) - is_admin = check_if_admin_access_token(connector, access_token) - if table_info["system"] and not is_admin: + try: + is_admin = check_if_admin_access_token(connector, access_token) + if table_info["system"] and not is_admin: + raise Exception("Not allowed") + + user, group = get_user_by_access_token(connector, access_token) + if not user: + raise Exception("Not allowed") + acl = get_user_permissions_for_table(connector, tableName, user) + if acl != AccessType.WRITE and acl != AccessType.READ_WRITE: + raise Exception("Not allowed") + except Exception as e: return JSONResponse( - ErrorResponse(error="Not allowed").dict(), - status_code=status.HTTP_403_FORBIDDEN, + ErrorResponse(error=str(e)).dict(), status_code=status.HTTP_403_FORBIDDEN ) - user, group = get_user_by_access_token(connector, access_token) if not is_admin: allowedColumns = get_allowed_columns_for_group( connector, tableName, group.id if group else -1 @@ -682,17 +722,25 @@ async def itemsDelete( status_code=status.HTTP_404_NOT_FOUND, ) - is_admin = check_if_admin_access_token(connector, access_token) - if table_info["system"] and not is_admin: + try: + is_admin = check_if_admin_access_token(connector, access_token) + if table_info["system"] and not is_admin: + raise Exception("Not allowed") + + user, group = get_user_by_access_token(connector, access_token) + if not user: + raise Exception("Not allowed") + acl = get_user_permissions_for_table(connector, tableName, user) + if acl != AccessType.WRITE and acl != AccessType.READ_WRITE: + raise Exception("Not allowed") + except Exception as e: return JSONResponse( - ErrorResponse(error="Not allowed").dict(), - status_code=status.HTTP_403_FORBIDDEN, + ErrorResponse(error=str(e)).dict(), status_code=status.HTTP_403_FORBIDDEN ) - user, group = get_user_by_access_token(connector, access_token) if not is_admin: allowedColumns = get_allowed_columns_for_group( - connector, tableName, group.id if group else -1 + connector, tableName, group.id if group else 1 ) if not allowedColumns: return JSONResponse( @@ -730,16 +778,21 @@ async def itemsDelete( }, 403: { "model": ErrorResponse, - "description": "Requesting this endpoint requires admin access token", + "description": "Requesting this endpoint requires permissions", }, }, ) async def getAssets(access_token: str | None = Header(default=None)): - is_admin = check_if_admin_access_token(connector, access_token) - if not is_admin: + try: + user, _ = get_user_by_access_token(connector, access_token) + if not user: + raise Exception("Not allowed") + acl = get_user_permissions_for_table(connector, "assets", user) + if acl != AccessType.READ and acl != AccessType.READ_WRITE: + raise Exception("Not allowed") + except Exception as e: return JSONResponse( - ErrorResponse(error="Not allowed").dict(), - status_code=status.HTTP_403_FORBIDDEN, + ErrorResponse(error=str(e)).dict(), status_code=status.HTTP_403_FORBIDDEN ) assets = get_assets(connector) @@ -761,11 +814,16 @@ async def getAssets(access_token: str | None = Header(default=None)): }, ) async def getAssetsTags(access_token: str | None = Header(default=None)): - is_admin = check_if_admin_access_token(connector, access_token) - if not is_admin: + try: + user, _ = get_user_by_access_token(connector, access_token) + if not user: + raise Exception("Not allowed") + acl = get_user_permissions_for_table(connector, "assets", user) + if acl != AccessType.READ and acl != AccessType.READ_WRITE: + raise Exception("Not allowed") + except Exception as e: return JSONResponse( - ErrorResponse(error="Not allowed").dict(), - status_code=status.HTTP_403_FORBIDDEN, + ErrorResponse(error=str(e)).dict(), status_code=status.HTTP_403_FORBIDDEN ) assets = get_assets_tags(connector) @@ -833,11 +891,16 @@ async def createAsset( asset: UploadFile, access_token: str | None = Header(default=None), ): - user, _ = get_user_by_access_token(connector, access_token) - if not user: + try: + user, _ = get_user_by_access_token(connector, access_token) + if not user: + raise Exception("Not allowed") + acl = get_user_permissions_for_table(connector, "assets", user) + if acl != AccessType.WRITE and acl != AccessType.READ_WRITE: + raise Exception("Not allowed") + except Exception as e: return JSONResponse( - ErrorResponse(error="Not allowed").dict(), - status_code=status.HTTP_403_FORBIDDEN, + ErrorResponse(error=str(e)).dict(), status_code=status.HTTP_403_FORBIDDEN ) filename = asset.filename @@ -902,14 +965,21 @@ async def updateAsset( asset_update: AssetUpdateDefinition, access_token: str | None = Header(default=None), ): - user = get_user_by_access_token(connector, access_token) - if not user: + try: + user, _ = get_user_by_access_token(connector, access_token) + if not user: + raise Exception("Not allowed") + acl = get_user_permissions_for_table(connector, "assets", user) + if acl != AccessType.WRITE and acl != AccessType.READ_WRITE: + raise Exception("Not allowed") + except Exception as e: return JSONResponse( - ErrorResponse(error="Not allowed").dict(), - status_code=status.HTTP_403_FORBIDDEN, + ErrorResponse(error=str(e)).dict(), status_code=status.HTTP_403_FORBIDDEN ) - ok, e = update_asset(connector, asset_id, asset_update.description, asset_update.tags) + ok, e = update_asset( + connector, asset_id, asset_update.description, asset_update.tags + ) if not ok: if e: return JSONResponse( @@ -952,11 +1022,16 @@ async def removeAsset( delete_referencing: bool = False, access_token: str | None = Header(default=None), ): - user = get_user_by_access_token(connector, access_token) - if not user: + try: + user, _ = get_user_by_access_token(connector, access_token) + if not user: + raise Exception("Not allowed") + acl = get_user_permissions_for_table(connector, "assets", user) + if acl != AccessType.WRITE and acl != AccessType.READ_WRITE: + raise Exception("Not allowed") + except Exception as e: return JSONResponse( - ErrorResponse(error="Not allowed").dict(), - status_code=status.HTTP_403_FORBIDDEN, + ErrorResponse(error=str(e)).dict(), status_code=status.HTTP_403_FORBIDDEN ) asset = get_asset_by_id(connector, asset_id) @@ -967,7 +1042,7 @@ async def removeAsset( ) try: - minioClient.remove_object(BUCKET_NAME, asset.fid) + minioClient.remove_object(BUCKET_NAME, asset.name) except Exception as e: logger.error(f"Failed to remove asset from storage: {e}") return JSONResponse( diff --git a/dba.py b/dba.py index 89911e3..90f3bf9 100644 --- a/dba.py +++ b/dba.py @@ -206,6 +206,20 @@ def get_user_by_access_token(conn: DBConnector, access_token: str | None): return None, None +def get_user_permissions_for_table(conn: DBConnector, table_name:str, user: User) -> AccessType: + try: + groups = get_user_groups(conn, user.id) + for g in groups: + acl = get_table_access_level(conn, table_name, g) + if acl is not None and acl != AccessType.NONE: + return acl + + return AccessType.NONE + except Exception as e: + logger.exception(e) + return AccessType.NONE + + def check_user(conn: DBConnector, username: str, password: str): try: hashedPwd = hash_password(password) @@ -294,6 +308,25 @@ def set_user_group(conn: DBConnector, user_id: int, group_id: int): return False, e +def get_user_groups(conn: DBConnector, user_id: int) -> list[UserGroup]: + try: + _groups = conn.filterFromTable( + USER_IN_USER_GROUP_JOIN_TABLE_NAME, + ["*"], + [ColumnCondition("user_id", "eq", user_id)], + ) + if len(_groups) == 0: + logger.warning(f"User with id {user_id} not found, so no group") + return [] + + u_groups = [UserInUserGroup.parse_obj(group) for group in _groups] + groups = [get_group_by_id(conn, ug.user_group_id) for ug in u_groups] + return [group for group in groups if group is not None] + except Exception as e: + logger.exception(e) + return [] + + def get_user_group(conn: DBConnector, user_id: int): try: grp_usr_joint = conn.filterFromTable( @@ -353,13 +386,10 @@ def create_table(conn: DBConnector, table_name: str, schema: list[ColumnDefiniti def get_table_access_level( - conn: DBConnector, table_name: str, user_id: int + conn: DBConnector, table_name: str, group: UserGroup ) -> AccessType: try: - user_group = get_user_group(conn, user_id) - if not user_group: - return AccessType.NONE - elif user_group.name == "admin": + if group.id == 2 and group.name == "admin": return AccessType.READ_WRITE access = conn.filterFromTable( @@ -367,7 +397,7 @@ def get_table_access_level( ["*"], [ ColumnCondition("table_name", "eq", table_name), - ColumnCondition("user_group_id", "eq", user_group.id), + ColumnCondition("user_group_id", "eq", group.id), ], ) if not access: