diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 116aac5012e51e7586f1712e19180f38b5ac43a6..e9ed9bdfc1d370ed6aab5a2c3953a6176d33d005 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,4 +1,4 @@
-image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/python:3.11-slim
+image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/python:3.12-slim
 
 variables:
   PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
@@ -51,7 +51,7 @@ integration-test-job: # Runs integration tests with the database
         MYSQL_DATABASE: "$DB_DATABASE"
         MYSQL_USER: "$DB_USER"
         MYSQL_PASSWORD: "$DB_PASSWORD"
-    - name: $CI_REGISTRY/cmg/clowm/clowm-database:v2.3
+    - name: $CI_REGISTRY/cmg/clowm/clowm-database:v3.0
       alias: upgrade-db
   script:
     - python app/check_database_connection.py
@@ -79,7 +79,7 @@ e2e-test-job: # Runs e2e tests on the API endpoints
         MYSQL_DATABASE: "$DB_DATABASE"
         MYSQL_USER: "$DB_USER"
         MYSQL_PASSWORD: "$DB_PASSWORD"
-    - name: $CI_REGISTRY/cmg/clowm/clowm-database:v2.3
+    - name: $CI_REGISTRY/cmg/clowm/clowm-database:v3.0
       alias: upgrade-db
   script:
     - python app/check_database_connection.py
@@ -132,30 +132,30 @@ lint-test-job: # Runs linters checks on code
 build-publish-dev-docker-container-job:
   stage: deploy
   image:
-    name: gcr.io/kaniko-project/executor:v1.17.0-debug
+    name: gcr.io/kaniko-project/executor:v1.20.0-debug
     entrypoint: [""]
   dependencies: []
   only:
     refs:
-      - development
+      - main
   before_script:
     - echo "{\"auths\":{\"${CI_REGISTRY}\":{\"auth\":\"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64 | tr -d '\n')\"},\"$CI_DEPENDENCY_PROXY_SERVER\":{\"auth\":\"$(printf "%s:%s" ${CI_DEPENDENCY_PROXY_USER} "${CI_DEPENDENCY_PROXY_PASSWORD}" | base64 | tr -d '\n')\"}}}" > /kaniko/.docker/config.json
   script:
     - /kaniko/executor
       --context "${CI_PROJECT_DIR}"
       --dockerfile "${CI_PROJECT_DIR}/Dockerfile"
-      --destination "${CI_REGISTRY_IMAGE}:dev-${CI_COMMIT_SHA}"
-      --destination "${CI_REGISTRY_IMAGE}:dev-latest"
+      --destination "${CI_REGISTRY_IMAGE}:main-${CI_COMMIT_SHA}"
+      --destination "${CI_REGISTRY_IMAGE}:main-latest"
     - /kaniko/executor
       --context "${CI_PROJECT_DIR}"
       --dockerfile "${CI_PROJECT_DIR}/Dockerfile-Gunicorn"
-      --destination "${CI_REGISTRY_IMAGE}:dev-${CI_COMMIT_SHA}-gunicorn"
-      --destination "${CI_REGISTRY_IMAGE}:dev-latest-gunicorn"
+      --destination "${CI_REGISTRY_IMAGE}:main-${CI_COMMIT_SHA}-gunicorn"
+      --destination "${CI_REGISTRY_IMAGE}:main-latest-gunicorn"
 
 publish-docker-container-job:
   stage: deploy
   image:
-    name: gcr.io/kaniko-project/executor:v1.17.0-debug
+    name: gcr.io/kaniko-project/executor:v1.20.0-debug
     entrypoint: [""]
   dependencies: []
   only:
diff --git a/Dockerfile b/Dockerfile
index b791fde634f0c5dcc5dbbec9286cf43a47feb1f4..c68406e15ee89081d9aa17a4465880e508901f38 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,12 +1,15 @@
-FROM python:3.11-slim
-EXPOSE 8000
+FROM python:3.12-slim
+ENV PORT=8000
+EXPOSE $PORT
 
 # dumb-init forwards the kill signal to the python process
-RUN apt-get update && apt-get -y install dumb-init curl
+RUN apt-get update && apt-get -y install dumb-init
 RUN apt-get clean
 ENTRYPOINT ["/usr/bin/dumb-init", "--"]
+STOPSIGNAL SIGINT
+RUN pip install --no-cache-dir httpx[cli] "uvicorn>=0.27.0"
 
-HEALTHCHECK --interval=30s --timeout=2s CMD curl -f http://localhost:8000/health || exit 1
+HEALTHCHECK --interval=30s --timeout=2s CMD httpx http://localhost:$PORT/health || exit 1
 
 RUN useradd -m worker
 USER worker
@@ -14,10 +17,13 @@ WORKDIR /home/worker/code
 ENV PYTHONPATH=/home/worker/code
 ENV PATH="/home/worker/.local/bin:${PATH}"
 
+COPY ./start_service_uvicorn.sh /home/worker/code/start.sh
+COPY ./scripts/prestart.sh /home/worker/code/prestart.sh
+
 COPY --chown=worker:worker requirements.txt ./requirements.txt
 
 RUN pip install --user --no-cache-dir --upgrade -r requirements.txt
 
-COPY --chown=worker:worker . .
+COPY --chown=worker:worker ./app /home/worker/code/app
 
-CMD ["./start_service.sh"]
+CMD ["./start.sh"]
diff --git a/Dockerfile-Gunicorn b/Dockerfile-Gunicorn
index 79f82be9b4fa45d10d66ed9256a5d9f97337a34a..2da77b1a0f05cf346bcbaefe0f2f4ac2b43e7e80 100644
--- a/Dockerfile-Gunicorn
+++ b/Dockerfile-Gunicorn
@@ -1,10 +1,14 @@
-FROM tiangolo/uvicorn-gunicorn-fastapi:python3.11-slim
-EXPOSE 8000
+FROM python:3.12-slim
 ENV PORT=8000
+EXPOSE $PORT
+WORKDIR /app/
+ENV PYTHONPATH=/app
 
-RUN pip install --no-cache-dir httpx[cli]
+RUN pip install --no-cache-dir httpx[cli] "gunicorn<=21.2.0" "uvicorn>=0.27.0"
+COPY ./gunicorn_conf.py /app/gunicorn_conf.py
+COPY ./start_service_gunicorn.sh /app/start.sh
 
-HEALTHCHECK --interval=30s --timeout=4s CMD httpx http://localhost:$PORT/health || exit 1
+HEALTHCHECK --interval=30s --timeout=2s CMD httpx http://localhost:$PORT/health || exit 1
 
 COPY ./scripts/prestart.sh /app/prestart.sh
 COPY ./requirements.txt /app/requirements.txt
@@ -12,3 +16,5 @@ COPY ./requirements.txt /app/requirements.txt
 RUN pip install --no-cache-dir --upgrade -r requirements.txt
 
 COPY ./app /app/app
+
+CMD ["./start.sh"]
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..d645695673349e3947e8e5ae42332d0ac3164cd7
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/README.md b/README.md
index 977c63b0e00418ecd318e5c99a750bdca203a953..7f4f57c555dfbf235a13bf4a87c0006c3ffd22a1 100644
--- a/README.md
+++ b/README.md
@@ -46,3 +46,7 @@ user-friendly manner. 👍
 | `SQLALCHEMY_VERBOSE_LOGGER` | `false`              | `<"true"&#x7c;"false">`     | Enables verbose SQL output.<br>Should be `false` in production |
 | `OPA_POLICY_PATH`           | `/clowm/authz/allow` | URL path                    | Path to the OPA Policy for Authorization                       |
 | `OTLP_GRPC_ENDPOINT`        | unset                | <hostname / IP>             | OTLP compatible endpoint to send traces via gRPC, e.g. Jaeger  |
+
+## License
+
+The API is licensed under the [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0) license. See the [License](LICENSE) file for more information.
diff --git a/app/api/dependencies.py b/app/api/dependencies.py
index eae25ba0d8e6deb20454739e4f1df5938333a50c..69be9130118e79c9c33aaa97573f4a6e1e2d8fbc 100644
--- a/app/api/dependencies.py
+++ b/app/api/dependencies.py
@@ -1,4 +1,5 @@
 from typing import TYPE_CHECKING, Annotated, AsyncGenerator, Awaitable, Callable, Dict
+from uuid import UUID
 
 from authlib.jose.errors import BadSignatureError, DecodeError, ExpiredTokenError
 from clowmdb.db.session import get_async_session
@@ -88,9 +89,9 @@ def get_decode_jwt_function() -> Callable[[str], Dict[str, str]]:  # pragma: no
 
 @start_as_current_span_async("decode_jwt", tracer=tracer)
 async def decode_bearer_token(
-    token: HTTPAuthorizationCredentials = Depends(bearer_token),
-    decode: Callable[[str], Dict[str, str]] = Depends(get_decode_jwt_function),
-    db: AsyncSession = Depends(get_db),
+    token: Annotated[HTTPAuthorizationCredentials, Depends(bearer_token)],
+    decode: Annotated[Callable[[str], Dict[str, str]], Depends(get_decode_jwt_function)],
+    db: DBSession,
 ) -> JWT:
     """
     Get the decoded JWT or reject request if it is not valid.
@@ -121,6 +122,37 @@ async def decode_bearer_token(
         raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Malformed JWT")
 
 
+async def get_current_user(token: Annotated[JWT, Depends(decode_bearer_token)], db: DBSession) -> User:
+    """
+    Get the current user from the database based on the JWT.
+
+    FastAPI Dependency Injection Function.
+
+    Parameters
+    ----------
+    token : app.schemas.security.JWT
+        The verified and decoded JWT.
+    db : sqlalchemy.ext.asyncio.AsyncSession.
+        Async database session to perform query on. Dependency Injection.
+
+    Returns
+    -------
+    user : clowmdb.models.User
+        User associated with the JWT sent with the HTTP request.
+    """
+    try:
+        uid = UUID(token.sub)
+    except ValueError:
+        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Malformed JWT")
+    user = await CRUDUser.get(db, uid)
+    if user:
+        return user
+    raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")
+
+
+CurrentUser = Annotated[User, Depends(get_current_user)]
+
+
 class AuthorizationDependency:
     """
     Class to parameterize the authorization request with the resource to perform an operation on.
@@ -137,16 +169,16 @@ class AuthorizationDependency:
 
     def __call__(
         self,
-        token: JWT = Depends(decode_bearer_token),
-        client: AsyncClient = Depends(get_httpx_client),
+        user: CurrentUser,
+        client: HTTPXClient,
     ) -> Callable[[str], Awaitable[AuthzResponse]]:
         """
         Get the function to request the authorization service with the resource, JWT and HTTP Client already injected.
 
         Parameters
         ----------
-        token : app.schemas.security.JWT
-            The verified and decoded JWT. Dependency Injection.
+        user : clowmdb.models.User
+            The current user based on the JWT. Dependency Injection.
         client : httpx.AsyncClient
             HTTP Client with an open connection. Dependency Injection.
 
@@ -157,43 +189,14 @@ class AuthorizationDependency:
         """
 
         async def authorization_wrapper(operation: str) -> AuthzResponse:
-            params = AuthzRequest(operation=operation, resource=self.resource, uid=token.sub)
+            params = AuthzRequest(operation=operation, resource=self.resource, uid=user.lifescience_id)
             return await request_authorization(request_params=params, client=client)
 
         return authorization_wrapper
 
 
-async def get_current_user(token: JWT = Depends(decode_bearer_token), db: AsyncSession = Depends(get_db)) -> User:
-    """
-    Get the current user from the database based on the JWT.
-
-    FastAPI Dependency Injection Function.
-
-    Parameters
-    ----------
-    token : app.schemas.security.JWT
-        The verified and decoded JWT.
-    db : sqlalchemy.ext.asyncio.AsyncSession.
-        Async database session to perform query on. Dependency Injection.
-
-    Returns
-    -------
-    user : clowmdb.models.User
-        User associated with the JWT sent with the HTTP request.
-    """
-    user = await CRUDUser.get(db, token.sub)
-    if user:
-        return user
-    raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")
-
-
-CurrentUser = Annotated[User, Depends(get_current_user)]
-
-
 async def get_user_by_path_uid(
-    uid: str = Path(
-        default=..., description="UID of a user", examples=["28c5353b8bb34984a8bd4169ba94c606"], max_length=64
-    ),
+    uid: UUID = Path(default=..., description="UID of a user", examples=["1d3387f3-95c0-4813-8767-2cad87faeebf"]),
     db: AsyncSession = Depends(get_db),
 ) -> User:
     """
@@ -225,8 +228,10 @@ PathUser = Annotated[User, Depends(get_user_by_path_uid)]
 
 
 async def get_current_bucket(
-    bucket_name: str = Path(..., description="Name of bucket", examples=["test-bucket"], max_length=63, min_length=3),
-    db: AsyncSession = Depends(get_db),
+    bucket_name: Annotated[
+        str, Path(..., description="Name of bucket", examples=["test-bucket"], max_length=63, min_length=3)
+    ],
+    db: DBSession,
 ) -> Bucket:
     """
     Get the Bucket from the database based on the name in the path.
diff --git a/app/api/endpoints/bucket_permissions.py b/app/api/endpoints/bucket_permissions.py
index 7ab46c1823bf622c8dcee433a5e8689c684832c1..c3a0eaa65a5e3e9aadc9e2d1cfd7a0e8bc4df18f 100644
--- a/app/api/endpoints/bucket_permissions.py
+++ b/app/api/endpoints/bucket_permissions.py
@@ -1,9 +1,10 @@
 import json
-from typing import Annotated, Any, Awaitable, Callable, List, Optional
+from typing import Annotated, Any, Awaitable, Callable, List, Union
 
 from clowmdb.models import BucketPermission
 from fastapi import APIRouter, Body, Depends, HTTPException, Query, status
 from opentelemetry import trace
