diff --git a/backend/src/contaxy/managers/auth.py b/backend/src/contaxy/managers/auth.py index 5347011d..4e58d3dd 100644 --- a/backend/src/contaxy/managers/auth.py +++ b/backend/src/contaxy/managers/auth.py @@ -66,6 +66,7 @@ class AuthManager(AuthOperations): _API_TOKEN_COLLECTION = "tokens" _USER_COLLECTION = "users" _LOGIN_ID_MAPPING_COLLECTION = "login-id-mapping" + _PROJECT_COLLECTION = "projects" def __init__( self, @@ -972,9 +973,75 @@ def delete_user(self, user_id: str) -> None: Raises: ResourceNotFoundError: If no user with the specified ID exists. """ - self._json_db_manager.delete_json_document( - config.SYSTEM_INTERNAL_PROJECT, self._USER_COLLECTION, user_id - ) + try: + self._json_db_manager.delete_json_document( + config.SYSTEM_INTERNAL_PROJECT, self._USER_COLLECTION, user_id + ) + except ResourceNotFoundError: + logger.warning( + f"ResourceNotFoundError: No JSON document was found in the useres table with the given key: {user_id}." + ) + + try: + self._json_db_manager.delete_json_document( + config.SYSTEM_INTERNAL_PROJECT, self._USER_PASSWORD_COLLECTION, user_id + ) + except ResourceNotFoundError: + logger.warning( + f"ResourceNotFoundError: No JSON document was found in the password table with the given key: {user_id}." + ) + + try: + self._json_db_manager.delete_json_document( + config.SYSTEM_INTERNAL_PROJECT, + self._PERMISSION_COLLECTION, + "users/" + user_id, + ) + except ResourceNotFoundError: + logger.warning( + f"ResourceNotFoundError: No JSON document was found in the permissions table with the given key: {user_id}." + ) + + try: + for token in self._json_db_manager.list_json_documents( + config.SYSTEM_INTERNAL_PROJECT, self._API_TOKEN_COLLECTION + ): + user_mapping = ApiToken.parse_raw(token.json_value).subject + if user_mapping == "users/" + user_id: + self._json_db_manager.delete_json_document( + config.SYSTEM_INTERNAL_PROJECT, + self._API_TOKEN_COLLECTION, + token.key, + ) + except ResourceNotFoundError: + logger.warning( + f"ResourceNotFoundError: No JSON document was found in the token table with the given key: {user_id}." + ) + + try: + for doc in self._json_db_manager.list_json_documents( + config.SYSTEM_INTERNAL_PROJECT, self._LOGIN_ID_MAPPING_COLLECTION + ): + user_id_mapping = LoginIdMapping.parse_raw(doc.json_value).user_id + if user_id_mapping == user_id: + self._json_db_manager.delete_json_document( + config.SYSTEM_INTERNAL_PROJECT, + self._LOGIN_ID_MAPPING_COLLECTION, + doc.key, + ) + except ResourceNotFoundError: + logger.warning( + f"ResourceNotFoundError: No JSON document was found in the loginID table with the given key: {user_id}." + ) + + try: + self._json_db_manager.delete_json_document( + config.SYSTEM_INTERNAL_PROJECT, self._PROJECT_COLLECTION, user_id + ) + except ResourceNotFoundError: + logger.warning( + f"ResourceNotFoundError: No JSON document was found in the project table with the given key: {user_id}." + ) def _propose_username(self, email: str) -> str: MAX_RETRIES = 10000 diff --git a/backend/tests/test_auth_operations.py b/backend/tests/test_auth_operations.py index 95e43456..9618c6a4 100644 --- a/backend/tests/test_auth_operations.py +++ b/backend/tests/test_auth_operations.py @@ -12,6 +12,7 @@ from contaxy.managers.auth import AuthManager from contaxy.managers.json_db.inmemory_dict import InMemoryDictJsonDocumentManager from contaxy.managers.json_db.postgres import PostgresJsonDocumentManager +from contaxy.operations import JsonDocumentOperations from contaxy.schema.auth import ( AccessLevel, OAuth2Error, @@ -21,14 +22,18 @@ User, UserRegistration, ) -from contaxy.schema.exceptions import PermissionDeniedError, UnauthenticatedError +from contaxy.schema.exceptions import ( + PermissionDeniedError, + ResourceNotFoundError, + UnauthenticatedError, +) from contaxy.utils import id_utils from contaxy.utils.state_utils import GlobalState, RequestState from .conftest import test_settings from .utils import ComponentManagerMock -DEFAULT_USERS_TO_GENERATE = 10 +DEFAULT_USERS_TO_GENERATE = 3 def _generate_user_data(users_to_generate: int) -> List[UserRegistration]: @@ -58,6 +63,11 @@ class AuthOperationsTests(ABC): def auth_manager(self) -> AuthManager: pass + @property + @abstractmethod + def json_db(self) -> JsonDocumentOperations: + pass + def test_change_password(self, faker: Faker) -> None: user_id = id_utils.generate_short_uuid() user_password = faker.password() @@ -456,13 +466,35 @@ def test_update_user(self, user_data: List[UserRegistration]) -> None: assert updated_user.email == updated_user_data.email assert created_user.created_at == updated_user.created_at - def test_delete_user(self, user_data: List[UserRegistration]) -> None: + def test_delete_user(self) -> None: # Create and delete single user created_user = self.auth_manager.create_user(_generate_user_data(1)[0]) + user_resource_name = f"users/{created_user.id}" + self.auth_manager.add_permission(user_resource_name, "test#read") self.auth_manager.delete_user(created_user.id) assert len(self.auth_manager.list_users()) == 0 + # Check that all other resources have been cleaned up + # assert len(self.auth_manager.list_permissions(user_resource_name)) == 0 + with pytest.raises(ResourceNotFoundError): + self.json_db.get_json_document( + config.SYSTEM_INTERNAL_PROJECT, + AuthManager._USER_PASSWORD_COLLECTION, + key=created_user.id, + ) + with pytest.raises(ResourceNotFoundError): + self.json_db.get_json_document( + config.SYSTEM_INTERNAL_PROJECT, + AuthManager._LOGIN_ID_MAPPING_COLLECTION, + key=created_user.username, + ) + with pytest.raises(ResourceNotFoundError): + self.json_db.get_json_document( + config.SYSTEM_INTERNAL_PROJECT, + AuthManager._LOGIN_ID_MAPPING_COLLECTION, + key=created_user.email, + ) - # Create and delete multiple users + def test_delete_multiple_users(self, user_data: List[UserRegistration]) -> None: created_users: List[User] = [] for user in user_data: created_users.append(self.auth_manager.create_user(user)) @@ -485,20 +517,24 @@ class TestAuthManagerWithPostgresDB(AuthOperationsTests): def _init_auth_manager( self, global_state: GlobalState, request_state: RequestState ) -> Generator: - json_db = PostgresJsonDocumentManager(global_state, request_state) + self._json_db = PostgresJsonDocumentManager(global_state, request_state) # Cleanup everything at the startup - json_db.delete_json_collections(config.SYSTEM_INTERNAL_PROJECT) + self._json_db.delete_json_collections(config.SYSTEM_INTERNAL_PROJECT) self._auth_manager = AuthManager( - ComponentManagerMock(global_state, request_state, json_db_manager=json_db) + ComponentManagerMock( + global_state, request_state, json_db_manager=self._json_db + ) ) yield - json_db.delete_json_collections(config.SYSTEM_INTERNAL_PROJECT) - # Do cleanup @property def auth_manager(self) -> AuthManager: return self._auth_manager + @property + def json_db(self) -> JsonDocumentOperations: + return self._json_db + @pytest.mark.unit class TestAuthManagerWithInMemoryDB(AuthOperationsTests): @@ -506,14 +542,20 @@ class TestAuthManagerWithInMemoryDB(AuthOperationsTests): def _init_auth_manager( self, global_state: GlobalState, request_state: RequestState ) -> Generator: - json_db = InMemoryDictJsonDocumentManager(global_state, request_state) - json_db.delete_json_collections(config.SYSTEM_INTERNAL_PROJECT) + self._json_db = InMemoryDictJsonDocumentManager(global_state, request_state) + self._json_db.delete_json_collections(config.SYSTEM_INTERNAL_PROJECT) self._auth_manager = AuthManager( - ComponentManagerMock(global_state, request_state, json_db_manager=json_db) + ComponentManagerMock( + global_state, request_state, json_db_manager=self._json_db + ) ) yield - json_db.delete_json_collections(config.SYSTEM_INTERNAL_PROJECT) + self._json_db.delete_json_collections(config.SYSTEM_INTERNAL_PROJECT) @property def auth_manager(self) -> AuthManager: return self._auth_manager + + @property + def json_db(self) -> JsonDocumentOperations: + return self._json_db diff --git a/webapp/src/pages/Services/Services.jsx b/webapp/src/pages/Services/Services.jsx index 39724b20..7619554b 100644 --- a/webapp/src/pages/Services/Services.jsx +++ b/webapp/src/pages/Services/Services.jsx @@ -15,6 +15,7 @@ import GlobalStateContainer from '../../app/store'; import ResourceActionsDialog from '../../components/Dialogs/ResourceActionsDialog'; import ServicesContainer from './ServicesContainer'; import showStandardSnackbar from '../../app/showStandardSnackbar'; +import { BodyIntrospectTokenAuthOauthIntrospectPost } from '../../services/contaxy-client'; function Services(props) { const { className } = props; @@ -46,8 +47,14 @@ function Services(props) { onClose(); reloadServices(); } catch (err) { - showStandardSnackbar(`Could not deploy service '${deploymentName}'.`); - } + if (err.body.code == 409){ + showStandardSnackbar(`Service with name '${deploymentName}' already exists.`); + } + else { + showStandardSnackbar(`Could not deploy service '${deploymentName}'.`); + } + + } }, }); }, [activeProject.id, showAppDialog, reloadServices]);