def update_category(public_id: int, data: dict): """ HTTP `PUT`/`PATCH` route for update a single category resource. Args: public_id (int): Public ID of the updatable category data (CategoryModel.SCHEMA): New category data to update Raises: ManagerGetError: When the category with the `public_id` was not found. ManagerUpdateError: When something went wrong during the update. Returns: UpdateSingleResponse: With update result of the new updated category. """ category_manager: CategoryManager = CategoryManager( database_manager=current_app.database_manager) try: category = CategoryModel.from_data(data=data) category_manager.update(public_id=PublicID(public_id), category=CategoryModel.to_json(category)) api_response = UpdateSingleResponse(result=data, url=request.url, model=CategoryModel.MODEL) except ManagerGetError as err: return abort(404, err.message) except ManagerUpdateError as err: return abort(400, err.message) return api_response.make_response()
def delete_category(public_id: int): """ HTTP `DELETE` route for delete a single category resource. Args: public_id (int): Public ID of the deletable category Raises: ManagerGetError: When the category with the `public_id` was not found. ManagerDeleteError: When something went wrong during the deletion. Returns: DeleteSingleResponse: Delete result with the deleted category as data. """ category_manager: CategoryManager = CategoryManager( database_manager=current_app.database_manager) try: deleted_category = category_manager.delete( public_id=PublicID(public_id)) api_response = DeleteSingleResponse( raw=CategoryModel.to_json(deleted_category), model=CategoryModel.MODEL) except ManagerGetError as err: return abort(404, err.message) except ManagerDeleteError as err: return abort(404, err.message) return api_response.make_response()
def count( self, filter: Union[List[dict], dict], user: UserModel = None, permission: AccessControlPermission = None ) -> Union[Query, Pipeline]: """ Count the number of documents in the stages Args: filter: filter requirement user: request user permission: acl permission Returns: Query with count stages. """ self.clear() self.query = Pipeline([]) if isinstance(filter, dict): self.query.append(self.match_(filter)) elif isinstance(filter, list): for pipe in filter: self.query.append(pipe) if user and permission: self.query += (AccessControlQueryBuilder().build( group_id=PublicID(user.group_id), permission=permission)) self.query.append(self.count_('total')) return self.query
def update_group(public_id: int, data: dict): """ HTTP `PUT`/`PATCH` route for update a single group resource. Args: public_id (int): Public ID of the updatable group. data (UserGroupModel.SCHEMA): New group data to update. Raises: ManagerGetError: When the group with the `public_id` was not found. ManagerUpdateError: When something went wrong during the update. Returns: UpdateSingleResponse: With update result of the new updated group. """ group_manager: GroupManager = GroupManager(database_manager=current_app.database_manager, right_manager=RightManager(rights)) try: group = UserGroupModel.from_data(data=data, rights=RightManager(rights).rights) group_dict = UserGroupModel.to_dict(group) group_dict['rights'] = [right.get('name') for right in group_dict.get('rights', [])] group_manager.update(public_id=PublicID(public_id), group=group_dict) api_response = UpdateSingleResponse(result=group_dict, url=request.url, model=UserGroupModel.MODEL) except ManagerGetError as err: return abort(404, err.message) except ManagerUpdateError as err: return abort(400, err.message) return api_response.make_response()
def change_user_password(public_id: int): """ HTTP `PATCH` route for updating a single user password. Args: public_id (int): Public ID of the user. Raises: ManagerGetError: When the user with the `public_id` was not found. ManagerUpdateError: When something went wrong during the updated. Returns: UpdateSingleResponse: User with new password """ user_manager: UserManager = UserManager( database_manager=current_app.database_manager) security_manager: SecurityManager = SecurityManager( database_manager=current_app.database_manager) try: user = user_manager.get(public_id=public_id) password = security_manager.generate_hmac(request.json.get('password')) user.password = password user_manager.update(public_id=PublicID(public_id), user=user) api_response = UpdateSingleResponse(result=UserModel.to_dict(user), url=request.url, model=UserModel.MODEL) except ManagerGetError as err: return abort(404, err.message) except ManagerUpdateError as err: return abort(400, err.message) return api_response.make_response()
def delete_user(public_id: int): """ HTTP `DELETE` route for delete a single user resource. Args: public_id (int): Public ID of the user. Raises: ManagerGetError: When the user with the `public_id` was not found. ManagerDeleteError: When something went wrong during the deletion. Returns: DeleteSingleResponse: Delete result with the deleted user as data. """ user_manager: UserManager = UserManager( database_manager=current_app.database_manager) try: deleted_group = user_manager.delete(public_id=PublicID(public_id)) api_response = DeleteSingleResponse( raw=UserModel.to_dict(deleted_group), model=UserModel.MODEL) except ManagerGetError as err: return abort(404, err.message) except ManagerDeleteError as err: return abort(404, err.message) return api_response.make_response()
def update_user(public_id: int, data: dict): """ HTTP `PUT`/`PATCH` route for update a single user resource. Args: public_id (int): Public ID of the updatable user. data (UserModel.SCHEMA): New user data to update. Raises: ManagerGetError: When the user with the `public_id` was not found. ManagerUpdateError: When something went wrong during the update. Returns: UpdateSingleResponse: With update result of the new updated user. """ user_manager: UserManager = UserManager( database_manager=current_app.database_manager) try: user = UserModel.from_data(data=data) user_manager.update(public_id=PublicID(public_id), user=user) api_response = UpdateSingleResponse(result=UserModel.to_dict(user), url=request.url, model=UserModel.MODEL) except ManagerGetError as err: return abort(404, err.message) except ManagerUpdateError as err: return abort(400, err.message) return api_response.make_response()
def delete_group(public_id: int, params: GroupDeletionParameters): """ HTTP `DELETE` route for delete a single group resource. Args: public_id (int): Public ID of the user. params (GroupDeletionParameters): Optional action parameters for handling users when the group \ is going to be deleted. Notes: Based on the params attribute. Users can be moved or deleted. Raises: ManagerGetError: When the group with the `public_id` was not found. ManagerDeleteError: When something went wrong during the deletion. Returns: DeleteSingleResponse: Delete result with the deleted group as data. """ group_manager: GroupManager = GroupManager(database_manager=current_app.database_manager, right_manager=RightManager(rights)) user_manager: UserManager = UserManager(database_manager=current_app.database_manager) # Check of action is set if params.action: users_in_group: List[UserModel] = user_manager.get_many(Query({'group_id': public_id})) if len(users_in_group) > 0: if params.action == GroupDeleteMode.MOVE.value: if params.group_id: for user in users_in_group: user.group_id = int(params.group_id) try: user_manager.update(user.public_id, user) except ManagerUpdateError as err: return abort(400, f'Could not move user: {user.public_id} to group: {params.group_id} | ' f'Error: {err.message}') if params.action == GroupDeleteMode.DELETE.value: for user in users_in_group: try: user_manager.delete(user.public_id) except ManagerDeleteError as err: return abort(400, f'Could not delete user: {user.public_id} | Error: {err.message}') try: deleted_group = group_manager.delete(public_id=PublicID(public_id)) api_response = DeleteSingleResponse(raw=UserGroupModel.to_dict(deleted_group), model=UserGroupModel.MODEL) except ManagerGetError as err: return abort(404, err.message) except ManagerDeleteError as err: return abort(404, err.message) return api_response.make_response()
def build(self, filter: Union[List[dict], dict], limit: int, skip: int, sort: str, order: int, user: UserModel = None, permission: AccessControlPermission = None, *args, **kwargs) -> \ Union[Query, Pipeline]: """ Converts the parameters from the call to a mongodb aggregation pipeline Args: filter: dict or list of dict query/queries which the elements have to match. limit: max number of documents to return. skip: number of documents to skip first. sort: sort field order: sort order user: request user permission: AccessControlPermission *args: **kwargs: Returns: The `LogQueryBuilder` query pipeline with the parameter contents. """ self.clear() self.query = Pipeline([]) if isinstance(filter, dict): self.query.append(self.match_(filter)) elif isinstance(filter, list): for pipe in filter: self.query.append(pipe) if user and permission: self.query += (LookedAccessControlQueryBuilder().build( group_id=PublicID(user.group_id), permission=permission)) if limit == 0: results_query = [self.skip_(limit)] else: results_query = [self.skip_(skip), self.limit_(limit)] self.query.append(self.sort_(sort=sort, order=order)) self.query.append( self.facet_({ 'meta': [self.count_('total')], 'results': results_query })) return self.query
def delete_link(public_id: int, request_user: UserModel): link_manager = ObjectLinkManager( database_manager=current_app.database_manager) try: deleted_type = link_manager.delete( public_id=PublicID(public_id), user=request_user, permission=AccessControlPermission.DELETE) api_response = DeleteSingleResponse( raw=ObjectLinkModel.to_json(deleted_type), model=ObjectLinkModel.MODEL) except ManagerGetError as err: return abort(404, err.message) except ManagerDeleteError as err: return abort(400, err.message) except AccessDeniedError as err: return abort(403, err.message) return api_response.make_response()
def delete_type(public_id: int): """ HTTP `DELETE` route for delete a single type resource. Args: public_id (int): Public ID of the deletable type Raises: ManagerGetError: When the type with the `public_id` was not found. ManagerDeleteError: When something went wrong during the deletion. Notes: Deleting the type will also delete all objects in this type! Returns: DeleteSingleResponse: Delete result with the deleted type as data. """ type_manager = TypeManager(database_manager=current_app.database_manager) from cmdb.framework.cmdb_object_manager import CmdbObjectManager deprecated_object_manager = CmdbObjectManager( database_manager=current_app.database_manager) try: objects_ids = [ object_.get_public_id() for object_ in deprecated_object_manager.get_objects_by_type(public_id) ] deprecated_object_manager.delete_many_objects({'type_id': public_id}, objects_ids, None) deleted_type = type_manager.delete(public_id=PublicID(public_id)) api_response = DeleteSingleResponse( raw=TypeModel.to_json(deleted_type), model=TypeModel.MODEL) except ManagerGetError as err: return abort(404, err.message) except ManagerDeleteError as err: return abort(400, err.message) except Exception as err: return abort(400, str(err)) return api_response.make_response()
def build(self, search_term, user: UserModel = None, permission: AccessControlPermission = None, active_flag: bool = False, *args, **kwargs) -> Pipeline: """Build a pipeline query out of search search term""" regex = self.regex_('fields.value', f'{search_term}', 'ims') pipe_and = self.and_( [regex, { 'active': { "$eq": True } } if active_flag else {}]) pipe_match = self.match_(pipe_and) # load reference fields in runtime. self.pipeline = SearchReferencesPipelineBuilder().build() # permission builds if user and permission: self.pipeline = [ *self.pipeline, *(AccessControlQueryBuilder().build( group_id=PublicID(user.group_id), permission=permission)) ] self.add_pipe(pipe_match) self.add_pipe( {'$group': { "_id": { 'active': '$active' }, 'count': { '$sum': 1 } }}) self.add_pipe({ '$group': { '_id': 0, 'levels': { '$push': { '_id': '$_id.active', 'count': '$count' } }, 'total': { '$sum': '$count' } } }) self.add_pipe({'$unwind': '$levels'}) self.add_pipe({'$sort': {"levels._id": -1}}) self.add_pipe({ '$group': { '_id': 0, 'levels': { '$push': { 'count': "$levels.count" } }, "total": { '$avg': '$total' } } }) self.add_pipe({ '$project': { 'total': "$total", 'active': { '$arrayElemAt': ["$levels", 0] }, 'inactive': { '$arrayElemAt': ["$levels", 1] } } }) self.add_pipe({ '$project': { '_id': 0, 'active': { '$cond': [{ '$ifNull': ["$active", False] }, '$active.count', 0] }, 'inactive': { '$cond': [{ '$ifNull': ['$inactive', False] }, '$inactive.count', 0] }, 'total': '$total' } }) return self.pipeline
def build(self, params: List[SearchParam], obj_manager: CmdbObjectManager = None, user: UserModel = None, permission: AccessControlPermission = None, active_flag: bool = False, *args, **kwargs) -> Pipeline: """Build a pipeline query out of frontend params""" # clear pipeline self.clear() # load reference fields in runtime. self.pipeline = SearchReferencesPipelineBuilder().build() # fetch only active objects if active_flag: self.add_pipe(self.match_({'active': {"$eq": True}})) # text builds text_params = [ _ for _ in params if _.search_form == 'text' or _.search_form == 'regex' ] for param in text_params: regex = self.regex_('fields.value', param.search_text, 'ims') self.add_pipe(self.match_(regex)) # type builds disjunction_query = [] type_params = [_ for _ in params if _.search_form == 'type'] for param in type_params: if param.settings and len(param.settings.get('types', [])) > 0: type_id_in = self.in_('type_id', param.settings['types']) if param.disjunction: disjunction_query.append(type_id_in) else: self.add_pipe(self.match_(type_id_in)) if len(disjunction_query) > 0: self.add_pipe(self.match_(self.or_(disjunction_query))) # category builds category_params = [_ for _ in params if _.search_form == 'category'] for param in category_params: if param.settings and len(param.settings.get('categories', [])) > 0: categories = obj_manager.get_categories_by( **self.regex_('label', param.search_text)) for curr_category in categories: type_id_in = self.in_('type_id', curr_category.types) self.add_pipe(self.match_(type_id_in)) # public builds id_params = [_ for _ in params if _.search_form == 'publicID'] for param in id_params: self.add_pipe(self.match_({'public_id': int(param.search_text)})) # permission builds if user and permission: self.pipeline = [ *self.pipeline, *(AccessControlQueryBuilder().build( group_id=PublicID(user.group_id), permission=permission)) ] return self.pipeline
def build(self, filter: Union[List[dict], dict], limit: int, skip: int, sort: str, order: int, user: UserModel = None, permission: AccessControlPermission = None, *args, **kwargs) -> \ Union[Query, Pipeline]: """ Converts the parameters from the call to a mongodb aggregation pipeline Args: filter: dict or list of dict query/queries which the elements have to match. limit: max number of documents to return. skip: number of documents to skip first. sort: sort field order: sort order user: request user permission: AccessControlPermission *args: **kwargs: Returns: The `FrameworkQueryBuilder` query pipeline with the parameter contents. """ self.clear() loading_dep_preset = [ self.lookup_(_from='framework.types', _local='type_id', _foreign='public_id', _as='type'), self.unwind_({'path': '$type'}), self.match_({'type': { '$ne': None }}), self.lookup_(_from='management.users', _local='author_id', _foreign='public_id', _as='author'), self.unwind_({ 'path': '$author', 'preserveNullAndEmptyArrays': True }), self.lookup_(_from='management.users', _local='editor_id', _foreign='public_id', _as='editor'), self.unwind_({ 'path': '$editor', 'preserveNullAndEmptyArrays': True }), ] self.query = Pipeline(loading_dep_preset) if isinstance(filter, dict): self.query.append(self.match_(filter)) elif isinstance(filter, list): for pipe in filter: self.query.append(pipe) if user and permission: self.query += (AccessControlQueryBuilder().build( group_id=PublicID(user.group_id), permission=permission)) if limit == 0: results_query = [self.skip_(limit)] else: results_query = [self.skip_(skip), self.limit_(limit)] # TODO: Remove nasty quick hack if sort.startswith('fields'): sort_value = sort[7:] self.query.append({ '$addFields': { 'order': { '$filter': { 'input': '$fields', 'as': 'fields', 'cond': { '$eq': ['$$fields.name', sort_value] } } } } }) self.query.append({'$sort': {'order': order}}) else: self.query.append(self.sort_(sort=sort, order=order)) self.query += results_query return self.query