From c525020102518708a8a03d03605c652029a5b79e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20G=C3=B6bel?= <dgoebel@techfak.uni-bielefeld.de>
Date: Wed, 27 Jul 2022 09:46:56 +0200
Subject: [PATCH] Miscellaneous Improvents to clean up the code/repository

---
 ProxyAPI/.dockerignore => .dockerignore       |   2 +
 ProxyAPI/.flake8 => .flake8                   |   0
 ProxyAPI/.gitignore => .gitignore             |   1 +
 .gitlab-ci.yml                                |  21 ++-
 ...mit-config.yaml => .pre-commit-config.yaml |  17 +--
 ProxyAPI/README.md => DEVELOPING.md           |  73 +++-------
 ProxyAPI/Dockerfile => Dockerfile             |   2 +-
 README.md                                     | 136 ++++++------------
 ProxyAPI/alembic.ini => alembic.ini           |   0
 {ProxyAPI/alembic => alembic}/README          |   0
 {ProxyAPI/alembic => alembic}/env.py          |   0
 {ProxyAPI/alembic => alembic}/script.py.mako  |   0
 ...21b5759004_create_user_and_bucket_table.py |   0
 ...add_username_and_display_name_and_drop_.py |   0
 .../cafa1e01b782_create_permission_table.py   |   0
 {ProxyAPI/app => app}/__init__.py             |   0
 {ProxyAPI/app => app}/api/__init__.py         |   0
 {ProxyAPI/app => app}/api/api.py              |   0
 {ProxyAPI/app => app}/api/dependencies.py     |   0
 .../app => app}/api/endpoints/__init__.py     |   0
 .../api/endpoints/bucket_permissions.py       |   0
 .../app => app}/api/endpoints/buckets.py      |   0
 {ProxyAPI/app => app}/api/endpoints/login.py  |   2 +-
 {ProxyAPI/app => app}/api/endpoints/users.py  |   0
 .../api/miscellaneous_endpoints.py            |   0
 {ProxyAPI/app => app}/ceph/__init__.py        |   0
 {ProxyAPI/app => app}/ceph/rgw.py             |   0
 .../app => app}/check_ceph_connection.py      |   0
 .../app => app}/check_database_connection.py  |   0
 .../app => app}/check_oidc_connection.py      |   0
 {ProxyAPI/app => app}/core/__init__.py        |   0
 {ProxyAPI/app => app}/core/config.py          |   0
 {ProxyAPI/app => app}/core/security.py        |   0
 {ProxyAPI/app => app}/crud/__init__.py        |   0
 {ProxyAPI/app => app}/crud/crud_bucket.py     |   0
 .../crud/crud_bucket_permission.py            |   0
 {ProxyAPI/app => app}/crud/crud_user.py       |   0
 {ProxyAPI/app => app}/db/__init__.py          |   0
 {ProxyAPI/app => app}/db/base.py              |   0
 {ProxyAPI/app => app}/db/base_class.py        |   0
 {ProxyAPI/app => app}/db/session.py           |   0
 {ProxyAPI/app => app}/main.py                 |  17 ++-
 {ProxyAPI/app => app}/models/__init__.py      |   0
 {ProxyAPI/app => app}/models/bucket.py        |   0
 .../app => app}/models/bucket_permission.py   |   0
 {ProxyAPI/app => app}/models/user.py          |   0
 {ProxyAPI/app => app}/schemas/__init__.py     |   0
 {ProxyAPI/app => app}/schemas/bucket.py       |   0
 .../app => app}/schemas/bucket_permission.py  |   0
 {ProxyAPI/app => app}/schemas/security.py     |   0
 {ProxyAPI/app => app}/schemas/user.py         |   0
 {ProxyAPI/app => app}/tests/__init__.py       |   0
 {ProxyAPI/app => app}/tests/api/__init__.py   |   0
 .../tests/api/test_bucket_permissions.py      |   3 +-
 .../app => app}/tests/api/test_buckets.py     |   3 +-
 {ProxyAPI/app => app}/tests/api/test_login.py |   2 +-
 .../app => app}/tests/api/test_s3_keys.py     |   3 +-
 .../app => app}/tests/api/test_s3_objects.py  |   3 +-
 .../app => app}/tests/api/test_security.py    |   3 +-
 {ProxyAPI/app => app}/tests/api/test_users.py |   3 +-
 {ProxyAPI/app => app}/tests/conftest.py       |   0
 {ProxyAPI/app => app}/tests/crud/__init__.py  |   0
 .../app => app}/tests/crud/test_bucket.py     |   0
 .../tests/crud/test_bucket_permission.py      |   0
 {ProxyAPI/app => app}/tests/crud/test_user.py |   0
 {ProxyAPI/app => app}/tests/mocks/__init__.py |   0
 .../app => app}/tests/mocks/mock_rgw_admin.py |   0
 .../tests/mocks/mock_s3_resource.py           |   0
 {ProxyAPI/app => app}/tests/unit/__init__.py  |   0
 .../tests/unit/test_bucket_name.py            |   0
 .../unit/test_bucket_permission_scheme.py     |   0
 {ProxyAPI/app => app}/tests/utils/__init__.py |   0
 {ProxyAPI/app => app}/tests/utils/bucket.py   |   0
 {ProxyAPI/app => app}/tests/utils/user.py     |   0
 {ProxyAPI/app => app}/tests/utils/utils.py    |   0
 ceph/README.md                                |   2 +-
 ceph/playbook/files/public_keys/dgoebel       |   2 +-
 ceph/playbook/files/public_keys/jkrueger      |   2 +-
 ceph/playbook/site.yml                        |   2 +-
 .../cloud_object_storage.svg                  |   0
 .../clients_config.json                       |   3 +-
 .../identity_resources.json                   |   0
 .../server_options.json                       |   0
 .../users_config.json                         |   0
 ProxyAPI/pyproject.toml => pyproject.toml     |   0
 ...quirements-dev.txt => requirements-dev.txt |   0
 ProxyAPI/requirements.txt => requirements.txt |   0
 .../scripts => scripts}/format-imports.sh     |   0
 {ProxyAPI/scripts => scripts}/format.sh       |   0
 {ProxyAPI/scripts => scripts}/lint.sh         |   0
 {ProxyAPI/scripts => scripts}/test.sh         |   0
 ProxyAPI/start_service.sh => start_service.sh |   0
 ProxyAPI/tests-start.sh => tests-start.sh     |   0
 traefik_dev/routes.toml                       |  28 ++++
 traefik_dev/traefik.toml                      |   7 +
 95 files changed, 148 insertions(+), 189 deletions(-)
 rename ProxyAPI/.dockerignore => .dockerignore (89%)
 rename ProxyAPI/.flake8 => .flake8 (100%)
 rename ProxyAPI/.gitignore => .gitignore (89%)
 rename ProxyAPI/.pre-commit-config.yaml => .pre-commit-config.yaml (76%)
 rename ProxyAPI/README.md => DEVELOPING.md (51%)
 rename ProxyAPI/Dockerfile => Dockerfile (94%)
 rename ProxyAPI/alembic.ini => alembic.ini (100%)
 rename {ProxyAPI/alembic => alembic}/README (100%)
 rename {ProxyAPI/alembic => alembic}/env.py (100%)
 rename {ProxyAPI/alembic => alembic}/script.py.mako (100%)
 rename {ProxyAPI/alembic => alembic}/versions/5521b5759004_create_user_and_bucket_table.py (100%)
 rename {ProxyAPI/alembic => alembic}/versions/83a3a47a6351_add_username_and_display_name_and_drop_.py (100%)
 rename {ProxyAPI/alembic => alembic}/versions/cafa1e01b782_create_permission_table.py (100%)
 rename {ProxyAPI/app => app}/__init__.py (100%)
 rename {ProxyAPI/app => app}/api/__init__.py (100%)
 rename {ProxyAPI/app => app}/api/api.py (100%)
 rename {ProxyAPI/app => app}/api/dependencies.py (100%)
 rename {ProxyAPI/app => app}/api/endpoints/__init__.py (100%)
 rename {ProxyAPI/app => app}/api/endpoints/bucket_permissions.py (100%)
 rename {ProxyAPI/app => app}/api/endpoints/buckets.py (100%)
 rename {ProxyAPI/app => app}/api/endpoints/login.py (97%)
 rename {ProxyAPI/app => app}/api/endpoints/users.py (100%)
 rename {ProxyAPI/app => app}/api/miscellaneous_endpoints.py (100%)
 rename {ProxyAPI/app => app}/ceph/__init__.py (100%)
 rename {ProxyAPI/app => app}/ceph/rgw.py (100%)
 rename {ProxyAPI/app => app}/check_ceph_connection.py (100%)
 rename {ProxyAPI/app => app}/check_database_connection.py (100%)
 rename {ProxyAPI/app => app}/check_oidc_connection.py (100%)
 rename {ProxyAPI/app => app}/core/__init__.py (100%)
 rename {ProxyAPI/app => app}/core/config.py (100%)
 rename {ProxyAPI/app => app}/core/security.py (100%)
 rename {ProxyAPI/app => app}/crud/__init__.py (100%)
 rename {ProxyAPI/app => app}/crud/crud_bucket.py (100%)
 rename {ProxyAPI/app => app}/crud/crud_bucket_permission.py (100%)
 rename {ProxyAPI/app => app}/crud/crud_user.py (100%)
 rename {ProxyAPI/app => app}/db/__init__.py (100%)
 rename {ProxyAPI/app => app}/db/base.py (100%)
 rename {ProxyAPI/app => app}/db/base_class.py (100%)
 rename {ProxyAPI/app => app}/db/session.py (100%)
 rename {ProxyAPI/app => app}/main.py (80%)
 rename {ProxyAPI/app => app}/models/__init__.py (100%)
 rename {ProxyAPI/app => app}/models/bucket.py (100%)
 rename {ProxyAPI/app => app}/models/bucket_permission.py (100%)
 rename {ProxyAPI/app => app}/models/user.py (100%)
 rename {ProxyAPI/app => app}/schemas/__init__.py (100%)
 rename {ProxyAPI/app => app}/schemas/bucket.py (100%)
 rename {ProxyAPI/app => app}/schemas/bucket_permission.py (100%)
 rename {ProxyAPI/app => app}/schemas/security.py (100%)
 rename {ProxyAPI/app => app}/schemas/user.py (100%)
 rename {ProxyAPI/app => app}/tests/__init__.py (100%)
 rename {ProxyAPI/app => app}/tests/api/__init__.py (100%)
 rename {ProxyAPI/app => app}/tests/api/test_bucket_permissions.py (99%)
 rename {ProxyAPI/app => app}/tests/api/test_buckets.py (99%)
 rename {ProxyAPI/app => app}/tests/api/test_login.py (98%)
 rename {ProxyAPI/app => app}/tests/api/test_s3_keys.py (98%)
 rename {ProxyAPI/app => app}/tests/api/test_s3_objects.py (98%)
 rename {ProxyAPI/app => app}/tests/api/test_security.py (96%)
 rename {ProxyAPI/app => app}/tests/api/test_users.py (97%)
 rename {ProxyAPI/app => app}/tests/conftest.py (100%)
 rename {ProxyAPI/app => app}/tests/crud/__init__.py (100%)
 rename {ProxyAPI/app => app}/tests/crud/test_bucket.py (100%)
 rename {ProxyAPI/app => app}/tests/crud/test_bucket_permission.py (100%)
 rename {ProxyAPI/app => app}/tests/crud/test_user.py (100%)
 rename {ProxyAPI/app => app}/tests/mocks/__init__.py (100%)
 rename {ProxyAPI/app => app}/tests/mocks/mock_rgw_admin.py (100%)
 rename {ProxyAPI/app => app}/tests/mocks/mock_s3_resource.py (100%)
 rename {ProxyAPI/app => app}/tests/unit/__init__.py (100%)
 rename {ProxyAPI/app => app}/tests/unit/test_bucket_name.py (100%)
 rename {ProxyAPI/app => app}/tests/unit/test_bucket_permission_scheme.py (100%)
 rename {ProxyAPI/app => app}/tests/utils/__init__.py (100%)
 rename {ProxyAPI/app => app}/tests/utils/bucket.py (100%)
 rename {ProxyAPI/app => app}/tests/utils/user.py (100%)
 rename {ProxyAPI/app => app}/tests/utils/utils.py (100%)
 rename {ProxyAPI/figures => figures}/cloud_object_storage.svg (100%)
 rename {ProxyAPI/oidc_dev_example => oidc_dev_example}/clients_config.json (76%)
 rename {ProxyAPI/oidc_dev_example => oidc_dev_example}/identity_resources.json (100%)
 rename {ProxyAPI/oidc_dev_example => oidc_dev_example}/server_options.json (100%)
 rename {ProxyAPI/oidc_dev_example => oidc_dev_example}/users_config.json (100%)
 rename ProxyAPI/pyproject.toml => pyproject.toml (100%)
 rename ProxyAPI/requirements-dev.txt => requirements-dev.txt (100%)
 rename ProxyAPI/requirements.txt => requirements.txt (100%)
 rename {ProxyAPI/scripts => scripts}/format-imports.sh (100%)
 rename {ProxyAPI/scripts => scripts}/format.sh (100%)
 rename {ProxyAPI/scripts => scripts}/lint.sh (100%)
 rename {ProxyAPI/scripts => scripts}/test.sh (100%)
 rename ProxyAPI/start_service.sh => start_service.sh (100%)
 rename ProxyAPI/tests-start.sh => tests-start.sh (100%)
 create mode 100644 traefik_dev/routes.toml
 create mode 100644 traefik_dev/traefik.toml

