From 8bedda803f6ac3ed64135f472870d7e18171cc3e Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Sun, 8 Sep 2024 18:05:55 +0200 Subject: [PATCH 1/2] [qa] Use a single exception to manage parameter errors --- zou/app/__init__.py | 2 +- zou/app/blueprints/chats/resources.py | 4 +-- zou/app/blueprints/crud/base.py | 10 +++---- zou/app/blueprints/crud/day_off.py | 6 ++-- zou/app/blueprints/crud/entity.py | 6 ++-- .../blueprints/crud/metadata_descriptor.py | 6 ++-- zou/app/blueprints/crud/person.py | 28 +++++++++---------- .../crud/preview_background_file.py | 6 ++-- zou/app/blueprints/crud/project.py | 10 +++---- zou/app/blueprints/crud/schedule_item.py | 4 +-- zou/app/blueprints/crud/task_type.py | 6 ++-- zou/app/blueprints/persons/resources.py | 4 +-- zou/app/blueprints/previews/resources.py | 4 +-- zou/app/blueprints/shots/resources.py | 4 +-- zou/app/services/exception.py | 6 +--- zou/app/services/preview_files_service.py | 4 +-- zou/event_stream.py | 1 + 17 files changed, 53 insertions(+), 58 deletions(-) diff --git a/zou/app/__init__.py b/zou/app/__init__.py index 698ff032df..062d87d2e5 100644 --- a/zou/app/__init__.py +++ b/zou/app/__init__.py @@ -96,7 +96,7 @@ def id_parameter_format_error(error): @app.errorhandler(WrongParameterException) def wrong_parameter(error): - return jsonify(error=True, message=str(error)), 400 + return jsonify(error=True, message=str(error), data=error.dict), 400 @app.errorhandler(ExpiredSignatureError) diff --git a/zou/app/blueprints/chats/resources.py b/zou/app/blueprints/chats/resources.py index c90fca0bc1..cd9e6a2533 100644 --- a/zou/app/blueprints/chats/resources.py +++ b/zou/app/blueprints/chats/resources.py @@ -10,7 +10,7 @@ persons_service, user_service, ) -from zou.app.services.exception import ArgumentsException +from zou.app.services.exception import WrongParameterException class ChatResource(Resource): @@ -116,7 +116,7 @@ def post(self, entity_id): chat = chats_service.get_chat(entity_id) if person["id"] not in chat["participants"]: - raise ArgumentsException("You are not a participant of this chat") + raise WrongParameterException("You are not a participant of this chat") return ( chats_service.create_chat_message( diff --git a/zou/app/blueprints/crud/base.py b/zou/app/blueprints/crud/base.py index fe615c1b30..5f43a88fbd 100644 --- a/zou/app/blueprints/crud/base.py +++ b/zou/app/blueprints/crud/base.py @@ -12,7 +12,7 @@ from zou.app.mixin import ArgsMixin from zou.app.utils import events, fields, permissions, query from zou.app.services.exception import ( - ArgumentsException, + WrongParameterException, WrongParameterException, ) @@ -233,7 +233,7 @@ def post(self): try: data = request.json if data is None: - raise ArgumentsException( + raise WrongParameterException( "Data are empty. Please verify that you sent JSON data and" " that you set the right headers." ) @@ -254,7 +254,7 @@ def post(self): current_app.logger.error(str(exception), exc_info=1) return {"message": str(exception)}, 400 - except ArgumentsException as exception: + except WrongParameterException as exception: current_app.logger.error(str(exception), exc_info=1) return ( exception.dict @@ -402,7 +402,7 @@ def put(self, instance_id): try: data = self.get_arguments() if data is None: - raise ArgumentsException( + raise WrongParameterException( "Data are empty. Please verify that you sent JSON data and" " that you set the right headers." ) @@ -425,7 +425,7 @@ def put(self, instance_id): current_app.logger.error(str(exception), exc_info=1) return {"message": str(exception)}, 400 - except ArgumentsException as exception: + except WrongParameterException as exception: current_app.logger.error(str(exception), exc_info=1) return ( exception.dict diff --git a/zou/app/blueprints/crud/day_off.py b/zou/app/blueprints/crud/day_off.py index 4dcaee4de1..b69bbd6f6d 100644 --- a/zou/app/blueprints/crud/day_off.py +++ b/zou/app/blueprints/crud/day_off.py @@ -5,7 +5,7 @@ from zou.app.services import user_service, time_spents_service -from zou.app.services.exception import ArgumentsException +from zou.app.services.exception import WrongParameterException from zou.app.utils import permissions @@ -21,7 +21,7 @@ def check_creation_integrity(self, data): if time_spents_service.get_day_offs_between( data["date"], data["end_date"], data["person_id"] ): - raise ArgumentsException("Day off already exists for this period") + raise WrongParameterException("Day off already exists for this period") return data def post_creation(self, instance): @@ -67,5 +67,5 @@ def pre_update(self, instance_dict, data): data.get("person_id", instance_dict["person_id"]), exclude_id=instance_dict["id"], ): - raise ArgumentsException("Day off already exists for this period") + raise WrongParameterException("Day off already exists for this period") return data diff --git a/zou/app/blueprints/crud/entity.py b/zou/app/blueprints/crud/entity.py index ce0d12955c..b56991acf5 100644 --- a/zou/app/blueprints/crud/entity.py +++ b/zou/app/blueprints/crud/entity.py @@ -28,7 +28,7 @@ ) from zou.app.utils import date_helpers, events, permissions -from zou.app.services.exception import ArgumentsException +from zou.app.services.exception import WrongParameterException from werkzeug.exceptions import NotFound @@ -88,7 +88,7 @@ def check_creation_integrity(self, data): if "status" in data: types = [entity_status for entity_status, _ in ENTITY_STATUSES] if data["status"] not in types: - raise ArgumentsException("Invalid status") + raise WrongParameterException("Invalid status") return True def all_entries(self, query=None, relations=False): @@ -255,5 +255,5 @@ def update_data(self, data, instance_id): if "status" in data: types = [entity_status for entity_status, _ in ENTITY_STATUSES] if data["status"] not in types: - raise ArgumentsException("Invalid status") + raise WrongParameterException("Invalid status") return data diff --git a/zou/app/blueprints/crud/metadata_descriptor.py b/zou/app/blueprints/crud/metadata_descriptor.py index 473f4c7112..7a38947b96 100644 --- a/zou/app/blueprints/crud/metadata_descriptor.py +++ b/zou/app/blueprints/crud/metadata_descriptor.py @@ -6,7 +6,7 @@ from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource from zou.app.services.exception import ( - ArgumentsException, + WrongParameterException, ) @@ -30,7 +30,7 @@ def check_creation_integrity(self, data): if "data_type" in data: types = [type_name for type_name, _ in METADATA_DESCRIPTOR_TYPES] if data["data_type"] not in types: - raise ArgumentsException("Invalid data_type") + raise WrongParameterException("Invalid data_type") return True @@ -47,5 +47,5 @@ def update_data(self, data, instance_id): if "data_type" in data: types = [type_name for type_name, _ in METADATA_DESCRIPTOR_TYPES] if data["data_type"] not in types: - raise ArgumentsException("Invalid data_type") + raise WrongParameterException("Invalid data_type") return data diff --git a/zou/app/blueprints/crud/person.py b/zou/app/blueprints/crud/person.py index 2c145636a4..4cffb933fa 100644 --- a/zou/app/blueprints/crud/person.py +++ b/zou/app/blueprints/crud/person.py @@ -20,7 +20,7 @@ from zou.app.mixin import ArgsMixin from zou.app.services.exception import ( - ArgumentsException, + WrongParameterException, PersonInProtectedAccounts, ) @@ -61,11 +61,9 @@ def check_create_permissions(self, data): and data.get("active", True) and persons_service.is_user_limit_reached() ): - raise ArgumentsException( + raise WrongParameterException( "User limit reached.", { - "error": True, - "message": "User limit reached.", "limit": config.USER_LIMIT, }, ) @@ -75,18 +73,18 @@ def check_creation_integrity(self, data): if "role" in data and data["role"] not in [ role for role, _ in ROLE_TYPES ]: - raise ArgumentsException("Invalid role") + raise WrongParameterException("Invalid role") if "contract_type" in data and data["contract_type"] not in [ contract_type for contract_type, _ in CONTRACT_TYPES ]: - raise ArgumentsException("Invalid contract_type") + raise WrongParameterException("Invalid contract_type") if "two_factor_authentication" in data and data[ "two_factor_authentication" ] not in [ two_factor_authentication for two_factor_authentication, _ in TWO_FACTOR_AUTHENTICATION_TYPES ]: - raise ArgumentsException("Invalid two_factor_authentication") + raise WrongParameterException("Invalid two_factor_authentication") if "expiration_date" in data and data["expiration_date"] is not None: try: @@ -96,11 +94,11 @@ def check_creation_integrity(self, data): ).date() < datetime.date.today() ): - raise ArgumentsException( + raise WrongParameterException( "Expiration date can't be in the past." ) except: - raise ArgumentsException("Expiration date is not valid.") + raise WrongParameterException("Expiration date is not valid.") return data def update_data(self, data): @@ -156,18 +154,18 @@ def update_data(self, data, instance_id): if "role" in data and data["role"] not in [ role for role, _ in ROLE_TYPES ]: - raise ArgumentsException("Invalid role") + raise WrongParameterException("Invalid role") if "contract_type" in data and data["contract_type"] not in [ contract_type for contract_type, _ in CONTRACT_TYPES ]: - raise ArgumentsException("Invalid contract_type") + raise WrongParameterException("Invalid contract_type") if "two_factor_authentication" in data and data[ "two_factor_authentication" ] not in [ two_factor_authentication for two_factor_authentication, _ in TWO_FACTOR_AUTHENTICATION_TYPES ]: - raise ArgumentsException("Invalid two_factor_authentication") + raise WrongParameterException("Invalid two_factor_authentication") if "expiration_date" in data and data["expiration_date"] is not None: try: @@ -177,11 +175,11 @@ def update_data(self, data, instance_id): ).date() < datetime.date.today() ): - raise ArgumentsException( + raise WrongParameterException( "Expiration date can't be in the past." ) except: - raise ArgumentsException("Expiration date is not valid.") + raise WrongParameterException("Expiration date is not valid.") return data def check_delete_permissions(self, instance_dict): @@ -204,7 +202,7 @@ def pre_update(self, instance_dict, data): and not data.get("is_bot", False) and persons_service.is_user_limit_reached() ): - raise ArgumentsException("User limit reached.") + raise WrongParameterException("User limit reached.") if instance_dict["email"] in config.PROTECTED_ACCOUNTS: message = None if data.get("active") is False: diff --git a/zou/app/blueprints/crud/preview_background_file.py b/zou/app/blueprints/crud/preview_background_file.py index 806502924c..e64118976e 100644 --- a/zou/app/blueprints/crud/preview_background_file.py +++ b/zou/app/blueprints/crud/preview_background_file.py @@ -1,5 +1,5 @@ from zou.app.models.preview_background_file import PreviewBackgroundFile -from zou.app.services.exception import ArgumentsException +from zou.app.services.exception import WrongParameterException from zou.app.services import files_service, deletion_service from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource @@ -17,7 +17,7 @@ def update_data(self, data): name = data.get("name", None) preview_background_file = PreviewBackgroundFile.get_by(name=name) if preview_background_file is not None: - raise ArgumentsException( + raise WrongParameterException( "A preview background file with similar name already exists" ) return data @@ -44,7 +44,7 @@ def update_data(self, data, instance_id): if preview_background_file is not None and instance_id != str( preview_background_file.id ): - raise ArgumentsException( + raise WrongParameterException( "A preview background file with similar name already exists" ) return data diff --git a/zou/app/blueprints/crud/project.py b/zou/app/blueprints/crud/project.py index 5ef8d8ea8a..ab1c0be230 100644 --- a/zou/app/blueprints/crud/project.py +++ b/zou/app/blueprints/crud/project.py @@ -18,7 +18,7 @@ from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource -from zou.app.services.exception import ArgumentsException +from zou.app.services.exception import WrongParameterException class ProjectsResource(BaseModelsResource): @@ -44,7 +44,7 @@ def check_creation_integrity(self, data): if data["production_style"] not in [ type_name for type_name, _ in PROJECT_STYLES ]: - raise ArgumentsException("Invalid production_style") + raise WrongParameterException("Invalid production_style") return True def update_data(self, data): @@ -71,7 +71,7 @@ def update_data(self, data): or data["preview_background_file_id"] not in data["preview_background_files_ids"] ): - raise ArgumentsException("Invalid preview_background_file_id") + raise WrongParameterException("Invalid preview_background_file_id") return data def post_creation(self, project): @@ -131,7 +131,7 @@ def pre_update(self, project_dict, data): data["preview_background_file_id"] not in preview_background_files_ids ): - raise ArgumentsException("Invalid preview_background_file_id") + raise WrongParameterException("Invalid preview_background_file_id") return data @@ -167,7 +167,7 @@ def update_data(self, data, instance_id): if data["production_style"] not in [ type_name for type_name, _ in PROJECT_STYLES ]: - raise ArgumentsException("Invalid production_style") + raise WrongParameterException("Invalid production_style") return data @jwt_required() diff --git a/zou/app/blueprints/crud/schedule_item.py b/zou/app/blueprints/crud/schedule_item.py index af5b8a2ac6..c3dae3e187 100644 --- a/zou/app/blueprints/crud/schedule_item.py +++ b/zou/app/blueprints/crud/schedule_item.py @@ -3,7 +3,7 @@ from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource from zou.app.services import user_service -from zou.app.services.exception import ArgumentsException +from zou.app.services.exception import WrongParameterException class ScheduleItemsResource(BaseModelsResource): @@ -17,7 +17,7 @@ def check_creation_integrity(self, data): object_id=data.get("object_id", None), ) if schedule_item is not None: - raise ArgumentsException("A similar schedule item already exists") + raise WrongParameterException("A similar schedule item already exists") return schedule_item diff --git a/zou/app/blueprints/crud/task_type.py b/zou/app/blueprints/crud/task_type.py index 4f7ec8feed..7dba5ef578 100644 --- a/zou/app/blueprints/crud/task_type.py +++ b/zou/app/blueprints/crud/task_type.py @@ -1,5 +1,5 @@ from zou.app.models.task_type import TaskType -from zou.app.services.exception import ArgumentsException +from zou.app.services.exception import WrongParameterException from zou.app.services import tasks_service from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource @@ -17,7 +17,7 @@ def update_data(self, data): name = data.get("name", None) task_type = TaskType.get_by(name=name) if task_type is not None: - raise ArgumentsException( + raise WrongParameterException( "A task type with similar name already exists" ) return data @@ -40,7 +40,7 @@ def update_data(self, data, instance_id): if name is not None: task_type = TaskType.get_by(name=name) if task_type is not None and instance_id != str(task_type.id): - raise ArgumentsException( + raise WrongParameterException( "A task type with similar name already exists" ) return data diff --git a/zou/app/blueprints/persons/resources.py b/zou/app/blueprints/persons/resources.py index cd8e8a53f6..45d6d8c7c7 100644 --- a/zou/app/blueprints/persons/resources.py +++ b/zou/app/blueprints/persons/resources.py @@ -30,7 +30,7 @@ UnactiveUserException, TwoFactorAuthenticationNotEnabledException, PersonInProtectedAccounts, - ArgumentsException, + WrongParameterException, ) from zou.app.services.auth_service import ( disable_two_factor_authentication_for_person, @@ -779,7 +779,7 @@ def get_person_project_department_arguments(self): )["departments"] if department_id is not None: if department_id not in persons_departments: - raise ArgumentsException( + raise WrongParameterException( "Supervisor not allowed to access this department" ) else: diff --git a/zou/app/blueprints/previews/resources.py b/zou/app/blueprints/previews/resources.py index 101a91fa76..e36194371c 100644 --- a/zou/app/blueprints/previews/resources.py +++ b/zou/app/blueprints/previews/resources.py @@ -35,7 +35,7 @@ date_helpers, ) from zou.app.services.exception import ( - ArgumentsException, + WrongParameterException, PreviewBackgroundFileNotFoundException, PreviewFileReuploadNotAllowedException, ) @@ -1074,7 +1074,7 @@ def put(self, preview_file_id): user_service.check_entity_access(task["entity_id"]) if frame_number is not None: if preview_file["extension"] != "mp4": - raise ArgumentsException( + raise WrongParameterException( "Can't use a given frame on non movie preview" ) preview_files_service.replace_extracted_frame_for_preview_file( diff --git a/zou/app/blueprints/shots/resources.py b/zou/app/blueprints/shots/resources.py index 3f617277b6..da70d7b88f 100644 --- a/zou/app/blueprints/shots/resources.py +++ b/zou/app/blueprints/shots/resources.py @@ -20,7 +20,7 @@ from zou.app.utils import fields, query, permissions from zou.app.services.exception import ( WrongParameterException, - ArgumentsException, + WrongParameterException, ) @@ -77,7 +77,7 @@ def put(self, shot_id): user_service.check_manager_project_access(shot["project_id"]) data = request.json if data is None: - raise ArgumentsException( + raise WrongParameterException( "Data are empty. Please verify that you sent JSON data and" " that you set the right headers." ) diff --git a/zou/app/services/exception.py b/zou/app/services/exception.py index 8ff9aeabde..803bcced12 100644 --- a/zou/app/services/exception.py +++ b/zou/app/services/exception.py @@ -251,7 +251,7 @@ class EntryAlreadyExistsException(Exception): pass -class ArgumentsException(Exception): +class WrongParameterException(Exception): def __init__(self, message, dict=None): super().__init__(message) self.dict = dict @@ -261,10 +261,6 @@ class WrongIdFormatException(Exception): pass -class WrongParameterException(Exception): - pass - - class ModelWithRelationsDeletionException(Exception): pass diff --git a/zou/app/services/preview_files_service.py b/zou/app/services/preview_files_service.py index 5f3f4d2ec3..d51462bb5f 100644 --- a/zou/app/services/preview_files_service.py +++ b/zou/app/services/preview_files_service.py @@ -34,7 +34,7 @@ thumbnail as thumbnail_utils, ) from zou.app.services.exception import ( - ArgumentsException, + WrongParameterException, PreviewFileNotFoundException, ProjectNotFoundException, EpisodeNotFoundException, @@ -650,7 +650,7 @@ def extract_tile_from_preview_file(preview_file): extracted_tile_path = movie.generate_tile(preview_file_path) return extracted_tile_path else: - return ArgumentsException("Preview file is not a movie") + return WrongParameterException("Preview file is not a movie") def reset_movie_files_metadata(): diff --git a/zou/event_stream.py b/zou/event_stream.py index c5aa540cf6..c86bc10b0d 100644 --- a/zou/event_stream.py +++ b/zou/event_stream.py @@ -11,6 +11,7 @@ ) from flask_socketio import SocketIO, disconnect, join_room, emit from flask_sqlalchemy import SQLAlchemy + from zou.app import config from zou.app.stores import auth_tokens_store from zou.app.utils.monitoring import init_monitoring From c205ebf192fdef8481cbb886e11e2d038034e186 Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Mon, 9 Sep 2024 23:13:44 +0200 Subject: [PATCH 2/2] [metadata] Allow all project members to get available metadata --- zou/app/blueprints/crud/metadata_descriptor.py | 13 +++++++++++++ zou/app/blueprints/projects/resources.py | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/zou/app/blueprints/crud/metadata_descriptor.py b/zou/app/blueprints/crud/metadata_descriptor.py index 7a38947b96..44f65656cf 100644 --- a/zou/app/blueprints/crud/metadata_descriptor.py +++ b/zou/app/blueprints/crud/metadata_descriptor.py @@ -4,6 +4,8 @@ ) from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource +from zou.app.utils import permissions +from zou.app.models.project import Project from zou.app.services.exception import ( WrongParameterException, @@ -14,6 +16,16 @@ class MetadataDescriptorsResource(BaseModelsResource): def __init__(self): BaseModelsResource.__init__(self, MetadataDescriptor) + def check_read_permissions(self): + return not permissions.has_vendor_permissions() + + def add_project_permission_filter(self, query): + if not permissions.has_admin_permissions(): + query = query.join(Project).filter( + user_service.build_related_projects_filter() + ) + return query + def all_entries(self, query=None, relations=True): if query is None: query = self.model.query @@ -35,6 +47,7 @@ def check_creation_integrity(self, data): class MetadataDescriptorResource(BaseModelResource): + def __init__(self): BaseModelResource.__init__(self, MetadataDescriptor) diff --git a/zou/app/blueprints/projects/resources.py b/zou/app/blueprints/projects/resources.py index c12f4eace0..f891fd71fd 100644 --- a/zou/app/blueprints/projects/resources.py +++ b/zou/app/blueprints/projects/resources.py @@ -659,7 +659,7 @@ def get(self, project_id): 200: description: All metadata descriptors """ - user_service.check_manager_project_access(project_id) + user_service.check_project_access(project_id) for_client = permissions.has_client_permissions() return projects_service.get_metadata_descriptors( project_id, for_client