From 3eb19e1c078dd900fb28a723b8e2e5ae80288cd8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20G=C3=B6bel?= <dgoebel@techfak.uni-bielefeld.de>
Date: Thu, 30 Jun 2022 16:22:35 +0200
Subject: [PATCH] Add endpoint to update a permission

#9
---
 ProxyAPI/app/api/dependencies.py              |  2 +-
 .../app/api/endpoints/bucket_permissions.py   | 54 +++++++++++++++++--
 ProxyAPI/app/crud/crud_bucket_permission.py   | 27 ++++++++++
 ProxyAPI/app/models/bucket_permission.py      | 18 +++++++
 ProxyAPI/app/schemas/bucket_permission.py     | 15 ++++--
 5 files changed, 108 insertions(+), 8 deletions(-)

diff --git a/ProxyAPI/app/api/dependencies.py b/ProxyAPI/app/api/dependencies.py
index 7da9fa5..b69ddf7 100644
--- a/ProxyAPI/app/api/dependencies.py
+++ b/ProxyAPI/app/api/dependencies.py
@@ -201,6 +201,6 @@ async def get_authorized_user_for_permission(
         raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")
     elif current_user != user and current_user.uid != bucket.owner_id:
         raise HTTPException(
-            status.HTTP_403_FORBIDDEN, detail="Only the owner or the grantee can delete a bucket permission"
+            status.HTTP_403_FORBIDDEN, detail="Only the owner or the grantee can access a bucket permission"
         )
     return user
diff --git a/ProxyAPI/app/api/endpoints/bucket_permissions.py b/ProxyAPI/app/api/endpoints/bucket_permissions.py
index f491dd4..906643f 100644
--- a/ProxyAPI/app/api/endpoints/bucket_permissions.py
+++ b/ProxyAPI/app/api/endpoints/bucket_permissions.py
@@ -12,6 +12,7 @@ from app.crud.crud_bucket_permission import CRUDBucketPermission, DuplicateError
 from app.models.bucket import Bucket as BucketDB
 from app.models.user import User as UserDB
 from app.schemas.bucket_permission import BucketPermission as PermissionSchema
+from app.schemas.bucket_permission import BucketPermissionParameters as PermissionParametersSchema
 
 router = APIRouter()
 
@@ -38,7 +39,7 @@ async def get_permission_for_bucket(
     db : sqlalchemy.ext.asyncio.AsyncSession.
         Async database session to perform query on. Dependency Injection.
     user : app.models.user.User
-        Current user. Dependency Injection.
+        User with the username in the URL. Dependency Injection.
 
     Returns
     -------
@@ -75,7 +76,7 @@ async def delete_permission_for_bucket(
     db : sqlalchemy.ext.asyncio.AsyncSession.
         Async database session to perform query on. Dependency Injection.
     user : app.models.user.User
-        Current user. Dependency Injection.
+        User with the username in the URL. Dependency Injection.
 
     Returns
     -------
@@ -174,7 +175,7 @@ async def create_permission(
         Information about the permission which should be created. HTTP Body parameter.
     db : sqlalchemy.ext.asyncio.AsyncSession.
         Async database session to perform query on. Dependency Injection.
-    current_user :  : app.models.user.User
+    current_user : app.models.user.User
         Current user. Dependency Injection.
     Returns
     -------
@@ -196,3 +197,50 @@ async def create_permission(
         )
     except KeyError:
         raise HTTPException(status.HTTP_404_NOT_FOUND, detail=f"User with username={permission.username} not found")
+
+
+@router.put(
+    "/bucket/{bucket_name}/user/{username}",
+    status_code=status.HTTP_200_OK,
+    response_model=PermissionSchema,
+    summary="Get permissions for bucket and user combination.",
+    response_model_exclude_none=True,
+)
+async def update_permission(
+    permission_parameters: PermissionParametersSchema = Body(..., description="Permission to create"),
+    user: UserDB = Depends(get_authorized_user_for_permission),
+    bucket: BucketDB = Depends(get_current_bucket),
+    db: AsyncSession = Depends(get_db),
+    current_user: UserDB = Depends(get_current_user),
+) -> PermissionSchema:
+    """
+    Update a permission for a bucket and user.
+    \f
+    Parameters
+    ----------
+    permission_parameters : app.schemas.bucket_permission.BucketPermission
+        Information about the permission which should be updated. HTTP Body parameter.
+    user : app.models.user.User
+        User with the username in the URL. Dependency Injection.
+    bucket : app.models.bucket.Bucket
+        Bucket with the name provided in the URL path. Dependency Injection.
+    db : sqlalchemy.ext.asyncio.AsyncSession.
+        Async database session to perform query on. Dependency Injection.
+    current_user : app.models.user.User
+        Current user. Dependency Injection.
+    Returns
+    -------
+    permissions : app.schemas.bucket_permission.BucketPermission
+        Updated permission.
+    """
+    if not await CRUDBucketPermission.check_permission(db, bucket.name, current_user.uid, only_own=True):
+        raise HTTPException(status.HTTP_403_FORBIDDEN, "You can only modify permissions on your own bucket")
+    bucket_permission = await CRUDBucketPermission.get(db, bucket.name, user.uid)
+
+    if bucket_permission is None:
+        raise HTTPException(
+            status.HTTP_404_NOT_FOUND,
+            detail=f"Permission for combination of bucket={bucket.name} and user={user.username} doesn't exists",
+        )
+    updated_permission = await CRUDBucketPermission.update_permission(db, bucket_permission, permission_parameters)
+    return PermissionSchema.from_db_model(updated_permission)
diff --git a/ProxyAPI/app/crud/crud_bucket_permission.py b/ProxyAPI/app/crud/crud_bucket_permission.py
index 0b5ff8b..d4f9306 100644
--- a/ProxyAPI/app/crud/crud_bucket_permission.py
+++ b/ProxyAPI/app/crud/crud_bucket_permission.py
@@ -7,6 +7,7 @@ from app.crud.crud_bucket import CRUDBucket
 from app.crud.crud_user import CRUDUser
 from app.models.bucket_permission import BucketPermission as BucketPermissionDB
 from app.schemas.bucket_permission import BucketPermission as BucketPermissionSchema
+from app.schemas.bucket_permission import BucketPermissionParameters as BucketPermissionParametersSchema
 
 
 class CRUDBucketPermission:
@@ -156,6 +157,32 @@ class CRUDBucketPermission:
         await db.delete(permission)
         await db.commit()
 
+    @staticmethod
+    async def update_permission(
+        db: AsyncSession, permission: BucketPermissionDB, new_params: BucketPermissionParametersSchema
+    ) -> BucketPermissionDB:
+        """
+        Update a permission in the database.
+
+        Parameters
+        ----------
+        db : sqlalchemy.ext.asyncio.AsyncSession
+            Async database session to perform query on.
+        permission : app.schemas.bucket_permission.BucketPermission
+            The permission to update.
+        new_params : app.schemas.bucket_permission.BucketPermissionParameters
+            The parameters which should be updated.
+
+        Returns
+        -------
+        permission : app.models.bucket_permission.BucketPermission
+            Updated permission model from the db.
+        """
+        permission.update_parameters(new_params)
+        await db.commit()
+        await db.refresh(permission)
+        return permission
+
 
 class DuplicateError(Exception):
     pass
diff --git a/ProxyAPI/app/models/bucket_permission.py b/ProxyAPI/app/models/bucket_permission.py
index b081f0b..7b26d80 100644
--- a/ProxyAPI/app/models/bucket_permission.py
+++ b/ProxyAPI/app/models/bucket_permission.py
@@ -9,8 +9,12 @@ from sqlalchemy.orm import relationship
 from app.db.base_class import Base
 
 if TYPE_CHECKING:
+    from app.schemas.bucket_permission import BucketPermissionParameters
+
     from .bucket import Bucket
     from .user import User
+else:
+    BucketPermissionParameters = object
 
 
 @unique
@@ -40,5 +44,19 @@ class BucketPermission(Base):
     grantee: "User" = relationship("User", back_populates="permissions")
     bucket: "Bucket" = relationship("Bucket", back_populates="permissions")
 
+    def update_parameters(self, params: BucketPermissionParameters) -> None:  # pragma: no cover
+        """
+        Update the object with the new parameters.
+
+        Parameters
+        ----------
+        params : app.schemas.bucket_permission.BucketPermissionParameters
+            The parameters which should be updated.
+        """
+        self.from_ = params.from_timestamp
+        self.to = params.to_timestamp
+        self.file_prefix = params.file_prefix
+        self.permissions = params.permission
+
     def __repr__(self) -> str:
         return f"BucketPermission(uid={self.user_id} bucket_name={self.bucket_name})"
diff --git a/ProxyAPI/app/schemas/bucket_permission.py b/ProxyAPI/app/schemas/bucket_permission.py
index cfc9750..12b8e37 100644
--- a/ProxyAPI/app/schemas/bucket_permission.py
+++ b/ProxyAPI/app/schemas/bucket_permission.py
@@ -6,13 +6,11 @@ from app.models.bucket_permission import BucketPermission as BucketPermissionDB
 from app.models.bucket_permission import PermissionEnum
 
 
-class BucketPermission(BaseModel):
+class BucketPermissionParameters(BaseModel):
     """
-    Schema for the bucket permissions.
+    Schema for the parameters of a bucket permission.
     """
 
-    username: str = Field(..., description="Name of User", example="baggins")
-    bucket_name: str = Field(..., description="Name of Bucket", example="test-bucket")
     from_timestamp: datetime | None = Field(
         None, description="Start date of permission", example=datetime(2022, 1, 1, 0, 0)
     )
@@ -22,6 +20,15 @@ class BucketPermission(BaseModel):
     file_prefix: str | None = Field(None, description="Prefix of subfolder", example="pseudo/sub/folder/")
     permission: PermissionEnum | str = Field(PermissionEnum.READ, description="Permission", example=PermissionEnum.READ)
 
+
+class BucketPermission(BucketPermissionParameters):
+    """
+    Schema for the bucket permissions.
+    """
+
+    username: str = Field(..., description="Name of User", example="baggins")
+    bucket_name: str = Field(..., description="Name of Bucket", example="test-bucket")
+
     @staticmethod
     def from_db_model(permission: BucketPermissionDB, username: str | None = None) -> "BucketPermission":
         """
-- 
GitLab