diff --git a/ProxyAPI/.dockerignore b/.dockerignore
similarity index 89%
rename from ProxyAPI/.dockerignore
rename to .dockerignore
index 572726d..21c2745 100644
--- a/ProxyAPI/.dockerignore
+++ b/.dockerignore
@@ -13,3 +13,5 @@ app/tests
 figures/
 oidc_dev_example
 oidc_dev/
+traefik_dev
+ceph
diff --git a/ProxyAPI/.flake8 b/.flake8
similarity index 100%
rename from ProxyAPI/.flake8
rename to .flake8
diff --git a/ProxyAPI/.gitignore b/.gitignore
similarity index 89%
rename from ProxyAPI/.gitignore
rename to .gitignore
index 3fab9dc..1a91d71 100644
--- a/ProxyAPI/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ venv/
 ENV/
 .coverage
 oidc_dev/
+traefik
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 71c7337..113c8ca 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -2,8 +2,7 @@ image: python:3.10-slim
 
 variables:
   PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
-  BASE_DIR: "$CI_PROJECT_DIR/ProxyAPI"
-  PYTHONPATH: "$CI_PROJECT_DIR/ProxyAPI"
+  PYTHONPATH: "$CI_PROJECT_DIR"
   OBJECT_GATEWAY_URI: "http://127.0.0.1:8000"
   CEPH_ACCESS_KEY: ""
   CEPH_SECRET_KEY: ""
@@ -21,7 +20,6 @@ before_script:
   - pip install virtualenv
   - virtualenv venv
   - source venv/bin/activate
-  - cd $BASE_DIR
   - python -m pip install -r requirements.txt
   - python -m pip install -r requirements-dev.txt
 
@@ -60,9 +58,9 @@ integration-test-job: # Runs integration tests with the database
     - mv .coverage coverage-integration
   artifacts:
     paths:
-      - $BASE_DIR/coverage-integration/.coverage
+      - $CI_PROJECT_DIR/coverage-integration/.coverage
     reports:
