diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..96b326678 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,39 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + +repos: + - repo: https://github.com/Lucas-C/pre-commit-hooks + rev: v1.1.13 + hooks: + - id: insert-license + name: Insert Licence for Python, YAML and Dockerfiles + files: '\.py$|\.yaml$|^Dockerfile$|^Makefile$' + args: + - --license-filepath + - license_header.txt + - --comment-style + - '#' + - id: insert-license + name: Insert Licence for HTML files + files: '\.html$' + args: + - --license-filepath + - license_header.txt + - --comment-style + - '' + - id: insert-license + name: Insert Licence for Typescript files + files: '\.ts$' + args: + - --license-filepath + - license_header.txt + - --comment-style + - '//' + - id: insert-license + name: Insert Licence for CSS files + files: '\.css$' + args: + - --license-filepath + - license_header.txt + - --comment-style + - '/*| *| */' \ No newline at end of file diff --git a/Makefile b/Makefile index d2f90d009..aebe1d6a9 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + CLUSTER_NAME = mycluster LOCAL_REGISTRY_NAME = localhost CLUSTER_REGISTRY_NAME = myregistry.localhost diff --git a/backend/setup.py b/backend/setup.py index b908cbe55..5ec2057d4 100644 --- a/backend/setup.py +++ b/backend/setup.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import setuptools setuptools.setup() diff --git a/backend/t4cclient/__init__.py b/backend/t4cclient/__init__.py index 78571a6c4..3dfc47e8f 100644 --- a/backend/t4cclient/__init__.py +++ b/backend/t4cclient/__init__.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import logging import random import string diff --git a/backend/t4cclient/__main__.py b/backend/t4cclient/__main__.py index 9446cdc22..d776fe7b6 100644 --- a/backend/t4cclient/__main__.py +++ b/backend/t4cclient/__main__.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import uvicorn from t4cclient import app diff --git a/backend/t4cclient/alembic/env.py b/backend/t4cclient/alembic/env.py index 2de4b8659..a2f8b8871 100644 --- a/backend/t4cclient/alembic/env.py +++ b/backend/t4cclient/alembic/env.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import os from logging.config import fileConfig diff --git a/backend/t4cclient/alembic/versions/0a16fb85f762_add_t4c_username_to_ease_backup.py b/backend/t4cclient/alembic/versions/0a16fb85f762_add_t4c_username_to_ease_backup.py index 5a9799d61..5dd229d09 100644 --- a/backend/t4cclient/alembic/versions/0a16fb85f762_add_t4c_username_to_ease_backup.py +++ b/backend/t4cclient/alembic/versions/0a16fb85f762_add_t4c_username_to_ease_backup.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + """Add t4c username to ease backup Revision ID: 0a16fb85f762 diff --git a/backend/t4cclient/alembic/versions/1b4c1dc944d6_remove_rdp_username.py b/backend/t4cclient/alembic/versions/1b4c1dc944d6_remove_rdp_username.py index dbead8db8..072b0af11 100644 --- a/backend/t4cclient/alembic/versions/1b4c1dc944d6_remove_rdp_username.py +++ b/backend/t4cclient/alembic/versions/1b4c1dc944d6_remove_rdp_username.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + """Remove RDP Username Revision ID: 1b4c1dc944d6 diff --git a/backend/t4cclient/alembic/versions/279ec954b302_session_add_passwords_for_rdp_and_.py b/backend/t4cclient/alembic/versions/279ec954b302_session_add_passwords_for_rdp_and_.py index 316f372b8..1778b5339 100644 --- a/backend/t4cclient/alembic/versions/279ec954b302_session_add_passwords_for_rdp_and_.py +++ b/backend/t4cclient/alembic/versions/279ec954b302_session_add_passwords_for_rdp_and_.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + """session: add passwords for rdp and guacamole Revision ID: 279ec954b302 diff --git a/backend/t4cclient/alembic/versions/2eeda6a7bd66_add_notices_datastructures.py b/backend/t4cclient/alembic/versions/2eeda6a7bd66_add_notices_datastructures.py index 3b7e96b00..2595a1d4a 100644 --- a/backend/t4cclient/alembic/versions/2eeda6a7bd66_add_notices_datastructures.py +++ b/backend/t4cclient/alembic/versions/2eeda6a7bd66_add_notices_datastructures.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + """Add notices datastructures Revision ID: 2eeda6a7bd66 diff --git a/backend/t4cclient/alembic/versions/377b60de4441_link_git_repositories_with_projects.py b/backend/t4cclient/alembic/versions/377b60de4441_link_git_repositories_with_projects.py index 2f8108f00..119bedd83 100644 --- a/backend/t4cclient/alembic/versions/377b60de4441_link_git_repositories_with_projects.py +++ b/backend/t4cclient/alembic/versions/377b60de4441_link_git_repositories_with_projects.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + """Link Git Repositories with Projects Revision ID: 377b60de4441 diff --git a/backend/t4cclient/alembic/versions/52aec4f341a5_use_repository_name_instead_of_.py b/backend/t4cclient/alembic/versions/52aec4f341a5_use_repository_name_instead_of_.py index 7bd3acb74..6f6a64fd5 100644 --- a/backend/t4cclient/alembic/versions/52aec4f341a5_use_repository_name_instead_of_.py +++ b/backend/t4cclient/alembic/versions/52aec4f341a5_use_repository_name_instead_of_.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + """Use repository_name instead of repository_id as Foreign Key Revision ID: 52aec4f341a5 diff --git a/backend/t4cclient/alembic/versions/687484695147_add_persistent_enum.py b/backend/t4cclient/alembic/versions/687484695147_add_persistent_enum.py index 52dbdae35..77b70c66f 100644 --- a/backend/t4cclient/alembic/versions/687484695147_add_persistent_enum.py +++ b/backend/t4cclient/alembic/versions/687484695147_add_persistent_enum.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + """Add Persistent Enum Revision ID: 687484695147 diff --git a/backend/t4cclient/alembic/versions/6a8bdec1dccb_add_databaseproject.py b/backend/t4cclient/alembic/versions/6a8bdec1dccb_add_databaseproject.py index 815b86fd5..a6f7c135a 100644 --- a/backend/t4cclient/alembic/versions/6a8bdec1dccb_add_databaseproject.py +++ b/backend/t4cclient/alembic/versions/6a8bdec1dccb_add_databaseproject.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + """Add DatabaseProject Revision ID: 6a8bdec1dccb diff --git a/backend/t4cclient/alembic/versions/9259062f0f62_remove_unique_constraint_for_project.py b/backend/t4cclient/alembic/versions/9259062f0f62_remove_unique_constraint_for_project.py index ee255d7cd..e08c7090b 100644 --- a/backend/t4cclient/alembic/versions/9259062f0f62_remove_unique_constraint_for_project.py +++ b/backend/t4cclient/alembic/versions/9259062f0f62_remove_unique_constraint_for_project.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + """Remove unique constraint for project Revision ID: 9259062f0f62 diff --git a/backend/t4cclient/alembic/versions/bfafdd03e30c_add_git_models_for_repositories.py b/backend/t4cclient/alembic/versions/bfafdd03e30c_add_git_models_for_repositories.py index e579518d1..edccc6c50 100644 --- a/backend/t4cclient/alembic/versions/bfafdd03e30c_add_git_models_for_repositories.py +++ b/backend/t4cclient/alembic/versions/bfafdd03e30c_add_git_models_for_repositories.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + """Add Git models for repositories Revision ID: bfafdd03e30c diff --git a/backend/t4cclient/alembic/versions/c926d3e402a8_merge_branches_permissions_and_.py b/backend/t4cclient/alembic/versions/c926d3e402a8_merge_branches_permissions_and_.py index 5b95c1af7..5bfbb031d 100644 --- a/backend/t4cclient/alembic/versions/c926d3e402a8_merge_branches_permissions_and_.py +++ b/backend/t4cclient/alembic/versions/c926d3e402a8_merge_branches_permissions_and_.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + """Merge branches permissions and guacamole-rework Revision ID: c926d3e402a8 diff --git a/backend/t4cclient/alembic/versions/caa0ecb7b28d_add_ease_backup_table.py b/backend/t4cclient/alembic/versions/caa0ecb7b28d_add_ease_backup_table.py index 2a9e9ebb1..a65d9e155 100644 --- a/backend/t4cclient/alembic/versions/caa0ecb7b28d_add_ease_backup_table.py +++ b/backend/t4cclient/alembic/versions/caa0ecb7b28d_add_ease_backup_table.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + """Add EASE backup table Revision ID: caa0ecb7b28d diff --git a/backend/t4cclient/alembic/versions/d3c85f34aae6_add_host_to_session.py b/backend/t4cclient/alembic/versions/d3c85f34aae6_add_host_to_session.py index d93ac924c..2912854af 100644 --- a/backend/t4cclient/alembic/versions/d3c85f34aae6_add_host_to_session.py +++ b/backend/t4cclient/alembic/versions/d3c85f34aae6_add_host_to_session.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + """Add host to Session Revision ID: d3c85f34aae6 diff --git a/backend/t4cclient/alembic/versions/d6a23ac7f263_add_credentials_for_gitmodels.py b/backend/t4cclient/alembic/versions/d6a23ac7f263_add_credentials_for_gitmodels.py index 2e8f950de..15629c65c 100644 --- a/backend/t4cclient/alembic/versions/d6a23ac7f263_add_credentials_for_gitmodels.py +++ b/backend/t4cclient/alembic/versions/d6a23ac7f263_add_credentials_for_gitmodels.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + """Add credentials for gitmodels Revision ID: d6a23ac7f263 diff --git a/backend/t4cclient/alembic/versions/d7fe491603c3_add_cascades.py b/backend/t4cclient/alembic/versions/d7fe491603c3_add_cascades.py index 3e41ec73d..477bd228f 100644 --- a/backend/t4cclient/alembic/versions/d7fe491603c3_add_cascades.py +++ b/backend/t4cclient/alembic/versions/d7fe491603c3_add_cascades.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + """Add cascades Revision ID: d7fe491603c3 diff --git a/backend/t4cclient/alembic/versions/f3ed32b2cfb0_add_jenkinspipeline.py b/backend/t4cclient/alembic/versions/f3ed32b2cfb0_add_jenkinspipeline.py index 4d63d0e3f..046bdba01 100644 --- a/backend/t4cclient/alembic/versions/f3ed32b2cfb0_add_jenkinspipeline.py +++ b/backend/t4cclient/alembic/versions/f3ed32b2cfb0_add_jenkinspipeline.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + """Add JenkinsPipeline Revision ID: f3ed32b2cfb0 diff --git a/backend/t4cclient/alembic/versions/f3efdcedfdde_add_read_write_access.py b/backend/t4cclient/alembic/versions/f3efdcedfdde_add_read_write_access.py index 20725ac69..c2111e110 100644 --- a/backend/t4cclient/alembic/versions/f3efdcedfdde_add_read_write_access.py +++ b/backend/t4cclient/alembic/versions/f3efdcedfdde_add_read_write_access.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + """Add read/write access Revision ID: f3efdcedfdde diff --git a/backend/t4cclient/alembic/versions/fc6250459067_use_project_id_as_reference_instead_of_.py b/backend/t4cclient/alembic/versions/fc6250459067_use_project_id_as_reference_instead_of_.py index 82708c43c..c12a74d66 100644 --- a/backend/t4cclient/alembic/versions/fc6250459067_use_project_id_as_reference_instead_of_.py +++ b/backend/t4cclient/alembic/versions/fc6250459067_use_project_id_as_reference_instead_of_.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + """Use Project ID as reference instead of Project Name Revision ID: fc6250459067 diff --git a/backend/t4cclient/config.py b/backend/t4cclient/config.py index 616fd0fed..063ca4ad5 100644 --- a/backend/t4cclient/config.py +++ b/backend/t4cclient/config.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import configparser import logging import os diff --git a/backend/t4cclient/core/__init__.py b/backend/t4cclient/core/__init__.py index e69de29bb..5705423e2 100644 --- a/backend/t4cclient/core/__init__.py +++ b/backend/t4cclient/core/__init__.py @@ -0,0 +1,3 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + diff --git a/backend/t4cclient/core/credentials.py b/backend/t4cclient/core/credentials.py index 998230487..98bfc1389 100644 --- a/backend/t4cclient/core/credentials.py +++ b/backend/t4cclient/core/credentials.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import secrets import string diff --git a/backend/t4cclient/core/database/__init__.py b/backend/t4cclient/core/database/__init__.py index a2f8742e6..35742554b 100644 --- a/backend/t4cclient/core/database/__init__.py +++ b/backend/t4cclient/core/database/__init__.py @@ -1,17 +1,20 @@ -from sqlalchemy import create_engine -from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import sessionmaker -from t4cclient import config - -engine = create_engine(config.DATABASE_URL) -SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) - -Base = declarative_base() - - -def get_db(): - db = SessionLocal() - try: - yield db - finally: - db.close() +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker +from t4cclient import config + +engine = create_engine(config.DATABASE_URL) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +Base = declarative_base() + + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() diff --git a/backend/t4cclient/core/database/__main__.py b/backend/t4cclient/core/database/__main__.py index 8f1bd5c40..09a64bf77 100644 --- a/backend/t4cclient/core/database/__main__.py +++ b/backend/t4cclient/core/database/__main__.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import logging import os import pathlib diff --git a/backend/t4cclient/core/database/notices.py b/backend/t4cclient/core/database/notices.py index da264dc4a..b88730607 100644 --- a/backend/t4cclient/core/database/notices.py +++ b/backend/t4cclient/core/database/notices.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + from sqlalchemy.orm import Session from t4cclient.schemas.notices import CreateNoticeRequest, NoticeResponse from t4cclient.sql_models.notices import DatabaseNotice diff --git a/backend/t4cclient/core/database/repositories.py b/backend/t4cclient/core/database/repositories.py index 584e07dca..255bac9a2 100644 --- a/backend/t4cclient/core/database/repositories.py +++ b/backend/t4cclient/core/database/repositories.py @@ -1,25 +1,28 @@ -import typing as t - -from sqlalchemy.orm import Session -from t4cclient.sql_models.repositories import DatabaseRepository - - -def get_repository(db: Session, name: str): - return db.query(DatabaseRepository).filter(DatabaseRepository.name == name).first() - - -def get_all_repositories(db: Session) -> t.List[DatabaseRepository]: - return db.query(DatabaseRepository).all() - - -def create_repository(db: Session, name: str): - repo = DatabaseRepository(name=name, users=[]) - db.add(repo) - db.commit() - db.refresh(repo) - return repo - - -def delete_repository(db: Session, name: str): - db.query(DatabaseRepository).filter(DatabaseRepository.name == name).delete() - db.commit() +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + +import typing as t + +from sqlalchemy.orm import Session +from t4cclient.sql_models.repositories import DatabaseRepository + + +def get_repository(db: Session, name: str): + return db.query(DatabaseRepository).filter(DatabaseRepository.name == name).first() + + +def get_all_repositories(db: Session) -> t.List[DatabaseRepository]: + return db.query(DatabaseRepository).all() + + +def create_repository(db: Session, name: str): + repo = DatabaseRepository(name=name, users=[]) + db.add(repo) + db.commit() + db.refresh(repo) + return repo + + +def delete_repository(db: Session, name: str): + db.query(DatabaseRepository).filter(DatabaseRepository.name == name).delete() + db.commit() diff --git a/backend/t4cclient/core/database/repository_users.py b/backend/t4cclient/core/database/repository_users.py index 739e0b12f..b4e81f40f 100644 --- a/backend/t4cclient/core/database/repository_users.py +++ b/backend/t4cclient/core/database/repository_users.py @@ -1,87 +1,90 @@ -from sqlalchemy.orm import Session -from t4cclient.schemas.repositories import (RepositoryUserPermission, - RepositoryUserRole) -from t4cclient.sql_models.repositories import RepositoryUserAssociation - - -def get_users_of_repository(db: Session, repository_name: str): - return ( - db.query(RepositoryUserAssociation) - .filter(RepositoryUserAssociation.repository_name == repository_name) - .all() - ) - -def get_user_of_repository(db: Session, repository_name: str, username: str): - return ( - db.query(RepositoryUserAssociation) - .filter(RepositoryUserAssociation.repository_name == repository_name) - .filter(RepositoryUserAssociation.username == username) - .first() - ) - -def add_user_to_repository( - db: Session, - repository_name: str, - role: RepositoryUserRole, - username: str, - permission: RepositoryUserPermission, -): - association = RepositoryUserAssociation( - repository_name=repository_name, - username=username, - role=role, - permission=permission, - ) - db.add(association) - db.commit() - db.refresh(association) - return association - - -def change_role_of_user_in_repository( - db: Session, repository_name: str, role: RepositoryUserRole, username: str -): - repo_user = ( - db.query(RepositoryUserAssociation) - .filter(RepositoryUserAssociation.repository_name == repository_name) - .filter(RepositoryUserAssociation.username == username) - .first() - ) - if role == RepositoryUserRole.MANAGER: - repo_user.permission = RepositoryUserPermission.WRITE - repo_user.role = role - db.commit() - db.refresh(repo_user) - return repo_user - - -def change_permission_of_user_in_repository( - db: Session, - repository_name: str, - permission: RepositoryUserPermission, - username: str, -): - repo_user = ( - db.query(RepositoryUserAssociation) - .filter(RepositoryUserAssociation.repository_name == repository_name) - .filter(RepositoryUserAssociation.username == username) - .first() - ) - repo_user.permission = permission - db.commit() - db.refresh(repo_user) - return repo_user - - -def delete_user_from_repository(db: Session, repository_name: str, username: str): - db.query(RepositoryUserAssociation).filter( - RepositoryUserAssociation.username == username - ).filter(RepositoryUserAssociation.repository_name == repository_name).delete() - db.commit() - - -def delete_all_repositories_for_user(db: Session, username: str): - db.query(RepositoryUserAssociation).filter( - RepositoryUserAssociation.username == username - ).delete() - db.commit() +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + +from sqlalchemy.orm import Session +from t4cclient.schemas.repositories import (RepositoryUserPermission, + RepositoryUserRole) +from t4cclient.sql_models.repositories import RepositoryUserAssociation + + +def get_users_of_repository(db: Session, repository_name: str): + return ( + db.query(RepositoryUserAssociation) + .filter(RepositoryUserAssociation.repository_name == repository_name) + .all() + ) + +def get_user_of_repository(db: Session, repository_name: str, username: str): + return ( + db.query(RepositoryUserAssociation) + .filter(RepositoryUserAssociation.repository_name == repository_name) + .filter(RepositoryUserAssociation.username == username) + .first() + ) + +def add_user_to_repository( + db: Session, + repository_name: str, + role: RepositoryUserRole, + username: str, + permission: RepositoryUserPermission, +): + association = RepositoryUserAssociation( + repository_name=repository_name, + username=username, + role=role, + permission=permission, + ) + db.add(association) + db.commit() + db.refresh(association) + return association + + +def change_role_of_user_in_repository( + db: Session, repository_name: str, role: RepositoryUserRole, username: str +): + repo_user = ( + db.query(RepositoryUserAssociation) + .filter(RepositoryUserAssociation.repository_name == repository_name) + .filter(RepositoryUserAssociation.username == username) + .first() + ) + if role == RepositoryUserRole.MANAGER: + repo_user.permission = RepositoryUserPermission.WRITE + repo_user.role = role + db.commit() + db.refresh(repo_user) + return repo_user + + +def change_permission_of_user_in_repository( + db: Session, + repository_name: str, + permission: RepositoryUserPermission, + username: str, +): + repo_user = ( + db.query(RepositoryUserAssociation) + .filter(RepositoryUserAssociation.repository_name == repository_name) + .filter(RepositoryUserAssociation.username == username) + .first() + ) + repo_user.permission = permission + db.commit() + db.refresh(repo_user) + return repo_user + + +def delete_user_from_repository(db: Session, repository_name: str, username: str): + db.query(RepositoryUserAssociation).filter( + RepositoryUserAssociation.username == username + ).filter(RepositoryUserAssociation.repository_name == repository_name).delete() + db.commit() + + +def delete_all_repositories_for_user(db: Session, username: str): + db.query(RepositoryUserAssociation).filter( + RepositoryUserAssociation.username == username + ).delete() + db.commit() diff --git a/backend/t4cclient/core/database/sessions.py b/backend/t4cclient/core/database/sessions.py index 9df8531f1..17c461bc2 100644 --- a/backend/t4cclient/core/database/sessions.py +++ b/backend/t4cclient/core/database/sessions.py @@ -1,34 +1,37 @@ -from sqlalchemy.orm import Session -from t4cclient.sql_models.sessions import DatabaseSession - - -def get_sessions_for_user(db: Session, username: str): - return ( - db.query(DatabaseSession).filter(DatabaseSession.owner_name == username).all() - ) - - -def get_sessions_for_repository(db: Session, repository: str): - return ( - db.query(DatabaseSession).filter(DatabaseSession.repository == repository).all() - ) - - -def get_session_by_id(db: Session, id: str): - return db.query(DatabaseSession).filter(DatabaseSession.id == id).first() - - -def get_all_sessions(db: Session): - return db.query(DatabaseSession).all() - - -def create_session(db: Session, session: DatabaseSession): - db.add(session) - db.commit() - db.refresh(session) - return session - - -def delete_session(db: Session, session_id: str): - db.query(DatabaseSession).filter(DatabaseSession.id == session_id).delete() - db.commit() +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + +from sqlalchemy.orm import Session +from t4cclient.sql_models.sessions import DatabaseSession + + +def get_sessions_for_user(db: Session, username: str): + return ( + db.query(DatabaseSession).filter(DatabaseSession.owner_name == username).all() + ) + + +def get_sessions_for_repository(db: Session, repository: str): + return ( + db.query(DatabaseSession).filter(DatabaseSession.repository == repository).all() + ) + + +def get_session_by_id(db: Session, id: str): + return db.query(DatabaseSession).filter(DatabaseSession.id == id).first() + + +def get_all_sessions(db: Session): + return db.query(DatabaseSession).all() + + +def create_session(db: Session, session: DatabaseSession): + db.add(session) + db.commit() + db.refresh(session) + return session + + +def delete_session(db: Session, session_id: str): + db.query(DatabaseSession).filter(DatabaseSession.id == session_id).delete() + db.commit() diff --git a/backend/t4cclient/core/database/users.py b/backend/t4cclient/core/database/users.py index bbf83f3fb..67d8db9d3 100644 --- a/backend/t4cclient/core/database/users.py +++ b/backend/t4cclient/core/database/users.py @@ -1,44 +1,47 @@ -from sqlalchemy.orm import Session -from t4cclient.schemas.repositories.users import Role -from t4cclient.sql_models.users import DatabaseUser - - -def find_or_create_user(db: Session, username: str): - user = get_user(db, username) - if user: - return user - - return create_user(db, username) - - -def get_user(db: Session, username: str): - return db.query(DatabaseUser).filter(DatabaseUser.name == username).first() - - -def get_all_users(db: Session): - return db.query(DatabaseUser).all() - - -def create_user(db: Session, username: str, role: Role = Role.USER): - user = DatabaseUser( - name=username, - role=role, - repositories=[], - ) - db.add(user) - db.commit() - db.refresh(user) - return user - - -def update_role_of_user(db: Session, username: str, role: Role): - user = get_user(db, username) - user.role = role - db.commit() - db.refresh(user) - return user - - -def delete_user(db: Session, username: str): - db.query(DatabaseUser).filter(DatabaseUser.name == username).delete() - db.commit() +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + +from sqlalchemy.orm import Session +from t4cclient.schemas.repositories.users import Role +from t4cclient.sql_models.users import DatabaseUser + + +def find_or_create_user(db: Session, username: str): + user = get_user(db, username) + if user: + return user + + return create_user(db, username) + + +def get_user(db: Session, username: str): + return db.query(DatabaseUser).filter(DatabaseUser.name == username).first() + + +def get_all_users(db: Session): + return db.query(DatabaseUser).all() + + +def create_user(db: Session, username: str, role: Role = Role.USER): + user = DatabaseUser( + name=username, + role=role, + repositories=[], + ) + db.add(user) + db.commit() + db.refresh(user) + return user + + +def update_role_of_user(db: Session, username: str, role: Role): + user = get_user(db, username) + user.role = role + db.commit() + db.refresh(user) + return user + + +def delete_user(db: Session, username: str): + db.query(DatabaseUser).filter(DatabaseUser.name == username).delete() + db.commit() diff --git a/backend/t4cclient/core/garbage_cleaner.py b/backend/t4cclient/core/garbage_cleaner.py index 69586179f..c7ee133e6 100644 --- a/backend/t4cclient/core/garbage_cleaner.py +++ b/backend/t4cclient/core/garbage_cleaner.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import logging import click diff --git a/backend/t4cclient/core/oauth/__init__.py b/backend/t4cclient/core/oauth/__init__.py index b958becf8..4e3c3287e 100644 --- a/backend/t4cclient/core/oauth/__init__.py +++ b/backend/t4cclient/core/oauth/__init__.py @@ -1,53 +1,56 @@ -import typing as t - -from requests_oauthlib import OAuth2Session -from t4cclient.config import ( - OAUTH_CLIENT_ID, - OAUTH_CLIENT_SECRET, - OAUTH_ENDPOINT, - OAUTH_REDIRECT_URI, - OAUTH_TOKEN_ENDPOINT, -) - -auth_session = OAuth2Session(OAUTH_CLIENT_ID, redirect_uri=OAUTH_REDIRECT_URI) - - -def get_auth_redirect_url(state) -> t.Dict[str, str]: - auth_url, state = auth_session.authorization_url( - OAUTH_ENDPOINT + "/authorize", state=state, grant_type="authorization_code" - ) - return {"auth_url": auth_url, "state": state} - - -def get_token(code: str) -> t.Dict[str, t.Any]: - return auth_session.fetch_token( - OAUTH_TOKEN_ENDPOINT, - code=code, - client_id=OAUTH_CLIENT_ID, - client_secret=OAUTH_CLIENT_SECRET, - ) - - -def refresh_token(refresh_token: str) -> t.Dict[str, t.Any]: - return auth_session.refresh_token( - OAUTH_TOKEN_ENDPOINT, - refresh_token=refresh_token, - client_id=OAUTH_CLIENT_ID, - client_secret=OAUTH_CLIENT_SECRET, - ) - -class OAuthStub: - def initiate_auth_code_flow(self, scopes, state): - return get_auth_redirect_url(state) - - def acquire_token_by_auth_code_flow(self, auth_data, body, scopes=[]): - get_token(body.code) - - def acquire_token_by_refresh_token(self, body, scopes=[]): - ... - - def get_accounts(self): - return [] - - def remove_account(self, account): - ... +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + +import typing as t + +from requests_oauthlib import OAuth2Session +from t4cclient.config import ( + OAUTH_CLIENT_ID, + OAUTH_CLIENT_SECRET, + OAUTH_ENDPOINT, + OAUTH_REDIRECT_URI, + OAUTH_TOKEN_ENDPOINT, +) + +auth_session = OAuth2Session(OAUTH_CLIENT_ID, redirect_uri=OAUTH_REDIRECT_URI) + + +def get_auth_redirect_url(state) -> t.Dict[str, str]: + auth_url, state = auth_session.authorization_url( + OAUTH_ENDPOINT + "/authorize", state=state, grant_type="authorization_code" + ) + return {"auth_url": auth_url, "state": state} + + +def get_token(code: str) -> t.Dict[str, t.Any]: + return auth_session.fetch_token( + OAUTH_TOKEN_ENDPOINT, + code=code, + client_id=OAUTH_CLIENT_ID, + client_secret=OAUTH_CLIENT_SECRET, + ) + + +def refresh_token(refresh_token: str) -> t.Dict[str, t.Any]: + return auth_session.refresh_token( + OAUTH_TOKEN_ENDPOINT, + refresh_token=refresh_token, + client_id=OAUTH_CLIENT_ID, + client_secret=OAUTH_CLIENT_SECRET, + ) + +class OAuthStub: + def initiate_auth_code_flow(self, scopes, state): + return get_auth_redirect_url(state) + + def acquire_token_by_auth_code_flow(self, auth_data, body, scopes=[]): + get_token(body.code) + + def acquire_token_by_refresh_token(self, body, scopes=[]): + ... + + def get_accounts(self): + return [] + + def remove_account(self, account): + ... diff --git a/backend/t4cclient/core/oauth/database/__init__.py b/backend/t4cclient/core/oauth/database/__init__.py index 1f6972f56..1c24d18c7 100644 --- a/backend/t4cclient/core/oauth/database/__init__.py +++ b/backend/t4cclient/core/oauth/database/__init__.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + from fastapi import Depends, HTTPException from sqlalchemy import true from t4cclient.config import USERNAME_CLAIM diff --git a/backend/t4cclient/core/oauth/database/git_models.py b/backend/t4cclient/core/oauth/database/git_models.py index dcde8bcf0..212284246 100644 --- a/backend/t4cclient/core/oauth/database/git_models.py +++ b/backend/t4cclient/core/oauth/database/git_models.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import t4cclient.extensions.modelsources.git.crud as crud_git_models from fastapi import Depends, HTTPException from t4cclient.core.database import get_db diff --git a/backend/t4cclient/core/oauth/database/jenkins.py b/backend/t4cclient/core/oauth/database/jenkins.py index acd2a074d..9dfcb6c0b 100644 --- a/backend/t4cclient/core/oauth/database/jenkins.py +++ b/backend/t4cclient/core/oauth/database/jenkins.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + from fastapi import Depends, HTTPException from t4cclient.core.database import get_db from t4cclient.extensions.backups import jenkins diff --git a/backend/t4cclient/core/oauth/jwt_bearer.py b/backend/t4cclient/core/oauth/jwt_bearer.py index 315f53551..24812b82a 100644 --- a/backend/t4cclient/core/oauth/jwt_bearer.py +++ b/backend/t4cclient/core/oauth/jwt_bearer.py @@ -1,158 +1,161 @@ -import logging -import time -import typing as t - -import requests -from fastapi import HTTPException, Request -from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer -from jose import jwt -from pydantic import BaseModel -from t4cclient import config -from t4cclient.config import OAUTH_CLIENT_ID, OAUTH_ENDPOINT, USERNAME_CLAIM -from t4cclient.core.database import SessionLocal, users - -log = logging.getLogger(__name__) - - -class JWTBearer(HTTPBearer): - def __init__(self, auto_error: bool = True): - super(JWTBearer, self).__init__(auto_error=auto_error) - - async def __call__(self, request: Request): - credentials: HTTPAuthorizationCredentials = await super( - JWTBearer, self - ).__call__(request) - if not credentials or credentials.scheme != "Bearer": - if self.auto_error: - raise HTTPException( - status_code=401, - detail="Not authenticated", - headers={"WWW-Authenticate": "Bearer"}, - ) - else: - return None - token_decoded = self.validate_token(credentials.credentials) - self.initialize_user(token_decoded) - return token_decoded - - def initialize_user(self, token_decoded: t.Dict[str, str]): - with SessionLocal() as session: - users.find_or_create_user(session, token_decoded[USERNAME_CLAIM]) - - def validate_token(self, token: str) -> t.Dict[str, t.Any]: - key = KeyStore.key_for_token(token) - try: - return jwt.decode(token, key.dict(), audience=OAUTH_CLIENT_ID) - except jwt.ExpiredSignatureError: - raise HTTPException( - status_code=401, - detail={ - "err_code": "token_exp", - "reason": "The Signature of the token is expired. Please request a new access token.", - }, - ) - except (jwt.JWTError, jwt.JWTClaimsError) as e: - raise HTTPException( - status_code=401, - detail="The token verification failed. Please try again with another access token.", - ) - - -# Copied and adapted from https://github.com/marpaia/jwks/blob/master/jwks/jwks.py: - - -class _KeyStore: - def __init__( - self, - *, - jwks_uri: str, - algorithms: t.List[str] = ["RS256"], - key_refresh_interval=3600, - ): - self.jwks_uri = jwks_uri - self.algorithms = algorithms - self.public_keys = {} - self.key_refresh_interval = key_refresh_interval - self.public_keys_last_refreshed = 0 - self.refresh_keys() - - def keys_need_refresh(self) -> bool: - return ( - time.time() - self.public_keys_last_refreshed - ) > self.key_refresh_interval - - def refresh_keys(self) -> None: - try: - resp = requests.get(self.jwks_uri, timeout=config.REQUESTS_TIMEOUT) - except Exception as e: - log.error("Could not retrieve JWKS data from %s", self.jwks_uri) - return - jwks = JSONWebKeySet.parse_raw(resp.text) - self.public_keys_last_refreshed = time.time() - self.public_keys.clear() - for key in jwks.keys: - self.public_keys[key.kid] = key - - def key_for_token(self, token: str, *, in_retry: int = 0) -> t.Dict[str, t.Any]: - # Before we do anything, the validation keys may need to be refreshed. - # If so, refresh them. - if self.keys_need_refresh(): - self.refresh_keys() - - # Try to extract the claims from the token so that we can use the key ID - # to determine which key we should use to validate the token. - try: - unverified_claims = jwt.get_unverified_header(token) - except Exception: - raise InvalidTokenError("Unable to parse key ID from token") - - # See if we have the key identified by this key ID. - try: - return self.public_keys[unverified_claims["kid"]] - except KeyError: - # If we don't have this key and this is the first attempt (ie: we - # haven't refreshed keys yet), then try to refresh the keys and try - # again. - if in_retry: - raise KeyIDNotFoundError() - self.refresh_keys() - return self.key_for_token(token, in_retry=1) - - -def get_jwks_uri_for_azure_ad(authorization_endpoint=OAUTH_ENDPOINT): - discoveryEndpoint = ( - f"{authorization_endpoint}/v2.0/.well-known/openid-configuration" - ) - - openid_config = requests.get( - discoveryEndpoint, - timeout=config.REQUESTS_TIMEOUT, - ).json() - return openid_config["jwks_uri"] - - -class JSONWebKey(BaseModel): - # alg: str - kty: str - use: str - n: str - e: str - kid: str - x5t: str - x5c: t.List[str] - - -class JSONWebKeySet(BaseModel): - keys: t.List[JSONWebKey] - - -class InvalidTokenError(Exception): - pass - - -class KeyIDNotFoundError(Exception): - pass - - -# Our "singleton" key store: -KeyStore = _KeyStore(jwks_uri=get_jwks_uri_for_azure_ad()) +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + +import logging +import time +import typing as t + +import requests +from fastapi import HTTPException, Request +from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer +from jose import jwt +from pydantic import BaseModel +from t4cclient import config +from t4cclient.config import OAUTH_CLIENT_ID, OAUTH_ENDPOINT, USERNAME_CLAIM +from t4cclient.core.database import SessionLocal, users + +log = logging.getLogger(__name__) + + +class JWTBearer(HTTPBearer): + def __init__(self, auto_error: bool = True): + super(JWTBearer, self).__init__(auto_error=auto_error) + + async def __call__(self, request: Request): + credentials: HTTPAuthorizationCredentials = await super( + JWTBearer, self + ).__call__(request) + if not credentials or credentials.scheme != "Bearer": + if self.auto_error: + raise HTTPException( + status_code=401, + detail="Not authenticated", + headers={"WWW-Authenticate": "Bearer"}, + ) + else: + return None + token_decoded = self.validate_token(credentials.credentials) + self.initialize_user(token_decoded) + return token_decoded + + def initialize_user(self, token_decoded: t.Dict[str, str]): + with SessionLocal() as session: + users.find_or_create_user(session, token_decoded[USERNAME_CLAIM]) + + def validate_token(self, token: str) -> t.Dict[str, t.Any]: + key = KeyStore.key_for_token(token) + try: + return jwt.decode(token, key.dict(), audience=OAUTH_CLIENT_ID) + except jwt.ExpiredSignatureError: + raise HTTPException( + status_code=401, + detail={ + "err_code": "token_exp", + "reason": "The Signature of the token is expired. Please request a new access token.", + }, + ) + except (jwt.JWTError, jwt.JWTClaimsError) as e: + raise HTTPException( + status_code=401, + detail="The token verification failed. Please try again with another access token.", + ) + + +# Copied and adapted from https://github.com/marpaia/jwks/blob/master/jwks/jwks.py: + + +class _KeyStore: + def __init__( + self, + *, + jwks_uri: str, + algorithms: t.List[str] = ["RS256"], + key_refresh_interval=3600, + ): + self.jwks_uri = jwks_uri + self.algorithms = algorithms + self.public_keys = {} + self.key_refresh_interval = key_refresh_interval + self.public_keys_last_refreshed = 0 + self.refresh_keys() + + def keys_need_refresh(self) -> bool: + return ( + time.time() - self.public_keys_last_refreshed + ) > self.key_refresh_interval + + def refresh_keys(self) -> None: + try: + resp = requests.get(self.jwks_uri, timeout=config.REQUESTS_TIMEOUT) + except Exception as e: + log.error("Could not retrieve JWKS data from %s", self.jwks_uri) + return + jwks = JSONWebKeySet.parse_raw(resp.text) + self.public_keys_last_refreshed = time.time() + self.public_keys.clear() + for key in jwks.keys: + self.public_keys[key.kid] = key + + def key_for_token(self, token: str, *, in_retry: int = 0) -> t.Dict[str, t.Any]: + # Before we do anything, the validation keys may need to be refreshed. + # If so, refresh them. + if self.keys_need_refresh(): + self.refresh_keys() + + # Try to extract the claims from the token so that we can use the key ID + # to determine which key we should use to validate the token. + try: + unverified_claims = jwt.get_unverified_header(token) + except Exception: + raise InvalidTokenError("Unable to parse key ID from token") + + # See if we have the key identified by this key ID. + try: + return self.public_keys[unverified_claims["kid"]] + except KeyError: + # If we don't have this key and this is the first attempt (ie: we + # haven't refreshed keys yet), then try to refresh the keys and try + # again. + if in_retry: + raise KeyIDNotFoundError() + self.refresh_keys() + return self.key_for_token(token, in_retry=1) + + +def get_jwks_uri_for_azure_ad(authorization_endpoint=OAUTH_ENDPOINT): + discoveryEndpoint = ( + f"{authorization_endpoint}/v2.0/.well-known/openid-configuration" + ) + + openid_config = requests.get( + discoveryEndpoint, + timeout=config.REQUESTS_TIMEOUT, + ).json() + return openid_config["jwks_uri"] + + +class JSONWebKey(BaseModel): + # alg: str + kty: str + use: str + n: str + e: str + kid: str + x5t: str + x5c: t.List[str] + + +class JSONWebKeySet(BaseModel): + keys: t.List[JSONWebKey] + + +class InvalidTokenError(Exception): + pass + + +class KeyIDNotFoundError(Exception): + pass + + +# Our "singleton" key store: +KeyStore = _KeyStore(jwks_uri=get_jwks_uri_for_azure_ad()) diff --git a/backend/t4cclient/core/operators/__init__.py b/backend/t4cclient/core/operators/__init__.py index 5faf5ee06..c691963ed 100644 --- a/backend/t4cclient/core/operators/__init__.py +++ b/backend/t4cclient/core/operators/__init__.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + from __future__ import annotations from t4cclient.config import OPERATOR_TYPE diff --git a/backend/t4cclient/core/operators/abc.py b/backend/t4cclient/core/operators/abc.py index 3185f08b5..4d1b77bb3 100644 --- a/backend/t4cclient/core/operators/abc.py +++ b/backend/t4cclient/core/operators/abc.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + from __future__ import annotations import abc diff --git a/backend/t4cclient/core/operators/docker.py b/backend/t4cclient/core/operators/docker.py index fa252f202..721ae1569 100644 --- a/backend/t4cclient/core/operators/docker.py +++ b/backend/t4cclient/core/operators/docker.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import logging import pathlib import shutil diff --git a/backend/t4cclient/core/operators/k8s.py b/backend/t4cclient/core/operators/k8s.py index c2e10dc0c..2fae90042 100644 --- a/backend/t4cclient/core/operators/k8s.py +++ b/backend/t4cclient/core/operators/k8s.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + from __future__ import annotations import logging diff --git a/backend/t4cclient/core/services/__init__.py b/backend/t4cclient/core/services/__init__.py index e69de29bb..5705423e2 100644 --- a/backend/t4cclient/core/services/__init__.py +++ b/backend/t4cclient/core/services/__init__.py @@ -0,0 +1,3 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + diff --git a/backend/t4cclient/core/services/repositories.py b/backend/t4cclient/core/services/repositories.py index 55d0e173c..fa5869166 100644 --- a/backend/t4cclient/core/services/repositories.py +++ b/backend/t4cclient/core/services/repositories.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import typing as t import t4cclient.extensions.modelsources.git.crud as git_model_crud diff --git a/backend/t4cclient/core/services/sessions.py b/backend/t4cclient/core/services/sessions.py index b2751197e..0d1e41ad3 100644 --- a/backend/t4cclient/core/services/sessions.py +++ b/backend/t4cclient/core/services/sessions.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import logging import re import typing as t diff --git a/backend/t4cclient/extensions/__init__.py b/backend/t4cclient/extensions/__init__.py index fb2df8c41..cfaa46817 100644 --- a/backend/t4cclient/extensions/__init__.py +++ b/backend/t4cclient/extensions/__init__.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + from importlib import metadata diff --git a/backend/t4cclient/extensions/backups/__init__.py b/backend/t4cclient/extensions/backups/__init__.py index e69de29bb..5705423e2 100644 --- a/backend/t4cclient/extensions/backups/__init__.py +++ b/backend/t4cclient/extensions/backups/__init__.py @@ -0,0 +1,3 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + diff --git a/backend/t4cclient/extensions/backups/ease/__init__.py b/backend/t4cclient/extensions/backups/ease/__init__.py index c367542d6..0b9365791 100644 --- a/backend/t4cclient/extensions/backups/ease/__init__.py +++ b/backend/t4cclient/extensions/backups/ease/__init__.py @@ -1 +1,4 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + from . import crud, models, routes diff --git a/backend/t4cclient/extensions/backups/ease/crud.py b/backend/t4cclient/extensions/backups/ease/crud.py index 5f9439610..35e08c2ee 100644 --- a/backend/t4cclient/extensions/backups/ease/crud.py +++ b/backend/t4cclient/extensions/backups/ease/crud.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import typing as t from sqlalchemy.orm import Session diff --git a/backend/t4cclient/extensions/backups/ease/helper.py b/backend/t4cclient/extensions/backups/ease/helper.py index 5e4e61a5c..197a2e761 100644 --- a/backend/t4cclient/extensions/backups/ease/helper.py +++ b/backend/t4cclient/extensions/backups/ease/helper.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + from t4cclient.core.operators import OPERATOR from . import models diff --git a/backend/t4cclient/extensions/backups/ease/models.py b/backend/t4cclient/extensions/backups/ease/models.py index f16e0ab8d..e78343272 100644 --- a/backend/t4cclient/extensions/backups/ease/models.py +++ b/backend/t4cclient/extensions/backups/ease/models.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import typing as t from datetime import datetime diff --git a/backend/t4cclient/extensions/backups/ease/routes.py b/backend/t4cclient/extensions/backups/ease/routes.py index 82a097664..848e2e8cf 100644 --- a/backend/t4cclient/extensions/backups/ease/routes.py +++ b/backend/t4cclient/extensions/backups/ease/routes.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import logging import typing as t import uuid diff --git a/backend/t4cclient/extensions/backups/jenkins/__init__.py b/backend/t4cclient/extensions/backups/jenkins/__init__.py index 71840674d..bb0a5fde9 100644 --- a/backend/t4cclient/extensions/backups/jenkins/__init__.py +++ b/backend/t4cclient/extensions/backups/jenkins/__init__.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import pathlib import re diff --git a/backend/t4cclient/extensions/backups/jenkins/crud.py b/backend/t4cclient/extensions/backups/jenkins/crud.py index 11da5b49d..612427d21 100644 --- a/backend/t4cclient/extensions/backups/jenkins/crud.py +++ b/backend/t4cclient/extensions/backups/jenkins/crud.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import t4cclient.extensions.backups.jenkins.models as jenkins_schema from sqlalchemy.orm import Session diff --git a/backend/t4cclient/extensions/backups/jenkins/models.py b/backend/t4cclient/extensions/backups/jenkins/models.py index 1e6acdc37..c3e26ff10 100644 --- a/backend/t4cclient/extensions/backups/jenkins/models.py +++ b/backend/t4cclient/extensions/backups/jenkins/models.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import typing as t from pydantic import BaseModel diff --git a/backend/t4cclient/extensions/backups/jenkins/routes.py b/backend/t4cclient/extensions/backups/jenkins/routes.py index 43e2a2f6c..3d8c2278a 100644 --- a/backend/t4cclient/extensions/backups/jenkins/routes.py +++ b/backend/t4cclient/extensions/backups/jenkins/routes.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import typing as t from os import path diff --git a/backend/t4cclient/extensions/guacamole.py b/backend/t4cclient/extensions/guacamole.py index 8039ce17b..c005d8535 100644 --- a/backend/t4cclient/extensions/guacamole.py +++ b/backend/t4cclient/extensions/guacamole.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import uuid import requests diff --git a/backend/t4cclient/extensions/modelsources/__init__.py b/backend/t4cclient/extensions/modelsources/__init__.py index e69de29bb..5705423e2 100644 --- a/backend/t4cclient/extensions/modelsources/__init__.py +++ b/backend/t4cclient/extensions/modelsources/__init__.py @@ -0,0 +1,3 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + diff --git a/backend/t4cclient/extensions/modelsources/git/__init__.py b/backend/t4cclient/extensions/modelsources/git/__init__.py index c367542d6..0b9365791 100644 --- a/backend/t4cclient/extensions/modelsources/git/__init__.py +++ b/backend/t4cclient/extensions/modelsources/git/__init__.py @@ -1 +1,4 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + from . import crud, models, routes diff --git a/backend/t4cclient/extensions/modelsources/git/crud.py b/backend/t4cclient/extensions/modelsources/git/crud.py index 50e326de4..b029828a2 100644 --- a/backend/t4cclient/extensions/modelsources/git/crud.py +++ b/backend/t4cclient/extensions/modelsources/git/crud.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import typing as t from sqlalchemy.orm import Session diff --git a/backend/t4cclient/extensions/modelsources/git/models.py b/backend/t4cclient/extensions/modelsources/git/models.py index 548c358f6..cd0dd43a6 100644 --- a/backend/t4cclient/extensions/modelsources/git/models.py +++ b/backend/t4cclient/extensions/modelsources/git/models.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import typing as t from pydantic import BaseModel diff --git a/backend/t4cclient/extensions/modelsources/git/routes.py b/backend/t4cclient/extensions/modelsources/git/routes.py index 0c9247284..b4ba0de09 100644 --- a/backend/t4cclient/extensions/modelsources/git/routes.py +++ b/backend/t4cclient/extensions/modelsources/git/routes.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import base64 import typing as t diff --git a/backend/t4cclient/extensions/modelsources/t4c/__init__.py b/backend/t4cclient/extensions/modelsources/t4c/__init__.py index a72359822..30fa08a3f 100644 --- a/backend/t4cclient/extensions/modelsources/t4c/__init__.py +++ b/backend/t4cclient/extensions/modelsources/t4c/__init__.py @@ -1 +1,4 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + from . import connection, crud, models, routes diff --git a/backend/t4cclient/extensions/modelsources/t4c/connection.py b/backend/t4cclient/extensions/modelsources/t4c/connection.py index 1ccb0a5d3..7769d5ba9 100644 --- a/backend/t4cclient/extensions/modelsources/t4c/connection.py +++ b/backend/t4cclient/extensions/modelsources/t4c/connection.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import json import logging import typing as t diff --git a/backend/t4cclient/extensions/modelsources/t4c/crud.py b/backend/t4cclient/extensions/modelsources/t4c/crud.py index 788e9ab9b..0feb6ba6f 100644 --- a/backend/t4cclient/extensions/modelsources/t4c/crud.py +++ b/backend/t4cclient/extensions/modelsources/t4c/crud.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + from sqlalchemy.orm import Session from t4cclient.extensions.modelsources.t4c.models import DatabaseProject diff --git a/backend/t4cclient/extensions/modelsources/t4c/models.py b/backend/t4cclient/extensions/modelsources/t4c/models.py index 6c3fbafd7..5fbd77614 100644 --- a/backend/t4cclient/extensions/modelsources/t4c/models.py +++ b/backend/t4cclient/extensions/modelsources/t4c/models.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + from sqlalchemy import Boolean, Column, Enum, ForeignKey, Integer, String, Table from sqlalchemy.orm import relationship from t4cclient.core.database import Base diff --git a/backend/t4cclient/extensions/modelsources/t4c/routes.py b/backend/t4cclient/extensions/modelsources/t4c/routes.py index a890bde1d..ca0310be5 100644 --- a/backend/t4cclient/extensions/modelsources/t4c/routes.py +++ b/backend/t4cclient/extensions/modelsources/t4c/routes.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import typing as t import t4cclient.core.database as database diff --git a/backend/t4cclient/routes/__init__.py b/backend/t4cclient/routes/__init__.py index b2fa2d149..dfdbecc4f 100644 --- a/backend/t4cclient/routes/__init__.py +++ b/backend/t4cclient/routes/__init__.py @@ -1,11 +1,14 @@ -from fastapi import APIRouter - -from . import notices, oauth, repositories, sessions, sync, users - -router = APIRouter() -router.include_router(sessions.router, prefix="/sessions", tags=["Sessions"]) -router.include_router(sync.router, prefix="/sync", tags=["T4C Server Synchronization"]) -router.include_router(repositories.router, prefix="/projects") -router.include_router(users.router, prefix="/users", tags=["Users"]) -router.include_router(oauth.router, prefix="/auth/oauth", tags=["Authentication"]) -router.include_router(notices.router, prefix="/notices", tags=["Notices"]) +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + +from fastapi import APIRouter + +from . import notices, oauth, repositories, sessions, sync, users + +router = APIRouter() +router.include_router(sessions.router, prefix="/sessions", tags=["Sessions"]) +router.include_router(sync.router, prefix="/sync", tags=["T4C Server Synchronization"]) +router.include_router(repositories.router, prefix="/projects") +router.include_router(users.router, prefix="/users", tags=["Users"]) +router.include_router(oauth.router, prefix="/auth/oauth", tags=["Authentication"]) +router.include_router(notices.router, prefix="/notices", tags=["Notices"]) diff --git a/backend/t4cclient/routes/guacamole.py b/backend/t4cclient/routes/guacamole.py index 51ba85521..ee5a34e33 100644 --- a/backend/t4cclient/routes/guacamole.py +++ b/backend/t4cclient/routes/guacamole.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import json from fastapi import APIRouter, Depends, HTTPException diff --git a/backend/t4cclient/routes/notices.py b/backend/t4cclient/routes/notices.py index 178ed3572..ddfa50d37 100644 --- a/backend/t4cclient/routes/notices.py +++ b/backend/t4cclient/routes/notices.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import typing as t from fastapi import APIRouter, Depends diff --git a/backend/t4cclient/routes/oauth.py b/backend/t4cclient/routes/oauth.py index dde61e51e..fc6efa10b 100644 --- a/backend/t4cclient/routes/oauth.py +++ b/backend/t4cclient/routes/oauth.py @@ -1,68 +1,71 @@ -import secrets -import typing as t -from functools import lru_cache - -from cachetools import TTLCache -from fastapi import APIRouter, Depends -from msal import ConfidentialClientApplication - -from t4cclient.config import ( - OAUTH_CLIENT_ID, - OAUTH_CLIENT_SECRET, - OAUTH_ENDPOINT, -) -from t4cclient.core.oauth import ( - jwt_bearer, - OAuthStub, -) -from t4cclient.schemas.oauth import RefreshTokenRequest, TokenRequest - - -router = APIRouter() - - -@lru_cache() -def ad_session(): - return ConfidentialClientApplication(OAUTH_CLIENT_ID, client_credential=OAUTH_CLIENT_SECRET, authority=OAUTH_ENDPOINT) - - -# Make this a cache: -global_session_data = TTLCache(maxsize=128, ttl=3600) - - -@router.get("/", name="Get redirect URL for OAuth") -async def get_redirect_url(): - state = secrets.token_hex(32) - assert state not in global_session_data - session_data = ad_session().initiate_auth_code_flow(scopes=[], state=state) - global_session_data[session_data["state"]] = session_data - return {"auth_url": session_data["auth_uri"], "state": session_data["state"]} - - -@router.post("/tokens", name="Create access_token") -async def api_get_token(body: TokenRequest): - auth_data = global_session_data[body.state] - del global_session_data[body.state] - token = ad_session().acquire_token_by_auth_code_flow(auth_data, body.dict(), scopes=[]) - - # *Sigh* This is microsoft again. Instead of the access_token, we should use id_token :/ - # https://stackoverflow.com/questions/63195081/how-to-validate-a-jwt-from-azuread-in-python - return {"access_token": token["id_token"], "refresh_token": token["refresh_token"], "token_type": token["token_type"]} - - -@router.put("/tokens", name="Refresh the access_token") -async def api_refresh_token(body: RefreshTokenRequest): - return ad_session().acquire_token_by_refresh_token(body.refresh_token, scopes=[]) - - -@router.get("/logout", name="Invalidate the token (log out)") -async def validate_token(jwt_decoded=Depends(jwt_bearer.JWTBearer())): - for account in ad_session().get_accounts(): - if account["username"] == jwt_decoded["preferred_username"]: - return ad_session().remove_account(account) - return None - - -@router.get("/tokens", name="Validate the token") -async def validate_token(jwt_decoded=Depends(jwt_bearer.JWTBearer())): - return jwt_decoded +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + +import secrets +import typing as t +from functools import lru_cache + +from cachetools import TTLCache +from fastapi import APIRouter, Depends +from msal import ConfidentialClientApplication + +from t4cclient.config import ( + OAUTH_CLIENT_ID, + OAUTH_CLIENT_SECRET, + OAUTH_ENDPOINT, +) +from t4cclient.core.oauth import ( + jwt_bearer, + OAuthStub, +) +from t4cclient.schemas.oauth import RefreshTokenRequest, TokenRequest + + +router = APIRouter() + + +@lru_cache() +def ad_session(): + return ConfidentialClientApplication(OAUTH_CLIENT_ID, client_credential=OAUTH_CLIENT_SECRET, authority=OAUTH_ENDPOINT) + + +# Make this a cache: +global_session_data = TTLCache(maxsize=128, ttl=3600) + + +@router.get("/", name="Get redirect URL for OAuth") +async def get_redirect_url(): + state = secrets.token_hex(32) + assert state not in global_session_data + session_data = ad_session().initiate_auth_code_flow(scopes=[], state=state) + global_session_data[session_data["state"]] = session_data + return {"auth_url": session_data["auth_uri"], "state": session_data["state"]} + + +@router.post("/tokens", name="Create access_token") +async def api_get_token(body: TokenRequest): + auth_data = global_session_data[body.state] + del global_session_data[body.state] + token = ad_session().acquire_token_by_auth_code_flow(auth_data, body.dict(), scopes=[]) + + # *Sigh* This is microsoft again. Instead of the access_token, we should use id_token :/ + # https://stackoverflow.com/questions/63195081/how-to-validate-a-jwt-from-azuread-in-python + return {"access_token": token["id_token"], "refresh_token": token["refresh_token"], "token_type": token["token_type"]} + + +@router.put("/tokens", name="Refresh the access_token") +async def api_refresh_token(body: RefreshTokenRequest): + return ad_session().acquire_token_by_refresh_token(body.refresh_token, scopes=[]) + + +@router.get("/logout", name="Invalidate the token (log out)") +async def validate_token(jwt_decoded=Depends(jwt_bearer.JWTBearer())): + for account in ad_session().get_accounts(): + if account["username"] == jwt_decoded["preferred_username"]: + return ad_session().remove_account(account) + return None + + +@router.get("/tokens", name="Validate the token") +async def validate_token(jwt_decoded=Depends(jwt_bearer.JWTBearer())): + return jwt_decoded diff --git a/backend/t4cclient/routes/open_api_configuration.py b/backend/t4cclient/routes/open_api_configuration.py index 4d28ec124..3cb3c1349 100644 --- a/backend/t4cclient/routes/open_api_configuration.py +++ b/backend/t4cclient/routes/open_api_configuration.py @@ -1,29 +1,32 @@ -AUTHENTICATION_RESPONSES = { - 401: { - "description": "Unauthorized", - "content": { - "application/json": { - "example": { - "error": { - "detail": { - "err_code": "token_exp", - "reason": "The Signature of the token is expired. Please request a new access token or refresh the token.", - } - } - } - } - }, - }, - 403: { - "description": "Forbidden", - "content": { - "application/json": { - "example": { - "error": { - "detail": "One of the roles '[user, manager, administrator]' in the repository test is required." - } - } - } - }, - }, -} +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + +AUTHENTICATION_RESPONSES = { + 401: { + "description": "Unauthorized", + "content": { + "application/json": { + "example": { + "error": { + "detail": { + "err_code": "token_exp", + "reason": "The Signature of the token is expired. Please request a new access token or refresh the token.", + } + } + } + } + }, + }, + 403: { + "description": "Forbidden", + "content": { + "application/json": { + "example": { + "error": { + "detail": "One of the roles '[user, manager, administrator]' in the repository test is required." + } + } + } + }, + }, +} diff --git a/backend/t4cclient/routes/repositories/__init__.py b/backend/t4cclient/routes/repositories/__init__.py index f5f98a7b8..4100e32fa 100644 --- a/backend/t4cclient/routes/repositories/__init__.py +++ b/backend/t4cclient/routes/repositories/__init__.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import logging import typing as t from importlib import metadata diff --git a/backend/t4cclient/routes/repositories/users.py b/backend/t4cclient/routes/repositories/users.py index fe36635f3..760a797e5 100644 --- a/backend/t4cclient/routes/repositories/users.py +++ b/backend/t4cclient/routes/repositories/users.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import typing as t import t4cclient.extensions.modelsources.t4c.connection as t4c_manager diff --git a/backend/t4cclient/routes/sessions.py b/backend/t4cclient/routes/sessions.py index 32fbcb8e0..bb41690f9 100644 --- a/backend/t4cclient/routes/sessions.py +++ b/backend/t4cclient/routes/sessions.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import itertools import logging import typing as t diff --git a/backend/t4cclient/routes/status.py b/backend/t4cclient/routes/status.py index 29180f027..3d3363bc7 100644 --- a/backend/t4cclient/routes/status.py +++ b/backend/t4cclient/routes/status.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + from fastapi import APIRouter, Depends from pydantic import BaseModel from sqlalchemy.orm import Session diff --git a/backend/t4cclient/routes/sync.py b/backend/t4cclient/routes/sync.py index 9585ad5b7..318a81181 100644 --- a/backend/t4cclient/routes/sync.py +++ b/backend/t4cclient/routes/sync.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import typing as t import t4cclient.extensions.modelsources.t4c.connection as t4c_manager diff --git a/backend/t4cclient/routes/users.py b/backend/t4cclient/routes/users.py index 360fc34a5..038129528 100644 --- a/backend/t4cclient/routes/users.py +++ b/backend/t4cclient/routes/users.py @@ -1,80 +1,83 @@ -import typing as t - -from fastapi import APIRouter, Depends, HTTPException -from sqlalchemy.orm import Session -from t4cclient.config import USERNAME_CLAIM -from t4cclient.core.database import get_db, repository_users, users -from t4cclient.core.oauth.database import is_admin, verify_admin -from t4cclient.core.oauth.jwt_bearer import JWTBearer -from t4cclient.routes.open_api_configuration import AUTHENTICATION_RESPONSES -from t4cclient.routes.sessions import inject_attrs_in_sessions -from t4cclient.schemas.repositories.users import ( - GetUserResponse, - PatchUserRoleRequest, - Role, -) -from t4cclient.schemas.sessions import AdvancedSessionResponse - -router = APIRouter() - - -@router.get( - "/", response_model=t.List[GetUserResponse], responses=AUTHENTICATION_RESPONSES -) -def get_users(token=Depends(JWTBearer()), db: Session = Depends(get_db)): - verify_admin(token, db) - return users.get_all_users(db) - - -@router.post("/", response_model=GetUserResponse, responses=AUTHENTICATION_RESPONSES) -def create_user(token=Depends(JWTBearer()), db: Session = Depends(get_db)): - return users.create_user(db, token[USERNAME_CLAIM]) - - -@router.get( - "/{username}", response_model=GetUserResponse, responses=AUTHENTICATION_RESPONSES -) -def get_user(username: str, db: Session = Depends(get_db), token=Depends(JWTBearer())): - if username != token[USERNAME_CLAIM] and not is_admin(token, db): - raise HTTPException( - status_code=403, - detail="The username does not match with your username. You have to be administrator to see other users.", - ) - return users.get_user(db=db, username=username) - - -@router.delete("/{username}", status_code=204, responses=AUTHENTICATION_RESPONSES) -def delete_user( - username: str, db: Session = Depends(get_db), token=Depends(JWTBearer()) -): - verify_admin(token, db) - repository_users.delete_all_repositories_for_user(db, username) - users.delete_user(db=db, username=username) - - -@router.patch("/{username}/roles", responses=AUTHENTICATION_RESPONSES) -def update_role_of_user( - username: str, - body: PatchUserRoleRequest, - db: Session = Depends(get_db), - token=Depends(JWTBearer()), -): - verify_admin(token, db) - users.find_or_create_user(db, username) - if body.role == Role.ADMIN: - repository_users.delete_all_repositories_for_user(db, username) - return users.update_role_of_user(db, username, body.role) - - -@router.get("/{username}/sessions", response_model=t.List[AdvancedSessionResponse]) -def get_sessions_for_user( - username: str, db: Session = Depends(get_db), token=Depends(JWTBearer()) -): - if username != token[USERNAME_CLAIM] and not is_admin(token, db): - raise HTTPException( - status_code=403, - detail="You can only see your own sessions. If you are a manager, please use the /sessions endpoint.", - ) - - user = users.get_user(db, username) - return inject_attrs_in_sessions(user.sessions) +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + +import typing as t + +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.orm import Session +from t4cclient.config import USERNAME_CLAIM +from t4cclient.core.database import get_db, repository_users, users +from t4cclient.core.oauth.database import is_admin, verify_admin +from t4cclient.core.oauth.jwt_bearer import JWTBearer +from t4cclient.routes.open_api_configuration import AUTHENTICATION_RESPONSES +from t4cclient.routes.sessions import inject_attrs_in_sessions +from t4cclient.schemas.repositories.users import ( + GetUserResponse, + PatchUserRoleRequest, + Role, +) +from t4cclient.schemas.sessions import AdvancedSessionResponse + +router = APIRouter() + + +@router.get( + "/", response_model=t.List[GetUserResponse], responses=AUTHENTICATION_RESPONSES +) +def get_users(token=Depends(JWTBearer()), db: Session = Depends(get_db)): + verify_admin(token, db) + return users.get_all_users(db) + + +@router.post("/", response_model=GetUserResponse, responses=AUTHENTICATION_RESPONSES) +def create_user(token=Depends(JWTBearer()), db: Session = Depends(get_db)): + return users.create_user(db, token[USERNAME_CLAIM]) + + +@router.get( + "/{username}", response_model=GetUserResponse, responses=AUTHENTICATION_RESPONSES +) +def get_user(username: str, db: Session = Depends(get_db), token=Depends(JWTBearer())): + if username != token[USERNAME_CLAIM] and not is_admin(token, db): + raise HTTPException( + status_code=403, + detail="The username does not match with your username. You have to be administrator to see other users.", + ) + return users.get_user(db=db, username=username) + + +@router.delete("/{username}", status_code=204, responses=AUTHENTICATION_RESPONSES) +def delete_user( + username: str, db: Session = Depends(get_db), token=Depends(JWTBearer()) +): + verify_admin(token, db) + repository_users.delete_all_repositories_for_user(db, username) + users.delete_user(db=db, username=username) + + +@router.patch("/{username}/roles", responses=AUTHENTICATION_RESPONSES) +def update_role_of_user( + username: str, + body: PatchUserRoleRequest, + db: Session = Depends(get_db), + token=Depends(JWTBearer()), +): + verify_admin(token, db) + users.find_or_create_user(db, username) + if body.role == Role.ADMIN: + repository_users.delete_all_repositories_for_user(db, username) + return users.update_role_of_user(db, username, body.role) + + +@router.get("/{username}/sessions", response_model=t.List[AdvancedSessionResponse]) +def get_sessions_for_user( + username: str, db: Session = Depends(get_db), token=Depends(JWTBearer()) +): + if username != token[USERNAME_CLAIM] and not is_admin(token, db): + raise HTTPException( + status_code=403, + detail="You can only see your own sessions. If you are a manager, please use the /sessions endpoint.", + ) + + user = users.get_user(db, username) + return inject_attrs_in_sessions(user.sessions) diff --git a/backend/t4cclient/schemas/__init__.py b/backend/t4cclient/schemas/__init__.py index e69de29bb..5705423e2 100644 --- a/backend/t4cclient/schemas/__init__.py +++ b/backend/t4cclient/schemas/__init__.py @@ -0,0 +1,3 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + diff --git a/backend/t4cclient/schemas/guacamole.py b/backend/t4cclient/schemas/guacamole.py index f24c85902..e1d4b1e50 100644 --- a/backend/t4cclient/schemas/guacamole.py +++ b/backend/t4cclient/schemas/guacamole.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + from pydantic import BaseModel diff --git a/backend/t4cclient/schemas/notices.py b/backend/t4cclient/schemas/notices.py index ca11c5469..2962f210f 100644 --- a/backend/t4cclient/schemas/notices.py +++ b/backend/t4cclient/schemas/notices.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import enum from pydantic import BaseModel diff --git a/backend/t4cclient/schemas/oauth.py b/backend/t4cclient/schemas/oauth.py index 33d2ad5fc..7262f026d 100644 --- a/backend/t4cclient/schemas/oauth.py +++ b/backend/t4cclient/schemas/oauth.py @@ -1,9 +1,12 @@ -from pydantic import BaseModel - - -class TokenRequest(BaseModel): - code: str - state: str - -class RefreshTokenRequest(BaseModel): - refresh_token: str +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + +from pydantic import BaseModel + + +class TokenRequest(BaseModel): + code: str + state: str + +class RefreshTokenRequest(BaseModel): + refresh_token: str diff --git a/backend/t4cclient/schemas/repositories/__init__.py b/backend/t4cclient/schemas/repositories/__init__.py index 28d53eb54..323f1bf9e 100644 --- a/backend/t4cclient/schemas/repositories/__init__.py +++ b/backend/t4cclient/schemas/repositories/__init__.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + from __future__ import annotations import enum diff --git a/backend/t4cclient/schemas/repositories/projects.py b/backend/t4cclient/schemas/repositories/projects.py index e3ce2b548..a767ec0b9 100644 --- a/backend/t4cclient/schemas/repositories/projects.py +++ b/backend/t4cclient/schemas/repositories/projects.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + from lib2to3.pytree import Base from pydantic import BaseModel diff --git a/backend/t4cclient/schemas/repositories/users.py b/backend/t4cclient/schemas/repositories/users.py index b31d9108c..5c2be59b5 100644 --- a/backend/t4cclient/schemas/repositories/users.py +++ b/backend/t4cclient/schemas/repositories/users.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + from enum import Enum from pydantic import BaseModel diff --git a/backend/t4cclient/schemas/sessions.py b/backend/t4cclient/schemas/sessions.py index eabb1a34f..e737f4e82 100644 --- a/backend/t4cclient/schemas/sessions.py +++ b/backend/t4cclient/schemas/sessions.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + import datetime import enum import typing as t diff --git a/backend/t4cclient/sql_models/__init__.py b/backend/t4cclient/sql_models/__init__.py index 558d83859..3424e43d7 100644 --- a/backend/t4cclient/sql_models/__init__.py +++ b/backend/t4cclient/sql_models/__init__.py @@ -1,2 +1,5 @@ -# These import statements of the models are required and should not be removed! (SQLAlchemy will not load the models otherwise) -from . import notices, repositories, sessions, users +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + +# These import statements of the models are required and should not be removed! (SQLAlchemy will not load the models otherwise) +from . import notices, repositories, sessions, users diff --git a/backend/t4cclient/sql_models/notices.py b/backend/t4cclient/sql_models/notices.py index 0c2ddfaaa..aef9f166e 100644 --- a/backend/t4cclient/sql_models/notices.py +++ b/backend/t4cclient/sql_models/notices.py @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + from sqlalchemy.sql.schema import Column from sqlalchemy.sql.sqltypes import Enum, Integer, String from t4cclient.core.database import Base diff --git a/backend/t4cclient/sql_models/repositories.py b/backend/t4cclient/sql_models/repositories.py index 4e905b304..de3209c44 100644 --- a/backend/t4cclient/sql_models/repositories.py +++ b/backend/t4cclient/sql_models/repositories.py @@ -1,28 +1,31 @@ -from sqlalchemy import Boolean, Column, Enum, ForeignKey, Integer, String, Table -from sqlalchemy.orm import relationship -from t4cclient.core.database import Base -from t4cclient.schemas.repositories import RepositoryUserPermission, RepositoryUserRole - - -class RepositoryUserAssociation(Base): - __tablename__ = "repository_user_association" - - username = Column(ForeignKey("users.name"), primary_key=True) - repository_name = Column(ForeignKey("repositories.name"), primary_key=True) - user = relationship("DatabaseUser", back_populates="repositories") - repository = relationship("DatabaseRepository", back_populates="users") - permission = Column(Enum(RepositoryUserPermission), nullable=False) - role = Column(Enum(RepositoryUserRole)) - - -class DatabaseRepository(Base): - __tablename__ = "repositories" - - id = Column(Integer, primary_key=True, index=True) - name = Column(String, unique=True, index=True) - users = relationship( - "RepositoryUserAssociation", - back_populates="repository", - ) - projects = relationship("DatabaseProject", back_populates="repository") - git_models = relationship("DB_GitModel", back_populates="repository") +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + +from sqlalchemy import Boolean, Column, Enum, ForeignKey, Integer, String, Table +from sqlalchemy.orm import relationship +from t4cclient.core.database import Base +from t4cclient.schemas.repositories import RepositoryUserPermission, RepositoryUserRole + + +class RepositoryUserAssociation(Base): + __tablename__ = "repository_user_association" + + username = Column(ForeignKey("users.name"), primary_key=True) + repository_name = Column(ForeignKey("repositories.name"), primary_key=True) + user = relationship("DatabaseUser", back_populates="repositories") + repository = relationship("DatabaseRepository", back_populates="users") + permission = Column(Enum(RepositoryUserPermission), nullable=False) + role = Column(Enum(RepositoryUserRole)) + + +class DatabaseRepository(Base): + __tablename__ = "repositories" + + id = Column(Integer, primary_key=True, index=True) + name = Column(String, unique=True, index=True) + users = relationship( + "RepositoryUserAssociation", + back_populates="repository", + ) + projects = relationship("DatabaseProject", back_populates="repository") + git_models = relationship("DB_GitModel", back_populates="repository") diff --git a/backend/t4cclient/sql_models/sessions.py b/backend/t4cclient/sql_models/sessions.py index ec271cb3b..656fbf4cd 100644 --- a/backend/t4cclient/sql_models/sessions.py +++ b/backend/t4cclient/sql_models/sessions.py @@ -1,23 +1,26 @@ -from sqlalchemy import ARRAY, TIMESTAMP, Column, Enum, ForeignKey, Integer, String -from sqlalchemy.orm import relationship -from t4cclient.core.database import Base -from t4cclient.schemas.repositories import RepositoryUserPermission -from t4cclient.schemas.sessions import WorkspaceType - - -class DatabaseSession(Base): - __tablename__ = "sessions" - - id = Column(String, primary_key=True, index=True) - owner_name = Column(String, ForeignKey("users.name")) - owner = relationship("DatabaseUser") - ports = Column(ARRAY(Integer)) - created_at = Column(TIMESTAMP) - rdp_password = Column(String) - guacamole_username = Column(String) - guacamole_password = Column(String) - guacamole_connection_id = Column(String) - host = Column(String) - type = Column(Enum(WorkspaceType), nullable=False) - repository = Column(String) - mac = Column(String) +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + +from sqlalchemy import ARRAY, TIMESTAMP, Column, Enum, ForeignKey, Integer, String +from sqlalchemy.orm import relationship +from t4cclient.core.database import Base +from t4cclient.schemas.repositories import RepositoryUserPermission +from t4cclient.schemas.sessions import WorkspaceType + + +class DatabaseSession(Base): + __tablename__ = "sessions" + + id = Column(String, primary_key=True, index=True) + owner_name = Column(String, ForeignKey("users.name")) + owner = relationship("DatabaseUser") + ports = Column(ARRAY(Integer)) + created_at = Column(TIMESTAMP) + rdp_password = Column(String) + guacamole_username = Column(String) + guacamole_password = Column(String) + guacamole_connection_id = Column(String) + host = Column(String) + type = Column(Enum(WorkspaceType), nullable=False) + repository = Column(String) + mac = Column(String) diff --git a/backend/t4cclient/sql_models/users.py b/backend/t4cclient/sql_models/users.py index 5be06f0e7..14f5db156 100644 --- a/backend/t4cclient/sql_models/users.py +++ b/backend/t4cclient/sql_models/users.py @@ -1,20 +1,23 @@ -from sqlalchemy import Column, Enum, Integer, String -from sqlalchemy.orm import relationship -from t4cclient.core.database import Base -from t4cclient.schemas.repositories.users import Role - - -class DatabaseUser(Base): - __tablename__ = "users" - - id = Column(Integer, primary_key=True, index=True) - name = Column(String, unique=True, index=True) - role = Column(Enum(Role)) - repositories = relationship( - "RepositoryUserAssociation", - back_populates="user", - ) - sessions = relationship( - "DatabaseSession", - back_populates="owner", - ) +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + +from sqlalchemy import Column, Enum, Integer, String +from sqlalchemy.orm import relationship +from t4cclient.core.database import Base +from t4cclient.schemas.repositories.users import Role + + +class DatabaseUser(Base): + __tablename__ = "users" + + id = Column(Integer, primary_key=True, index=True) + name = Column(String, unique=True, index=True) + role = Column(Enum(Role)) + repositories = relationship( + "RepositoryUserAssociation", + back_populates="user", + ) + sessions = relationship( + "DatabaseSession", + back_populates="owner", + ) diff --git a/development/ease/Makefile b/development/ease/Makefile index 161ea83f4..092db6370 100644 --- a/development/ease/Makefile +++ b/development/ease/Makefile @@ -1,4 +1,8 @@ #!make + +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + include .env export diff --git a/development/ease/helm/Chart.yaml b/development/ease/helm/Chart.yaml index cc6d5fc6f..a39dd75ab 100644 --- a/development/ease/helm/Chart.yaml +++ b/development/ease/helm/Chart.yaml @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + apiVersion: v2 name: ease-debug description: Ease debug container diff --git a/development/ease/helm/templates/ease.configmap.yaml b/development/ease/helm/templates/ease.configmap.yaml index 169c8da0e..1a3c8e03e 100644 --- a/development/ease/helm/templates/ease.configmap.yaml +++ b/development/ease/helm/templates/ease.configmap.yaml @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + apiVersion: v1 kind: ConfigMap metadata: diff --git a/development/ease/helm/templates/ease.deployment.yaml b/development/ease/helm/templates/ease.deployment.yaml index 23bb385d0..cb2d96178 100644 --- a/development/ease/helm/templates/ease.deployment.yaml +++ b/development/ease/helm/templates/ease.deployment.yaml @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + apiVersion: apps/v1 kind: Deployment metadata: diff --git a/development/ease/helm/templates/ease.service.yaml b/development/ease/helm/templates/ease.service.yaml index 1dd013eee..9329a544d 100644 --- a/development/ease/helm/templates/ease.service.yaml +++ b/development/ease/helm/templates/ease.service.yaml @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + apiVersion: v1 kind: Service metadata: diff --git a/development/ease/helm/templates/ease_env.configmap.yaml b/development/ease/helm/templates/ease_env.configmap.yaml index c99f363ab..b409237f3 100644 --- a/development/ease/helm/templates/ease_env.configmap.yaml +++ b/development/ease/helm/templates/ease_env.configmap.yaml @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + apiVersion: v1 kind: ConfigMap metadata: diff --git a/development/ease/helm/templates/pyease.volume.yaml b/development/ease/helm/templates/pyease.volume.yaml index 2a9295c8a..56b296e67 100644 --- a/development/ease/helm/templates/pyease.volume.yaml +++ b/development/ease/helm/templates/pyease.volume.yaml @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + kind: PersistentVolumeClaim apiVersion: v1 metadata: diff --git a/development/ease/helm/values.yaml b/development/ease/helm/values.yaml index a39510fb2..0cd02c8f4 100644 --- a/development/ease/helm/values.yaml +++ b/development/ease/helm/values.yaml @@ -1,3 +1,6 @@ +# Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + git: url: branch: diff --git a/frontend/src/app/active-sessions/active-sessions.component.css b/frontend/src/app/active-sessions/active-sessions.component.css index 8f8a5a2f9..270d11283 100644 --- a/frontend/src/app/active-sessions/active-sessions.component.css +++ b/frontend/src/app/active-sessions/active-sessions.component.css @@ -1,3 +1,8 @@ +/* + * Copyright DB Netz AG and the capella-collab-manager contributors + * SPDX-License-Identifier: Apache-2.0 + */ + mat-card { width: fit-content; margin: 10px; diff --git a/frontend/src/app/active-sessions/active-sessions.component.html b/frontend/src/app/active-sessions/active-sessions.component.html index 5ae001922..1a6e546a4 100644 --- a/frontend/src/app/active-sessions/active-sessions.component.html +++ b/frontend/src/app/active-sessions/active-sessions.component.html @@ -1,3 +1,8 @@ + +
Active Sessions