+from pydantic.json_schema import SkipJsonSchema
 
 from app.api.dependencies import (
     AuthorizationDependency,
@@ -30,139 +31,149 @@ tracer = trace.get_tracer_provider().get_tracer(__name__)
 
 
 @router.get(
-    "/bucket/{bucket_name}/user/{uid}",
-    response_model=PermissionSchemaOut,
-    summary="Get permission for bucket and user combination.",
+    "",
+    response_model=List[PermissionSchemaOut],
+    summary="Get all permissions.",
     response_model_exclude_none=True,
 )
-@start_as_current_span_async("api_get_bucket_permission", tracer=tracer)
-async def get_permission_for_bucket(
-    bucket: CurrentBucket,
+@start_as_current_span_async("api_list_bucket_permission", tracer=tracer)
+async def list_permissions(
     db: DBSession,
-    current_user: CurrentUser,
     authorization: Authorization,
-    user: PathUser,
-) -> PermissionSchemaOut:
+    permission_types: Annotated[
+        Union[List[BucketPermission.Permission], SkipJsonSchema[None]],
+        Query(description="Type of Bucket Permissions to fetch"),
+    ] = None,
+    permission_status: Annotated[
+        Union[CRUDBucketPermission.PermissionStatus, SkipJsonSchema[None]],
+        Query(description="Status of Bucket Permissions to fetch"),
+    ] = None,
+) -> List[PermissionSchemaOut]:
     """
-    Get the bucket permissions for the specific combination of bucket and user.\n
-    The owner of the bucket and the grantee of the permission can view it.\n
-    Permission "bucket_permission:read" required if current user is the target or owner of the bucket permission,
-    otherwise "bucket_permission:read_any" required.
+    List all the bucket permissions in the system.\n
+    Permission `bucket_permission:list_all` required.
     \f
     Parameters
     ----------
-    bucket : clowmdb.models.Bucket
-        Bucket with the name provided in the URL path. Dependency Injection.
+    permission_types : List[clowmdb.models.BucketPermission.Permission] | None, default None
+        Type of Bucket Permissions to fetch. Query Parameter
+    permission_status : app.crud.crud_bucket_permission.CRUDBucketPermission.PermissionStatus | None, default None
+        Status of Bucket Permissions to fetch. Query Parameter.
     db : sqlalchemy.ext.asyncio.AsyncSession.
         Async database session to perform query on. Dependency Injection.
-    user : clowmdb.models.User
-        User with the uid in the URL. Dependency Injection.
     authorization : Callable[[str], Awaitable[Any]]
         Async function to ask the auth service for authorization. Dependency Injection.
-    current_user : clowmdb.models.User
-        Current user who will be the owner of the newly created bucket. Dependency Injection.
 
     Returns
     -------
-    permissions : app.schemas.bucket_permission.BucketPermissionOut
-        Permission for this bucket and user combination.
+    permissions : List[app.schemas.bucket_permission.BucketPermissionOut]
+        List of all permissions.
     """
-    trace.get_current_span().set_attributes({"bucket_name": bucket.name, "uid": user.uid})
-    rbac_operation = "read" if user == current_user or current_user.uid == bucket.owner_id else "read_any"
+    current_span = trace.get_current_span()
+    if permission_types is not None and len(permission_types) > 0:  # pragma: no cover
+        current_span.set_attribute("permission_types", [ptype.name for ptype in permission_types])
+    if permission_status is not None:  # pragma: no cover
+        current_span.set_attribute("permission_status", permission_status.name)
+    rbac_operation = "list_all"
     await authorization(rbac_operation)
-    bucket_permission = await CRUDBucketPermission.get(db, bucket.name, user.uid)
-    if bucket_permission:
-        return PermissionSchemaOut.from_db_model(
-            bucket_permission, uid=user.uid, grantee_display_name=user.display_name
-        )
-    raise HTTPException(
-        status.HTTP_404_NOT_FOUND,
-        detail=f"Permission for combination of bucket={bucket.name} and user={user.uid} doesn't exists",
+    bucket_permissions = await CRUDBucketPermission.list(
+        db, permission_types=permission_types, permission_status=permission_status
     )
+    return [PermissionSchemaOut.from_db_model(p) for p in bucket_permissions]
 
 
-@router.delete(
-    "/bucket/{bucket_name}/user/{uid}",
-    status_code=status.HTTP_204_NO_CONTENT,
-    summary="Delete a bucket permission",
+@router.post(
+    "",
+    response_model=PermissionSchemaOut,
+    status_code=status.HTTP_201_CREATED,
+    summary="Create a permission.",
+    response_model_exclude_none=True,
 )
-@start_as_current_span_async("api_delete_bucket_permission", tracer=tracer)
-async def delete_permission(
-    bucket: CurrentBucket,
+@start_as_current_span_async("api_create_bucket_permission", tracer=tracer)
+async def create_permission(
     db: DBSession,
-    s3: S3Resource,
     current_user: CurrentUser,
+    s3: S3Resource,
     authorization: Authorization,
-    user: PathUser,
-) -> None:
+    permission: Annotated[PermissionSchemaIn, Body(..., description="Permission to create")],
+) -> PermissionSchemaOut:
     """
-    Delete the bucket permissions for the specific combination of bucket and user.\n
-    The owner of the bucket and the grantee of the permission can delete it.\n
-    Permission "bucket_permission:delete" required if current user is the target or owner of the bucket permission,
-    otherwise "bucket_permission:delete_any" required.
+    Create a permission for a bucket and user.\n
+    Permission `bucket_permission:create` required.
     \f
     Parameters
     ----------
-    bucket : clowmdb.models.Bucket
-        Bucket with the name provided in the URL path. Dependency Injection.
+    permission : app.schemas.bucket_permission.BucketPermissionOut
+        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.
-    user : clowmdb.models.User
-        User with the uid in the URL. Dependency Injection.
+    current_user : clowmdb.models.User
+        Current user. Dependency Injection.
     s3 : boto3_type_annotations.s3.ServiceResource
         S3 Service to perform operations on buckets in Ceph. Dependency Injection.
     authorization : Callable[[str], Awaitable[Any]]
         Async function to ask the auth service for authorization. Dependency Injection.
-    current_user : clowmdb.models.User
-        Current user who will be the owner of the newly created bucket. Dependency Injection.
 
     Returns
     -------
     permissions : app.schemas.bucket_permission.BucketPermissionOut
-        Permission for this bucket and user combination.
+        Newly created permission.
     """
-    trace.get_current_span().set_attributes({"bucket_name": bucket.name, "uid": user.uid})
-    rbac_operation = "delete" if user == current_user or current_user.uid == bucket.owner_id else "delete_any"
-    await authorization(rbac_operation)
-    bucket_permission = await CRUDBucketPermission.get(db, bucket.name, user.uid)
-    if bucket_permission is None:
+    current_span = trace.get_current_span()
+    current_span.set_attributes({"uid": str(permission.uid), "bucket_name": permission.bucket_name})
+    await authorization("create")
+    target_bucket = await get_current_bucket(permission.bucket_name, db=db)  # Check if the target bucket exists
+    if target_bucket.owner_id != current_user.uid:
+        raise HTTPException(status.HTTP_403_FORBIDDEN, detail="Action forbidden.")
+    if target_bucket.owner_constraint is not None:
+        raise HTTPException(status.HTTP_403_FORBIDDEN, detail="Initial Buckets can be target of Bucket Permissions.")
+    await get_user_by_path_uid(permission.uid, db)  # Check if target user exists
+    try:
+        permission_db = await CRUDBucketPermission.create(db, permission)
+    except ValueError as e:
+        current_span.record_exception(e)
         raise HTTPException(
-            status.HTTP_404_NOT_FOUND,
-            detail=f"Permission for combination of bucket={bucket.name} and user={user.uid} doesn't exists",
+            status.HTTP_400_BAD_REQUEST, detail="The owner of the bucket can't get any more permissions"
         )
-    await CRUDBucketPermission.delete(db, bucket_name=bucket_permission.bucket_name, uid=bucket_permission.user_id)
-    bucket_permission_schema = PermissionSchemaOut.from_db_model(bucket_permission, user.uid, user.display_name)
-    s3_policy = get_s3_bucket_policy(s3, bucket_name=bucket_permission_schema.bucket_name)
-    policy = json.loads(s3_policy.policy)
-    policy["Statement"] = [
-        stmt for stmt in policy["Statement"] if stmt["Sid"] != bucket_permission_schema.to_hash(user.uid)
-    ]
-    put_s3_bucket_policy(s3, bucket_name=bucket_permission_schema.bucket_name, policy=json.dumps(policy))
+    except DuplicateError as e:
+        current_span.record_exception(e)
+        raise HTTPException(
+            status.HTTP_400_BAD_REQUEST,
+            detail=f"Permission for combination of bucket={permission.bucket_name} and user={permission.uid} already exists",  # noqa:E501
+        )
+    s3_policy = get_s3_bucket_policy(s3, bucket_name=permission.bucket_name)
+    json_policy = json.loads(s3_policy.policy)
+    json_policy["Statement"] += permission.map_to_bucket_policy_statement(permission_db.uid)
+    put_s3_bucket_policy(s3, bucket_name=permission.bucket_name, policy=json.dumps(json_policy))
+
+    return PermissionSchemaOut.from_db_model(permission_db)
 
 
 @router.get(
-    "/bucket/{bucket_name}",
+    "/user/{uid}",
     response_model=List[PermissionSchemaOut],
-    summary="Get all permissions for a bucket.",
+    summary="Get all permissions for a user.",
     response_model_exclude_none=True,
 )
-@start_as_current_span_async("api_list_bucket_permission_for_bucket", tracer=tracer)
-async def list_permissions_per_bucket(
-    bucket: CurrentBucket,
+@start_as_current_span_async("api_list_bucket_permission_for_user", tracer=tracer)
+async def list_permissions_per_user(
     db: DBSession,
     current_user: CurrentUser,
     authorization: Authorization,
-    permission_types: Optional[List[BucketPermission.Permission]] = Query(
-        None, description="Type of Bucket Permissions to fetch"
-    ),
-    permission_status: Optional[CRUDBucketPermission.PermissionStatus] = Query(
-        None, description="Status of Bucket Permissions to fetch"
-    ),
+    user: PathUser,
+    permission_types: Annotated[
+        Union[List[BucketPermission.Permission], SkipJsonSchema[None]],
+        Query(description="Type of Bucket Permissions to fetch"),
+    ] = None,
+    permission_status: Annotated[
+        Union[CRUDBucketPermission.PermissionStatus, SkipJsonSchema[None]],
+        Query(description="Status of Bucket Permissions to fetch"),
+    ] = None,
 ) -> List[PermissionSchemaOut]:
     """
-    List all the bucket permissions for the given bucket.\n
-    Permission "bucket_permission:read" required if current user is owner of the bucket,
-    otherwise "bucket_permission:read_any" required.
+    List all the bucket permissions for the given user.\n
+    Permission `bucket_permission:list_user` required if current user is the target the bucket permission,
+    otherwise `bucket_permission:list_all` required.
     \f
     Parameters
     ----------
@@ -170,8 +181,8 @@ async def list_permissions_per_bucket(
         Type of Bucket Permissions to fetch. Query Parameter
     permission_status : app.crud.crud_bucket_permission.CRUDBucketPermission.PermissionStatus | None, default None
         Status of Bucket Permissions to fetch. Query Parameter.
-    bucket : clowmdb.models.Bucket
-        Bucket with the name provided in the URL path. Dependency Injection.
+    user : clowmdb.models.User
+        User with given uid. Dependency Injection.
     db : sqlalchemy.ext.asyncio.AsyncSession.
         Async database session to perform query on. Dependency Injection.
     authorization : Callable[[str], Awaitable[Any]]
@@ -182,45 +193,47 @@ async def list_permissions_per_bucket(
     Returns
     -------
     permissions : List[app.schemas.bucket_permission.BucketPermissionOut]
-        List of all permissions for this bucket.
+        List of all permissions for this user.
     """
     current_span = trace.get_current_span()
-    current_span.set_attribute("bucket_name", bucket.name)
+    current_span.set_attribute("uid", user.uid)
     if permission_types is not None and len(permission_types) > 0:  # pragma: no cover
         current_span.set_attribute("permission_types", [ptype.name for ptype in permission_types])
     if permission_status is not None:  # pragma: no cover
         current_span.set_attribute("permission_status", permission_status.name)
-    rbac_operation = "list_bucket" if bucket.owner_id == current_user.uid else "list_all"
+    rbac_operation = "list_user" if user == current_user else "list_all"
     await authorization(rbac_operation)
     bucket_permissions = await CRUDBucketPermission.list(
-        db, bucket_name=bucket.name, permission_types=permission_types, permission_status=permission_status
+        db, uid=user.uid, permission_types=permission_types, permission_status=permission_status
     )
     return [PermissionSchemaOut.from_db_model(p) for p in bucket_permissions]
 
 
 @router.get(
-    "/user/{uid}",
+    "/bucket/{bucket_name}",
     response_model=List[PermissionSchemaOut],
-    summary="Get all permissions for a user.",
+    summary="Get all permissions for a bucket.",
     response_model_exclude_none=True,
 )
-@start_as_current_span_async("api_list_bucket_permission_for_user", tracer=tracer)
-async def list_permissions_per_user(
+@start_as_current_span_async("api_list_bucket_permission_for_bucket", tracer=tracer)
+async def list_permissions_per_bucket(
+    bucket: CurrentBucket,
     db: DBSession,
     current_user: CurrentUser,
     authorization: Authorization,
-    user: PathUser,
-    permission_types: Optional[List[BucketPermission.Permission]] = Query(
-        None, description="Type of Bucket Permissions to fetch"
-    ),
-    permission_status: Optional[CRUDBucketPermission.PermissionStatus] = Query(
-        None, description="Status of Bucket Permissions to fetch"
-    ),
+    permission_types: Annotated[
+        Union[List[BucketPermission.Permission], SkipJsonSchema[None]],
+        Query(description="Type of Bucket Permissions to fetch"),
+    ] = None,
+    permission_status: Annotated[
+        Union[CRUDBucketPermission.PermissionStatus, SkipJsonSchema[None]],
+        Query(description="Status of Bucket Permissions to fetch"),
+    ] = None,
 ) -> List[PermissionSchemaOut]:
     """
-    List all the bucket permissions for the given user.\n
-    Permission "bucket_permission:read" required if current user is the target the bucket permission,
-    otherwise "bucket_permission:read_any" required.
+    List all the bucket permissions for the given bucket.\n
+    Permission `bucket_permission:list_bucket` required if current user is owner of the bucket,
+    otherwise `bucket_permission:list_all` required.
     \f
     Parameters
     ----------
@@ -228,8 +241,8 @@ async def list_permissions_per_user(
         Type of Bucket Permissions to fetch. Query Parameter
     permission_status : app.crud.crud_bucket_permission.CRUDBucketPermission.PermissionStatus | None, default None
         Status of Bucket Permissions to fetch. Query Parameter.
-    user : clowmdb.models.User
-        User with given uid. Dependency Injection.
+    bucket : clowmdb.models.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.
     authorization : Callable[[str], Awaitable[Any]]
@@ -240,90 +253,129 @@ async def list_permissions_per_user(
     Returns
     -------
     permissions : List[app.schemas.bucket_permission.BucketPermissionOut]
-        List of all permissions for this user.
+        List of all permissions for this bucket.
     """
     current_span = trace.get_current_span()
-    current_span.set_attribute("uid", user.uid)
+    current_span.set_attribute("bucket_name", bucket.name)
     if permission_types is not None and len(permission_types) > 0:  # pragma: no cover
         current_span.set_attribute("permission_types", [ptype.name for ptype in permission_types])
     if permission_status is not None:  # pragma: no cover
         current_span.set_attribute("permission_status", permission_status.name)
-    rbac_operation = "list_user" if user == current_user else "list_all"
+    rbac_operation = "list_bucket" if bucket.owner_id == current_user.uid else "list_all"
     await authorization(rbac_operation)
     bucket_permissions = await CRUDBucketPermission.list(
-        db, uid=user.uid, permission_types=permission_types, permission_status=permission_status
+        db, bucket_name=bucket.name, permission_types=permission_types, permission_status=permission_status
     )
-    return [
-        PermissionSchemaOut.from_db_model(p, uid=user.uid, grantee_display_name=user.display_name)
-        for p in bucket_permissions
-    ]
+    return [PermissionSchemaOut.from_db_model(p) for p in bucket_permissions]
 
 
-@router.post(
-    "",
+@router.get(
+    "/bucket/{bucket_name}/user/{uid}",
     response_model=PermissionSchemaOut,
-    status_code=status.HTTP_201_CREATED,
-    summary="Create a permission.",
+    summary="Get permission for bucket and user combination.",
     response_model_exclude_none=True,
 )
-@start_as_current_span_async("api_create_bucket_permission", tracer=tracer)
-async def create_permission(
+@start_as_current_span_async("api_get_bucket_permission", tracer=tracer)
+async def get_permission_for_bucket(
+    bucket: CurrentBucket,
     db: DBSession,
     current_user: CurrentUser,
-    s3: S3Resource,
     authorization: Authorization,
-    permission: PermissionSchemaIn = Body(..., description="Permission to create"),
+    user: PathUser,
 ) -> PermissionSchemaOut:
     """
-    Create a permission for a bucket and user.\n
-    Permission "bucket_permission:create" required.
+    Get the bucket permissions for the specific combination of bucket and user.\n
+    The owner of the bucket and the grantee of the permission can view it.\n
+    Permission `bucket_permission:read` required if current user is the target or owner of the bucket permission,
+    otherwise `bucket_permission:read_any` required.
     \f
     Parameters
     ----------
-    permission : app.schemas.bucket_permission.BucketPermissionOut
-        Information about the permission which should be created. HTTP Body parameter.
+    bucket : clowmdb.models.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.
+    user : clowmdb.models.User
+        User with the uid in the URL. Dependency Injection.
+    authorization : Callable[[str], Awaitable[Any]]
+        Async function to ask the auth service for authorization. Dependency Injection.
     current_user : clowmdb.models.User
-        Current user. Dependency Injection.
+        Current user who will be the owner of the newly created bucket. Dependency Injection.
+
+    Returns
+    -------
+    permissions : app.schemas.bucket_permission.BucketPermissionOut
+        Permission for this bucket and user combination.
+    """
+    trace.get_current_span().set_attributes({"bucket_name": bucket.name, "uid": str(user.uid)})
+    rbac_operation = "read" if user == current_user or current_user.uid == bucket.owner_id else "read_any"
+    await authorization(rbac_operation)
+    bucket_permission = await CRUDBucketPermission.get(db, bucket.name, user.uid)
+    if bucket_permission:
+        return PermissionSchemaOut.from_db_model(bucket_permission)
+    raise HTTPException(
+        status.HTTP_404_NOT_FOUND,
+        detail=f"Permission for combination of bucket={bucket.name} and user={user.uid} doesn't exists",
+    )
+
+
+@router.delete(
+    "/bucket/{bucket_name}/user/{uid}",
+    status_code=status.HTTP_204_NO_CONTENT,
+    summary="Delete a bucket permission",
+)
+@start_as_current_span_async("api_delete_bucket_permission", tracer=tracer)
+async def delete_permission(
+    bucket: CurrentBucket,
+    db: DBSession,
+    s3: S3Resource,
+    current_user: CurrentUser,
+    authorization: Authorization,
+    user: PathUser,
+) -> None:
+    """
+    Delete the bucket permissions for the specific combination of bucket and user.\n
+    The owner of the bucket and the grantee of the permission can delete it.\n
+    Permission `bucket_permission:delete` required if current user is the target or owner of the bucket permission,
+    otherwise `bucket_permission:delete_any` required.
+    \f
+    Parameters
+    ----------
+    bucket : clowmdb.models.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.
+    user : clowmdb.models.User
+        User with the uid in the URL. Dependency Injection.
     s3 : boto3_type_annotations.s3.ServiceResource
         S3 Service to perform operations on buckets in Ceph. Dependency Injection.
     authorization : Callable[[str], Awaitable[Any]]
         Async function to ask the auth service for authorization. Dependency Injection.
+    current_user : clowmdb.models.User
+        Current user who will be the owner of the newly created bucket. Dependency Injection.
 
     Returns
     -------
     permissions : app.schemas.bucket_permission.BucketPermissionOut
-        Newly created permission.
+        Permission for this bucket and user combination.
     """
-    current_span = trace.get_current_span()
-    current_span.set_attributes({"uid": permission.uid, "bucket_name": permission.bucket_name})
-    await authorization("create")
-    target_bucket = await get_current_bucket(permission.bucket_name, db=db)  # Check if the target bucket exists
-    if target_bucket.owner_id != current_user.uid:
-        raise HTTPException(status.HTTP_403_FORBIDDEN, detail="Action forbidden.")
-    if target_bucket.owner_constraint is not None:
-        raise HTTPException(status.HTTP_403_FORBIDDEN, detail="Initial Buckets can be target of Bucket Permissions.")
-    grantee = await get_user_by_path_uid(permission.uid, db)  # Check if target user exists
-    try:
-        permission_db = await CRUDBucketPermission.create(db, permission)
-    except ValueError as e:
-        current_span.record_exception(e)
-        raise HTTPException(
-            status.HTTP_400_BAD_REQUEST, detail="The owner of the bucket can't get any more permissions"
-        )
-    except DuplicateError as e:
-        current_span.record_exception(e)
+    trace.get_current_span().set_attributes({"bucket_name": bucket.name, "uid": str(user.uid)})
+    rbac_operation = "delete" if user == current_user or current_user.uid == bucket.owner_id else "delete_any"
+    await authorization(rbac_operation)
+    bucket_permission = await CRUDBucketPermission.get(db, bucket.name, user.uid)
+    if bucket_permission is None:
         raise HTTPException(
-            status.HTTP_400_BAD_REQUEST,
-            detail=f"Permission for combination of bucket={permission.bucket_name} and user={permission.uid} already exists",  # noqa:E501
+            status.HTTP_404_NOT_FOUND,
+            detail=f"Permission for combination of bucket={bucket.name} and user={str(user.uid)} doesn't exists",
         )
-    s3_policy = get_s3_bucket_policy(s3, bucket_name=permission.bucket_name)
-    json_policy = json.loads(s3_policy.policy)
-    json_policy["Statement"] += permission.map_to_bucket_policy_statement(permission_db.user_id)
-    put_s3_bucket_policy(s3, bucket_name=permission.bucket_name, policy=json.dumps(json_policy))
-
-    return PermissionSchemaOut.from_db_model(permission_db, uid=grantee.uid, grantee_display_name=grantee.display_name)
+    await CRUDBucketPermission.delete(db, bucket_name=bucket_permission.bucket_name, uid=bucket_permission.uid)
+    bucket_permission_schema = PermissionSchemaOut.from_db_model(bucket_permission)
+    s3_policy = get_s3_bucket_policy(s3, bucket_name=bucket_permission_schema.bucket_name)
+    policy = json.loads(s3_policy.policy)
+    policy["Statement"] = [
+        stmt for stmt in policy["Statement"] if stmt["Sid"] != bucket_permission_schema.to_hash(user.uid)
+    ]
+    put_s3_bucket_policy(s3, bucket_name=bucket_permission_schema.bucket_name, policy=json.dumps(policy))
 
 
 @router.put(
@@ -341,12 +393,11 @@ async def update_permission(
     s3: S3Resource,
     authorization: Authorization,
     user: PathUser,
-    permission_parameters: PermissionParametersSchema = Body(..., description="Permission to create"),
+    permission_parameters: Annotated[PermissionParametersSchema, Body(..., description="Permission to create")],
 ) -> PermissionSchemaOut:
     """
     Update a permission for a bucket and user.\n
-    Permission "bucket_permission:read" required if current user is the target the bucket permission,
-    otherwise "bucket_permission:update" required.
+    Permission `bucket_permission:update` required.
     \f
     Parameters
     ----------
@@ -370,7 +421,7 @@ async def update_permission(
     permissions : app.schemas.bucket_permission.BucketPermissionOut
         Updated permission.
     """
-    trace.get_current_span().set_attributes({"uid": user.uid, "bucket_name": bucket.name})
+    trace.get_current_span().set_attributes({"uid": str(user.uid), "bucket_name": bucket.name})
     await authorization("update")
     if bucket.owner_id != current_user.uid:
         raise HTTPException(status.HTTP_403_FORBIDDEN, "Action forbidden")
@@ -388,6 +439,6 @@ async def update_permission(
     policy["Statement"] = [
         stmt for stmt in policy["Statement"] if stmt["Sid"] != updated_permission_schema.to_hash(user.uid)
     ]
-    policy["Statement"] += updated_permission_schema.map_to_bucket_policy_statement(updated_permission.user_id)
+    policy["Statement"] += updated_permission_schema.map_to_bucket_policy_statement(updated_permission.uid)
     put_s3_bucket_policy(s3, bucket_name=bucket.name, policy=json.dumps(policy))
     return updated_permission_schema
diff --git a/app/api/endpoints/buckets.py b/app/api/endpoints/buckets.py
index eaad0ef07c1eb9e2f4e7d19432a3e7b4849961fc..6a86650efa43b1b5670a4567140820e7de760033 100644
--- a/app/api/endpoints/buckets.py
+++ b/app/api/endpoints/buckets.py
@@ -1,11 +1,13 @@
 import json
 from functools import reduce
-from typing import TYPE_CHECKING, Annotated, Any, Awaitable, Callable, List, Optional
+from typing import TYPE_CHECKING, Annotated, Any, Awaitable, Callable, List, Union
+from uuid import UUID
 
 from botocore.exceptions import ClientError
 from clowmdb.models import Bucket
 from fastapi import APIRouter, Depends, HTTPException, Query, status
 from opentelemetry import trace
+from pydantic.json_schema import SkipJsonSchema
 
 from app.api.dependencies import AuthorizationDependency, CurrentBucket, CurrentUser, DBSession, S3Resource
 from app.ceph.s3 import get_s3_bucket_objects, put_s3_bucket_policy
@@ -60,21 +62,25 @@ async def list_buckets(
     s3: S3Resource,
     current_user: CurrentUser,
     authorization: Authorization,
-    user: Optional[str] = Query(
-        None,
-        description="UID of the user for whom to fetch the buckets for. Permission 'bucket:read_any' required if current user is not the target.",  # noqa:E501
-    ),
-    bucket_type: CRUDBucket.BucketType = Query(
-        CRUDBucket.BucketType.ALL, description="Type of the bucket to get. Ignored when `user` parameter not set"
-    ),
+    owner_id: Annotated[
+        Union[UUID, SkipJsonSchema[None]],
+        Query(
+            description="UID of the user for whom to fetch the buckets for. Permission 'bucket:read_any' required if current user is not the target.",  # noqa:E501
+            examples=["1d3387f3-95c0-4813-8767-2cad87faeebf"],
+        ),
+    ] = None,
+    bucket_type: Annotated[
+        CRUDBucket.BucketType, Query(description="Type of the bucket to get. Ignored when `user` parameter not set")
+    ] = CRUDBucket.BucketType.ALL,
 ) -> List[BucketOutSchema]:
     """
     List all the buckets in the system or of the desired user where the user has READ permissions for.\n
-    Permission "bucket:read" required.
+    Permission `bucket:list` required if the current user is the owner of the bucket,
+    otherwise `bucket:list_all` required.
     \f
     Parameters
     ----------
-    user : clowmdb.models.User
+    uid : uuid.UUID
         User for which to retrieve the buckets. Dependency Injection.
     bucket_type : app.crud.crud_bucket.CRUDBucket.BucketType, default BucketType.ALL
         Type of the bucket to get. Query Parameter.
@@ -92,14 +98,14 @@ async def list_buckets(
         All the buckets for which the user has READ permissions.
     """
     current_span = trace.get_current_span()
-    if user is not None:  # pragma: no cover
-        current_span.set_attribute("uid", user)
+    if owner_id is not None:  # pragma: no cover
+        current_span.set_attribute("uid", str(owner_id))
     current_span.set_attribute("bucket_type", bucket_type.name)
-    await authorization("list_all" if user is None or current_user.uid != user else "list")
-    if user is None:
+    await authorization("list_all" if current_user.uid != owner_id else "list")
+    if owner_id is None:
         buckets = await CRUDBucket.get_all(db)
     else:
-        buckets = await CRUDBucket.get_for_user(db, user, bucket_type)
+        buckets = await CRUDBucket.get_for_user(db, owner_id, bucket_type)
 
     def map_buckets(bucket: Bucket) -> BucketOutSchema:
         # define function to fetch objects only one time for each bucket
@@ -109,7 +115,7 @@ async def list_buckets(
                 "description": bucket.description,
                 "name": bucket.name,
                 "created_at": bucket.created_at,
-                "owner": bucket.owner_id,
+                "owner_id": bucket.owner_id,
                 "num_objects": sum(1 for obj in objects if not obj.key.endswith("/")),
                 "size": reduce(lambda x, y: x + y.size, objects, 0),
                 "owner_constraint": bucket.owner_constraint,
@@ -138,7 +144,7 @@ async def create_bucket(
     The name of the bucket has some constraints.
     For more information see the
     [Ceph documentation](https://docs.ceph.com/en/quincy/radosgw/s3/bucketops/#constraints)\n
-    Permission "bucket:create" required.
+    Permission `bucket:create` required.
     \f
     Parameters
     ----------
@@ -204,7 +210,7 @@ async def create_bucket(
             "description": db_bucket.description,
             "name": db_bucket.name,
             "created_at": db_bucket.created_at,
-            "owner": db_bucket.owner.uid,
+            "owner_id": db_bucket.owner.uid,
             "num_objects": 0,
             "size": 0,
         }
@@ -222,8 +228,8 @@ async def get_bucket(
 ) -> BucketOutSchema:
     """
     Get a bucket by its name if the current user has READ permissions for the bucket.\n
-    Permission "bucket:read" required if the current user is the owner of the bucket,
-    otherwise "bucket:read_any" required.
+    Permission `bucket:read` required if the current user is the owner of the bucket,
+    otherwise `bucket:read_any` required.
     \f
     Parameters
     ----------
@@ -256,7 +262,7 @@ async def get_bucket(
             "description": bucket.description,
             "name": bucket.name,
             "created_at": bucket.created_at,
-            "owner": bucket.owner_id,
+            "owner_id": bucket.owner_id,
             "num_objects": sum(1 for obj in objects if not obj.key.endswith("/")),
             "size": reduce(lambda x, y: x + y.size, objects, 0),
             "owner_constraint": bucket.owner_constraint,
@@ -272,12 +278,12 @@ async def delete_bucket(
     current_user: CurrentUser,
     authorization: Authorization,
     s3: S3Resource,
-    force_delete: bool = Query(False, description="Delete even non-empty bucket"),
+    force_delete: Annotated[bool, Query(description="Delete even non-empty bucket")] = False,
 ) -> None:
     """
     Delete a bucket by its name. Only the owner of the bucket can delete the bucket.\n
-    Permission "bucket:delete" required if the current user is the owner of the bucket,
-    otherwise "bucket:delete_any" required.
+    Permission `bucket:delete` required if the current user is the owner of the bucket,
+    otherwise `bucket:delete_any` required.
     \f
     Parameters
     ----------
diff --git a/app/api/endpoints/s3key.py b/app/api/endpoints/s3key.py
index 03b9cbff02a6b2255e9453155bb0dd9310da5bad..0ca0b01a8e5429f56210fe6e41c61a6607609a97 100644
--- a/app/api/endpoints/s3key.py
+++ b/app/api/endpoints/s3key.py
@@ -7,7 +7,7 @@ from rgwadmin.exceptions import RGWAdminException
 from app.api.dependencies import AuthorizationDependency, CurrentUser, PathUser, RGWAdminResource
 from app.ceph.rgw import get_s3_keys
 from app.otlp import start_as_current_span_async
-from app.schemas.user import S3Key
+from app.schemas.s3key import S3Key
 
 router = APIRouter(prefix="/users/{uid}/keys", tags=["S3Key"])
 s3key_authorization = AuthorizationDependency(resource="s3_key")
@@ -38,7 +38,7 @@ async def get_user_keys(
 ) -> List[S3Key]:
     """
     Get all the S3 Access keys for a specific user.\n
-    Permission "s3_key:list" required.
+    Permission `s3_key:list` required.
     \f
     Parameters
     ----------
@@ -56,7 +56,7 @@ async def get_user_keys(
     keys : List(app.schemas.user.S3Key)
         All S3 keys from the user.
     """
-    trace.get_current_span().set_attribute("uid", user.uid)
+    trace.get_current_span().set_attribute("uid", str(user.uid))
     if current_user.uid != user.uid:
         raise HTTPException(status.HTTP_403_FORBIDDEN, detail="Action forbidden.")
     await authorization("list")
@@ -78,7 +78,7 @@ async def create_user_key(
 ) -> S3Key:
     """
     Create a S3 Access key for a specific user.\n
-    Permission "s3_key:create" required.
+    Permission `s3_key:create` required.
     \f
     Parameters
     ----------
@@ -93,23 +93,21 @@ async def create_user_key(
 
     Returns
     -------
-    key : app.schemas.user.S3Key
+    key : app.schemas.s3key.S3Key
         Newly created S3 key.
     """
-    trace.get_current_span().set_attribute("uid", user.uid)
+    trace.get_current_span().set_attribute("uid", str(user.uid))
     if current_user.uid != user.uid:
         raise HTTPException(status.HTTP_403_FORBIDDEN, detail="Action forbidden.")
     await authorization("create")
-    with tracer.start_as_current_span("rgw_list_keys") as span:
-        span.set_attribute("uid", user.uid)
+    with tracer.start_as_current_span("rgw_list_keys", attributes={"uid": str(user.uid)}):
         before_keys_set = set(map(lambda key: key.access_key, get_s3_keys(rgw, user.uid)))
-    with tracer.start_as_current_span("rgw_create_key") as span:
-        span.set_attribute("uid", user.uid)
+    with tracer.start_as_current_span("rgw_create_key", attributes={"uid": str(user.uid)}):
         # create keys returns all keys for a user including the new one
         after_keys = rgw.create_key(uid=user.uid, key_type="s3", generate_key=True)
     new_key_id = list(set(map(lambda key: key["access_key"], after_keys)) - before_keys_set)[0]  # find ID of the key
     index = [key["access_key"] for key in after_keys].index(new_key_id)  # find new key by ID
-    return S3Key(**after_keys[index])
+    return S3Key(uid=user.uid, **after_keys[index])
 
 
 @router.get(
@@ -127,7 +125,7 @@ async def get_user_key(
 ) -> S3Key:
     """
     Get a specific S3 Access Key for a specific user.\n
-    Permission "s3_key:read" required.
+    Permission `s3_key:read` required.
     \f
     Parameters
     ----------
@@ -144,10 +142,10 @@ async def get_user_key(
 
     Returns
     -------
-    key : app.schemas.user.S3Key
+    key : app.schemas.s3key.S3Key
         Requested S3 key.
     """
-    trace.get_current_span().set_attribute("uid", user.uid)
+    trace.get_current_span().set_attribute("uid", str(user.uid))
     if current_user.uid != user.uid:
         raise HTTPException(status.HTTP_403_FORBIDDEN, detail="Action forbidden.")
     await authorization("read")
@@ -174,7 +172,7 @@ async def delete_user_key(
 ) -> None:
     """
     Delete a specific S3 Access key for a specific user.\n
-    Permission "s3_key:delete" required if the current user is the target, otherwise "s3_key:delete_any" required.
+    Permission `s3_key:delete` required if the current user is the target, otherwise `s3_key:delete_any` required.
     \f
     Parameters
     ----------
@@ -189,13 +187,12 @@ async def delete_user_key(
     current_user : clowmdb.models.User
         Current user who will be the owner of the newly created bucket. Dependency Injection.
     """
-    trace.get_current_span().set_attribute("uid", user.uid)
+    trace.get_current_span().set_attribute("uid", str(user.uid))
     await authorization("delete" if current_user.uid == user.uid else "delete_any")
     if len(get_s3_keys(rgw, user.uid)) <= 1:
         raise HTTPException(status.HTTP_400_BAD_REQUEST, detail="It's not possible to delete the last key")
     try:
-        with tracer.start_as_current_span("rgw_delete_key") as span:
-            span.set_attribute("uid", user.uid)
-            rgw.remove_key(access_key=access_id, uid=user.uid)
+        with tracer.start_as_current_span("rgw_delete_key", attributes={"uid": str(user.uid)}):
+            rgw.remove_key(access_key=access_id, uid=str(user.uid))
     except RGWAdminException:
         raise HTTPException(status.HTTP_404_NOT_FOUND, detail="Key not found")
diff --git a/app/ceph/rgw.py b/app/ceph/rgw.py
index 417fae6ec423b0621fc87e9f6918496ad63ac785..a93c7638a5c5d3cf4d00a20c476a59b7bcda12fd 100644
--- a/app/ceph/rgw.py
+++ b/app/ceph/rgw.py
@@ -1,10 +1,11 @@
 from typing import List
+from uuid import UUID
 
 from opentelemetry import trace
 from rgwadmin import RGWAdmin
 
 from app.core.config import settings
-from app.schemas.user import S3Key
+from app.schemas.s3key import S3Key
 
 tracer = trace.get_tracer_provider().get_tracer(__name__)
 
@@ -16,7 +17,6 @@ rgw = RGWAdmin(
 )
 
 
-def get_s3_keys(rgw: RGWAdmin, uid: str) -> List[S3Key]:
-    with tracer.start_as_current_span("s3_get_user_keys") as span:
-        span.set_attribute("uid", uid)
-        return [S3Key(**key) for key in rgw.get_user(uid=uid, stats=False)["keys"]]
+def get_s3_keys(rgw: RGWAdmin, uid: UUID) -> List[S3Key]:
+    with tracer.start_as_current_span("s3_get_user_keys", attributes={"uid": str(uid)}):
+        return [S3Key(uid=uid, **key) for key in rgw.get_user(uid=str(uid), stats=False)["keys"]]
diff --git a/app/core/security.py b/app/core/security.py
index 40e1febb83fc4308892c2ec6a038e04c10858640..a497a343e28b864620742411ef54a792de315c26 100644
--- a/app/core/security.py
+++ b/app/core/security.py
@@ -66,7 +66,7 @@ async def request_authorization(request_params: AuthzRequest, client: AsyncClien
         f"{settings.OPA_URI}v1/data{settings.OPA_POLICY_PATH}", json={"input": request_params.model_dump()}
     )
 
-    parsed_response = AuthzResponse(**response.json())
+    parsed_response = AuthzResponse.model_validate(response.json())
     current_span.set_attribute("decision_id", str(parsed_response.decision_id))
     if not parsed_response.result:  # pragma: no cover
         raise HTTPException(
diff --git a/app/crud/crud_bucket.py b/app/crud/crud_bucket.py
index 875f304ca832d4fa4f463d7292e7b03f49b9b0ce..5327ded8a85bedefe70519b35931ff5ec667fb70 100644
--- a/app/crud/crud_bucket.py
+++ b/app/crud/crud_bucket.py
@@ -1,5 +1,6 @@
 from enum import Enum, unique
 from typing import Optional, Sequence
+from uuid import UUID
 
 from clowmdb.models import Bucket
 from clowmdb.models import BucketPermission as BucketPermissionDB
@@ -8,7 +9,6 @@ from sqlalchemy import delete, func, or_, select
 from sqlalchemy.ext.asyncio import AsyncSession
 
 from app.crud import DuplicateError
-from app.otlp import start_as_current_span_async
 from app.schemas.bucket import BucketIn as BucketInSchema
 
 tracer = trace.get_tracer_provider().get_tracer(__name__)
@@ -30,7 +30,6 @@ class CRUDBucket:
         PERMISSION: str = "PERMISSION"
 
     @staticmethod
-    @start_as_current_span_async("db_get_bucket", tracer=tracer)
     async def get(db: AsyncSession, bucket_name: str) -> Optional[Bucket]:
         """
         Get a bucket by its name.
@@ -48,19 +47,19 @@ class CRUDBucket:
             Returns the bucket if it exists, None otherwise.
         """
         stmt = select(Bucket).where(Bucket.name == bucket_name)
-        trace.get_current_span().set_attributes({"bucket_name": bucket_name, "sql_query": str(stmt)})
-        return await db.scalar(stmt)
+        with tracer.start_as_current_span(
+            "db_get_bucket", attributes={"bucket_name": bucket_name, "sql_query": str(stmt)}
+        ):
+            return await db.scalar(stmt)
 
     @staticmethod
-    @start_as_current_span_async("db_list_all_buckets", tracer=tracer)
     async def get_all(db: AsyncSession) -> Sequence[Bucket]:
         stmt = select(Bucket)
-        trace.get_current_span().set_attribute("sql_query", str(stmt))
-        return (await db.scalars(stmt)).all()
+        with tracer.start_as_current_span("db_list_all_buckets", attributes={"sql_query": str(stmt)}):
+            return (await db.scalars(stmt)).all()
 
     @staticmethod
-    @start_as_current_span_async("db_list_buckets_for_user", tracer=tracer)
-    async def get_for_user(db: AsyncSession, uid: str, bucket_type: BucketType = BucketType.ALL) -> Sequence[Bucket]:
+    async def get_for_user(db: AsyncSession, uid: UUID, bucket_type: BucketType = BucketType.ALL) -> Sequence[Bucket]:
         """
         Get all buckets for a user. Depending on the `bucket_type`, the user is either owner of the bucket or has
         permission for the bucket
@@ -114,8 +113,8 @@ class CRUDBucket:
         if bucket_type == CRUDBucket.BucketType.ALL:
             stmt = stmt.where(
                 or_(
-                    Bucket.owner_id == uid,
-                    Bucket.permissions.any(BucketPermissionDB.user_id == uid)
+                    Bucket._owner_id == uid.bytes,
+                    Bucket.permissions.any(BucketPermissionDB._uid == uid.bytes)
                     .where(
                         or_(
                             func.UNIX_TIMESTAMP() >= BucketPermissionDB.from_,
@@ -131,10 +130,10 @@ class CRUDBucket:
                 )
             )
         elif bucket_type == CRUDBucket.BucketType.OWN:
-            stmt = stmt.where(Bucket.owner_id == uid)
+            stmt = stmt.where(Bucket._owner_id == uid.bytes)
         else:
             stmt = stmt.where(
-                Bucket.permissions.any(BucketPermissionDB.user_id == uid)
+                Bucket.permissions.any(BucketPermissionDB._uid == uid.bytes)
                 .where(
                     or_(
                         func.UNIX_TIMESTAMP() >= BucketPermissionDB.from_,
@@ -148,12 +147,14 @@ class CRUDBucket:
                     )
                 ),
             )
-        trace.get_current_span().set_attributes({"sql_query": str(stmt), "uid": uid, "bucket_type": bucket_type.name})
-        return (await db.scalars(stmt)).all()
+        with tracer.start_as_current_span(
+            "db_list_buckets_for_user",
+            attributes={"sql_query": str(stmt), "uid": str(uid), "bucket_type": bucket_type.name},
+        ):
+            return (await db.scalars(stmt)).all()
 
     @staticmethod
-    @start_as_current_span_async("db_create_bucket", tracer=tracer)
-    async def create(db: AsyncSession, bucket_in: BucketInSchema, uid: str) -> Bucket:
+    async def create(db: AsyncSession, bucket_in: BucketInSchema, uid: UUID) -> Bucket:
         """
         Create a bucket for a given user.
 
@@ -171,18 +172,19 @@ class CRUDBucket:
         bucket : clowmdb.models.Bucket
             Returns the created bucket.
         """
-        bucket = Bucket(**bucket_in.model_dump(), owner_id=uid)
-        current_span = trace.get_current_span()
-        current_span.set_attribute("bucket_name", bucket.name)
-        if await CRUDBucket.get(db, bucket.name) is not None:
-            raise DuplicateError(f"Bucket {bucket.name} exists already")
-        db.add(bucket)
-        await db.commit()
-        await db.refresh(bucket)
-        return bucket
+        bucket = Bucket(**bucket_in.model_dump(), _owner_id=uid.bytes)
+        with tracer.start_as_current_span(
+            "db_create_bucket",
+            attributes={"uid": str(uid), "bucket_name": bucket.name},
+        ):
+            if await CRUDBucket.get(db, bucket.name) is not None:
+                raise DuplicateError(f"Bucket {bucket.name} exists already")
+            db.add(bucket)
+            await db.commit()
+            await db.refresh(bucket)
+            return bucket
 
     @staticmethod
-    @start_as_current_span_async("db_delete_bucket", tracer=tracer)
     async def delete(db: AsyncSession, bucket_name: str) -> None:
         """
         Delete a specific bucket.
@@ -195,6 +197,9 @@ class CRUDBucket:
             The name of the bucket to delete.
         """
         stmt = delete(Bucket).where(Bucket.name == bucket_name)
-        trace.get_current_span().set_attributes({"sql_query": str(stmt), "bucket_name": bucket_name})
-        await db.execute(stmt)
-        await db.commit()
+        with tracer.start_as_current_span(
+            "db_delete_bucket",
+            attributes={"sql_query": str(stmt), "bucket_name": bucket_name},
+        ):
+            await db.execute(stmt)
+            await db.commit()
diff --git a/app/crud/crud_bucket_permission.py b/app/crud/crud_bucket_permission.py
index 6b32bf6895e9dedffbffc24370aeba9243509a9d..e41b3c508bb466b238340215d2c8656b93fdb25f 100644
--- a/app/crud/crud_bucket_permission.py
+++ b/app/crud/crud_bucket_permission.py
@@ -1,5 +1,6 @@
 from enum import Enum, unique
 from typing import List, Optional, Sequence
+from uuid import UUID
 
 from clowmdb.models import BucketPermission as BucketPermissionDB
 from opentelemetry import trace
@@ -30,20 +31,22 @@ class CRUDBucketPermission:
         INACTIVE: str = "INACTIVE"
 
     @staticmethod
-    @start_as_current_span_async("db_get_bucket_permission", tracer=tracer)
-    async def get(db: AsyncSession, bucket_name: str, uid: str) -> Optional[BucketPermissionDB]:
+    async def get(db: AsyncSession, bucket_name: str, uid: UUID) -> Optional[BucketPermissionDB]:
         stmt = select(BucketPermissionDB).where(
-            BucketPermissionDB.user_id == uid, BucketPermissionDB.bucket_name == bucket_name
+            BucketPermissionDB._uid == uid.bytes, BucketPermissionDB.bucket_name == bucket_name
         )
-        trace.get_current_span().set_attributes({"sql_query": str(stmt), "bucket_name": bucket_name, "uid": uid})
-        return await db.scalar(stmt)
+        with tracer.start_as_current_span(
+            "db_get_bucket_permission",
+            attributes={"sql_query": str(stmt), "bucket_name": bucket_name, "uid": str(uid)},
+        ):
+            return await db.scalar(stmt)
 
     @staticmethod
     @start_as_current_span_async("db_list_bucket_permissions", tracer=tracer)
     async def list(
         db: AsyncSession,
         bucket_name: Optional[str] = None,
-        uid: Optional[str] = None,
+        uid: Optional[UUID] = None,
         permission_types: Optional[List[BucketPermissionDB.Permission]] = None,
         permission_status: Optional[PermissionStatus] = None,
     ) -> Sequence[BucketPermissionDB]:
@@ -77,8 +80,8 @@ class CRUDBucketPermission:
                 BucketPermissionDB.bucket_name == bucket_name
             )
         if uid is not None:
-            current_span.set_attribute("uid", uid)
-            stmt = stmt.where(BucketPermissionDB.user_id == uid)
+            current_span.set_attribute("uid", str(uid))
+            stmt = stmt.where(BucketPermissionDB._uid == uid.bytes)
         if permission_types is not None and len(permission_types) > 0:
             current_span.set_attribute("permission_types", [ptype.name for ptype in permission_types])
             stmt = stmt.where(or_(*[BucketPermissionDB.permissions == p_type for p_type in permission_types]))
@@ -89,8 +92,7 @@ class CRUDBucketPermission:
         return (await db.scalars(stmt)).all()
 
     @staticmethod
-    @start_as_current_span_async("db_check_bucket_permission", tracer=tracer)
-    async def check_permission(db: AsyncSession, bucket_name: str, uid: str) -> bool:
+    async def check_permission(db: AsyncSession, bucket_name: str, uid: UUID) -> bool:
         """
         Check if the provided user has any permission to the provided bucket.
 
@@ -108,9 +110,12 @@ class CRUDBucketPermission:
         permission_check : bool
             Return True if the user has any permission on the bucket, False otherwise.
         """
-        trace.get_current_span().set_attributes({"uid": uid, "bucket_name": bucket_name})
-        buckets = await CRUDBucket.get_for_user(db, uid, bucket_type=CRUDBucket.BucketType.ALL)
-        return bucket_name in map(lambda x: x.name, buckets)
+        with tracer.start_as_current_span(
+            "db_check_bucket_permission",
+            attributes={"uid": str(uid), "bucket_name": bucket_name},
+        ):
+            buckets = await CRUDBucket.get_for_user(db, uid, bucket_type=CRUDBucket.BucketType.ALL)
+            return bucket_name in map(lambda x: x.name, buckets)
 
     @staticmethod
     @start_as_current_span_async("db_create_bucket_permission", tracer=tracer)
@@ -129,26 +134,26 @@ class CRUDBucketPermission:
         permission : clowmdb.models.BucketPermission
             Newly created permission model from the db.
         """
-        trace.get_current_span().set_attributes({"bucket_name": permission.bucket_name, "uid": permission.uid})
+        trace.get_current_span().set_attributes({"bucket_name": permission.bucket_name, "uid": str(permission.uid)})
         # Check if user exists
         user = await CRUDUser.get(db, uid=permission.uid)
         if user is None:
             raise KeyError(
-                f"Unknown user with uid {permission.uid}",
+                f"Unknown user with uid {str(permission.uid)}",
             )
         # Check that grantee is not the owner of the bucket
         bucket = await CRUDBucket.get(db, permission.bucket_name)
         if bucket is None or bucket.owner_id == user.uid:
-            raise ValueError(f"User {permission.uid} is the owner of the bucket {permission.bucket_name}")
+            raise ValueError(f"User {str(permission.uid)} is the owner of the bucket {permission.bucket_name}")
         # Check if combination of user and bucket already exists
         previous_permission = await CRUDBucketPermission.get(db, bucket_name=permission.bucket_name, uid=user.uid)
         if previous_permission is not None:
             raise DuplicateError(
-                f"bucket permission for combination {permission.bucket_name} {permission.uid} already exists."
+                f"bucket permission for combination {permission.bucket_name} {str(permission.uid)} already exists."
             )
         # Add permission to db
         permission_db = BucketPermissionDB(
-            user_id=user.uid,
+            _uid=user.uid.bytes,
             bucket_name=permission.bucket_name,
             from_=permission.from_timestamp,
             to=permission.to_timestamp,
@@ -161,8 +166,7 @@ class CRUDBucketPermission:
         return permission_db
 
     @staticmethod
-    @start_as_current_span_async("db_delete_bucket_permission", tracer=tracer)
-    async def delete(db: AsyncSession, bucket_name: str, uid: str) -> None:
+    async def delete(db: AsyncSession, bucket_name: str, uid: UUID) -> None:
         """
         Delete a permission in the database.
 
@@ -176,14 +180,16 @@ class CRUDBucketPermission:
             UID of the user.
         """
         stmt = delete(BucketPermissionDB).where(
-            BucketPermissionDB.user_id == uid, BucketPermissionDB.bucket_name == bucket_name
+            BucketPermissionDB._uid == uid.bytes, BucketPermissionDB.bucket_name == bucket_name
         )
-        trace.get_current_span().set_attributes({"bucket_name": bucket_name, "uid": uid, "sql_query": str(stmt)})
-        await db.execute(stmt)
-        await db.commit()
+        with tracer.start_as_current_span(
+            "db_delete_bucket_permission",
+            attributes={"bucket_name": bucket_name, "uid": str(uid), "sql_query": str(stmt)},
+        ):
+            await db.execute(stmt)
+            await db.commit()
 
     @staticmethod
-    @start_as_current_span_async("db_update_bucket_permission", tracer=tracer)
     async def update_permission(
         db: AsyncSession, permission: BucketPermissionDB, new_params: BucketPermissionParametersSchema
     ) -> BucketPermissionDB:
@@ -208,7 +214,7 @@ class CRUDBucketPermission:
             update(BucketPermissionDB)
             .where(
                 BucketPermissionDB.bucket_name == permission.bucket_name,
-                BucketPermissionDB.user_id == permission.user_id,
+                BucketPermissionDB._uid == permission.uid.bytes,
             )
             .values(
                 from_=new_params.from_timestamp,
@@ -217,13 +223,14 @@ class CRUDBucketPermission:
                 permissions=new_params.permission,
             )
         )
-        trace.get_current_span().set_attributes(
-            {"sql_query": str(stmt), "bucket_name": permission.bucket_name, "uid": permission.user_id}
-        )
-        await db.execute(stmt)
-        await db.commit()
-        await db.refresh(permission)
-        return permission
+        with tracer.start_as_current_span(
+            "db_update_bucket_permission",
+            attributes={"sql_query": str(stmt), "bucket_name": permission.bucket_name, "uid": str(permission.uid)},
+        ):
+            await db.execute(stmt)
+            await db.commit()
+            await db.refresh(permission)
+            return permission
 
     @staticmethod
     def _filter_permission_status(stmt: SQLSelect, permission_status: PermissionStatus) -> SQLSelect:
diff --git a/app/crud/crud_user.py b/app/crud/crud_user.py
index 34151eb1b24567eb0eaf1243c0117c2430e07fba..9138d7774ecf8eede39b10c75d664bd0fecdb43b 100644
--- a/app/crud/crud_user.py
+++ b/app/crud/crud_user.py
@@ -1,4 +1,5 @@
 from typing import Optional
+from uuid import UUID
 
 from clowmdb.models import User
 from opentelemetry import trace
@@ -10,7 +11,7 @@ tracer = trace.get_tracer_provider().get_tracer(__name__)
 
 class CRUDUser:
     @staticmethod
-    async def get(db: AsyncSession, uid: str) -> Optional[User]:
+    async def get(db: AsyncSession, uid: UUID) -> Optional[User]:
         """
         Get a user by its UID.
 
@@ -26,7 +27,6 @@ class CRUDUser:
         user : clowmdb.models.User | None
             The user for the given UID if he exists, None otherwise
         """
-        with tracer.start_as_current_span("db_get_user") as span:
-            stmt = select(User).where(User.uid == uid)
-            span.set_attribute("sql_query", str(stmt))
+        stmt = select(User).where(User._uid == uid.bytes)
+        with tracer.start_as_current_span("db_get_user", attributes={"uid": str(uid), "sql_query": str(stmt)}):
             return await db.scalar(stmt)
diff --git a/app/schemas/bucket.py b/app/schemas/bucket.py
index 4e1790d0563eb1b23d692d9bad94183802836328..c30770b7c515f734eac3ace2356c74a78c944116 100644
--- a/app/schemas/bucket.py
+++ b/app/schemas/bucket.py
@@ -1,5 +1,6 @@
 import re
 from typing import Optional
+from uuid import UUID
 
 from clowmdb.models import Bucket
 from pydantic import BaseModel, ConfigDict, Field, field_validator
@@ -53,7 +54,7 @@ class BucketOut(_BaseBucket):
         examples=[1640991600],  # 01.01.2022 00:00
         description="Time when the bucket was created as UNIX timestamp",
     )
-    owner: str = Field(..., description="UID of the owner", examples=["28c5353b8bb34984a8bd4169ba94c606"])
+    owner_id: UUID = Field(..., description="UID of the owner", examples=["1d3387f3-95c0-4813-8767-2cad87faeebf"])
     num_objects: int = Field(..., description="Number of Objects in this bucket", examples=[6])
     size: int = Field(..., description="Total size of objects in this bucket in bytes", examples=[3256216])
     owner_constraint: Optional[Bucket.Constraint] = Field(None, description="Constraint for the owner of the bucket")
diff --git a/app/schemas/bucket_permission.py b/app/schemas/bucket_permission.py
index e4fa93f6a2c275822da96d22b6e80ef7590cb36b..99217183c251d9d139e0067eda9fc5767d35eaa2 100644
--- a/app/schemas/bucket_permission.py
+++ b/app/schemas/bucket_permission.py
@@ -1,9 +1,10 @@
 import hashlib
 from datetime import datetime
 from typing import Any, Dict, List, Optional, Union
+from uuid import UUID
 
 from clowmdb.models import BucketPermission as BucketPermissionDB
-from pydantic import BaseModel, Field
+from pydantic import BaseModel, Field, field_serializer
 
 
 class BucketPermissionParameters(BaseModel):
@@ -28,16 +29,20 @@ class BucketPermissionParameters(BaseModel):
 
 
 class BucketPermissionIn(BucketPermissionParameters):
-    uid: str = Field(..., description="UID of the grantee", examples=["28c5353b8bb34984a8bd4169ba94c606"])
+    uid: UUID = Field(..., description="UID of the grantee", examples=["1d3387f3-95c0-4813-8767-2cad87faeebf"])
     bucket_name: str = Field(..., description="Name of Bucket", examples=["test-bucket"])
 
-    def to_hash(self, user_id: str) -> str:
+    @field_serializer("uid")
+    def serialize_uid(self, uid: UUID, _info: Any) -> str:
+        return str(uid)
+
+    def to_hash(self, uid: UUID) -> str:
         """
         Combine the bucket name and user id and produce the MD5 hash of it.
 
         Parameters
         ----------
-        user_id : str
+        uid : uuid.UUID
             The unique and unchanging user id
 
         Returns
@@ -45,17 +50,18 @@ class BucketPermissionIn(BucketPermissionParameters):
         hash : str
             The resulting MD5 hash.
         """
-        str_for_id_hash = self.bucket_name + user_id
-        return hashlib.md5(str_for_id_hash.encode("utf-8")).hexdigest()
+        hasher = hashlib.md5(self.bucket_name.encode("utf-8"))
+        hasher.update(uid.bytes)
+        return hasher.hexdigest()
 
-    def map_to_bucket_policy_statement(self, user_id: str) -> List[Dict[str, Any]]:
+    def map_to_bucket_policy_statement(self, uid: UUID) -> List[Dict[str, Any]]:
         """
         Create a bucket policy statement from the schema and the user_id.\n
         The Sid is unique for every bucket and user combination.
 
         Parameters
         ----------
-        user_id : str
+        uid : uuid.UUID
             The unique and unchanging user id belonging to this permission.
 
         Returns
@@ -63,18 +69,19 @@ class BucketPermissionIn(BucketPermissionParameters):
         statements : List[Dict[str, Any]]
             Bucket and object permission statements.
         """
+        own_hash = self.to_hash(uid)
         obj_policy: Dict[str, Any] = {
-            "Sid": self.to_hash(user_id),
+            "Sid": own_hash,
             "Effect": "Allow",
-            "Principal": {"AWS": f"arn:aws:iam:::user/{self.uid}"},
+            "Principal": {"AWS": f"arn:aws:iam:::user/{str(self.uid)}"},
             "Resource": f"arn:aws:s3:::{self.bucket_name}/{'' if self.file_prefix is None else self.file_prefix}*",
             "Action": [],
             "Condition": {},
         }
         bucket_policy: Dict[str, Any] = {
-            "Sid": self.to_hash(user_id),
+            "Sid": own_hash,
             "Effect": "Allow",
-            "Principal": {"AWS": f"arn:aws:iam:::user/{self.uid}"},
+            "Principal": {"AWS": f"arn:aws:iam:::user/{str(self.uid)}"},
             "Resource": f"arn:aws:s3:::{self.bucket_name}",
             "Action": [],
             "Condition": {},
@@ -117,12 +124,8 @@ class BucketPermissionOut(BucketPermissionIn):
     Schema for the bucket permissions.
     """
 
-    grantee_display_name: str = Field(..., description="Display Name of the grantee", examples=["Bilbo Baggins"])
-
     @staticmethod
-    def from_db_model(
-        permission: BucketPermissionDB, uid: Optional[str] = None, grantee_display_name: Optional[str] = None
-    ) -> "BucketPermissionOut":
+    def from_db_model(permission: BucketPermissionDB) -> "BucketPermissionOut":
         """
         Create a bucket permission schema from the database model.
 
@@ -130,10 +133,6 @@ class BucketPermissionOut(BucketPermissionIn):
         ----------
         permission : clowmdb.models.BucketPermission
             DB model for the permission.
-        uid : str | None, default None
-            Sets the uid in the schema. If None it will be taken from the database model.
-        grantee_display_name: str | None, default None
-            Sets the display name of the grantee in the schema. If None it will be taken from the database model.
 
         Returns
         -------
@@ -141,8 +140,7 @@ class BucketPermissionOut(BucketPermissionIn):
             Schema populated with the values from the database model.
         """
         return BucketPermissionOut(
-            uid=uid if uid else permission.grantee.uid,
-            grantee_display_name=grantee_display_name if grantee_display_name else permission.grantee.display_name,
+            uid=permission.uid,
             bucket_name=permission.bucket_name,
             from_timestamp=permission.from_,
             to_timestamp=permission.to,
diff --git a/app/schemas/user.py b/app/schemas/s3key.py
similarity index 59%
rename from app/schemas/user.py
rename to app/schemas/s3key.py
index c9ca4704b89afe36a3ac3ece46224fc908927d78..eae80d27a78ccd1eb46149f1a6eed815f2a6e860 100644
--- a/app/schemas/user.py
+++ b/app/schemas/s3key.py
@@ -1,4 +1,7 @@
-from pydantic import BaseModel, Field
+from typing import Any
+from uuid import UUID
+
+from pydantic import BaseModel, Field, field_serializer
 
 
 class S3Key(BaseModel):
@@ -6,8 +9,8 @@ class S3Key(BaseModel):
     Schema for a S3 key associated with a user.
     """
 
-    user: str = Field(
-        ..., description="UID of the user of that access key", examples=["28c5353b8bb34984a8bd4169ba94c606"]
+    uid: UUID = Field(
+        ..., description="UID of the user of that access key", examples=["1d3387f3-95c0-4813-8767-2cad87faeebf"]
     )
     access_key: str = Field(..., description="ID of the S3 access key", examples=["CRJ6B037V2ZT4U3W17VC"])
     secret_key: str = Field(
@@ -15,3 +18,7 @@ class S3Key(BaseModel):
         description="Secret of the S3 access key",
         examples=["2F5uNTI1qvt4oAroXV0wWct8rWclL2QvFXKqSqjS"],
     )
+
+    @field_serializer("uid")
+    def serialize_uid(self, uid: UUID, _info: Any) -> str:
+        return str(uid)
diff --git a/app/schemas/security.py b/app/schemas/security.py
index 3c0f596a45e25b0eaa0ea6b86f961733c0d7e3c7..a6d7bd8293e64f047eeff0369271b577612a74e5 100644
--- a/app/schemas/security.py
+++ b/app/schemas/security.py
@@ -1,23 +1,29 @@
 from datetime import datetime
+from typing import Any
+from uuid import UUID
 
-from pydantic import BaseModel, Field
+from pydantic import BaseModel, Field, field_serializer
 
 
 class AuthzResponse(BaseModel):
     """Schema for a response from OPA"""
 
-    decision_id: str = Field(
+    decision_id: UUID = Field(
         ...,
         description="Decision ID for for the specific decision",
         examples=["8851dce0-7546-4e81-a89d-111cbec376c1"],
     )
     result: bool = Field(..., description="Result of the Authz request")
 
+    @field_serializer("decision_id")
+    def serialize_decision_id(self, decision_id: UUID, _info: Any) -> str:
+        return str(decision_id)
+
 
 class AuthzRequest(BaseModel):
     """Schema for a Request to OPA"""
 
-    uid: str = Field(..., description="UID of user", examples=["28c5353b8bb34984a8bd4169ba94c606"])
+    uid: str = Field(..., description="lifescience id of user", examples=["28c5353b8bb34984a8bd4169ba94c606"])
     operation: str = Field(..., description="Operation the user wants to perform", examples=["read"])
     resource: str = Field(..., description="Resource the operation should be performed on", examples=["bucket"])
 
diff --git a/app/tests/api/test_bucket_permissions.py b/app/tests/api/test_bucket_permissions.py
index e209798f43a5e3629207f47014e90b9078c1384a..6ea86c774cf1bc379fa3e00856cb54b4b4bd340e 100644
--- a/app/tests/api/test_bucket_permissions.py
+++ b/app/tests/api/test_bucket_permissions.py
@@ -1,5 +1,6 @@
 import json
 from datetime import datetime
+from uuid import uuid4
 
 import pytest
 from clowmdb.models import Bucket, BucketPermission
@@ -9,6 +10,7 @@ from sqlalchemy import update
 from sqlalchemy.ext.asyncio import AsyncSession
 
 from app.schemas.bucket_permission import BucketPermissionIn as BucketPermissionSchema
+from app.schemas.bucket_permission import BucketPermissionOut
 from app.schemas.bucket_permission import BucketPermissionParameters as BucketPermissionParametersSchema
 from app.tests.utils.bucket import add_permission_for_bucket
 from app.tests.utils.user import UserWithAuthHeader
@@ -40,16 +42,16 @@ class TestBucketPermissionRoutesGet(_TestBucketPermissionRoutes):
             Bucket permission for a random bucket for testing. pytest fixture.
         """
         response = await client.get(
-            f"{self.base_path}/bucket/{random_bucket_permission_schema.bucket_name}/user/{random_bucket_permission_schema.uid}",  # noqa:E501
+            f"{self.base_path}/bucket/{random_bucket_permission_schema.bucket_name}/user/{str(random_bucket_permission_schema.uid)}",  # noqa:E501
             headers=random_user.auth_headers,
         )
         assert response.status_code == status.HTTP_200_OK
 
-        permission = response.json()
+        permission = BucketPermissionOut.model_validate(response.json())
 
         assert permission
-        assert permission["uid"] == random_bucket_permission_schema.uid
-        assert permission["bucket_name"] == random_bucket_permission_schema.bucket_name
+        assert permission.uid == random_bucket_permission_schema.uid
+        assert permission.bucket_name == random_bucket_permission_schema.bucket_name
 
     @pytest.mark.asyncio
     async def test_get_bucket_permission_for_unknown_user(
@@ -71,7 +73,7 @@ class TestBucketPermissionRoutesGet(_TestBucketPermissionRoutes):
             Bucket permission for a random bucket for testing. pytest fixture.
         """
         response = await client.get(
-            f"{self.base_path}/bucket/{random_bucket_permission_schema.bucket_name}/user/ImpossibleUser",
+            f"{self.base_path}/bucket/{random_bucket_permission_schema.bucket_name}/user/{str(uuid4())}",
             headers=random_user.auth_headers,
         )
         assert response.status_code == status.HTTP_404_NOT_FOUND
@@ -99,7 +101,7 @@ class TestBucketPermissionRoutesGet(_TestBucketPermissionRoutes):
             Random second user for testing. pytest fixture.
         """
         response = await client.get(
-            f"{self.base_path}/bucket/{random_bucket.name}/user/{random_second_user.user.uid}",
+            f"{self.base_path}/bucket/{random_bucket.name}/user/{str(random_second_user.user.uid)}",
             headers=random_user.auth_headers,
         )
         assert response.status_code == status.HTTP_404_NOT_FOUND
@@ -124,15 +126,15 @@ class TestBucketPermissionRoutesGet(_TestBucketPermissionRoutes):
             Random second user for testing. pytest fixture.
         """
         response = await client.get(
-            f"{self.base_path}/bucket/{random_bucket_permission_schema.bucket_name}/user/{random_second_user.user.uid}",
+            f"{self.base_path}/bucket/{random_bucket_permission_schema.bucket_name}/user/{str(random_second_user.user.uid)}",
             headers=random_second_user.auth_headers,
         )
         assert response.status_code == status.HTTP_200_OK
-        permission = response.json()
+        permission = BucketPermissionOut.model_validate(response.json())
 
         assert permission
-        assert permission["uid"] == random_bucket_permission_schema.uid
-        assert permission["bucket_name"] == random_bucket_permission_schema.bucket_name
+        assert permission.uid == random_bucket_permission_schema.uid
+        assert permission.bucket_name == random_bucket_permission_schema.bucket_name
 
     @pytest.mark.asyncio
     async def test_get_wrong_bucket_permission_with_permission(
@@ -157,8 +159,9 @@ class TestBucketPermissionRoutesGet(_TestBucketPermissionRoutes):
             Random third user who has no permissions for the bucket. pytest fixture.
         """
         await add_permission_for_bucket(db, random_bucket_permission_schema.bucket_name, random_third_user.user.uid)
+
         response = await client.get(
-            f"{self.base_path}/bucket/{random_bucket_permission_schema.bucket_name}/user/{random_bucket_permission_schema.uid}",  # noqa:E501
+            f"{self.base_path}/bucket/{random_bucket_permission_schema.bucket_name}/user/{str(random_bucket_permission_schema.uid)}",  # noqa:E501
             headers=random_third_user.auth_headers,
         )
         assert response.status_code == status.HTTP_403_FORBIDDEN
@@ -183,16 +186,44 @@ class TestBucketPermissionRoutesGet(_TestBucketPermissionRoutes):
             Bucket permission for a random bucket for testing. pytest fixture.
         """
         response = await client.get(
-            f"{self.base_path}/user/{random_bucket_permission_schema.uid}",
+            f"{self.base_path}/user/{str(random_bucket_permission_schema.uid)}",
             headers=random_second_user.auth_headers,
         )
         assert response.status_code == status.HTTP_200_OK
         permission_list = response.json()
         assert isinstance(permission_list, list)
         assert len(permission_list) == 1
-        permission = permission_list[0]
-        assert permission["uid"] == random_bucket_permission_schema.uid
-        assert permission["bucket_name"] == random_bucket_permission_schema.bucket_name
+        permission = BucketPermissionOut.model_validate(permission_list[0])
+        assert permission.uid == random_bucket_permission_schema.uid
+        assert permission.bucket_name == random_bucket_permission_schema.bucket_name
+
+    @pytest.mark.asyncio
+    async def test_get_all_bucket_permissions(
+        self,
+        client: AsyncClient,
+        random_user: UserWithAuthHeader,
+        random_bucket_permission_schema: BucketPermissionSchema,
+    ) -> None:
+        """
+        Test for getting all bucket permission for a user.
+
+        Parameters
+        ----------
+        client : httpx.AsyncClient
+            HTTP Client to perform the request on. pytest fixture.
+        random_user : app.tests.utils.user.UserWithAuthHeader
+            Random second user for testing. pytest fixture.
+        random_bucket_permission_schema : app.schemas.bucket_permission.BucketPermissionOut
+            Bucket permission for a random bucket for testing. pytest fixture.
+        """
+        response = await client.get(self.base_path, headers=random_user.auth_headers, params={"allow_admin": True})
+        assert response.status_code == status.HTTP_200_OK
+        permission_list = response.json()
+        assert isinstance(permission_list, list)
+        assert len(permission_list) == 1
+        permission = BucketPermissionOut.model_validate(permission_list[0])
+        assert permission.uid == random_bucket_permission_schema.uid
+        assert permission.bucket_name == random_bucket_permission_schema.bucket_name
 
     @pytest.mark.asyncio
     async def test_get_bucket_permissions_for_bucket(
@@ -221,9 +252,9 @@ class TestBucketPermissionRoutesGet(_TestBucketPermissionRoutes):
         permission_list = response.json()
         assert isinstance(permission_list, list)
         assert len(permission_list) == 1
-        permission = permission_list[0]
-        assert permission["uid"] == random_bucket_permission_schema.uid
-        assert permission["bucket_name"] == random_bucket_permission_schema.bucket_name
+        permission = BucketPermissionOut.model_validate(permission_list[0])
+        assert permission.uid == random_bucket_permission_schema.uid
+        assert permission.bucket_name == random_bucket_permission_schema.bucket_name
 
     @pytest.mark.asyncio
     async def test_get_bucket_permissions_for_foreign_bucket(
@@ -268,7 +299,7 @@ class TestBucketPermissionRoutesCreate(_TestBucketPermissionRoutes):
         random_bucket : clowmdb.models.Bucket
             Random bucket for testing. pytest fixture.
         """
-        permission = BucketPermissionSchema(bucket_name=random_bucket.name, uid="ImpossibleUser")
+        permission = BucketPermissionSchema(bucket_name=random_bucket.name, uid=uuid4())
         response = await client.post(self.base_path, headers=random_user.auth_headers, json=permission.model_dump())
         assert response.status_code == status.HTTP_404_NOT_FOUND
 
@@ -352,6 +383,7 @@ class TestBucketPermissionRoutesCreate(_TestBucketPermissionRoutes):
         random_user: UserWithAuthHeader,
         random_second_user: UserWithAuthHeader,
         random_bucket: Bucket,
+        db: AsyncSession,
     ) -> None:
         """
         Test for creating a valid bucket permission.
@@ -366,15 +398,17 @@ class TestBucketPermissionRoutesCreate(_TestBucketPermissionRoutes):
             Random second user for testing. pytest fixture.
         random_bucket : clowmdb.models.Bucket
             Random bucket for testing. pytest fixture.
+        db : sqlalchemy.ext.asyncio.AsyncSession.
+            Async database session to perform query on. pytest fixture.
         """
         permission = BucketPermissionSchema(bucket_name=random_bucket.name, uid=random_second_user.user.uid)
 
         response = await client.post(self.base_path, headers=random_user.auth_headers, json=permission.model_dump())
+
         assert response.status_code == status.HTTP_201_CREATED
-        created_permission = response.json()
-        assert created_permission["uid"] == random_second_user.user.uid
-        assert created_permission["bucket_name"] == random_bucket.name
-        assert created_permission["grantee_display_name"] == random_second_user.user.display_name
+        created_permission = BucketPermissionOut.model_validate(response.json())
+        assert created_permission.uid == random_second_user.user.uid
+        assert created_permission.bucket_name == random_bucket.name
 
     @pytest.mark.asyncio
     async def test_create_bucket_permissions_on_initial_bucket(
@@ -433,7 +467,7 @@ class TestBucketPermissionRoutesDelete(_TestBucketPermissionRoutes):
             Bucket permission for a random bucket for testing. pytest fixture.
         """
         response = await client.delete(
-            f"{self.base_path}/bucket/{random_bucket_permission_schema.bucket_name}/user/{random_bucket_permission_schema.uid}",  # noqa:E501
+            f"{self.base_path}/bucket/{random_bucket_permission_schema.bucket_name}/user/{str(random_bucket_permission_schema.uid)}",  # noqa:E501
             headers=random_user.auth_headers,
         )
         assert response.status_code == status.HTTP_204_NO_CONTENT
@@ -458,7 +492,7 @@ class TestBucketPermissionRoutesDelete(_TestBucketPermissionRoutes):
             Bucket permission for a random bucket for testing. pytest fixture.
         """
         response = await client.get(
-            f"{self.base_path}/bucket/{random_bucket_permission_schema.bucket_name}/user/{random_bucket_permission_schema.uid}",  # noqa:E501
+            f"{self.base_path}/bucket/{random_bucket_permission_schema.bucket_name}/user/{str(random_bucket_permission_schema.uid)}",  # noqa:E501
             headers=random_second_user.auth_headers,
         )
         assert response.status_code == status.HTTP_200_OK
@@ -481,7 +515,7 @@ class TestBucketPermissionRoutesDelete(_TestBucketPermissionRoutes):
             Bucket permission for a random bucket for testing. pytest fixture.
         """
         response = await client.delete(
-            f"{self.base_path}/bucket/{random_bucket_permission_schema.bucket_name}/user/ImpossibleUser",
+            f"{self.base_path}/bucket/{random_bucket_permission_schema.bucket_name}/user/{str(uuid4())}",
             headers=random_user.auth_headers,
         )
         assert response.status_code == status.HTTP_404_NOT_FOUND
@@ -507,7 +541,7 @@ class TestBucketPermissionRoutesDelete(_TestBucketPermissionRoutes):
             Random second user for testing. pytest fixture.
         """
         response = await client.delete(
-            f"{self.base_path}/bucket/{random_bucket.name}/user/{random_second_user.user.uid}",
+            f"{self.base_path}/bucket/{random_bucket.name}/user/{str(random_second_user.user.uid)}",
             headers=random_user.auth_headers,
         )
         assert response.status_code == status.HTTP_404_NOT_FOUND
@@ -536,7 +570,7 @@ class TestBucketPermissionRoutesDelete(_TestBucketPermissionRoutes):
         """
         await add_permission_for_bucket(db, random_bucket_permission_schema.bucket_name, random_third_user.user.uid)
         response = await client.delete(
-            f"{self.base_path}/bucket/{random_bucket_permission_schema.bucket_name}/user/{random_bucket_permission_schema.uid}",  # noqa:E501
+            f"{self.base_path}/bucket/{random_bucket_permission_schema.bucket_name}/user/{str(random_bucket_permission_schema.uid)}",  # noqa:E501
             headers=random_third_user.auth_headers,
         )
         assert response.status_code == status.HTTP_403_FORBIDDEN
@@ -568,19 +602,19 @@ class TestBucketPermissionRoutesUpdate(_TestBucketPermissionRoutes):
             file_prefix="pseudo/folder/",
         )
         response = await client.put(
-            f"{self.base_path}/bucket/{random_bucket_permission_schema.bucket_name}/user/{random_bucket_permission_schema.uid}",  # noqa:E501
+            f"{self.base_path}/bucket/{random_bucket_permission_schema.bucket_name}/user/{str(random_bucket_permission_schema.uid)}",  # noqa:E501
             headers=random_user.auth_headers,
             content=json.dumps(new_params.model_dump(), default=json_datetime_converter),
         )
         assert response.status_code == status.HTTP_200_OK
-        updated_permission = response.json()
-        assert updated_permission["uid"] == random_bucket_permission_schema.uid
-        assert updated_permission["bucket_name"] == random_bucket_permission_schema.bucket_name
+        updated_permission = BucketPermissionOut.model_validate(response.json())
+        assert updated_permission.uid == random_bucket_permission_schema.uid
+        assert updated_permission.bucket_name == random_bucket_permission_schema.bucket_name
         if new_params.from_timestamp is not None and new_params.to_timestamp is not None:
-            assert updated_permission["from_timestamp"] == new_params.from_timestamp
-            assert updated_permission["to_timestamp"] == new_params.to_timestamp
-        assert updated_permission["permission"] == new_params.permission
-        assert updated_permission["file_prefix"] == new_params.file_prefix
+            assert updated_permission.from_timestamp == new_params.from_timestamp
+            assert updated_permission.to_timestamp == new_params.to_timestamp
+        assert updated_permission.permission == new_params.permission
+        assert updated_permission.file_prefix == new_params.file_prefix
 
     @pytest.mark.asyncio
     async def test_update_unknown_bucket_permission(
@@ -604,7 +638,7 @@ class TestBucketPermissionRoutesUpdate(_TestBucketPermissionRoutes):
             file_prefix="pseudo/folder/",
         )
         response = await client.put(
-            f"{self.base_path}/bucket/{random_bucket_permission_schema.bucket_name}/user/impossibleUser",
+            f"{self.base_path}/bucket/{random_bucket_permission_schema.bucket_name}/user/{str(uuid4())}",
             headers=random_user.auth_headers,
             json=new_params.model_dump(),
         )
@@ -635,7 +669,7 @@ class TestBucketPermissionRoutesUpdate(_TestBucketPermissionRoutes):
             file_prefix="pseudo/folder/",
         )
         response = await client.put(
-            f"{self.base_path}/bucket/{random_bucket.name}/user/{random_second_user.user.uid}",
+            f"{self.base_path}/bucket/{random_bucket.name}/user/{str(random_second_user.user.uid)}",
             headers=random_user.auth_headers,
             json=new_params.model_dump(),
         )
@@ -665,7 +699,7 @@ class TestBucketPermissionRoutesUpdate(_TestBucketPermissionRoutes):
             file_prefix="pseudo/folder/",
         )
         response = await client.put(
-            f"{self.base_path}/bucket/{random_bucket_permission_schema.bucket_name}/user/{random_second_user.user.uid}",
+            f"{self.base_path}/bucket/{random_bucket_permission_schema.bucket_name}/user/{str(random_second_user.user.uid)}",
             headers=random_second_user.auth_headers,
             json=new_params.model_dump(),
         )
@@ -695,7 +729,7 @@ class TestBucketPermissionRoutesUpdate(_TestBucketPermissionRoutes):
             file_prefix="pseudo/folder/",
         )
         response = await client.put(
-            f"{self.base_path}/bucket/{random_bucket_permission_schema.bucket_name}/user/{random_bucket_permission_schema.uid}",  # noqa:E501
+            f"{self.base_path}/bucket/{random_bucket_permission_schema.bucket_name}/user/{str(random_bucket_permission_schema.uid)}",  # noqa:E501
             headers=random_third_user.auth_headers,
             json=new_params.model_dump(),
         )
diff --git a/app/tests/api/test_buckets.py b/app/tests/api/test_buckets.py
index 0e9ab652b008f73b33109ac76d2bf0145be08f2d..a709921ceaca91bc897d31be22ef8ec370276e7e 100644
--- a/app/tests/api/test_buckets.py
+++ b/app/tests/api/test_buckets.py
@@ -5,9 +5,10 @@ from httpx import AsyncClient
 from sqlalchemy.ext.asyncio import AsyncSession
 
 from app.crud.crud_bucket import CRUDBucket
-from app.schemas.bucket import BucketIn
+from app.schemas.bucket import BucketIn, BucketOut
 from app.tests.mocks.mock_s3_resource import MockS3ServiceResource
-from app.tests.utils.bucket import add_permission_for_bucket
+from app.tests.utils.bucket import add_permission_for_bucket, delete_bucket
+from app.tests.utils.cleanup import CleanupList
 from app.tests.utils.user import UserWithAuthHeader
 from app.tests.utils.utils import random_lower_string
 
@@ -39,10 +40,10 @@ class TestBucketRoutesGet(_TestBucketRoutes):
         buckets = response.json()
 
         assert len(buckets) == 1
-        bucket = buckets[0]
+        bucket = BucketOut.model_validate(buckets[0])
 
-        assert bucket["name"].split(":")[-1] == random_bucket.name
-        assert bucket["owner"] == random_bucket.owner_id
+        assert bucket.name == random_bucket.name
+        assert bucket.owner_id == random_bucket.owner_id
 
     @pytest.mark.asyncio
     async def test_get_own_buckets(
@@ -64,17 +65,17 @@ class TestBucketRoutesGet(_TestBucketRoutes):
             Random user for testing. pytest fixture.
         """
         response = await client.get(
-            f"{self.base_path}", params={"user": random_bucket.owner_id}, headers=random_user.auth_headers
+            f"{self.base_path}", params={"owner_id": str(random_bucket.owner_id)}, headers=random_user.auth_headers
         )
         assert response.status_code == status.HTTP_200_OK
 
         buckets = response.json()
 
         assert len(buckets) == 1
-        bucket = buckets[0]
+        bucket = BucketOut.model_validate(buckets[0])
 
-        assert bucket["name"].split(":")[-1] == random_bucket.name
-        assert bucket["owner"] == random_bucket.owner_id
+        assert bucket.name == random_bucket.name
+        assert bucket.owner_id == random_bucket.owner_id
 
     @pytest.mark.asyncio
     async def test_get_bucket_by_name(
@@ -98,10 +99,10 @@ class TestBucketRoutesGet(_TestBucketRoutes):
         response = await client.get(f"{self.base_path}/{random_bucket.name}", headers=random_user.auth_headers)
         assert response.status_code == status.HTTP_200_OK
 
-        bucket = response.json()
+        bucket = BucketOut.model_validate(response.json())
 
-        assert bucket["name"].split(":")[-1] == random_bucket.name
-        assert bucket["owner"] == random_bucket.owner.uid
+        assert bucket.name == random_bucket.name
+        assert bucket.owner_id == random_bucket.owner.uid
 
     @pytest.mark.asyncio
     async def test_get_unknown_bucket(self, client: AsyncClient, random_user: UserWithAuthHeader) -> None:
@@ -145,6 +146,7 @@ class TestBucketRoutesCreate(_TestBucketRoutes):
         db: AsyncSession,
         client: AsyncClient,
         random_user: UserWithAuthHeader,
+        cleanup: CleanupList,
     ) -> None:
         """
         Test for creating a bucket.
@@ -157,23 +159,28 @@ class TestBucketRoutesCreate(_TestBucketRoutes):
             HTTP Client to perform the request on. pytest fixture.
         random_user : app.tests.utils.user.UserWithAuthHeader
             Random user for testing. pytest fixture.
+        cleanup : app.tests.utils.utils.CleanupList
+            Cleanup object where (async) functions can be registered which get executed after a (failed) test.
         """
         bucket_info = BucketIn(name=random_lower_string(), description=random_lower_string(127))
+        cleanup.add_task(
+            delete_bucket,
+            db=db,
+            bucket_name=bucket_info.name,
+        )
         response = await client.post(self.base_path, headers=random_user.auth_headers, json=bucket_info.model_dump())
 
         assert response.status_code == status.HTTP_201_CREATED
-        bucket = response.json()
+        bucket = BucketOut.model_validate(response.json())
         assert bucket
-        assert bucket["name"].split(":")[-1] == bucket_info.name
-        assert bucket["owner"] == random_user.user.uid
+        assert bucket.name == bucket_info.name
+        assert bucket.owner_id == random_user.user.uid
 
         dbBucket = await CRUDBucket.get(db, bucket_info.name)
         assert dbBucket
         assert dbBucket.name == bucket_info.name
         assert dbBucket.owner_id == random_user.user.uid
 
-        await CRUDBucket.delete(db, dbBucket.name)
-
     @pytest.mark.asyncio
     async def test_create_duplicated_bucket(
         self,
@@ -232,6 +239,7 @@ class TestBucketRoutesDelete(_TestBucketRoutes):
         db: AsyncSession,
         random_user: UserWithAuthHeader,
         random_second_user: UserWithAuthHeader,
+        cleanup: CleanupList,
     ) -> None:
         """
         Test for deleting a foreign bucket.
@@ -244,14 +252,21 @@ class TestBucketRoutesDelete(_TestBucketRoutes):
             Random user for testing. pytest fixture.
         random_second_user : app.tests.utils.user.UserWithAuthHeader
             Random user which is not the owner of the bucket. pytest fixture.
+        cleanup : app.tests.utils.utils.CleanupList
+            Cleanup object where (async) functions can be registered which get executed after a (failed) test.
         """
         bucket = Bucket(
             name=random_lower_string(),
             description=random_lower_string(127),
-            owner_id=random_second_user.user.uid,
+            _owner_id=random_second_user.user.uid.bytes,
         )
         db.add(bucket)
         await db.commit()
+        cleanup.add_task(
+            delete_bucket,
+            db=db,
+            bucket_name=bucket.name,
+        )
         await add_permission_for_bucket(
             db, bucket.name, random_user.user.uid, permission=BucketPermission.Permission.READWRITE
         )
@@ -262,8 +277,6 @@ class TestBucketRoutesDelete(_TestBucketRoutes):
 
         assert response.status_code == status.HTTP_403_FORBIDDEN
 
-        await db.delete(bucket)
-
     @pytest.mark.asyncio
     async def test_delete_non_empty_bucket(
         self,
diff --git a/app/tests/api/test_s3_keys.py b/app/tests/api/test_s3_keys.py
index 2e060573b551ed8aa6d2a6bd00a327dba7ec77a1..1413da4aa9de94c44e6ec23ca97c4920e3df9ec5 100644
--- a/app/tests/api/test_s3_keys.py
+++ b/app/tests/api/test_s3_keys.py
@@ -2,6 +2,7 @@ import pytest
 from fastapi import status
 from httpx import AsyncClient
 
+from app.schemas.s3key import S3Key
 from app.tests.mocks.mock_rgw_admin import MockRGWAdmin
 from app.tests.utils.user import UserWithAuthHeader
 
@@ -31,7 +32,7 @@ class TestS3KeyRoutesGet(_TestS3KeyRoutes):
             Random foreign user for testing. pytest fixture.
         """
         response = await client.get(
-            f"{self.base_path}/{random_second_user.user.uid}/keys", headers=random_user.auth_headers
+            f"{self.base_path}/{str(random_second_user.user.uid)}/keys", headers=random_user.auth_headers
         )
 
         assert response.status_code == status.HTTP_403_FORBIDDEN
@@ -52,12 +53,14 @@ class TestS3KeyRoutesGet(_TestS3KeyRoutes):
         random_user : app.tests.utils.user.UserWithAuthHeader
             Random user for testing. pytest fixture.
         """
-        response = await client.get(f"{self.base_path}/{random_user.user.uid}/keys", headers=random_user.auth_headers)
+        response = await client.get(
+            f"{self.base_path}/{str(random_user.user.uid)}/keys", headers=random_user.auth_headers
+        )
         keys = response.json()
         assert response.status_code == status.HTTP_200_OK
         assert isinstance(keys, list)
         assert len(keys) == 1
-        assert keys[0]["user"] == random_user.user.uid
+        assert S3Key.model_validate(keys[0]).uid == random_user.user.uid
 
     @pytest.mark.asyncio
     async def test_get_specific_s3_key_for_user(
@@ -78,15 +81,16 @@ class TestS3KeyRoutesGet(_TestS3KeyRoutes):
         mock_rgw_admin : app.tests.mocks.mock_rgw_admin.MockRGWAdmin
             Mock class for rgwadmin package. pytest fixture.
         """
-        s3_key = mock_rgw_admin.get_user(uid=random_user.user.uid)["keys"][0]
+        s3_key = mock_rgw_admin.get_user(uid=str(random_user.user.uid))["keys"][0]
         response = await client.get(
-            f"{self.base_path}/{random_user.user.uid}/keys/{s3_key['access_key']}", headers=random_user.auth_headers
+            f"{self.base_path}/{str(random_user.user.uid)}/keys/{s3_key['access_key']}",
+            headers=random_user.auth_headers,
         )
-        response_key = response.json()
+        response_key = S3Key.model_validate(response.json())
         assert response.status_code == status.HTTP_200_OK
-        assert response_key["access_key"] == s3_key["access_key"]
-        assert response_key["secret_key"] == s3_key["secret_key"]
-        assert response_key["user"] == s3_key["user"]
+        assert response_key.access_key == s3_key["access_key"]
+        assert response_key.secret_key == s3_key["secret_key"]
+        assert str(response_key.uid) == s3_key["user"]
 
     @pytest.mark.asyncio
     async def test_get_specific_s3_key_from_foreign_user(
@@ -110,9 +114,9 @@ class TestS3KeyRoutesGet(_TestS3KeyRoutes):
         mock_rgw_admin : app.tests.mocks.mock_rgw_admin.MockRGWAdmin
             Mock class for rgwadmin package. pytest fixture.
         """
-        s3_key = mock_rgw_admin.get_user(uid=random_user.user.uid)["keys"][0]
+        s3_key = mock_rgw_admin.get_user(uid=str(random_user.user.uid))["keys"][0]
         response = await client.get(
-            f"{self.base_path}/{random_user.user.uid}/keys/{s3_key['access_key']}",
+            f"{self.base_path}/{str(random_user.user.uid)}/keys/{s3_key['access_key']}",
             headers=random_second_user.auth_headers,
         )
         assert response.status_code == status.HTTP_403_FORBIDDEN
@@ -130,7 +134,7 @@ class TestS3KeyRoutesGet(_TestS3KeyRoutes):
             Random user for testing. pytest fixture.
         """
         response = await client.get(
-            f"{self.base_path}/{random_user.user.uid}/keys/impossible_key", headers=random_user.auth_headers
+            f"{self.base_path}/{str(random_user.user.uid)}/keys/impossible_key", headers=random_user.auth_headers
         )
         assert response.status_code == status.HTTP_404_NOT_FOUND
 
@@ -138,7 +142,10 @@ class TestS3KeyRoutesGet(_TestS3KeyRoutes):
 class TestS3KeyRoutesCreate(_TestS3KeyRoutes):
     @pytest.mark.asyncio
     async def test_create_s3_key_for_user(
-        self, client: AsyncClient, random_user: UserWithAuthHeader, mock_rgw_admin: MockRGWAdmin
+        self,
+        client: AsyncClient,
+        random_user: UserWithAuthHeader,
+        mock_rgw_admin: MockRGWAdmin,
     ) -> None:
         """
         Test for getting a specific S3 key from a user.
@@ -152,15 +159,15 @@ class TestS3KeyRoutesCreate(_TestS3KeyRoutes):
         mock_rgw_admin : app.tests.mocks.mock_rgw_admin.MockRGWAdmin
             Mock class for rgwadmin package. pytest fixture.
         """
-        old_s3_key = mock_rgw_admin.get_user(uid=random_user.user.uid)["keys"][0]
-        response = await client.post(f"{self.base_path}/{random_user.user.uid}/keys", headers=random_user.auth_headers)
-        new_key = response.json()
+        old_s3_key = mock_rgw_admin.get_user(uid=str(random_user.user.uid))["keys"][0]
+        response = await client.post(
+            f"{self.base_path}/{str(random_user.user.uid)}/keys", headers=random_user.auth_headers
+        )
+        new_key = S3Key.model_validate(response.json())
 
         assert response.status_code == status.HTTP_201_CREATED
-        assert new_key["access_key"] != old_s3_key["access_key"]
-        assert new_key["user"] == random_user.user.uid
-
-        mock_rgw_admin.remove_key(uid=random_user.user.uid, access_key=new_key["access_key"])
+        assert new_key.access_key != old_s3_key["access_key"]
+        assert new_key.uid == random_user.user.uid
 
     @pytest.mark.asyncio
     async def test_create_s3_key_for_foreign_user(
@@ -179,7 +186,7 @@ class TestS3KeyRoutesCreate(_TestS3KeyRoutes):
             Random second user for testing. pytest fixture.
         """
         response = await client.post(
-            f"{self.base_path}/{random_second_user.user.uid}/keys", headers=random_user.auth_headers
+            f"{self.base_path}/{str(random_second_user.user.uid)}/keys", headers=random_user.auth_headers
         )
 
         assert response.status_code == status.HTTP_403_FORBIDDEN
@@ -202,14 +209,15 @@ class TestS3KeyRoutesDelete(_TestS3KeyRoutes):
         mock_rgw_admin : app.tests.mocks.mock_rgw_admin.MockRGWAdmin
             Mock class for rgwadmin package. pytest fixture.
         """
-        new_s3_key = mock_rgw_admin.create_key(uid=random_user.user.uid)[-1]
-        assert len(mock_rgw_admin.get_user(uid=random_user.user.uid)["keys"]) == 2
+        new_s3_key = mock_rgw_admin.create_key(uid=str(random_user.user.uid))[-1]
+        assert len(mock_rgw_admin.get_user(uid=str(random_user.user.uid))["keys"]) == 2
         response = await client.delete(
-            f"{self.base_path}/{random_user.user.uid}/keys/{new_s3_key['access_key']}", headers=random_user.auth_headers
+            f"{self.base_path}/{str(random_user.user.uid)}/keys/{new_s3_key['access_key']}",
+            headers=random_user.auth_headers,
         )
 
         assert response.status_code == status.HTTP_204_NO_CONTENT
-        assert len(mock_rgw_admin.get_user(uid=random_user.user.uid)["keys"]) == 1
+        assert len(mock_rgw_admin.get_user(uid=str(random_user.user.uid))["keys"]) == 1
 
     @pytest.mark.asyncio
     async def test_delete_last_s3_key_for_user(
@@ -227,18 +235,21 @@ class TestS3KeyRoutesDelete(_TestS3KeyRoutes):
         mock_rgw_admin : app.tests.mocks.mock_rgw_admin.MockRGWAdmin
             Mock class for rgwadmin package. pytest fixture.
         """
-        assert len(mock_rgw_admin.get_user(uid=random_user.user.uid)["keys"]) == 1
-        key_id = mock_rgw_admin.get_user(uid=random_user.user.uid)["keys"][0]
+        assert len(mock_rgw_admin.get_user(uid=str(random_user.user.uid))["keys"]) == 1
+        key_id = mock_rgw_admin.get_user(uid=str(random_user.user.uid))["keys"][0]
         response = await client.delete(
-            f"{self.base_path}/{random_user.user.uid}/keys/{key_id}", headers=random_user.auth_headers
+            f"{self.base_path}/{str(random_user.user.uid)}/keys/{key_id}", headers=random_user.auth_headers
         )
 
         assert response.status_code == status.HTTP_400_BAD_REQUEST
-        assert len(mock_rgw_admin.get_user(uid=random_user.user.uid)["keys"]) == 1
+        assert len(mock_rgw_admin.get_user(uid=str(random_user.user.uid))["keys"]) == 1
 
     @pytest.mark.asyncio
     async def test_delete_unknown_s3_key_for_user(
-        self, client: AsyncClient, random_user: UserWithAuthHeader, mock_rgw_admin: MockRGWAdmin
+        self,
+        client: AsyncClient,
+        random_user: UserWithAuthHeader,
+        mock_rgw_admin: MockRGWAdmin,
     ) -> None:
         """
         Test for deleting an unknown S3 key from a user.
@@ -252,8 +263,8 @@ class TestS3KeyRoutesDelete(_TestS3KeyRoutes):
         mock_rgw_admin : app.tests.mocks.mock_rgw_admin.MockRGWAdmin
             Mock class for rgwadmin package. pytest fixture.
         """
-        mock_rgw_admin.create_key(uid=random_user.user.uid)
+        mock_rgw_admin.create_key(uid=str(random_user.user.uid))
         response = await client.delete(
-            f"{self.base_path}/{random_user.user.uid}/keys/impossible", headers=random_user.auth_headers
+            f"{self.base_path}/{str(random_user.user.uid)}/keys/impossible", headers=random_user.auth_headers
         )
         assert response.status_code == status.HTTP_404_NOT_FOUND
diff --git a/app/tests/api/test_security.py b/app/tests/api/test_security.py
index 6b7b1fea50703cab3f179fb48e0868b63e3b9413..870c4007039ac14c2ab15c594fce7bebb6952627 100644
--- a/app/tests/api/test_security.py
+++ b/app/tests/api/test_security.py
@@ -54,7 +54,7 @@ class TestJWTProtectedRoutes:
             Random user for testing. pytest fixture.
         """
         response = await client.get(
-            self.protected_route, params={"user": random_user.user.uid}, headers=random_user.auth_headers
+            self.protected_route, params={"owner_id": str(random_user.user.uid)}, headers=random_user.auth_headers
         )
         assert response.status_code == status.HTTP_200_OK
 
@@ -81,6 +81,6 @@ class TestJWTProtectedRoutes:
         await db.commit()
 
         response = await client.get(
-            self.protected_route, params={"user": random_user.user.uid}, headers=random_user.auth_headers
+            self.protected_route, params={"owner_id": str(random_user.user.uid)}, headers=random_user.auth_headers
         )
         assert response.status_code == status.HTTP_404_NOT_FOUND
diff --git a/app/tests/conftest.py b/app/tests/conftest.py
index c3e84d9d9613a1459a092bba9d24de0d9406e532..936ddf69059e8c73d382066dfff707c0e0cee6b8 100644
--- a/app/tests/conftest.py
+++ b/app/tests/conftest.py
@@ -2,7 +2,7 @@ import asyncio
 import json
 from functools import partial
 from secrets import token_urlsafe
-from typing import AsyncGenerator, Callable, Dict, Generator
+from typing import AsyncIterator, Callable, Dict, Generator
 from uuid import uuid4
 
 import httpx
@@ -21,6 +21,7 @@ from app.schemas.security import AuthzResponse
 from app.tests.mocks.mock_rgw_admin import MockRGWAdmin
 from app.tests.mocks.mock_s3_resource import MockS3ServiceResource
 from app.tests.utils.bucket import create_random_bucket
+from app.tests.utils.cleanup import CleanupList
 from app.tests.utils.user import UserWithAuthHeader, create_random_user, decode_mock_token, get_authorization_headers
 from app.tests.utils.utils import request_admin_permission
 
@@ -54,28 +55,24 @@ def mock_s3_service() -> MockS3ServiceResource:
 
 
 @pytest_asyncio.fixture(scope="module")
-async def client(mock_rgw_admin: MockRGWAdmin, mock_s3_service: MockS3ServiceResource) -> AsyncGenerator:
+async def client(
+    mock_rgw_admin: MockRGWAdmin, mock_s3_service: MockS3ServiceResource
+) -> AsyncIterator[httpx.AsyncClient]:
     """
     Fixture for creating a TestClient and perform HTTP Request on it.
     Overrides the dependency for the RGW admin operations.
     """
 
-    def get_mock_rgw() -> MockRGWAdmin:
-        return mock_rgw_admin
-
-    def get_mock_s3() -> MockS3ServiceResource:
-        return mock_s3_service
-
     def get_decode_token_function() -> Callable[[str], Dict[str, str]]:
         # Override the decode_jwt function with mock function for tests and inject random shared secret
         return partial(decode_mock_token, secret=jwt_secret)
 
-    async def get_mock_httpx_client() -> AsyncGenerator[httpx.AsyncClient, None]:
+    async def get_mock_httpx_client(allow_admin: bool = False) -> AsyncIterator[httpx.AsyncClient]:
         def mock_request_handler(request: httpx.Request) -> httpx.Response:
             response_body = {}
             if str(request.url).startswith(str(settings.OPA_URI)):
                 response_body = AuthzResponse(
-                    result=not request_admin_permission(request), decision_id=str(uuid4())
+                    result=not request_admin_permission(request) or allow_admin, decision_id=uuid4()
                 ).model_dump()
             return httpx.Response(200, json=response_body)
 
@@ -83,8 +80,8 @@ async def client(mock_rgw_admin: MockRGWAdmin, mock_s3_service: MockS3ServiceRes
             yield http_client
 
     app.dependency_overrides[get_httpx_client] = get_mock_httpx_client
-    app.dependency_overrides[get_rgw_admin] = get_mock_rgw
-    app.dependency_overrides[get_s3_resource] = get_mock_s3
+    app.dependency_overrides[get_rgw_admin] = lambda: mock_rgw_admin
+    app.dependency_overrides[get_s3_resource] = lambda: mock_s3_service
     app.dependency_overrides[get_decode_jwt_function] = get_decode_token_function
     async with httpx.AsyncClient(app=app, base_url="http://localhost") as ac:
         yield ac
@@ -92,7 +89,7 @@ async def client(mock_rgw_admin: MockRGWAdmin, mock_s3_service: MockS3ServiceRes
 
 
 @pytest_asyncio.fixture(scope="module")
-async def db() -> AsyncGenerator[AsyncSession, None]:
+async def db() -> AsyncIterator[AsyncSession]:
     """
     Fixture for creating a database session to connect to.
     """
@@ -102,43 +99,41 @@ async def db() -> AsyncGenerator[AsyncSession, None]:
         yield dbSession
 
 
-@pytest_asyncio.fixture(scope="module")
-async def random_user(db: AsyncSession, mock_rgw_admin: MockRGWAdmin) -> AsyncGenerator[UserWithAuthHeader, None]:
+@pytest_asyncio.fixture(scope="function")
+async def random_user(db: AsyncSession, mock_rgw_admin: MockRGWAdmin) -> AsyncIterator[UserWithAuthHeader]:
     """
     Create a random user and deletes him afterwards.
     """
     user = await create_random_user(db)
-    mock_rgw_admin.create_key(uid=user.uid)
+    mock_rgw_admin.create_key(uid=str(user.uid))
     yield UserWithAuthHeader(user=user, auth_headers=get_authorization_headers(uid=user.uid, secret=jwt_secret))
-    mock_rgw_admin.delete_user(uid=user.uid)
+    mock_rgw_admin.delete_user(uid=str(user.uid))
     await db.delete(user)
     await db.commit()
 
 
-@pytest_asyncio.fixture(scope="module")
-async def random_second_user(
-    db: AsyncSession, mock_rgw_admin: MockRGWAdmin
-) -> AsyncGenerator[UserWithAuthHeader, None]:
+@pytest_asyncio.fixture(scope="function")
+async def random_second_user(db: AsyncSession, mock_rgw_admin: MockRGWAdmin) -> AsyncIterator[UserWithAuthHeader]:
     """
     Create a random second user and deletes him afterwards.
     """
     user = await create_random_user(db)
-    mock_rgw_admin.create_key(uid=user.uid)
+    mock_rgw_admin.create_key(uid=str(user.uid))
     yield UserWithAuthHeader(user=user, auth_headers=get_authorization_headers(uid=user.uid, secret=jwt_secret))
-    mock_rgw_admin.delete_user(uid=user.uid)
+    mock_rgw_admin.delete_user(uid=str(user.uid))
     await db.delete(user)
     await db.commit()
 
 
-@pytest_asyncio.fixture(scope="module")
-async def random_third_user(db: AsyncSession, mock_rgw_admin: MockRGWAdmin) -> AsyncGenerator[UserWithAuthHeader, None]:
+@pytest_asyncio.fixture(scope="function")
+async def random_third_user(db: AsyncSession, mock_rgw_admin: MockRGWAdmin) -> AsyncIterator[UserWithAuthHeader]:
     """
     Create a random third user and deletes him afterwards.
     """
     user = await create_random_user(db)
-    mock_rgw_admin.create_key(uid=user.uid)
+    mock_rgw_admin.create_key(uid=str(user.uid))
     yield UserWithAuthHeader(user=user, auth_headers=get_authorization_headers(uid=user.uid, secret=jwt_secret))
-    mock_rgw_admin.delete_user(uid=user.uid)
+    mock_rgw_admin.delete_user(uid=str(user.uid))
     await db.delete(user)
     await db.commit()
 
@@ -146,7 +141,7 @@ async def random_third_user(db: AsyncSession, mock_rgw_admin: MockRGWAdmin) -> A
 @pytest_asyncio.fixture(scope="function")
 async def random_bucket(
     db: AsyncSession, random_user: UserWithAuthHeader, mock_s3_service: MockS3ServiceResource
-) -> AsyncGenerator[Bucket, None]:
+) -> AsyncIterator[Bucket]:
     """
     Create a random user and deletes him afterwards.
     """
@@ -160,7 +155,7 @@ async def random_bucket(
                     {
                         "Sid": "PseudoOwnerPerm",
                         "Effect": "Allow",
-                        "Principal": {"AWS": [f"arn:aws:iam:::user/{random_user.user.uid}"]},
+                        "Principal": {"AWS": [f"arn:aws:iam:::user/{str(random_user.user.uid)}"]},
                         "Action": ["s3:GetObject", "s3:DeleteObject", "s3:PutObject", "s3:ListBucket"],
                         "Resource": [f"arn:aws:s3:::{bucket.name}/*", f"arn:aws:s3:::{bucket.name}"],
                     }
@@ -184,7 +179,7 @@ async def random_bucket_permission(
     """
     Create a bucket READ permission for the second user on a bucket.
     """
-    permission_db = BucketPermissionDB(user_id=random_second_user.user.uid, bucket_name=random_bucket.name)
+    permission_db = BucketPermissionDB(_uid=random_second_user.user.uid.bytes, bucket_name=random_bucket.name)
     db.add(permission_db)
     await db.commit()
     await db.refresh(permission_db)
@@ -209,4 +204,14 @@ async def random_bucket_permission_schema(
     Create a bucket READ permission for the second user on a bucket.
     """
 
-    return BucketPermissionSchema.from_db_model(random_bucket_permission, random_second_user.user.uid)
+    return BucketPermissionSchema.from_db_model(random_bucket_permission)
+
+
+@pytest_asyncio.fixture(scope="function")
+async def cleanup(db: AsyncSession) -> AsyncIterator[CleanupList]:
+    """
+    Yields a Cleanup object where (async) functions can be registered which get executed after a (failed) test
+    """
+    cleanup_list = CleanupList()
+    yield cleanup_list
+    await cleanup_list.empty_queue()
diff --git a/app/tests/crud/test_bucket.py b/app/tests/crud/test_bucket.py
index 2250e511e4da32a0dd27ea167e38f4de1b6cd687..2af9aadc7fc707bd43fedfa52febe1ef66d5ecc6 100644
--- a/app/tests/crud/test_bucket.py
+++ b/app/tests/crud/test_bucket.py
@@ -8,7 +8,8 @@ from sqlalchemy.ext.asyncio import AsyncSession
 from app.crud import DuplicateError
 from app.crud.crud_bucket import CRUDBucket
 from app.schemas.bucket import BucketIn
-from app.tests.utils.bucket import add_permission_for_bucket
+from app.tests.utils.bucket import add_permission_for_bucket, delete_bucket
+from app.tests.utils.cleanup import CleanupList
 from app.tests.utils.user import UserWithAuthHeader
 from app.tests.utils.utils import random_lower_string
 
@@ -83,7 +84,11 @@ class TestBucketCRUDGet:
 
     @pytest.mark.asyncio
     async def test_get_only_foreign_bucket(
-        self, db: AsyncSession, random_bucket: Bucket, random_second_user: UserWithAuthHeader
+        self,
+        db: AsyncSession,
+        random_bucket: Bucket,
+        random_second_user: UserWithAuthHeader,
+        cleanup: CleanupList,
     ) -> None:
         """
         Test for getting only foreign buckets with permissions for a user from CRUD Repository.
@@ -96,14 +101,21 @@ class TestBucketCRUDGet:
             Random bucket for testing. pytest fixture.
         random_second_user : app.tests.utils.user.UserWithAuthHeader
             Random second user for testing. pytest fixture.
+        cleanup : app.tests.utils.utils.CleanupList
+            Cleanup object where (async) functions can be registered which get executed after a (failed) test.
         """
         bucket = Bucket(
             name=random_lower_string(),
             description=random_lower_string(127),
-            owner_id=random_second_user.user.uid,
+            _owner_id=random_second_user.user.uid.bytes,
         )
         db.add(bucket)
         await db.commit()
+        cleanup.add_task(
+            delete_bucket,
+            db=db,
+            bucket_name=bucket.name,
+        )
         await add_permission_for_bucket(
             db, bucket.name, random_bucket.owner_id, permission=BucketPermission.Permission.READ
         )
@@ -113,14 +125,13 @@ class TestBucketCRUDGet:
         assert buckets[0] != random_bucket
         assert buckets[0].name == bucket.name
 
-        await db.delete(bucket)
-
     @pytest.mark.asyncio
     async def test_get_bucket_with_read_permission_and_own(
         self,
         db: AsyncSession,
         random_bucket: Bucket,
         random_second_user: UserWithAuthHeader,
+        cleanup: CleanupList,
     ) -> None:
         """
         Test for getting the users own bucket and a foreign bucket with READ permissions from CRUD Repository.
@@ -133,14 +144,21 @@ class TestBucketCRUDGet:
             Random bucket for testing. pytest fixture.
         random_second_user : app.tests.utils.user.UserWithAuthHeader
             Random second user for testing. pytest fixture.
+        cleanup : app.tests.utils.utils.CleanupList
+            Cleanup object where (async) functions can be registered which get executed after a (failed) test.
         """
         bucket = Bucket(
             name=random_lower_string(),
             description=random_lower_string(127),
-            owner_id=random_second_user.user.uid,
+            _owner_id=random_second_user.user.uid.bytes,
         )
         db.add(bucket)
         await db.commit()
+        cleanup.add_task(
+            delete_bucket,
+            db=db,
+            bucket_name=bucket.name,
+        )
         await add_permission_for_bucket(
             db, bucket.name, random_bucket.owner_id, permission=BucketPermission.Permission.READ
         )
@@ -151,8 +169,6 @@ class TestBucketCRUDGet:
         assert buckets[0].name == random_bucket.name or buckets[1].name == random_bucket.name
         assert buckets[0].name == bucket.name or buckets[1].name == bucket.name
 
-        await db.delete(bucket)
-
     @pytest.mark.asyncio
     async def test_get_bucket_with_read_permission(
         self, db: AsyncSession, random_bucket: Bucket, random_second_user: UserWithAuthHeader
@@ -308,7 +324,12 @@ class TestBucketCRUDGet:
 
 class TestBucketCRUDCreate:
     @pytest.mark.asyncio
-    async def test_create_bucket(self, db: AsyncSession, random_user: UserWithAuthHeader) -> None:
+    async def test_create_bucket(
+        self,
+        db: AsyncSession,
+        random_user: UserWithAuthHeader,
+        cleanup: CleanupList,
+    ) -> None:
         """
         Test for creating a bucket with the CRUD Repository.
 
@@ -318,9 +339,16 @@ class TestBucketCRUDCreate:
             Async database session to perform query on. pytest fixture.
         random_user : app.tests.utils.user.UserWithAuthHeader
             Random user for testing. pytest fixture.
+        cleanup : app.tests.utils.utils.CleanupList
+            Cleanup object where (async) functions can be registered which get executed after a (failed) test.
         """
         bucket_info = BucketIn(name=random_lower_string(), description=random_lower_string(127))
         bucket = await CRUDBucket.create(db, bucket_info, random_user.user.uid)
+        cleanup.add_task(
+            delete_bucket,
+            db=db,
+            bucket_name=bucket.name,
+        )
         assert bucket.name == bucket_info.name
         assert bucket.owner_id == random_user.user.uid
         assert bucket.description == bucket_info.description
@@ -333,8 +361,6 @@ class TestBucketCRUDCreate:
         assert bucket_db.owner_id == random_user.user.uid
         assert bucket_db.description == bucket_info.description
 
-        await db.delete(bucket)
-
     @pytest.mark.asyncio
     async def test_create_duplicated_bucket(self, db: AsyncSession, random_bucket: Bucket) -> None:
         """
diff --git a/app/tests/crud/test_bucket_permission.py b/app/tests/crud/test_bucket_permission.py
index a92f97f6035cc385c2709ab5ecfc137706d77469..20b94e14d2d44e39b8778d34c0aae2a38a1278d9 100644
--- a/app/tests/crud/test_bucket_permission.py
+++ b/app/tests/crud/test_bucket_permission.py
@@ -1,4 +1,5 @@
 from datetime import datetime, timedelta
+from uuid import uuid4
 
 import pytest
 from clowmdb.models import Bucket
@@ -30,10 +31,10 @@ class TestBucketPermissionCRUDGet:
             Bucket permission for a random bucket for testing. pytest fixture.
         """
         bucket_permission = await CRUDBucketPermission.get(
-            db, bucket_name=random_bucket_permission.bucket_name, uid=random_bucket_permission.user_id
+            db, bucket_name=random_bucket_permission.bucket_name, uid=random_bucket_permission.uid
         )
         assert bucket_permission
-        assert bucket_permission.user_id == random_bucket_permission.user_id
+        assert bucket_permission.uid == random_bucket_permission.uid
         assert bucket_permission.bucket_name == random_bucket_permission.bucket_name
         assert bucket_permission.permissions == random_bucket_permission.permissions
 
@@ -54,7 +55,7 @@ class TestBucketPermissionCRUDGet:
         bucket_permissions = await CRUDBucketPermission.list(db, bucket_name=random_bucket_permission.bucket_name)
         assert len(bucket_permissions) == 1
         bucket_permission = bucket_permissions[0]
-        assert bucket_permission.user_id == random_bucket_permission.user_id
+        assert bucket_permission.uid == random_bucket_permission.uid
         assert bucket_permission.bucket_name == random_bucket_permission.bucket_name
         assert bucket_permission.permissions == random_bucket_permission.permissions
 
@@ -86,7 +87,7 @@ class TestBucketPermissionCRUDGet:
             db, bucket_name=random_bucket.name, permission_types=[BucketPermissionDB.Permission.READ]
         )
         assert len(bucket_permissions) == 1
-        assert bucket_permissions[0].user_id == random_second_user.user.uid
+        assert bucket_permissions[0].uid == random_second_user.user.uid
 
     @pytest.mark.asyncio
     async def test_get_read_and_write_bucket_permissions_by_bucket_name(
@@ -118,8 +119,8 @@ class TestBucketPermissionCRUDGet:
             permission_types=[BucketPermissionDB.Permission.READ, BucketPermissionDB.Permission.WRITE],
         )
         assert len(bucket_permissions) == 2
-        assert random_second_user.user.uid in map(lambda x: x.user_id, bucket_permissions)
-        assert random_third_user.user.uid in map(lambda x: x.user_id, bucket_permissions)
+        assert random_second_user.user.uid in map(lambda x: x.uid, bucket_permissions)
+        assert random_third_user.user.uid in map(lambda x: x.uid, bucket_permissions)
 
     @pytest.mark.asyncio
     async def test_get_active_bucket_permissions_by_bucket_name1(
@@ -152,7 +153,7 @@ class TestBucketPermissionCRUDGet:
             db, bucket_name=random_bucket.name, permission_status=CRUDBucketPermission.PermissionStatus.ACTIVE
         )
         assert len(bucket_permissions) == 1
-        assert bucket_permissions[0].user_id == random_third_user.user.uid
+        assert bucket_permissions[0].uid == random_third_user.user.uid
 
     @pytest.mark.asyncio
     async def test_get_active_bucket_permissions_by_bucket_name2(
@@ -185,7 +186,7 @@ class TestBucketPermissionCRUDGet:
             db, bucket_name=random_bucket.name, permission_status=CRUDBucketPermission.PermissionStatus.ACTIVE
         )
         assert len(bucket_permissions) == 1
-        assert bucket_permissions[0].user_id == random_third_user.user.uid
+        assert bucket_permissions[0].uid == random_third_user.user.uid
 
     @pytest.mark.asyncio
     async def test_get_active_bucket_permissions_by_bucket_name3(
@@ -226,7 +227,7 @@ class TestBucketPermissionCRUDGet:
             db, bucket_name=random_bucket.name, permission_status=CRUDBucketPermission.PermissionStatus.ACTIVE
         )
         assert len(bucket_permissions) == 1
-        assert bucket_permissions[0].user_id == random_third_user.user.uid
+        assert bucket_permissions[0].uid == random_third_user.user.uid
 
     @pytest.mark.asyncio
     async def test_get_inactive_bucket_permissions_by_bucket_name1(
@@ -259,7 +260,7 @@ class TestBucketPermissionCRUDGet:
             db, bucket_name=random_bucket.name, permission_status=CRUDBucketPermission.PermissionStatus.INACTIVE
         )
         assert len(bucket_permissions) == 1
-        assert bucket_permissions[0].user_id == random_second_user.user.uid
+        assert bucket_permissions[0].uid == random_second_user.user.uid
 
     @pytest.mark.asyncio
     async def test_get_inactive_bucket_permissions_by_bucket_name2(
@@ -292,7 +293,7 @@ class TestBucketPermissionCRUDGet:
             db, bucket_name=random_bucket.name, permission_status=CRUDBucketPermission.PermissionStatus.INACTIVE
         )
         assert len(bucket_permissions) == 1
-        assert bucket_permissions[0].user_id == random_second_user.user.uid
+        assert bucket_permissions[0].uid == random_second_user.user.uid
 
     @pytest.mark.asyncio
     async def test_get_inactive_bucket_permissions_by_bucket_name3(
@@ -333,7 +334,7 @@ class TestBucketPermissionCRUDGet:
             db, bucket_name=random_bucket.name, permission_status=CRUDBucketPermission.PermissionStatus.INACTIVE
         )
         assert len(bucket_permissions) == 1
-        assert bucket_permissions[0].user_id == random_second_user.user.uid
+        assert bucket_permissions[0].uid == random_second_user.user.uid
 
     @pytest.mark.asyncio
     async def test_get_bucket_permissions_by_uid(
@@ -349,10 +350,10 @@ class TestBucketPermissionCRUDGet:
         random_bucket_permission : clowmdb.models.BucketPermission
             Bucket permission for a random bucket for testing. pytest fixture.
         """
-        bucket_permissions = await CRUDBucketPermission.list(db, uid=random_bucket_permission.user_id)
+        bucket_permissions = await CRUDBucketPermission.list(db, uid=random_bucket_permission.uid)
         assert len(bucket_permissions) == 1
         bucket_permission = bucket_permissions[0]
-        assert bucket_permission.user_id == random_bucket_permission.user_id
+        assert bucket_permission.uid == random_bucket_permission.uid
         assert bucket_permission.bucket_name == random_bucket_permission.bucket_name
         assert bucket_permission.permissions == random_bucket_permission.permissions
 
@@ -422,7 +423,7 @@ class TestBucketPermissionCRUDCreate:
         random_bucket : clowmdb.models.Bucket
             Random bucket for testing. pytest fixture.
         """
-        permission = BucketPermissionSchema(bucket_name=random_bucket.name, uid="ImpossibleUser")
+        permission = BucketPermissionSchema(bucket_name=random_bucket.name, uid=uuid4())
         with pytest.raises(KeyError):
             await CRUDBucketPermission.create(db, permission)
 
@@ -485,7 +486,7 @@ class TestBucketPermissionCRUDCreate:
         permission = BucketPermissionSchema(bucket_name=random_bucket.name, uid=random_second_user.user.uid)
         created_permission = await CRUDBucketPermission.create(db, permission)
 
-        assert created_permission.user_id == random_second_user.user.uid
+        assert created_permission.uid == random_second_user.user.uid
         assert created_permission.bucket_name == random_bucket.name
 
 
@@ -505,13 +506,13 @@ class TestBucketPermissionCRUDDelete:
             Bucket permission for a random bucket for testing. pytest fixture.
         """
         await CRUDBucketPermission.delete(
-            db, bucket_name=random_bucket_permission.bucket_name, uid=random_bucket_permission.user_id
+            db, bucket_name=random_bucket_permission.bucket_name, uid=random_bucket_permission.uid
         )
 
         stmt = select(BucketPermissionDB).where(
             and_(
                 BucketPermissionDB.bucket_name == random_bucket_permission.bucket_name,
-                BucketPermissionDB.user_id == random_bucket_permission.user_id,
+                BucketPermissionDB._uid == random_bucket_permission.uid.bytes,
             )
         )
         bucket_permission_db = await db.scalar(stmt)
@@ -543,7 +544,7 @@ class TestBucketPermissionCRUDUpdate:
         )
         new_permission = await CRUDBucketPermission.update_permission(db, random_bucket_permission, new_params)
 
-        assert new_permission.user_id == random_bucket_permission.user_id
+        assert new_permission.uid == random_bucket_permission.uid
         assert new_permission.bucket_name == random_bucket_permission.bucket_name
         assert new_permission.from_ == new_params.from_timestamp
         assert new_permission.to == new_params.to_timestamp
diff --git a/app/tests/crud/test_user.py b/app/tests/crud/test_user.py
index e387b67a4d2728ad89590917b3afb1c4944a0e31..c9856e1fb219f1b0d0b6f86718660404d7334fe1 100644
--- a/app/tests/crud/test_user.py
+++ b/app/tests/crud/test_user.py
@@ -1,9 +1,10 @@
+from uuid import uuid4
+
 import pytest
 from sqlalchemy.ext.asyncio import AsyncSession
 
 from app.crud.crud_user import CRUDUser
 from app.tests.utils.user import UserWithAuthHeader
-from app.tests.utils.utils import random_lower_string
 
 
 class TestUserCRUD:
@@ -37,5 +38,5 @@ class TestUserCRUD:
         db : sqlalchemy.ext.asyncio.AsyncSession.
             Async database session to perform query on. pytest fixture.
         """
-        user = await CRUDUser.get(db, random_lower_string(length=16))
+        user = await CRUDUser.get(db, uuid4())
         assert user is None
diff --git a/app/tests/unit/test_bucket_permission_scheme.py b/app/tests/unit/test_bucket_permission_scheme.py
index 8eb79ec9168f8e802632ffea073f5d12c5f572d3..263d0afc61a8d6eb5bf64f708de42f701fcddbb6 100644
--- a/app/tests/unit/test_bucket_permission_scheme.py
+++ b/app/tests/unit/test_bucket_permission_scheme.py
@@ -1,4 +1,5 @@
 from datetime import datetime
+from uuid import uuid4
 
 import pytest
 from clowmdb.models import BucketPermission
@@ -14,7 +15,7 @@ class _TestPermissionPolicy:
         Generate a base READ bucket permission schema.
         """
         return BucketPermissionIn(
-            uid=random_lower_string(),
+            uid=uuid4(),
             bucket_name=random_lower_string(),
             permission=BucketPermission.Permission.READ,
         )
@@ -30,14 +31,14 @@ class TestPermissionPolicyPermissionType(_TestPermissionPolicy):
         random_base_permission : app.schemas.bucket_permission.BucketPermissionOut
             Random base bucket permission for testing. pytest fixture.
         """
-        uid = random_lower_string()
-        stmts = random_base_permission.map_to_bucket_policy_statement(user_id=uid)
+        uid = uuid4()
+        stmts = random_base_permission.map_to_bucket_policy_statement(uid=uid)
         assert len(stmts) == 2
 
         object_stmt = stmts[0]
         assert object_stmt["Effect"] == "Allow"
         assert object_stmt["Sid"] == random_base_permission.to_hash(uid)
-        assert object_stmt["Principal"]["AWS"] == f"arn:aws:iam:::user/{random_base_permission.uid}"
+        assert object_stmt["Principal"]["AWS"] == f"arn:aws:iam:::user/{str(random_base_permission.uid)}"
         assert object_stmt["Resource"] == f"arn:aws:s3:::{random_base_permission.bucket_name}/*"
         with pytest.raises(KeyError):
             assert object_stmt["Condition"]
@@ -47,7 +48,7 @@ class TestPermissionPolicyPermissionType(_TestPermissionPolicy):
         bucket_stmt = stmts[1]
         assert bucket_stmt["Sid"] == random_base_permission.to_hash(uid)
         assert bucket_stmt["Effect"] == "Allow"
-        assert bucket_stmt["Principal"]["AWS"] == f"arn:aws:iam:::user/{random_base_permission.uid}"
+        assert bucket_stmt["Principal"]["AWS"] == f"arn:aws:iam:::user/{str(random_base_permission.uid)}"
         assert bucket_stmt["Resource"] == f"arn:aws:s3:::{random_base_permission.bucket_name}"
         with pytest.raises(KeyError):
             assert bucket_stmt["Condition"]
@@ -64,7 +65,7 @@ class TestPermissionPolicyPermissionType(_TestPermissionPolicy):
             Random base bucket permission for testing. pytest fixture.
         """
         random_base_permission.permission = BucketPermission.Permission.WRITE
-        stmts = random_base_permission.map_to_bucket_policy_statement(user_id=random_lower_string())
+        stmts = random_base_permission.map_to_bucket_policy_statement(uid=uuid4())
         assert len(stmts) == 2
 
         object_stmt = stmts[0]
@@ -91,7 +92,7 @@ class TestPermissionPolicyPermissionType(_TestPermissionPolicy):
             Random base bucket permission for testing. pytest fixture.
         """
         random_base_permission.permission = BucketPermission.Permission.READWRITE
-        stmts = random_base_permission.map_to_bucket_policy_statement(user_id=random_lower_string())
+        stmts = random_base_permission.map_to_bucket_policy_statement(uid=uuid4())
         assert len(stmts) == 2
 
         object_stmt = stmts[0]
@@ -124,7 +125,7 @@ class TestPermissionPolicyCondition(_TestPermissionPolicy):
         time = datetime.fromtimestamp(timestamp)  # avoid rounding error
         random_base_permission.to_timestamp = timestamp
 
-        stmts = random_base_permission.map_to_bucket_policy_statement(user_id=random_lower_string())
+        stmts = random_base_permission.map_to_bucket_policy_statement(uid=uuid4())
         assert len(stmts) == 2
 
         object_stmt = stmts[0]
@@ -153,7 +154,7 @@ class TestPermissionPolicyCondition(_TestPermissionPolicy):
         time = datetime.fromtimestamp(timestamp)  # avoid rounding error
         random_base_permission.from_timestamp = timestamp
 
-        stmts = random_base_permission.map_to_bucket_policy_statement(user_id=random_lower_string())
+        stmts = random_base_permission.map_to_bucket_policy_statement(uid=uuid4())
         assert len(stmts) == 2
 
         object_stmt = stmts[0]
@@ -179,7 +180,7 @@ class TestPermissionPolicyCondition(_TestPermissionPolicy):
         """
         random_base_permission.file_prefix = random_lower_string(length=8) + "/" + random_lower_string(length=8) + "/"
 
-        stmts = random_base_permission.map_to_bucket_policy_statement(user_id=random_lower_string())
+        stmts = random_base_permission.map_to_bucket_policy_statement(uid=uuid4())
         assert len(stmts) == 2
 
         object_stmt = stmts[0]
diff --git a/app/tests/utils/bucket.py b/app/tests/utils/bucket.py
index 76b5634a4f066010829961ce74aa5172d9319d10..47d32f264acd984084cab8d8d6b1dd7327b642a7 100644
--- a/app/tests/utils/bucket.py
+++ b/app/tests/utils/bucket.py
@@ -1,14 +1,23 @@
 from datetime import datetime
 from typing import Optional
+from uuid import UUID
 
-import pytest
 from clowmdb.models import Bucket, BucketPermission, User
+from sqlalchemy import delete
 from sqlalchemy.ext.asyncio import AsyncSession
 
 from .utils import random_lower_string
 
 
-@pytest.mark.asyncio
+async def delete_bucket(db: AsyncSession, bucket_name: str) -> None:
+    await db.execute(
+        delete(Bucket).where(
+            Bucket.name == bucket_name,
+        )
+    )
+    await db.commit()
+
+
 async def create_random_bucket(db: AsyncSession, user: User) -> Bucket:
     """
     Creates a random bucket in the database.
@@ -28,18 +37,17 @@ async def create_random_bucket(db: AsyncSession, user: User) -> Bucket:
     bucket = Bucket(
         name=random_lower_string(),
         description=random_lower_string(length=127),
-        owner_id=user.uid,
+        _owner_id=user.uid.bytes,
     )
     db.add(bucket)
     await db.commit()
     return bucket
 
 
-@pytest.mark.asyncio
 async def add_permission_for_bucket(
     db: AsyncSession,
     bucket_name: str,
-    uid: str,
+    uid: UUID,
     from_: Optional[datetime] = None,
     to: Optional[datetime] = None,
     permission: BucketPermission.Permission = BucketPermission.Permission.READ,
@@ -63,7 +71,7 @@ async def add_permission_for_bucket(
         The permission the user is granted.
     """
     perm = BucketPermission(
-        user_id=uid,
+        _uid=uid.bytes,
         bucket_name=bucket_name,
         from_=round(from_.timestamp()) if from_ is not None else None,
         to=round(to.timestamp()) if to is not None else None,
diff --git a/app/tests/utils/cleanup.py b/app/tests/utils/cleanup.py
new file mode 100644
index 0000000000000000000000000000000000000000..1657553e8803c394664ca59753a1841042f8cee6
--- /dev/null
+++ b/app/tests/utils/cleanup.py
@@ -0,0 +1,66 @@
+from inspect import iscoroutinefunction
+from typing import Any, Awaitable, Callable, Generic, List, ParamSpec, TypeVar
+
+P = ParamSpec("P")
+T = TypeVar("T")
+
+
+class Job(Generic[P, T]):
+    def __init__(self, func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> None:
+        self.func = func
+        self.args = args
+        self.kwargs = kwargs
+
+    @property
+    def is_async(self) -> bool:
+        return iscoroutinefunction(self.func)
+
+    def __call__(self) -> T:
+        return self.func(*self.args, **self.kwargs)
+
+
+class AsyncJob(Job):
+    def __init__(self, func: Callable[P, Awaitable[T]], *args: P.args, **kwargs: P.kwargs) -> None:
+        super().__init__(func, *args, **kwargs)
+        assert iscoroutinefunction(self.func)
+
+    async def __call__(self) -> T:
+        return await super().__call__()
+
+
+class CleanupList:
+    """
+    Helper object to hold a queue of functions that can be executed later
+    """
+
+    def __init__(self) -> None:
+        self.queue: List[Job] = []
+
+    def add_task(self, func: Callable[P, Any], *args: P.args, **kwargs: P.kwargs) -> None:
+        """
+        Add a (async) function to the queue.
+
+        Parameters
+        ----------
+        func : Callable[P, Any]
+            Function to register.
+        args : P.args
+            Arguments to the function.
+        kwargs : P.kwargs
+            Keyword arguments to the function.
+        """
+        if iscoroutinefunction(func):
+            self.queue.append(AsyncJob(func, *args, **kwargs))
+        else:
+            self.queue.append(Job(func, *args, **kwargs))
+
+    async def empty_queue(self) -> None:
+        """
+        Empty the queue by executing the registered functions.
+        """
+        while len(self.queue) > 0:
+            func = self.queue.pop()
+            if func.is_async:
+                await func()
+            else:
+                func()
diff --git a/app/tests/utils/user.py b/app/tests/utils/user.py
index 109cc0a9e56bf546a3d23c060026f70ceb31f200..417f6872b77b404634e66dc1db07bc63903de7f7 100644
--- a/app/tests/utils/user.py
+++ b/app/tests/utils/user.py
@@ -1,6 +1,7 @@
 from dataclasses import dataclass
-from datetime import datetime, timedelta
+from datetime import UTC, datetime, timedelta
 from typing import Dict
+from uuid import UUID
 
 import pytest
 from authlib.jose import JsonWebToken
@@ -18,7 +19,7 @@ class UserWithAuthHeader:
     user: User
 
 
-def get_authorization_headers(uid: str, secret: str = "SuperSecret") -> Dict[str, str]:
+def get_authorization_headers(uid: UUID, secret: str = "SuperSecret") -> Dict[str, str]:
     """
     Create a valid JWT and return the correct headers for subsequent requests.
 
@@ -33,7 +34,7 @@ def get_authorization_headers(uid: str, secret: str = "SuperSecret") -> Dict[str
     headers : Dict[str,str]
         HTTP Headers to authorize each request.
     """
-    to_encode = {"sub": uid, "exp": datetime.utcnow() + timedelta(hours=1)}
+    to_encode = {"sub": str(uid), "exp": datetime.now(UTC) + timedelta(hours=1)}
     encoded_jwt = _jwt.encode(header={"alg": "HS256"}, payload=to_encode, key=secret)
 
     headers = {"Authorization": f"Bearer {encoded_jwt.decode('utf-8')}"}
@@ -84,7 +85,7 @@ async def create_random_user(db: AsyncSession) -> User:
         Newly created user.
     """
     user = User(
-        uid=random_lower_string(),
+        lifescience_id=random_lower_string(),
         display_name=random_lower_string(),
     )
     db.add(user)
diff --git a/gunicorn_conf.py b/gunicorn_conf.py
new file mode 100644
index 0000000000000000000000000000000000000000..7dd141dfc55f98de00b07daffea9a898677e9df4
--- /dev/null
+++ b/gunicorn_conf.py
@@ -0,0 +1,67 @@
+import json
+import multiprocessing
+import os
+
+workers_per_core_str = os.getenv("WORKERS_PER_CORE", "1")
+max_workers_str = os.getenv("MAX_WORKERS")
+use_max_workers = None
+if max_workers_str:
+    use_max_workers = int(max_workers_str)
+web_concurrency_str = os.getenv("WEB_CONCURRENCY", None)
+
+host = os.getenv("HOST", "0.0.0.0")
+port = os.getenv("PORT", "80")
+bind_env = os.getenv("BIND", None)
+use_loglevel = os.getenv("LOG_LEVEL", "info")
+if bind_env:
+    use_bind = bind_env
+else:
+    use_bind = f"{host}:{port}"
+
+cores = multiprocessing.cpu_count()
+workers_per_core = float(workers_per_core_str)
+default_web_concurrency = workers_per_core * cores
+if web_concurrency_str:
+    web_concurrency = int(web_concurrency_str)
+    assert web_concurrency > 0
+else:
+    web_concurrency = max(int(default_web_concurrency), 2)
+    if use_max_workers:
+        web_concurrency = min(web_concurrency, use_max_workers)
+accesslog_var = os.getenv("ACCESS_LOG", "-")
+use_accesslog = accesslog_var or None
+errorlog_var = os.getenv("ERROR_LOG", "-")
+use_errorlog = errorlog_var or None
+graceful_timeout_str = os.getenv("GRACEFUL_TIMEOUT", "120")
+timeout_str = os.getenv("TIMEOUT", "120")
+keepalive_str = os.getenv("KEEP_ALIVE", "5")
+
+# Gunicorn config variables
+loglevel = use_loglevel
+workers = web_concurrency
+bind = use_bind
+errorlog = use_errorlog
+worker_tmp_dir = "/dev/shm"
+accesslog = use_accesslog
+graceful_timeout = int(graceful_timeout_str)
+timeout = int(timeout_str)
+keepalive = int(keepalive_str)
+
+
+# For debugging and testing
+log_data = {
+    "loglevel": loglevel,
+    "workers": workers,
+    "bind": bind,
+    "graceful_timeout": graceful_timeout,
+    "timeout": timeout,
+    "keepalive": keepalive,
+    "errorlog": errorlog,
+    "accesslog": accesslog,
+    # Additional, non-gunicorn variables
+    "workers_per_core": workers_per_core,
+    "use_max_workers": use_max_workers,
+    "host": host,
+    "port": port,
+}
+print(json.dumps(log_data))
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 05c18639d37836dec2a6ad7ab360790a16f3c573..9d1bcf30b0fcbd3281e1827154613d7a313d0792 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -2,15 +2,16 @@
 pytest>=7.4.0,<7.5.0
 pytest-asyncio>=0.21.0,<0.22.0
 pytest-cov>=4.1.0,<4.2.0
-coverage[toml]>=7.3.0,<7.4.0
+coverage[toml]>=7.4.0,<7.5.0
 # Linters
 ruff>=0.1.0,<0.2.0
-black>=23.11.0,<23.12.0
-isort>=5.12.0,<5.13.0
-mypy>=1.7.0,<1.8.0
+black>=23.12.0,<24.1.0
+isort>=5.13.0,<5.14.0
+mypy>=1.8.0,<1.9.0
 # stubs for mypy
-boto3-stubs-lite[s3]>=1.33.0,<1.34.0
+boto3-stubs-lite[s3]>=1.34.0,<1.35.0
 types-requests
 # Miscellaneous
-pre-commit>=3.5.0,<3.6.0
+pre-commit>=3.6.0,<3.7.0
 python-dotenv
+uvicorn>=0.27.0,<0.28.0
diff --git a/requirements.txt b/requirements.txt
index bf948033cd93090ac324ede1bb973d3524f3dc08..ddcfc6cd3b3b34b3839c96a0147d8f3dca097669 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,24 +1,22 @@
 --extra-index-url https://gitlab.ub.uni-bielefeld.de/api/v4/projects/5493/packages/pypi/simple
-clowmdb>=2.3.0,<2.4.0
+clowmdb>=3.0.0,<3.1.0
 
 # Webserver packages
-anyio>=3.7.0,<4.0.0
-fastapi>=0.104.0,<0.105.0
+fastapi>=0.109.0,<0.110.0
 pydantic>=2.5.0,<2.6.0
 pydantic-settings>=2.1.0,<2.2.0
-uvicorn>=0.24.0,<0.25.0
 # Database packages
 PyMySQL>=1.1.0,<1.2.0
 SQLAlchemy>=2.0.0,<2.1.0
 aiomysql>=0.2.0,<0.3.0
 # Security packages
-authlib>=1.2.0,<1.3.0
+authlib>=1.3.0,<1.4.0
 # Ceph and S3 packages
-boto3>=1.33.0,<1.34.0
+boto3>=1.34.0,<1.35.0
 rgwadmin>=2.4.0,<2.5.0
 # Miscellaneous
 tenacity>=8.2.0,<8.3.0
-httpx>=0.25.0,<0.26.0
+httpx>=0.26.0,<0.27.0
 itsdangerous
 # Monitoring
 opentelemetry-instrumentation-fastapi
diff --git a/start_service.sh b/start_service.sh
deleted file mode 100755
index 9fca45763981e431c287f06b937e0385f59892a5..0000000000000000000000000000000000000000
--- a/start_service.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#! /usr/bin/env bash
-
-./scripts/prestart
-
-# Start webserver
-uvicorn app.main:app --host 0.0.0.0 --port 8000 --no-server-header
diff --git a/start_service_gunicorn.sh b/start_service_gunicorn.sh
new file mode 100755
index 0000000000000000000000000000000000000000..1b65c1164b89af0c3f9da44d8135959f614d0c9d
--- /dev/null
+++ b/start_service_gunicorn.sh
@@ -0,0 +1,7 @@
+#! /usr/bin/env sh
+set -e
+
+./prestart.sh
+
+# Start Gunicorn
+exec gunicorn -k uvicorn.workers.UvicornWorker -c /app/gunicorn_conf.py app.main:app
diff --git a/start_service_uvicorn.sh b/start_service_uvicorn.sh
new file mode 100755
index 0000000000000000000000000000000000000000..392596854a58728ac008b79d7f5d5178d1c90c2b
--- /dev/null
+++ b/start_service_uvicorn.sh
@@ -0,0 +1,7 @@
+#! /usr/bin/env bash
+set -e
+
+./prestart.sh
+
+# Start webserver
+uvicorn app.main:app --host 0.0.0.0 --port "$PORT" --no-server-header