-      junit: $BASE_DIR/integration-report.xml
+      junit: $CI_PROJECT_DIR/integration-report.xml
 
 e2e-test-job: # Runs e2e tests on the API endpoints
   stage: test
@@ -97,9 +95,9 @@ e2e-test-job: # Runs e2e tests on the API endpoints
     - mv .coverage coverage-e2e
   artifacts:
     paths:
-      - $BASE_DIR/coverage-e2e/.coverage
+      - $CI_PROJECT_DIR/coverage-e2e/.coverage
     reports:
-      junit: $BASE_DIR/e2e-report.xml
+      junit: $CI_PROJECT_DIR/e2e-report.xml
 
 unit-test-job: # Runs unit tests
   stage: test
@@ -109,9 +107,9 @@ unit-test-job: # Runs unit tests
     - mv .coverage coverage-unit
   artifacts:
     paths:
-      - $BASE_DIR/coverage-unit/.coverage
+      - $CI_PROJECT_DIR/coverage-unit/.coverage
     reports:
-      junit: $BASE_DIR/unit-report.xml
+      junit: $CI_PROJECT_DIR/unit-report.xml
 
 combine-test-coverage-job: # Combine coverage reports from different test jobs
   stage: test
@@ -123,10 +121,9 @@ combine-test-coverage-job: # Combine coverage reports from different test jobs
     - job: "unit-test-job"
       artifacts: true
   script:
-    - coverage combine coverage-e2e/.coverage coverage-integration/.coverage coverage-unit
+    - coverage combine coverage-e2e/.coverage coverage-integration/.coverage coverage-unit/.coverage
     - coverage report
-    - cd ..
-    - coverage xml --data-file=$BASE_DIR/.coverage -o coverage.xml
+    - coverage xml --data-file=$CI_PROJECT_DIR/.coverage -o coverage.xml
   coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/'
   artifacts:
     reports:
diff --git a/ProxyAPI/.pre-commit-config.yaml b/.pre-commit-config.yaml
similarity index 76%
rename from ProxyAPI/.pre-commit-config.yaml
rename to .pre-commit-config.yaml
index b3a4d10..6e0e5b0 100644
--- a/ProxyAPI/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -5,38 +5,33 @@ repos:
     rev: v4.2.0
     hooks:
     -   id: end-of-file-fixer
-        files: ProxyAPI
     -   id: check-added-large-files
-        files: ProxyAPI
     -   id: check-toml
     -   id: check-docstring-first
-        files: ProxyAPI
     -   id: detect-private-key
     -   id: trailing-whitespace
     -   id: check-yaml
     -   id: debug-statements
-        files: ProxyAPI
     -   id: check-merge-conflict
     -   id: check-ast
-        files: ProxyAPI
 -   repo: https://github.com/psf/black
     rev: 22.3.0
     hooks:
     -   id: black
-        files: ProxyAPI/app
+        files: app
         args: [--check]
 -   repo: https://github.com/PyCQA/flake8
     rev: 4.0.1
     hooks:
     -   id: flake8
-        files: ProxyAPI/app
-        args: [--config=ProxyAPI/.flake8]
+        files: app
+        args: [--config=.flake8]
 -   repo: https://github.com/pre-commit/mirrors-mypy
     rev: v0.960
     hooks:
     -   id: mypy
-        files: ProxyAPI/app
-        args: [--config=ProxyAPI/pyproject.toml]
+        files: app
+        args: [--config=pyproject.toml]
         additional_dependencies:
             - sqlalchemy2-stubs
             - boto3-stubs-lite[s3]
@@ -47,5 +42,5 @@ repos:
     rev: 5.10.1
     hooks:
     -   id: isort
-        files: ProxyAPI/app
+        files: app
         args: [-c]
diff --git a/ProxyAPI/README.md b/DEVELOPING.md
similarity index 51%
rename from ProxyAPI/README.md
rename to DEVELOPING.md
index e4c1e4e..18bfe67 100644
--- a/ProxyAPI/README.md
+++ b/DEVELOPING.md
@@ -1,52 +1,3 @@
-# S3 Proxy API
-
-## Description
-Openstack is shipping with an integrated UI to access the Object Store provided by ceph. Unfortunately, this UI does not allow
-fine-grained control who can access a bucket or object. You can either make it accessible for everyone or nobody, but
-Ceph can do this and much more. 👎
-This is the backend for a new UI which can leverage the additional powerful functionality provided by Ceph in a
-user-friendly manner. 👍
-
-| Feature                     | Openstack Integration | New UI |
-|-----------------------------|:---------------------:|:------:|
-| Create / Delete Buckets UI  |           ✅           |   ✅    |
-| Create / Delete Buckets CLI |           ✅           |   ❌    |
-| Upload / Download Objects   |           ✅           |   ✅    |
-| Fine-grained Access Control |           ❌           |   ✅    |
-
-### Concept
-![Visualization of Concept](figures/cloud_object_storage.svg)
-
-## Environment Variables
-
-### Mandatory / Recommended Variables
-
-| Variable             | Default | Value                 | Description                           |
-|----------------------|---------|-----------------------|---------------------------------------|
-| `SECRET_KEY`         | random  | \<random key>         | Secret key to sign JWT                |
-| `DB_HOST`            | unset   | <db hostname / IP>    | IP or Hostname Adress of DB           |
-| `DB_PORT`            | 3306    | Number                | Port of the database                  |
-| `DB_USER`            | unset   | \<db username>        | Username of the database user         |
-| `DB_PASSWORD`        | unset   | \<db password>        | Password of the database user         |
-| `DB_DATABASE`        | unset   | \<db name>            | Name of the database                  |
-| `OBJECT_GATEWAY_URI` | unset   | HTTP URL              | HTTP URL of the Ceph Object Gateway   |
-| `CEPH_ACCESS_KEY`    | unset   | \<access key>         | Ceph access key with admin privileges |
-| `CEPH_SECRET_KEY`    | unset   | \<secret key>         | Ceph secret key with admin privileges |
-| `OIDC_CLIENT_ID`     | unset   | \<OIDC client id>     | Client ID from the OIDC provider      |
-| `OIDC_CLIENT_SECRET` | unset   | \<OIDC client secret> | Client Secret from the OIDC provider  |
-| `OIDC_BASE_URI`      | unset   | HTTP URL              | HTTP URL of the OIDC Provider         |
-
-### Optional Variables
-
-| Variable                    | Default                             | Value                       | Description                                                                           |
-|-----------------------------|-------------------------------------|-----------------------------|---------------------------------------------------------------------------------------|
-| `DOMAIN`                    | `localhost`                         | string                      | Domain under which the service will be hosted.                                        |
-| `API_PREFIX`                | `/api`                              | URL path                    | Prefix before every URL path                                                          |
-| `JWT_TOKEN_EXPIRE_MINUTES`  | 8 days                              | number                      | Minutes till a JWT expires                                                            |
-| `BACKEND_CORS_ORIGINS`      | `[]`                                | json formatted list of urls | List of valid CORS origins                                                            |
-| `SQLALCHEMY_VERBOSE_LOGGER` | `false`                             | `<"true"&#x7c;"false">`     | Enables verbose SQL output.<br>Should be `false` in production                        |
-| `OIDC_META_INFO_PATH`       | `/.well-known/openid-configuration` | URL path                    | Path to the OIDC configuration file<br> Will be concatenated with the `OIDC_BASE_URI` |
-
 ## Development Setup
 
 ### Python Setup 🐍
@@ -79,7 +30,10 @@ pre-commit install
 
 ### Ceph Setup
 For how to set up a ceph cluster or how to connect to an existing one see
-the [documentation in the ceph folder](../ceph/README.md).
+the [documentation in the ceph folder](ceph/README.md).
+
+A user with `user` capabilities should be created, e.g.<br>
+`radosgw-admin user create --uid=myadmin --caps="users=*"`
 
 ### Database Setup
 #### Dev database
