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)
Exemple #3
0
 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
Exemple #4
0
    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)
Exemple #5
0
    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,
                )