async def update_project_without_enforcing_checks( self, project_data: Dict, project_uuid: str) -> bool: """The garbage collector needs to alter the row without passing through the permissions layer.""" async with self.engine.acquire() as conn: # update timestamps project_data["lastChangeDate"] = now_str() # now update it result = await conn.execute( # pylint: disable=no-value-for-parameter projects.update().values(**_convert_to_db_names(project_data) ).where( projects.c.uuid == project_uuid)) return result.rowcount == 1
async def update_user_project( self, project_data: Dict, user_id: int, project_uuid: str, include_templates: Optional[bool] = False, ): """updates a project from a user""" log.info("Updating project %s for user %s", project_uuid, user_id) async with self.engine.acquire() as conn: row = await self._get_project( user_id, project_uuid, exclude_foreign=["tags"], include_templates=include_templates, ) user_groups: List[RowProxy] = await self.__load_user_groups( conn, user_id) _check_project_permissions(row, user_id, user_groups, "write") # uuid can ONLY be set upon creation if row[projects.c.uuid.key] != project_data["uuid"]: raise ProjectInvalidRightsError(user_id, project_data["uuid"]) # ensure the prj owner is always in the access rights owner_primary_gid = await self._get_user_primary_group_gid( conn, row[projects.c.prj_owner.key]) project_data["accessRights"].update( _create_project_access_rights(owner_primary_gid, ProjectAccessRights.OWNER)) # update timestamps project_data["lastChangeDate"] = now_str() # now update it result = await conn.execute( # pylint: disable=no-value-for-parameter projects.update().values( **_convert_to_db_names(project_data) ).where(projects.c.id == row[projects.c.id.key] ).returning(literal_column("*"))) project: RowProxy = await result.fetchone() user_email = await self._get_user_email(conn, project.prj_owner) tags = await self._get_tags_by_project( conn, project_id=project[projects.c.id]) return _convert_to_schema_names(project, user_email, tags=tags)
async def update_project_without_checking_permissions( self, project_data: Dict, project_uuid: ProjectIDStr, *, hidden: Optional[bool] = None, ) -> bool: """The garbage collector needs to alter the row without passing through the permissions layer.""" async with self.engine.acquire() as conn: # update timestamps project_data["lastChangeDate"] = now_str() # now update it updated_values = _convert_to_db_names(project_data) if hidden is not None: updated_values["hidden"] = hidden result = await conn.execute( # pylint: disable=no-value-for-parameter projects.update().values(**updated_values).where( projects.c.uuid == project_uuid)) return result.rowcount == 1
async def replace_user_project( self, new_project_data: Dict[str, Any], user_id: int, project_uuid: str, include_templates: Optional[bool] = False, ) -> Dict[str, Any]: """replaces a project from a user this method completely replaces a user project with new_project_data only keeping the old entries from the project workbench if they exists in the new project workbench. """ log.info("Updating project %s for user %s", project_uuid, user_id) async with self.engine.acquire() as conn: async with conn.begin() as _transaction: current_project: Dict = await self._get_project( conn, user_id, project_uuid, exclude_foreign=["tags"], include_templates=include_templates, for_update=True, ) user_groups: List[RowProxy] = await self.__load_user_groups( conn, user_id) _check_project_permissions(current_project, user_id, user_groups, "write") # uuid can ONLY be set upon creation if current_project["uuid"] != new_project_data["uuid"]: raise ProjectInvalidRightsError(user_id, new_project_data["uuid"]) # ensure the prj owner is always in the access rights owner_primary_gid = await self._get_user_primary_group_gid( conn, current_project[projects.c.prj_owner.key]) new_project_data.setdefault("accessRights", {}).update( _create_project_access_rights(owner_primary_gid, ProjectAccessRights.OWNER)) # update the workbench def _update_workbench(old_project: Dict[str, Any], new_project: Dict[str, Any]) -> None: # any non set entry in the new workbench is taken from the old one if available old_workbench = old_project["workbench"] new_workbench = new_project["workbench"] for node_key, node in new_workbench.items(): old_node = old_workbench.get(node_key) if not old_node: continue for prop in old_node: # check if the key is missing in the new node if prop not in node: # use the old value node[prop] = old_node[prop] return new_project _update_workbench(current_project, new_project_data) # update timestamps new_project_data["lastChangeDate"] = now_str() # now update it log.debug("DB updating with new_project_data=%s", json_dumps(new_project_data)) result = await conn.execute( # pylint: disable=no-value-for-parameter projects.update().values( **_convert_to_db_names(new_project_data) ).where(projects.c.id == current_project[projects.c.id.key] ).returning(literal_column("*"))) project: RowProxy = await result.fetchone() log.debug( "DB updated returned row project=%s", json_dumps(dict(project.items())), ) user_email = await self._get_user_email( conn, project.prj_owner) tags = await self._get_tags_by_project( conn, project_id=project[projects.c.id]) return _convert_to_schema_names(project, user_email, tags=tags)
async def patch_user_project_workbench( self, partial_workbench_data: Dict[str, Any], user_id: int, project_uuid: str) -> Tuple[Dict[str, Any], Dict[str, Any]]: """patches an EXISTING project from a user new_project_data only contains the entries to modify """ log.info("Patching project %s for user %s", project_uuid, user_id) async with self.engine.acquire() as conn: async with conn.begin() as _transaction: current_project: Dict = await self._get_project( conn, user_id, project_uuid, exclude_foreign=["tags"], include_templates=False, for_update=True, ) user_groups: List[RowProxy] = await self.__load_user_groups( conn, user_id) _check_project_permissions(current_project, user_id, user_groups, "write") def _patch_workbench( project: Dict[str, Any], new_partial_workbench_data: Dict[str, Any] ) -> Tuple[Dict[str, Any], Dict[str, Any]]: """patch the project workbench with the values in new_data and returns the changed project and changed values""" changed_entries = {} for node_key, new_node_data in new_partial_workbench_data.items( ): current_node_data = project.get("workbench", {}).get(node_key) if current_node_data is None: log.debug( "node %s is missing from project, no patch", node_key) raise NodeNotFoundError(project_uuid, node_key) # find changed keys changed_entries.update({ node_key: _find_changed_dict_keys( current_node_data, new_node_data, look_for_removed_keys=False, ) }) # patch current_node_data.update(new_node_data) return (project, changed_entries) new_project_data, changed_entries = _patch_workbench( current_project, partial_workbench_data) # update timestamps new_project_data["lastChangeDate"] = now_str() log.debug( "DB updating with new_project_data=%s", json_dumps(dict(new_project_data)), ) result = await conn.execute( # pylint: disable=no-value-for-parameter projects.update().values( **_convert_to_db_names(new_project_data) ).where(projects.c.id == current_project[projects.c.id.key] ).returning(literal_column("*"))) project: RowProxy = await result.fetchone() log.debug( "DB updated returned row project=%s", json_dumps(dict(project.items())), ) user_email = await self._get_user_email( conn, project.prj_owner) tags = await self._get_tags_by_project( conn, project_id=project[projects.c.id]) return ( _convert_to_schema_names(project, user_email, tags=tags), changed_entries, )