@@ -117,7 +71,7 @@ docker run --name proxyapi_testdb \
 ```
 
 ### Dev OIDC Provider Setup
-To avoid the complex process of connecting the local machine with the LifeScience AAI Dev server, a simple [OIDC provider](https://github.com/Soluto/oidc-server-mock)
+To avoid the complex process of connecting the local machine with the LifeScience AAI Test server, a simple [OIDC provider](https://github.com/Soluto/oidc-server-mock)
 can be setup with Docker.<br>
 Copy the `oidc_dev_example` directory to `oidc_dev`
 ```shell
@@ -143,6 +97,23 @@ docker run --name proxyapi_oidc_provider \
 ```
 Set the env variables `OIDC_BASE_URI` to `http://localhost:8002` and `OIDC_CLIENT_SECRET` / `OIDC_CLIENT_ID` to their appropriate value.
 
+### Reverse Proxy Setup
+The `API_PREFIX` is handles on the level of the reverse proxy. This simplifies the routing in the code and the cooperation with the [Frontend](https://gitlab.ub.uni-bielefeld.de/denbi/object-storage-access-ui).
+An simple Traefik reverse proxy configuration is stored in the repository.
+
+[Traefik](https://traefik.io/) is a reverse Proxy written in Go.
+To use it, download the [`traefik`](https://github.com/traefik/traefik/releases) binary and start it with
+```shell
+cd traefik_dev
+/path/to/binary/traefik --configFile=traefik.toml
+```
+The provided configuration does the following things
+ * It forwards all request to http://localhost:9999/api/* to http://localhost:8080 (this backend)
+ * It strips the prefix `/api` before it forwards the request to the backend
+ * All other request will be forwarded to http://localhost:5173, the corresponding dev [Frontend](https://gitlab.ub.uni-bielefeld.de/denbi/object-storage-access-ui)
+
+You don't have to use Traefik for that. You can use any reverse proxy for this task, like [Caddy](https://caddyserver.com/), [HAProxy](https://www.haproxy.org/) or [nginx](https://nginx.org/en/).<br>
+
 ### Run Dev Server
 Export all necessary environment variables or create a `.env` file.<br>
 Run the dev server with live reload after changes
diff --git a/ProxyAPI/Dockerfile b/Dockerfile
similarity index 94%
rename from ProxyAPI/Dockerfile
rename to Dockerfile
index c61cb49..e4434f1 100644
--- a/ProxyAPI/Dockerfile
+++ b/Dockerfile
@@ -7,7 +7,7 @@ EXPOSE 80
 RUN apt-get update && apt-get -y install dumb-init curl
 ENTRYPOINT ["/usr/bin/dumb-init", "--"]
 
-HEALTHCHECK --interval=35s --timeout=4s CMD curl -f http://localhost/api/health || exit 1
+HEALTHCHECK --interval=35s --timeout=4s CMD curl -f http://localhost/health || exit 1
 
 COPY requirements.txt ./requirements.txt
 
diff --git a/README.md b/README.md
index 8608b6c..9043c10 100644
--- a/README.md
+++ b/README.md
@@ -1,92 +1,48 @@
-# Object Storage Access
-
-
-
-## Getting started
-
-To make it easy for you to get started with GitLab, here's a list of recommended next steps.
-
-Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
-
-## Add your files
-
-- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
-- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
-
-```
-cd existing_repo
-git remote add origin https://gitlab.ub.uni-bielefeld.de/denbi/object-storage-access.git
-git branch -M main
-git push -uf origin main
-```
-
-## Integrate with your tools
-
-- [ ] [Set up project integrations](https://gitlab.ub.uni-bielefeld.de/denbi/object-storage-access/-/settings/integrations)
-
-## Collaborate with your team
-
-- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
-- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
-- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
-- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
-- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
-
-## Test and Deploy
-
-Use the built-in continuous integration in GitLab.
-
-- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
-- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
-- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
-- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
-- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
-
-***
-
-# Editing this README
-
-When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!).  Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
-
-## Suggestions for a good README
-Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
-
-## Name
-Choose a self-explaining name for your project.
+# S3 Proxy API
 
 ## Description
-Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
-
-## Badges
-On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
-
-## Visuals
-Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
-
-## Installation
-Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
-
-## Usage
-Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
-
-## Support
-Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
-
-## Roadmap
-If you have ideas for releases in the future, it is a good idea to list them in the README.
-
-## Contributing
-State if you are open to contributions and what your requirements are for accepting them.
-
-For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
-
-You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
-
-## Authors and acknowledgment
-Show your appreciation to those who have contributed to the project.
-
-## License
-For open source projects, say how it is licensed.
-
-## Project status
-If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
+Openstack is shipping with an integrated UI to access the Object Store provided by ceph. Unfortunately, this UI does not allow
+fine-grained control who can access a bucket or object. You can either make it accessible for everyone or nobody, but
+Ceph can do this and much more. 👎
+This is the backend for a new UI which can leverage the additional powerful functionality provided by Ceph in a
+user-friendly manner. 👍
+
+| Feature                     | Openstack Integration | New UI |
+|-----------------------------|:---------------------:|:------:|
+| Create / Delete Buckets UI  |           ✅           |   ✅    |
+| Create / Delete Buckets CLI |           ✅           |   ❌    |
+| Upload / Download Objects   |           ✅           |   ✅    |
+| Fine-grained Access Control |           ❌           |   ✅    |
+
+### Concept
+![Visualization of Concept](figures/cloud_object_storage.svg)
+
+## Environment Variables
+
+### Mandatory / Recommended Variables
+
+| Variable             | Default | Value                 | Description                           |
+|----------------------|---------|-----------------------|---------------------------------------|
+| `SECRET_KEY`         | random  | \<random key>         | Secret key to sign JWT                |
+| `DB_HOST`            | unset   | <db hostname / IP>    | IP or Hostname Adress of DB           |
+| `DB_PORT`            | 3306    | Number                | Port of the database                  |
+| `DB_USER`            | unset   | \<db username>        | Username of the database user         |
+| `DB_PASSWORD`        | unset   | \<db password>        | Password of the database user         |
+| `DB_DATABASE`        | unset   | \<db name>            | Name of the database                  |
+| `OBJECT_GATEWAY_URI` | unset   | HTTP URL              | HTTP URL of the Ceph Object Gateway   |
+| `CEPH_ACCESS_KEY`    | unset   | \<access key>         | Ceph access key with admin privileges |
+| `CEPH_SECRET_KEY`    | unset   | \<secret key>         | Ceph secret key with admin privileges |
+| `OIDC_CLIENT_ID`     | unset   | \<OIDC client id>     | Client ID from the OIDC provider      |
+| `OIDC_CLIENT_SECRET` | unset   | \<OIDC client secret> | Client Secret from the OIDC provider  |
+| `OIDC_BASE_URI`      | unset   | HTTP URL              | HTTP URL of the OIDC Provider         |
+
+### Optional Variables
+
+| Variable                    | Default                             | Value                       | Description                                                                           |
+|-----------------------------|-------------------------------------|-----------------------------|---------------------------------------------------------------------------------------|
+| `DOMAIN`                    | `localhost`                         | string                      | Domain under which the service will be hosted.                                        |
+| `API_PREFIX`                | `/api`                              | URL path                    | Prefix before every URL path                                                          |
+| `JWT_TOKEN_EXPIRE_MINUTES`  | 8 days                              | number                      | Minutes till a JWT expires                                                            |
+| `BACKEND_CORS_ORIGINS`      | `[]`                                | json formatted list of urls | List of valid CORS origins                                                            |
+| `SQLALCHEMY_VERBOSE_LOGGER` | `false`                             | `<"true"&#x7c;"false">`     | Enables verbose SQL output.<br>Should be `false` in production                        |
+| `OIDC_META_INFO_PATH`       | `/.well-known/openid-configuration` | URL path                    | Path to the OIDC configuration file<br> Will be concatenated with the `OIDC_BASE_URI` |
diff --git a/ProxyAPI/alembic.ini b/alembic.ini
similarity index 100%
rename from ProxyAPI/alembic.ini
rename to alembic.ini
diff --git a/ProxyAPI/alembic/README b/alembic/README
similarity index 100%
rename from ProxyAPI/alembic/README
rename to alembic/README
diff --git a/ProxyAPI/alembic/env.py b/alembic/env.py
similarity index 100%
rename from ProxyAPI/alembic/env.py
rename to alembic/env.py
diff --git a/ProxyAPI/alembic/script.py.mako b/alembic/script.py.mako
similarity index 100%
rename from ProxyAPI/alembic/script.py.mako
rename to alembic/script.py.mako
diff --git a/ProxyAPI/alembic/versions/5521b5759004_create_user_and_bucket_table.py b/alembic/versions/5521b5759004_create_user_and_bucket_table.py
similarity index 100%
rename from ProxyAPI/alembic/versions/5521b5759004_create_user_and_bucket_table.py
rename to alembic/versions/5521b5759004_create_user_and_bucket_table.py
diff --git a/ProxyAPI/alembic/versions/83a3a47a6351_add_username_and_display_name_and_drop_.py b/alembic/versions/83a3a47a6351_add_username_and_display_name_and_drop_.py
similarity index 100%
rename from ProxyAPI/alembic/versions/83a3a47a6351_add_username_and_display_name_and_drop_.py
rename to alembic/versions/83a3a47a6351_add_username_and_display_name_and_drop_.py
diff --git a/ProxyAPI/alembic/versions/cafa1e01b782_create_permission_table.py b/alembic/versions/cafa1e01b782_create_permission_table.py
similarity index 100%
rename from ProxyAPI/alembic/versions/cafa1e01b782_create_permission_table.py
rename to alembic/versions/cafa1e01b782_create_permission_table.py
diff --git a/ProxyAPI/app/__init__.py b/app/__init__.py
similarity index 100%
rename from ProxyAPI/app/__init__.py
rename to app/__init__.py
diff --git a/ProxyAPI/app/api/__init__.py b/app/api/__init__.py
similarity index 100%
rename from ProxyAPI/app/api/__init__.py
rename to app/api/__init__.py
diff --git a/ProxyAPI/app/api/api.py b/app/api/api.py
similarity index 100%
rename from ProxyAPI/app/api/api.py
rename to app/api/api.py
diff --git a/ProxyAPI/app/api/dependencies.py b/app/api/dependencies.py
similarity index 100%
rename from ProxyAPI/app/api/dependencies.py
rename to app/api/dependencies.py
diff --git a/ProxyAPI/app/api/endpoints/__init__.py b/app/api/endpoints/__init__.py
similarity index 100%
rename from ProxyAPI/app/api/endpoints/__init__.py
rename to app/api/endpoints/__init__.py
diff --git a/ProxyAPI/app/api/endpoints/bucket_permissions.py b/app/api/endpoints/bucket_permissions.py
similarity index 100%
rename from ProxyAPI/app/api/endpoints/bucket_permissions.py
rename to app/api/endpoints/bucket_permissions.py
diff --git a/ProxyAPI/app/api/endpoints/buckets.py b/app/api/endpoints/buckets.py
similarity index 100%
rename from ProxyAPI/app/api/endpoints/buckets.py
rename to app/api/endpoints/buckets.py
diff --git a/ProxyAPI/app/api/endpoints/login.py b/app/api/endpoints/login.py
similarity index 97%
rename from ProxyAPI/app/api/endpoints/login.py
rename to app/api/endpoints/login.py
index 4b3af14..1b6346f 100644
--- a/ProxyAPI/app/api/endpoints/login.py
+++ b/app/api/endpoints/login.py
@@ -34,7 +34,7 @@ async def login(request: Request) -> RedirectResponse:
     request.session.clear()
     # construct absolute url for callback
     base_url = str(request.base_url)[:-1]
-    redirect_uri = base_url + settings.API_PREFIX + router.prefix + "/callback"
+    redirect_uri = base_url + router.prefix + "/callback"
     return await oauth.lifescience.authorize_redirect(request, redirect_uri)
 
 
diff --git a/ProxyAPI/app/api/endpoints/users.py b/app/api/endpoints/users.py
similarity index 100%
rename from ProxyAPI/app/api/endpoints/users.py
rename to app/api/endpoints/users.py
diff --git a/ProxyAPI/app/api/miscellaneous_endpoints.py b/app/api/miscellaneous_endpoints.py
similarity index 100%
rename from ProxyAPI/app/api/miscellaneous_endpoints.py
rename to app/api/miscellaneous_endpoints.py
diff --git a/ProxyAPI/app/ceph/__init__.py b/app/ceph/__init__.py
similarity index 100%
rename from ProxyAPI/app/ceph/__init__.py
rename to app/ceph/__init__.py
diff --git a/ProxyAPI/app/ceph/rgw.py b/app/ceph/rgw.py
similarity index 100%
rename from ProxyAPI/app/ceph/rgw.py
rename to app/ceph/rgw.py
diff --git a/ProxyAPI/app/check_ceph_connection.py b/app/check_ceph_connection.py
similarity index 100%
rename from ProxyAPI/app/check_ceph_connection.py
rename to app/check_ceph_connection.py
diff --git a/ProxyAPI/app/check_database_connection.py b/app/check_database_connection.py
similarity index 100%
rename from ProxyAPI/app/check_database_connection.py
rename to app/check_database_connection.py
diff --git a/ProxyAPI/app/check_oidc_connection.py b/app/check_oidc_connection.py
similarity index 100%
rename from ProxyAPI/app/check_oidc_connection.py
rename to app/check_oidc_connection.py
diff --git a/ProxyAPI/app/core/__init__.py b/app/core/__init__.py
similarity index 100%
rename from ProxyAPI/app/core/__init__.py
rename to app/core/__init__.py
diff --git a/ProxyAPI/app/core/config.py b/app/core/config.py
similarity index 100%
rename from ProxyAPI/app/core/config.py
rename to app/core/config.py
diff --git a/ProxyAPI/app/core/security.py b/app/core/security.py
similarity index 100%
rename from ProxyAPI/app/core/security.py
rename to app/core/security.py
diff --git a/ProxyAPI/app/crud/__init__.py b/app/crud/__init__.py
similarity index 100%
rename from ProxyAPI/app/crud/__init__.py
rename to app/crud/__init__.py
diff --git a/ProxyAPI/app/crud/crud_bucket.py b/app/crud/crud_bucket.py
similarity index 100%
rename from ProxyAPI/app/crud/crud_bucket.py
rename to app/crud/crud_bucket.py
diff --git a/ProxyAPI/app/crud/crud_bucket_permission.py b/app/crud/crud_bucket_permission.py
similarity index 100%
rename from ProxyAPI/app/crud/crud_bucket_permission.py
rename to app/crud/crud_bucket_permission.py
diff --git a/ProxyAPI/app/crud/crud_user.py b/app/crud/crud_user.py
similarity index 100%
rename from ProxyAPI/app/crud/crud_user.py
rename to app/crud/crud_user.py
diff --git a/ProxyAPI/app/db/__init__.py b/app/db/__init__.py
similarity index 100%
rename from ProxyAPI/app/db/__init__.py
rename to app/db/__init__.py
diff --git a/ProxyAPI/app/db/base.py b/app/db/base.py
similarity index 100%
rename from ProxyAPI/app/db/base.py
rename to app/db/base.py
diff --git a/ProxyAPI/app/db/base_class.py b/app/db/base_class.py
similarity index 100%
rename from ProxyAPI/app/db/base_class.py
rename to app/db/base_class.py
diff --git a/ProxyAPI/app/db/session.py b/app/db/session.py
similarity index 100%
rename from ProxyAPI/app/db/session.py
rename to app/db/session.py
diff --git a/ProxyAPI/app/main.py b/app/main.py
similarity index 80%
rename from ProxyAPI/app/main.py
rename to app/main.py
index 2d88ec9..59d3135 100644
--- a/ProxyAPI/app/main.py
+++ b/app/main.py
@@ -3,6 +3,7 @@ from fastapi import FastAPI
 from fastapi.middleware.cors import CORSMiddleware
 from fastapi.middleware.gzip import GZipMiddleware
 from fastapi.responses import RedirectResponse
+from fastapi.routing import APIRoute
 from starlette.middleware.sessions import SessionMiddleware
 
 from app.api.api import api_router
@@ -14,17 +15,23 @@ This is the backend for a new UI which can leverage the additional powerful func
 user-friendly manner.
 """
 
+
+def custom_generate_unique_id(route: APIRoute) -> str:
+    return f"{route.tags[-1]}-{route.name}"
+
+
 app = FastAPI(
     title="S3-Proxy",
-    version="0.1.0",
+    version="1.0.0",
     description=description,
     contact={
         "name": "Daniel Goebel",
         "url": "https://ekvv.uni-bielefeld.de/pers_publ/publ/PersonDetail.jsp?personId=223066601",
         "email": "dgoebel@techfak.uni-bielefeld.de",
     },
+    generate_unique_id_function=custom_generate_unique_id,
     # license_info={"name": "MIT", "url": "https://mit-license.org/"},
-    openapi_url=settings.API_PREFIX + "/openapi.json",
+    root_path=settings.API_PREFIX,
 )
 
 # CORS Settings for the API
@@ -40,15 +47,15 @@ app.add_middleware(
 app.add_middleware(GZipMiddleware, minimum_size=500)
 
 # Include all routes
-app.include_router(api_router, prefix=settings.API_PREFIX)
-app.include_router(miscellaneous_router, prefix=settings.API_PREFIX)
+app.include_router(api_router)
+app.include_router(miscellaneous_router)
 
 app.add_middleware(SessionMiddleware, secret_key=settings.SECRET_KEY)
 
 
 @app.get("/", response_class=RedirectResponse, tags=["Miscellaneous"], include_in_schema=False)
 def redirect_docs() -> str:
-    return "/docs"
+    return settings.API_PREFIX + "/docs"
 
 
 if __name__ == "__main__":
diff --git a/ProxyAPI/app/models/__init__.py b/app/models/__init__.py
similarity index 100%
rename from ProxyAPI/app/models/__init__.py
rename to app/models/__init__.py
diff --git a/ProxyAPI/app/models/bucket.py b/app/models/bucket.py
similarity index 100%
rename from ProxyAPI/app/models/bucket.py
rename to app/models/bucket.py
diff --git a/ProxyAPI/app/models/bucket_permission.py b/app/models/bucket_permission.py
similarity index 100%
rename from ProxyAPI/app/models/bucket_permission.py
rename to app/models/bucket_permission.py
diff --git a/ProxyAPI/app/models/user.py b/app/models/user.py
similarity index 100%
rename from ProxyAPI/app/models/user.py
rename to app/models/user.py
diff --git a/ProxyAPI/app/schemas/__init__.py b/app/schemas/__init__.py
similarity index 100%
rename from ProxyAPI/app/schemas/__init__.py
rename to app/schemas/__init__.py
diff --git a/ProxyAPI/app/schemas/bucket.py b/app/schemas/bucket.py
similarity index 100%
rename from ProxyAPI/app/schemas/bucket.py
rename to app/schemas/bucket.py
diff --git a/ProxyAPI/app/schemas/bucket_permission.py b/app/schemas/bucket_permission.py
similarity index 100%
rename from ProxyAPI/app/schemas/bucket_permission.py
rename to app/schemas/bucket_permission.py
diff --git a/ProxyAPI/app/schemas/security.py b/app/schemas/security.py
similarity index 100%
rename from ProxyAPI/app/schemas/security.py
rename to app/schemas/security.py
diff --git a/ProxyAPI/app/schemas/user.py b/app/schemas/user.py
similarity index 100%
rename from ProxyAPI/app/schemas/user.py
rename to app/schemas/user.py
diff --git a/ProxyAPI/app/tests/__init__.py b/app/tests/__init__.py
similarity index 100%
rename from ProxyAPI/app/tests/__init__.py
rename to app/tests/__init__.py
diff --git a/ProxyAPI/app/tests/api/__init__.py b/app/tests/api/__init__.py
similarity index 100%
rename from ProxyAPI/app/tests/api/__init__.py
rename to app/tests/api/__init__.py
diff --git a/ProxyAPI/app/tests/api/test_bucket_permissions.py b/app/tests/api/test_bucket_permissions.py
similarity index 99%
rename from ProxyAPI/app/tests/api/test_bucket_permissions.py
rename to app/tests/api/test_bucket_permissions.py
index 709445f..1b316a6 100644
--- a/ProxyAPI/app/tests/api/test_bucket_permissions.py
+++ b/app/tests/api/test_bucket_permissions.py
@@ -6,7 +6,6 @@ from fastapi import status
 from httpx import AsyncClient
 from sqlalchemy.ext.asyncio import AsyncSession
 
-from app.core.config import settings
 from app.models.bucket import Bucket
 from app.models.bucket_permission import PermissionEnum
 from app.models.user import User
@@ -18,7 +17,7 @@ from app.tests.utils.utils import json_datetime_converter
 
 
 class _TestBucketPermissionRoutes:
-    base_path = f"{settings.API_PREFIX}/permissions/"
+    base_path = "/permissions/"
 
 
 class TestBucketPermissionRoutesGet(_TestBucketPermissionRoutes):
diff --git a/ProxyAPI/app/tests/api/test_buckets.py b/app/tests/api/test_buckets.py
similarity index 99%
rename from ProxyAPI/app/tests/api/test_buckets.py
rename to app/tests/api/test_buckets.py
index 52aa71b..0b396f1 100644
--- a/ProxyAPI/app/tests/api/test_buckets.py
+++ b/app/tests/api/test_buckets.py
@@ -3,7 +3,6 @@ from fastapi import status
 from httpx import AsyncClient
 from sqlalchemy.ext.asyncio import AsyncSession
 
-from app.core.config import settings
 from app.crud.crud_bucket import CRUDBucket
 from app.models.bucket import Bucket
 from app.models.bucket_permission import PermissionEnum
@@ -16,7 +15,7 @@ from app.tests.utils.utils import random_lower_string
 
 
 class _TestBucketRoutes:
-    base_path = f"{settings.API_PREFIX}/buckets/"
+    base_path = "/buckets/"
 
 
 class TestBucketRoutesGet(_TestBucketRoutes):
diff --git a/ProxyAPI/app/tests/api/test_login.py b/app/tests/api/test_login.py
similarity index 98%
rename from ProxyAPI/app/tests/api/test_login.py
rename to app/tests/api/test_login.py
index de5b546..b8202a6 100644
--- a/ProxyAPI/app/tests/api/test_login.py
+++ b/app/tests/api/test_login.py
@@ -14,7 +14,7 @@ from app.tests.utils.utils import random_lower_string
 
 
 class TestLoginRoute:
-    login_path: str = f"{settings.API_PREFIX}/auth/"
+    login_path: str = "/auth/"
 
     @pytest.mark.asyncio
     async def test_login_redirect(self, client: AsyncClient) -> None:
diff --git a/ProxyAPI/app/tests/api/test_s3_keys.py b/app/tests/api/test_s3_keys.py
similarity index 98%
rename from ProxyAPI/app/tests/api/test_s3_keys.py
rename to app/tests/api/test_s3_keys.py
index e9423d1..c705c6e 100644
--- a/ProxyAPI/app/tests/api/test_s3_keys.py
+++ b/app/tests/api/test_s3_keys.py
@@ -2,14 +2,13 @@ import pytest
 from fastapi import status
 from httpx import AsyncClient
 
-from app.core.config import settings
 from app.models.user import User
 from app.tests.mocks.mock_rgw_admin import MockRGWAdmin
 from app.tests.utils.user import get_authorization_headers
 
 
 class _TestS3KeyRoutes:
-    base_path = f"{settings.API_PREFIX}/users/"
+    base_path = "/users/"
 
 
 class TestS3KeyRoutesGet(_TestS3KeyRoutes):
diff --git a/ProxyAPI/app/tests/api/test_s3_objects.py b/app/tests/api/test_s3_objects.py
similarity index 98%
rename from ProxyAPI/app/tests/api/test_s3_objects.py
rename to app/tests/api/test_s3_objects.py
index 3a0bd5a..f7b2ee3 100644
--- a/ProxyAPI/app/tests/api/test_s3_objects.py
+++ b/app/tests/api/test_s3_objects.py
@@ -3,7 +3,6 @@ from fastapi import status
 from httpx import AsyncClient
 from sqlalchemy.ext.asyncio import AsyncSession
 
-from app.core.config import settings
 from app.models.bucket import Bucket
 from app.models.bucket_permission import BucketPermission, PermissionEnum
 from app.models.user import User
@@ -13,7 +12,7 @@ from app.tests.utils.utils import random_lower_string
 
 
 class _TestS3ObjectsRoutes:
-    base_path = f"{settings.API_PREFIX}/buckets/"
+    base_path = "/buckets/"
 
 
 class TestS3ObjectsRoutesGet(_TestS3ObjectsRoutes):
diff --git a/ProxyAPI/app/tests/api/test_security.py b/app/tests/api/test_security.py
similarity index 96%
rename from ProxyAPI/app/tests/api/test_security.py
rename to app/tests/api/test_security.py
index 60e7dcf..72115b2 100644
--- a/ProxyAPI/app/tests/api/test_security.py
+++ b/app/tests/api/test_security.py
@@ -3,12 +3,11 @@ from fastapi import status
 from httpx import AsyncClient
 from sqlalchemy.ext.asyncio import AsyncSession
 
-from app.core.config import settings
 from app.models.user import User
 
 
 class TestJWTProtectedRoutes:
-    protected_route: str = f"{settings.API_PREFIX}/users/me"
+    protected_route: str = "/users/me"
 
     @pytest.mark.asyncio
     async def test_missing_authorization_header(self, client: AsyncClient) -> None:
diff --git a/ProxyAPI/app/tests/api/test_users.py b/app/tests/api/test_users.py
similarity index 97%
rename from ProxyAPI/app/tests/api/test_users.py
rename to app/tests/api/test_users.py
index ef9defd..823c65b 100644
--- a/ProxyAPI/app/tests/api/test_users.py
+++ b/app/tests/api/test_users.py
@@ -2,13 +2,12 @@ import pytest
 from fastapi import status
 from httpx import AsyncClient
 
-from app.core.config import settings
 from app.models.user import User
 from app.tests.utils.user import get_authorization_headers
 
 
 class _TestUserRoutes:
-    base_path = f"{settings.API_PREFIX}/users/"
+    base_path = "/users/"
 
 
 class TestUserRoutesGet(_TestUserRoutes):
diff --git a/ProxyAPI/app/tests/conftest.py b/app/tests/conftest.py
similarity index 100%
rename from ProxyAPI/app/tests/conftest.py
rename to app/tests/conftest.py
diff --git a/ProxyAPI/app/tests/crud/__init__.py b/app/tests/crud/__init__.py
similarity index 100%
rename from ProxyAPI/app/tests/crud/__init__.py
rename to app/tests/crud/__init__.py
diff --git a/ProxyAPI/app/tests/crud/test_bucket.py b/app/tests/crud/test_bucket.py
similarity index 100%
rename from ProxyAPI/app/tests/crud/test_bucket.py
rename to app/tests/crud/test_bucket.py
diff --git a/ProxyAPI/app/tests/crud/test_bucket_permission.py b/app/tests/crud/test_bucket_permission.py
similarity index 100%
rename from ProxyAPI/app/tests/crud/test_bucket_permission.py
rename to app/tests/crud/test_bucket_permission.py
diff --git a/ProxyAPI/app/tests/crud/test_user.py b/app/tests/crud/test_user.py
similarity index 100%
rename from ProxyAPI/app/tests/crud/test_user.py
rename to app/tests/crud/test_user.py
diff --git a/ProxyAPI/app/tests/mocks/__init__.py b/app/tests/mocks/__init__.py
similarity index 100%
rename from ProxyAPI/app/tests/mocks/__init__.py
rename to app/tests/mocks/__init__.py
diff --git a/ProxyAPI/app/tests/mocks/mock_rgw_admin.py b/app/tests/mocks/mock_rgw_admin.py
similarity index 100%
rename from ProxyAPI/app/tests/mocks/mock_rgw_admin.py
rename to app/tests/mocks/mock_rgw_admin.py
diff --git a/ProxyAPI/app/tests/mocks/mock_s3_resource.py b/app/tests/mocks/mock_s3_resource.py
similarity index 100%
rename from ProxyAPI/app/tests/mocks/mock_s3_resource.py
rename to app/tests/mocks/mock_s3_resource.py
diff --git a/ProxyAPI/app/tests/unit/__init__.py b/app/tests/unit/__init__.py
similarity index 100%
rename from ProxyAPI/app/tests/unit/__init__.py
rename to app/tests/unit/__init__.py
diff --git a/ProxyAPI/app/tests/unit/test_bucket_name.py b/app/tests/unit/test_bucket_name.py
similarity index 100%
rename from ProxyAPI/app/tests/unit/test_bucket_name.py
rename to app/tests/unit/test_bucket_name.py
diff --git a/ProxyAPI/app/tests/unit/test_bucket_permission_scheme.py b/app/tests/unit/test_bucket_permission_scheme.py
similarity index 100%
rename from ProxyAPI/app/tests/unit/test_bucket_permission_scheme.py
rename to app/tests/unit/test_bucket_permission_scheme.py
diff --git a/ProxyAPI/app/tests/utils/__init__.py b/app/tests/utils/__init__.py
similarity index 100%
rename from ProxyAPI/app/tests/utils/__init__.py
rename to app/tests/utils/__init__.py
diff --git a/ProxyAPI/app/tests/utils/bucket.py b/app/tests/utils/bucket.py
similarity index 100%
rename from ProxyAPI/app/tests/utils/bucket.py
rename to app/tests/utils/bucket.py
diff --git a/ProxyAPI/app/tests/utils/user.py b/app/tests/utils/user.py
similarity index 100%
rename from ProxyAPI/app/tests/utils/user.py
rename to app/tests/utils/user.py
diff --git a/ProxyAPI/app/tests/utils/utils.py b/app/tests/utils/utils.py
similarity index 100%
rename from ProxyAPI/app/tests/utils/utils.py
rename to app/tests/utils/utils.py
diff --git a/ceph/README.md b/ceph/README.md
index c21d098..3c417a3 100644
--- a/ceph/README.md
+++ b/ceph/README.md
@@ -60,4 +60,4 @@ sudo cephadm shell -- ceph orch host add ceph-6 192.168.192.111
 With [sshuttle](https://github.com/sshuttle/sshuttle) you can easily create a VPN connection from your
 workstation/notebook to our cloud based ceph cluster. **sshuttle** can be installed using `pip`.
 
-`$ sshuttle -r  jkrueger@129.70.51.109:30118  192.168.192.0/24`
\ No newline at end of file
+`$ sshuttle -r  jkrueger@129.70.51.109:30118  192.168.192.0/24`
diff --git a/ceph/playbook/files/public_keys/dgoebel b/ceph/playbook/files/public_keys/dgoebel
index 7d12516..45fc147 100644
--- a/ceph/playbook/files/public_keys/dgoebel
+++ b/ceph/playbook/files/public_keys/dgoebel
@@ -1 +1 @@
-ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID7Cu6YaS6GanmMiL8pzFOCb8QXecUxFea51iBy97+OO daniel@daniel-thinkpad
\ No newline at end of file
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID7Cu6YaS6GanmMiL8pzFOCb8QXecUxFea51iBy97+OO daniel@daniel-thinkpad
diff --git a/ceph/playbook/files/public_keys/jkrueger b/ceph/playbook/files/public_keys/jkrueger
index ddd5942..71eb317 100644
--- a/ceph/playbook/files/public_keys/jkrueger
+++ b/ceph/playbook/files/public_keys/jkrueger
@@ -1 +1 @@
-ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDTnooRQoKGIbdtZvnG/qsANHeU4qzD/iV/VdLBsbSo0KlJUvjh0kIJGxe2Ums5qh/CV3QA4xjq5A0rDUfU84k4iR8zGRnxIrCjWImfm5/Dd1OvokqorJ02PmRjM1krZhVZaWjERIzSHRJTVd4ivw8pSm080lv4uo9T/0xzWeyvBQ0ZWi6KjClJYA7gl0DGOgypIh54JCAIaWgoYXAcCw4a5wu2W8dpjCJWn4M1Ci6eiAFQooa2xrgFRJI6/BLa3GgI38e7W9IbAiWN224RWWkjVp49J2tBwlVFNsrWoIcbIpcGsjlRnZWtRCquRncq5KKTt2V2BdKJFF36OAJaHGIP
\ No newline at end of file
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDTnooRQoKGIbdtZvnG/qsANHeU4qzD/iV/VdLBsbSo0KlJUvjh0kIJGxe2Ums5qh/CV3QA4xjq5A0rDUfU84k4iR8zGRnxIrCjWImfm5/Dd1OvokqorJ02PmRjM1krZhVZaWjERIzSHRJTVd4ivw8pSm080lv4uo9T/0xzWeyvBQ0ZWi6KjClJYA7gl0DGOgypIh54JCAIaWgoYXAcCw4a5wu2W8dpjCJWn4M1Ci6eiAFQooa2xrgFRJI6/BLa3GgI38e7W9IbAiWN224RWWkjVp49J2tBwlVFNsrWoIcbIpcGsjlRnZWtRCquRncq5KKTt2V2BdKJFF36OAJaHGIP
diff --git a/ceph/playbook/site.yml b/ceph/playbook/site.yml
index 90a6374..6084591 100644
--- a/ceph/playbook/site.yml
+++ b/ceph/playbook/site.yml
@@ -48,4 +48,4 @@
       mount:
         path: /mnt
         src: /dev/vdb
-        state: absent
\ No newline at end of file
+        state: absent
diff --git a/ProxyAPI/figures/cloud_object_storage.svg b/figures/cloud_object_storage.svg
similarity index 100%
rename from ProxyAPI/figures/cloud_object_storage.svg
rename to figures/cloud_object_storage.svg
diff --git a/ProxyAPI/oidc_dev_example/clients_config.json b/oidc_dev_example/clients_config.json
similarity index 76%
rename from ProxyAPI/oidc_dev_example/clients_config.json
rename to oidc_dev_example/clients_config.json
index 982cad8..c84f538 100644
--- a/ProxyAPI/oidc_dev_example/clients_config.json
+++ b/oidc_dev_example/clients_config.json
@@ -4,7 +4,8 @@
         "ClientSecrets": [
           ""
         ],
-        "RedirectUris": ["http://localhost:8000/api/auth/callback"],
+        "RedirectUris": ["http://localhost:8000/api/auth/callback", "http://localhost:9999/api/auth/callback",
+                         "http://127.0.0.1:8000/api/auth/callback", "http://127.0.0.1:9999/api/auth/callback"],
         "Description": "Client for authorization code flow",
         "AllowedGrantTypes": [
             "authorization_code"
diff --git a/ProxyAPI/oidc_dev_example/identity_resources.json b/oidc_dev_example/identity_resources.json
similarity index 100%
rename from ProxyAPI/oidc_dev_example/identity_resources.json
rename to oidc_dev_example/identity_resources.json
diff --git a/ProxyAPI/oidc_dev_example/server_options.json b/oidc_dev_example/server_options.json
similarity index 100%
rename from ProxyAPI/oidc_dev_example/server_options.json
rename to oidc_dev_example/server_options.json
diff --git a/ProxyAPI/oidc_dev_example/users_config.json b/oidc_dev_example/users_config.json
similarity index 100%
rename from ProxyAPI/oidc_dev_example/users_config.json
rename to oidc_dev_example/users_config.json
diff --git a/ProxyAPI/pyproject.toml b/pyproject.toml
similarity index 100%
rename from ProxyAPI/pyproject.toml
rename to pyproject.toml
diff --git a/ProxyAPI/requirements-dev.txt b/requirements-dev.txt
similarity index 100%
rename from ProxyAPI/requirements-dev.txt
rename to requirements-dev.txt
diff --git a/ProxyAPI/requirements.txt b/requirements.txt
similarity index 100%
rename from ProxyAPI/requirements.txt
rename to requirements.txt
diff --git a/ProxyAPI/scripts/format-imports.sh b/scripts/format-imports.sh
similarity index 100%
rename from ProxyAPI/scripts/format-imports.sh
rename to scripts/format-imports.sh
diff --git a/ProxyAPI/scripts/format.sh b/scripts/format.sh
similarity index 100%
rename from ProxyAPI/scripts/format.sh
rename to scripts/format.sh
diff --git a/ProxyAPI/scripts/lint.sh b/scripts/lint.sh
similarity index 100%
rename from ProxyAPI/scripts/lint.sh
rename to scripts/lint.sh
diff --git a/ProxyAPI/scripts/test.sh b/scripts/test.sh
similarity index 100%
rename from ProxyAPI/scripts/test.sh
rename to scripts/test.sh
diff --git a/ProxyAPI/start_service.sh b/start_service.sh
similarity index 100%
rename from ProxyAPI/start_service.sh
rename to start_service.sh
diff --git a/ProxyAPI/tests-start.sh b/tests-start.sh
similarity index 100%
rename from ProxyAPI/tests-start.sh
rename to tests-start.sh
diff --git a/traefik_dev/routes.toml b/traefik_dev/routes.toml
new file mode 100644
index 0000000..859e3b5
--- /dev/null
+++ b/traefik_dev/routes.toml
@@ -0,0 +1,28 @@
+[http]
+  [http.middlewares]
+    [http.middlewares.api-stripprefix.stripPrefix]
+      prefixes = ["/api"]
+
+  [http.routers]
+
+    [http.routers.api-http]
+      entryPoints = ["http"]
+      service = "proxyapi"
+      rule = "PathPrefix(`/api`)"
+      middlewares = ["api-stripprefix"]
+    [http.routers.api-ui]
+      entryPoints = ["http"]
+      service = "proxyapi-ui"
+      rule = "!PathPrefix(`/api`)"
+
+  [http.services]
+
+    [http.services.proxyapi]
+      [http.services.proxyapi.loadBalancer]
+        [[http.services.proxyapi.loadBalancer.servers]]
+          url = "http://127.0.0.1:8000"
+    [http.services.proxyapi-ui]
+      [http.services.proxyapi-ui.loadBalancer]
+        [[http.services.proxyapi-ui.loadBalancer.servers]]
+          url = "http://127.0.0.1:5173"
+          #url = "http://127.0.0.1:8080"
diff --git a/traefik_dev/traefik.toml b/traefik_dev/traefik.toml
new file mode 100644
index 0000000..c994ccd
--- /dev/null
+++ b/traefik_dev/traefik.toml
@@ -0,0 +1,7 @@
+[entryPoints]
+  [entryPoints.http]
+    address = ":9999"
+
+[providers]
+  [providers.file]
+    filename = "routes.toml"
-- 
GitLab