Esempio n. 1
0
def update_type(public_id: int, data: dict):
    """
    HTTP `PUT`/`PATCH` route for update a single type resource.

    Args:
        public_id (int): Public ID of the updatable type
        data (TypeModel.SCHEMA): New type data to update

    Raises:
        ManagerGetError: When the type with the `public_id` was not found.
        ManagerUpdateError: When something went wrong during the update.

    Returns:
        UpdateSingleResponse: With update result of the new updated type.
    """
    type_manager = TypeManager(database_manager=current_app.database_manager)
    try:
        type_ = TypeModel.from_data(data=data)

        type_manager.update(public_id=PublicID(public_id),
                            type=TypeModel.to_json(type_))
        api_response = UpdateSingleResponse(result=data,
                                            url=request.url,
                                            model=TypeModel.MODEL)
    except ManagerGetError as err:
        return abort(404, err.message)
    except ManagerUpdateError as err:
        return abort(400, err.message)

    return api_response.make_response()
Esempio n. 2
0
def insert_type(data: dict):
    """
    HTTP `POST` route for insert a single type resource.

    Args:
        data (TypeModel.SCHEMA): Insert data of a new type.

    Raises:
        ManagerGetError: If the inserted resource could not be found after inserting.
        ManagerInsertError: If something went wrong during insertion.

    Returns:
        InsertSingleResponse: Insert response with the new type and its public_id.
    """
    type_manager = TypeManager(database_manager=current_app.database_manager)
    data.setdefault('creation_time', datetime.utcnow())
    try:
        result_id: PublicID = type_manager.insert(data)
        raw_doc = type_manager.get(public_id=result_id)
    except ManagerGetError as err:
        return abort(404, err.message)
    except ManagerInsertError as err:
        return abort(400, err.message)
    api_response = InsertSingleResponse(result_id,
                                        raw=TypeModel.to_json(raw_doc),
                                        url=request.url,
                                        model=TypeModel.MODEL)
    return api_response.make_response(prefix='types')
Esempio n. 3
0
def get_unstructured_objects(public_id: int, request_user: UserModel):
    """
    HTTP `GET`/`HEAD` route for a multi resources which are not formatted according the type structure.
    Args:
        public_id (int): Public ID of the type.
    Raises:
        ManagerGetError: When the selected type does not exists or the objects could not be loaded.
    Returns:
        GetListResponse: Which includes the json data of multiple objects.
    """
    type_manager = TypeManager(database_manager=current_app.database_manager)
    object_manager = CmdbObjectManager(
        database_manager=current_app.database_manager)

    try:
        type: TypeModel = type_manager.get(public_id=public_id)
        objects: List[CmdbObject] = object_manager.get_objects_by(
            type_id=public_id)
    except ManagerGetError as err:
        return abort(400, err.message)
    type_fields = sorted([field.get('name') for field in type.fields])
    unstructured_objects: List[CmdbObject] = []
    for object_ in objects:
        object_fields = [field.get('name') for field in object_.fields]
        if sorted(object_fields) != type_fields:
            unstructured_objects.append(object_)

    api_response = GetListResponse(
        [un_object.__dict__ for un_object in unstructured_objects],
        url=request.url,
        model='Object',
        body=request.method == 'HEAD')
    return api_response.make_response()
Esempio n. 4
0
def add_type():
    from bson import json_util

    type_manager = TypeManager(database_manager=current_app.database_manager)

    error_collection = {}
    upload = request.form.get('uploadFile')
    new_type_list = json.loads(upload, object_hook=json_util.object_hook)
    for new_type_data in new_type_list:
        try:
            new_type_data['public_id'] = object_manager.get_new_id(TypeModel.COLLECTION)
            new_type_data['creation_time'] = datetime.now(timezone.utc)
        except TypeError as e:
            LOGGER.warning(e)
            return abort(400)
        try:
            type_instance = TypeModel.from_data(new_type_data)
        except CMDBError:
            return abort(400)
        try:
            type_manager.insert(type_instance)
        except Exception as ex:
            error_collection.update({"public_id": type_instance.public_id, "message": ex.message})

    resp = make_response(error_collection)
    return resp
Esempio n. 5
0
def get_type(public_id: int):
    """
    HTTP `GET`/`HEAD` route for a single type resource.

    Args:
        public_id (int): Public ID of the type.

    Raises:
        ManagerGetError: When the selected type does not exists.

    Returns:
        GetSingleResponse: Which includes the json data of a TypeModel.
    """
    type_manager = TypeManager(database_manager=current_app.database_manager)
    body = request.method == 'HEAD'

    try:
        type_ = type_manager.get(public_id)
    except ManagerGetError as err:
        return abort(404, err.message)
    api_response = GetSingleResponse(TypeModel.to_json(type_),
                                     url=request.url,
                                     model=TypeModel.MODEL,
                                     body=body)
    return api_response.make_response()
Esempio n. 6
0
    def __init__(self,
                 object_instance: CmdbObject,
                 type_instance: TypeModel,
                 render_user: UserModel,
                 object_manager: CmdbObjectManager = None,
                 ref_render=False):
        self.object_instance: CmdbObject = object_instance
        self.type_instance: TypeModel = type_instance
        self.render_user: UserModel = render_user

        self.object_manager = object_manager
        if self.object_manager:  # TODO: Refactor to pass database-manager in init
            self.type_manager = TypeManager(self.object_manager.dbm)
            self.user_manager = UserManager(self.object_manager.dbm)

        self.ref_render = ref_render
Esempio n. 7
0
def get_object(public_id, request_user: UserModel):
    try:
        object_instance = object_manager.get_object(
            public_id,
            user=request_user,
            permission=AccessControlPermission.READ)
    except (ObjectManagerGetError, ManagerGetError) as err:
        return abort(404, err.message)
    except AccessDeniedError as err:
        return abort(403, err.message)

    try:
        type_instance = TypeManager(
            database_manager=current_app.database_manager).get(
                object_instance.get_type_id())
    except ObjectManagerGetError as err:
        LOGGER.error(err.message)
        return abort(404, err.message)

    try:
        render = CmdbRender(object_instance=object_instance,
                            type_instance=type_instance,
                            render_user=request_user,
                            object_manager=object_manager,
                            ref_render=True)
        render_result = render.result()
    except RenderError as err:
        LOGGER.error(err)
        return abort(500)

    resp = make_response(render_result)
    return resp
Esempio n. 8
0
def verify_import_access(user: UserModel, _type: TypeModel, _manager: TypeManager):
    """Validate if a user has access to objects of this type."""

    location = 'acl.groups.includes.' + str(user.group_id)
    query = {'$and': [{'$or': [
        {'$or': [
            {'acl': {'$exists': False}}, {'acl.activated': False}]
        },
        {'$and': [
            {'acl.activated': True},
            {'$and': [
                {location: {'$exists': True}},
                {location: {'$all': [
                    AccessControlPermission.READ.value,
                    AccessControlPermission.CREATE.value,
                    AccessControlPermission.UPDATE.value]
                    }
                }
            ]
            }
        ]
        }]
    }, {'public_id': _type.public_id}]}
    types_ = _manager.iterate(filter=query, limit=1, skip=0, sort='public_id', order=1)
    if len([TypeModel.to_json(_) for _ in types_.results]) == 0:
        raise AccessDeniedError(f'The objects of the type `{_type.name}` are protected by ACL permission!')
Esempio n. 9
0
    def get(self,
            public_id: Union[PublicID, int],
            user: UserModel = None,
            permission: AccessControlPermission = None) -> CmdbObject:
        """
        Get a single object by its id.

        Args:
            public_id (int): ID of the object.
            user: Request user
            permission: ACL permission

        Returns:
            CmdbObject: Instance of CmdbObject with data.
        """
        cursor_result = self._get(self.collection,
                                  filter={'public_id': public_id},
                                  limit=1)
        for resource_result in cursor_result.limit(-1):
            resource = CmdbObject.from_data(resource_result)
            type_ = TypeManager(database_manager=self._database_manager).get(
                resource.type_id)
            verify_access(type_, user, permission)
            return resource
        else:
            raise ManagerGetError(f'Object with ID: {public_id} not found!')
Esempio n. 10
0
def get_types(params: CollectionParameters):
    """
    HTTP `GET`/`HEAD` route for getting a iterable collection of resources.

    Args:
        params (CollectionParameters): Passed parameters over the http query string

    Returns:
        GetMultiResponse: Which includes a IterationResult of the TypeModel.

    Example:
        You can pass any parameter based on the CollectionParameters.
        Optional parameters are passed over the function declaration.

    Raises:
        FrameworkIterationError: If the collection could not be iterated.
        ManagerGetError: If the collection could not be found.

    """
    type_manager = TypeManager(database_manager=current_app.database_manager)
    body = request.method == 'HEAD'

    try:
        iteration_result: IterationResult[TypeModel] = type_manager.iterate(
            filter=params.filter,
            limit=params.limit,
            skip=params.skip,
            sort=params.sort,
            order=params.order)
        types = [TypeModel.to_json(type) for type in iteration_result.results]
        api_response = GetMultiResponse(types,
                                        total=iteration_result.total,
                                        params=params,
                                        url=request.url,
                                        model=TypeModel.MODEL,
                                        body=body)
    except FrameworkIterationError as err:
        return abort(400, err.message)
    except ManagerGetError as err:
        return abort(404, err.message)
    return api_response.make_response()
Esempio n. 11
0
 def __init__(self):
     scr = SystemConfigReader()
     self.database_manager = DatabaseManagerMongo(
         **scr.get_all_values_from_section('Database'))
     self.object_manager = CmdbObjectManager(
         database_manager=self.database_manager)
     self.category_manager = CategoryManager(
         database_manager=self.database_manager)
     self.type_manager = TypeManager(database_manager=self.database_manager)
     self.log_manager = ExportdLogManager(
         database_manager=self.database_manager)
     super().__init__(self.database_manager)
Esempio n. 12
0
 def __init__(self,
              object_list: List[CmdbObject],
              request_user: UserModel,
              database_manager: DatabaseManagerMongo,
              ref_render=False,
              object_manager: CmdbObjectManager = None):
     self.object_list: List[CmdbObject] = object_list
     self.request_user = request_user
     self.ref_render = ref_render
     self.object_manager = object_manager
     self.type_manager = TypeManager(database_manager=database_manager)
     self.user_manager = UserManager(database_manager=database_manager)
Esempio n. 13
0
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()
Esempio n. 14
0
def import_objects(request_user: UserModel):
    # Check if file exists
    if not request.files:
        return abort(400, 'No import file was provided')
    request_file: FileStorage = get_file_in_request('file', request.files)

    filename = secure_filename(request_file.filename)
    working_file = f'/tmp/{filename}'
    request_file.save(working_file)

    # Load file format
    file_format = request.form.get('file_format', None)

    # Load parser config
    parser_config: dict = get_element_from_data_request('parser_config', request) or {}
    if parser_config == {}:
        LOGGER.info('No parser config was provided - using default parser config')

    # Check for importer config
    importer_config_request: dict = get_element_from_data_request('importer_config', request) or None
    if not importer_config_request:
        return abort(400, 'No import config was provided')

    # Check if type exists
    try:
        type_manager = TypeManager(database_manager=current_app.database_manager)
        type_ = type_manager.get(importer_config_request.get('type_id'))
        if not type_.active:
            raise AccessDeniedError(f'Objects cannot be created because type `{type_.name}` is deactivated.')
        verify_import_access(user=request_user,
                             _type=type_,
                             _manager=type_manager)
    except ObjectManagerGetError as err:
        return abort(404, err.message)
    except AccessDeniedError as err:
        return abort(403, err.message)

    # Load parser
    try:
        parser_class = load_parser_class('object', file_format)
    except ParserLoadError as ple:
        return abort(406, ple.message)
    parser = parser_class(parser_config)

    LOGGER.info(f'Parser {parser_class} was loaded')

    # Load importer config
    try:
        importer_config_class = load_importer_config_class('object', file_format)
    except ImporterLoadError as ile:
        return abort(406, ile.message)
    importer_config = importer_config_class(**importer_config_request)
    LOGGER.debug(importer_config_request)
    # Load importer
    try:
        importer_class = load_importer_class('object', file_format)
    except ImporterLoadError as ile:
        return abort(406, ile.message)
    importer = importer_class(working_file, importer_config, parser, object_manager, request_user)
    LOGGER.info(f'Importer {importer_class} was loaded')

    try:
        import_response: ImporterObjectResponse = importer.start_import()
    except ImportRuntimeError as ire:
        LOGGER.error(f'Error while importing objects: {ire.message}')
        return abort(500, ire.message)
    except AccessDeniedError as err:
        return abort(403, err.message)

    # close request file
    request_file.close()

    # log all successful imports
    for message in import_response.success_imports:
        try:

            # get object state of every imported object
            current_type_instance = object_manager.get_type(importer_config_request.get('type_id'))
            current_object = object_manager.get_object(message.public_id)
            current_object_render_result = CmdbRender(object_instance=current_object,
                                                      type_instance=current_type_instance,
                                                      render_user=request_user,
                                                      object_manager=object_manager).result()

            # insert object create log
            log_params = {
                'object_id': message.public_id,
                'user_id': request_user.get_public_id(),
                'user_name': request_user.get_display_name(),
                'comment': 'Object was imported',
                'render_state': json.dumps(current_object_render_result, default=default).encode('UTF-8'),
                'version': current_object.version
            }
            log_manager.insert(action=LogAction.CREATE, log_type=CmdbObjectLog.__name__, **log_params)

        except ObjectManagerGetError as err:
            LOGGER.error(err)
            return abort(404)
        except RenderError as err:
            LOGGER.error(err)
            return abort(500)
        except LogManagerInsertError as err:
            LOGGER.error(err)

    return make_response(import_response)
Esempio n. 15
0
def update_unstructured_objects(public_id: int, request_user: UserModel):
    """
    HTTP `PUT`/`PATCH` route for a multi resources which will be formatted based on the TypeModel
    Args:
        public_id (int): Public ID of the type.
    Raises:
        ManagerGetError: When the type with the `public_id` was not found.
        ManagerUpdateError: When something went wrong during the update.
    Returns:
        UpdateMultiResponse: Which includes the json data of multiple updated objects.
    """
    type_manager = TypeManager(database_manager=current_app.database_manager)
    object_manager = CmdbObjectManager(
        database_manager=current_app.database_manager)

    try:
        update_type_instance = type_manager.get(public_id)
        type_fields = update_type_instance.fields
        objects_by_type = object_manager.get_objects_by_type(type_id=public_id)

        for obj in objects_by_type:
            incorrect = []
            correct = []
            obj_fields = obj.get_all_fields()
            for t_field in type_fields:
                name = t_field["name"]
                for field in obj_fields:
                    if name == field["name"]:
                        correct.append(field["name"])
                    else:
                        incorrect.append(field["name"])
            removed_type_fields = [
                item for item in incorrect if not item in correct
            ]
            for field in removed_type_fields:
                object_manager.remove_object_fields(
                    filter_query={'public_id': obj.public_id},
                    update={'$pull': {
                        'fields': {
                            "name": field
                        }
                    }})

        objects_by_type = object_manager.get_objects_by_type(type_id=public_id)
        for obj in objects_by_type:
            for t_field in type_fields:
                name = t_field["name"]
                value = None
                if [
                        item for item in obj.get_all_fields()
                        if item["name"] == name
                ]:
                    continue
                if "value" in t_field:
                    value = t_field["value"]
                object_manager.update_object_fields(
                    filter={'public_id': obj.public_id},
                    update={
                        '$addToSet': {
                            'fields': {
                                "name": name,
                                "value": value
                            }
                        }
                    })

    except ManagerUpdateError as err:
        return abort(400, err.message)

    api_response = UpdateMultiResponse([], url=request.url, model='Object')

    return api_response.make_response()
Esempio n. 16
0
 def __init__(self, database_manager=None, event_queue: Queue = None):
     self._event_queue = event_queue
     self._type_manager = TypeManager(database_manager)
     super(CmdbObjectManager, self).__init__(database_manager)
Esempio n. 17
0
class CmdbObjectManager(CmdbManagerBase):

    def __init__(self, database_manager=None, event_queue: Queue = None):
        self._event_queue = event_queue
        self._type_manager = TypeManager(database_manager)
        super(CmdbObjectManager, self).__init__(database_manager)

    def is_ready(self) -> bool:
        return self.dbm.status()

    def get_new_id(self, collection: str) -> int:
        return self.dbm.get_next_public_id(collection)

    def aggregate(self, collection, pipeline: Pipeline, **kwargs):
        try:
            return self._aggregate(collection=collection, pipeline=pipeline, **kwargs)
        except Exception as err:
            raise ObjectManagerGetError(err)

    def get_object(self, public_id: int, user: UserModel = None,
                   permission: AccessControlPermission = None) -> CmdbObject:
        try:
            resource = CmdbObject(**self._get(
                collection=CmdbObject.COLLECTION,
                public_id=public_id))
        except Exception as err:
            raise ObjectManagerGetError(str(err))

        type_ = self._type_manager.get(resource.type_id)
        verify_access(type_, user, permission)
        return resource

    def get_objects_by(self, sort='public_id', direction=-1, user: UserModel = None,
                       permission: AccessControlPermission = None, **requirements):
        ack = []
        objects = self._get_many(collection=CmdbObject.COLLECTION, sort=sort, direction=direction, **requirements)
        for obj in objects:
            object_ = CmdbObject(**obj)
            try:
                type_ = self._type_manager.get(object_.type_id)
                verify_access(type_, user, permission)
            except CMDBError:
                continue
            ack.append(CmdbObject(**obj))
        return ack

    def get_objects_by_type(self, type_id: int):
        return self.get_objects_by(type_id=type_id)

    def count_objects_by_type(self, public_id: int):
        """This method does not actually
               performs the find() operation
               but instead returns
               a numerical count of the documents that meet the selection criteria.

               Args:
                   public_id (int): public id of document
               Returns:
                   returns the count of the documents
               """

        formatted_type_id = {'type_id': public_id}
        return self.dbm.count(CmdbObject.COLLECTION, formatted_type_id)

    def group_objects_by_value(self, value: str, match=None, user: UserModel = None,
                               permission: AccessControlPermission = None):
        """This method does not actually
           performs the find() operation
           but instead returns
           a objects grouped by type of the documents that meet the selection criteria.

           Args:
               value (str): grouped by value
               match (dict): stage filters the documents to only pass documents.
               user (UserModel): request user
               permission (AccessControlPermission):  ACL operations
           Returns:
               returns the objects grouped by value of the documents
           """
        ack = []
        agr = []
        if match:
            agr.append({'$match': match})
        agr.append({
            '$group': {
                '_id': '$' + value,
                'result': {'$first': '$$ROOT'},
                'count': {'$sum': 1},
            }
        })
        agr.append({'$sort': {'count': -1}})

        objects = self.dbm.aggregate(CmdbObject.COLLECTION, agr)
        for obj in objects:
            object_ = CmdbObject(**obj['result'])
            try:
                type_ = self._type_manager.get(object_.type_id)
                verify_access(type_, user, permission)
            except CMDBError:
                continue
            ack.append(obj)
        return ack

    def sort_objects_by_field_value(self, value: str, order=-1, match=None):
        """This method does not actually
           performs the find() operation
           but instead returns
           a objects sorted by value of the documents that meet the selection criteria.

           Args:
               value (str): sorted by value
               order : Ascending/Descending Sort e.g. -1
               match (dict): stage filters the documents to only pass documents.
           Returns:
               returns the list of CMDB Objects sorted by value of the documents
           """
        agr = []
        if match:
            agr.append({'$match': match})
        agr.append({"$addFields": {
            "order": {
                "$filter": {
                    "input": "$fields",
                    "as": "fields",
                    "cond": {"$eq": ["$$fields.name", value]}
                }
            }
        }})
        agr.append({'$sort': {'order': order}})

        object_list = []
        cursor = self.dbm.aggregate(CmdbObject.COLLECTION, agr)
        for document in cursor:
            put_data = json.loads(json_util.dumps(document), object_hook=object_hook)
            object_list.append(CmdbObject(**put_data))

        return object_list

    def count_objects(self):
        return self.dbm.count(collection=CmdbObject.COLLECTION)

    def _find_query_fields(self, query, match_fields=None):

        match_fields = match_fields or list()
        for key, items in query.items():
            if isinstance(items, dict):
                if 'fields.value' == key:
                    match_fields.append(items['$regex'])
                else:
                    for item in items:
                        self._find_query_fields(item, match_fields=match_fields)
        return match_fields

    def insert_object(self, data: (CmdbObject, dict), user: UserModel = None,
                      permission: AccessControlPermission = None) -> int:
        """
        Insert new CMDB Object
        Args:
            data: init data
            user: current user, to detect who triggered event
            permission: extended user acl rights
        Returns:
            Public ID of the new object in database
        """
        new_object = None
        if isinstance(data, dict):
            try:
                new_object = CmdbObject(**data)
            except CMDBError as e:
                LOGGER.debug(f'Error while inserting object - error: {e.message}')
                raise ObjectManagerInsertError(e)
        elif isinstance(data, CmdbObject):
            new_object = data

        type_ = self._type_manager.get(new_object.type_id)
        if not type_.active:
            raise AccessDeniedError(f'Objects cannot be created because type `{type_.name}` is deactivated.')

        verify_access(type_, user, permission)

        try:
            ack = self.dbm.insert(
                collection=CmdbObject.COLLECTION,
                data=new_object.__dict__
            )
            if self._event_queue:
                event = Event("cmdb.core.object.added", {"id": new_object.get_public_id(),
                                                         "type_id": new_object.get_type_id(),
                                                         "user_id": new_object.author_id,
                                                         "event": 'insert'})
                self._event_queue.put(event)
        except (CMDBError, PublicIDAlreadyExists) as e:
            raise ObjectInsertError(e)
        return ack

    def get_object_references(self, public_id: int, active_flag=None, user: UserModel = None,
                              permission: AccessControlPermission = None) -> list:
        # Type of given object id
        type_id = self.get_object(public_id=public_id, user=user, permission=permission).type_id

        # query for all types with ref input type with value of type id
        req_type_query = {
            "$and": [
                {'fields': {'$elemMatch': {'type': 'ref'}}},
                {'$or':
                    [
                        {'fields': {'$elemMatch': {'ref_types': type_id}}},
                        {'fields': {'$elemMatch': {'ref_types': {'$in': [type_id]}}}}
                    ]
                }
            ]
        }

        # get type list with given query
        req_type_list = self.get_types_by(**req_type_query)
        type_init_list = []
        for new_type_init in req_type_list:
            try:
                current_loop_type = new_type_init
                ref_fields = current_loop_type.get_fields_of_type_with_value(input_type='ref', _filter='ref_types',
                                                                             value=type_id)
                for ref_field in ref_fields:
                    type_init_list.append(
                        {"type_id": current_loop_type.get_public_id(), "field_name": ref_field['name']})
            except (CMDBError, FieldNotFoundError, FieldInitError):
                continue

        referenced_by_objects = []
        for possible_object_types in type_init_list:
            referenced_query = {"type_id": possible_object_types['type_id'], "fields": {
                "$elemMatch": {"$and": [{"name": possible_object_types['field_name']}],
                               "$or": [{"value": int(public_id)}, {"value": str(public_id)}]}}}
            if active_flag:
                referenced_query.update({'active': {"$eq": True}})

            referenced_by_objects = referenced_by_objects + self.get_objects_by(**referenced_query,
                                                                                user=user, permission=permission)

        return referenced_by_objects

    def delete_object(self, public_id: int, user: UserModel, permission: AccessControlPermission = None):
        type_id = self.get_object(public_id=public_id).type_id
        type_ = self._type_manager.get(type_id)
        if not type_.active:
            raise AccessDeniedError(f'Objects cannot be removed because type `{type_.name}` is deactivated.')
        verify_access(type_, user, permission)
        try:
            if self._event_queue:
                event = Event("cmdb.core.object.deleted",
                              {"id": public_id,
                               "type_id": self.get_object(public_id).get_type_id(),
                               "user_id": user.get_public_id(),
                               "event": 'delete'})
                self._event_queue.put(event)
            ack = self._delete(CmdbObject.COLLECTION, public_id)
            return ack
        except (CMDBError, Exception):
            raise ObjectDeleteError(msg=public_id)

    def delete_many_objects(self, filter_query: dict, public_ids, user: UserModel):
        ack = self._delete_many(CmdbObject.COLLECTION, filter_query)
        if self._event_queue:
            event = Event("cmdb.core.objects.deleted", {"ids": public_ids,
                                                        "user_id": user.get_public_id(),
                                                        "event": 'delete'})
            self._event_queue.put(event)
        return ack

    @deprecated
    def get_all_types(self) -> List[TypeModel]:
        try:
            raw_types: List[dict] = self._get_many(collection=TypeModel.COLLECTION)
        except Exception as err:
            raise ObjectManagerGetError(err=err)
        try:
            return [TypeModel.from_data(type) for type in raw_types]
        except Exception as err:
            raise ObjectManagerInitError(err=err)

    @deprecated
    def get_type(self, public_id: int):
        try:
            return TypeModel.from_data(self.dbm.find_one(
                collection=TypeModel.COLLECTION,
                public_id=public_id)
            )
        except RequiredInitKeyNotFoundError as err:
            raise ObjectManagerInitError(err=err.message)
        except Exception as err:
            raise ObjectManagerGetError(err=err)

    @deprecated
    def get_types_by(self, sort='public_id', **requirements):
        try:
            return [TypeModel.from_data(data) for data in
                    self._get_many(collection=TypeModel.COLLECTION, sort=sort, **requirements)]
        except Exception as err:
            raise ObjectManagerGetError(err=err)

    @deprecated
    def get_type_aggregate(self, arguments):
        """This method does not actually
           performs the find() operation
           but instead returns
           a objects sorted by value of the documents that meet the selection criteria.

           Args:
               arguments: query search for
           Returns:
               returns the list of CMDB Types sorted by value of the documents
           """
        type_list = []
        cursor = self.dbm.aggregate(TypeModel.COLLECTION, arguments)
        for document in cursor:
            put_data = json.loads(json_util.dumps(document), object_hook=object_hook)
            type_list.append(TypeModel.from_data(put_data))
        return type_list

    @deprecated
    def count_types(self):
        return self.dbm.count(collection=TypeModel.COLLECTION)

    @deprecated
    def get_categories(self) -> List[CategoryModel]:
        """Get all categories as nested list"""
        try:
            raw_categories = self._get_many(collection=CategoryModel.COLLECTION, sort='public_id')
        except Exception as err:
            raise ObjectManagerGetError(err)
        try:
            return [CategoryModel.from_data(category) for category in raw_categories]
        except Exception as err:
            raise ObjectManagerInitError(err)

    @deprecated
    def get_category_by(self, **requirements) -> CategoryModel:
        """Get a single category by requirements
        Notes:
            Even if multiple categories match the requirements only the first matched will be returned
        """
        try:
            raw_category = self._get_by(collection=CategoryModel.COLLECTION, **requirements)
        except Exception as err:
            raise ObjectManagerGetError(err)

        try:
            return CategoryModel.from_data(raw_category)
        except Exception as err:
            raise ObjectManagerInitError(err)

    @deprecated
    def get_categories_by(self, sort='public_id', **requirements: dict) -> List[CategoryModel]:
        """Get a list of categories by special requirements"""
        try:
            raw_categories = self._get_many(collection=CategoryModel.COLLECTION, sort=sort, **requirements)
        except Exception as err:
            raise ObjectManagerGetError(err)
        try:
            return [CategoryModel.from_data(category) for category in raw_categories]
        except Exception as err:
            raise ObjectManagerInitError(err)

    @deprecated
    def insert_category(self, category: CategoryModel):
        """Add a new category into the database or add the children list an existing category"""
        try:
            return self._insert(collection=CategoryModel.COLLECTION, data=CategoryModel.to_json(category))
        except Exception as err:
            raise ObjectManagerInsertError(err=err)
Esempio n. 18
0
 def __init__(self, database_manager: DatabaseManagerMongo):
     self.__type_manager = TypeManager(database_manager=database_manager)
     super(CategoryManager,
           self).__init__(CategoryModel.COLLECTION,
                          database_manager=database_manager)
Esempio n. 19
0
class CategoryManager(FrameworkManager):
    def __init__(self, database_manager: DatabaseManagerMongo):
        self.__type_manager = TypeManager(database_manager=database_manager)
        super(CategoryManager,
              self).__init__(CategoryModel.COLLECTION,
                             database_manager=database_manager)

    def iterate(self, filter: dict, limit: int, skip: int, sort: str, order: int, *args, **kwargs) \
            -> IterationResult[CategoryModel]:
        """
        Iterate over a collection of type resources.

        Args:
             filter: match requirements of field values
            limit: max number of elements to return
            skip: number of elements to skip first
            sort: sort field
            order: sort order

        Returns:
            IterationResult: Instance of IterationResult with generic CategoryModel.
        """

        try:
            query: Query = self.builder.build(filter=filter,
                                              limit=limit,
                                              skip=skip,
                                              sort=sort,
                                              order=order)
            aggregation_result = next(self._aggregate(self.collection, query))
        except ManagerGetError as err:
            raise ManagerIterationError(err=err)
        iteration_result: IterationResult[
            CategoryModel] = IterationResult.from_aggregation(
                aggregation_result)
        iteration_result.convert_to(CategoryModel)
        return iteration_result

    def get(self, public_id: Union[PublicID, int]) -> CategoryModel:
        """
        Get a single type by its id.

        Args:
            public_id (int): ID of the type.

        Returns:
            CategoryModel: Instance of CategoryModel with data.
        """
        cursor_result = self._get(self.collection,
                                  filter={'public_id': public_id},
                                  limit=1)
        for resource_result in cursor_result.limit(-1):
            return CategoryModel.from_data(resource_result)
        raise ManagerGetError(f'Category with ID: {public_id} not found!')

    def insert(self, category: dict) -> PublicID:
        """
        Insert a single category into the system.

        Args:
            category (dict): Raw data of the category.

        Notes:
            If no public id was given, the database manager will auto insert the next available ID.

        Returns:
            int: The Public ID of the new inserted category
        """
        if isinstance(category, CategoryModel):
            category = CategoryModel.to_json(category)
        return self._insert(self.collection, resource=category)

    def update(self, public_id: Union[PublicID, int],
               category: Union[CategoryModel, dict]):
        """
        Update a existing category in the system.

        Args:
            public_id (int): PublicID of the category in the system.
            category: New category data

        Notes:
            If a CategoryModel instance was passed as type argument, \
            it will be auto converted via the model `to_json` method.
        """
        if isinstance(category, CategoryModel):
            category = CategoryModel.to_json(category)
        update_result = self._update(self.collection,
                                     filter={'public_id': public_id},
                                     resource=category)
        if update_result.matched_count != 1:
            raise ManagerUpdateError(f'Something happened during the update!')
        return update_result

    def delete(self, public_id: Union[PublicID, int]) -> CategoryModel:
        """
        Delete a existing category by its PublicID.

        Args:
            public_id (int): PublicID of the type in the system.

        Returns:
            CategoryModel: The deleted type as its model.
        """
        raw_category = self.get(public_id=public_id)
        delete_result = self._delete(self.collection,
                                     filter={'public_id': public_id})
        if delete_result.deleted_count == 0:
            raise FrameworkDeleteError(
                err='No category matched this public id')
        return raw_category

    @property
    def tree(self) -> CategoryTree:
        """
        Get the complete category list as nested tree.

        Returns:
            CategoryTree: Categories as tree structure.
        """
        # Find all types
        types = self.__type_manager.find({}).results
        categories = self.iterate(filter={},
                                  limit=0,
                                  skip=0,
                                  sort='public_id',
                                  order=1).results

        return CategoryTree(categories=categories, types=types)
Esempio n. 20
0
from cmdb.utils import json_encoding
from cmdb.utils.helpers import load_class
from cmdb.utils.system_config import SystemConfigReader
from cmdb.database.managers import DatabaseManagerMongo
from cmdb.exporter.exporter_utils import ExperterUtils
from cmdb.exporter.format.format_base import BaseExporterFormat
from cmdb.exporter.config.config_type import ExporterConfigType
from cmdb.framework.managers.type_manager import TypeManager
from cmdb.framework.cmdb_object_manager import CmdbObjectManager
from cmdb.framework.cmdb_render import RenderResult

database_manager = DatabaseManagerMongo(
    **SystemConfigReader().get_all_values_from_section('Database'))
object_manager = CmdbObjectManager(database_manager=database_manager)
type_manager = TypeManager(database_manager=database_manager)


class ZipExportType(BaseExporterFormat):

    FILE_EXTENSION = "zip"
    LABEL = "ZIP"
    MULTITYPE_SUPPORT = True
    ICON = "file-archive"
    DESCRIPTION = "Export Zipped Files"
    ACTIVE = True

    def export(self, data: List[RenderResult], *args):
        """
        Export a zip file, containing the object list sorted by type in several files.
Esempio n. 21
0
from cmdb.interface.api_parameters import CollectionParameters
from cmdb.interface.response import GetMultiResponse, GetListResponse, UpdateMultiResponse, UpdateSingleResponse,\
    ResponseFailedMessage
from cmdb.interface.route_utils import make_response, insert_request_user
from cmdb.interface.blueprint import APIBlueprint
from cmdb.manager import ManagerIterationError, ManagerGetError, ManagerUpdateError
from cmdb.security.acl.errors import AccessDeniedError
from cmdb.security.acl.permission import AccessControlPermission
from cmdb.user_management import UserModel, UserManager
from cmdb.utils.error import CMDBError
from cmdb.framework.managers.object_manager import ObjectManager

with current_app.app_context():
    object_manager = CmdbObjectManager(current_app.database_manager,
                                       current_app.event_queue)
    type_manager = TypeManager(database_manager=current_app.database_manager)
    log_manager = CmdbLogManager(current_app.database_manager)
    user_manager = UserManager(current_app.database_manager)

LOGGER = logging.getLogger(__name__)

objects_blueprint = APIBlueprint('objects', __name__)


@objects_blueprint.route('/', methods=['GET', 'HEAD'])
@objects_blueprint.protect(auth=True, right='base.framework.object.view')
@objects_blueprint.parse_collection_parameters(view='native')
@insert_request_user
def get_objects(params: CollectionParameters, request_user: UserModel):

    manager = ObjectManager(database_manager=current_app.database_manager)
Esempio n. 22
0
class CmdbRender:
    AUTHOR_ANONYMOUS_NAME = 'unknown'

    def __init__(self,
                 object_instance: CmdbObject,
                 type_instance: TypeModel,
                 render_user: UserModel,
                 object_manager: CmdbObjectManager = None,
                 ref_render=False):
        self.object_instance: CmdbObject = object_instance
        self.type_instance: TypeModel = type_instance
        self.render_user: UserModel = render_user

        self.object_manager = object_manager
        if self.object_manager:  # TODO: Refactor to pass database-manager in init
            self.type_manager = TypeManager(self.object_manager.dbm)
            self.user_manager = UserManager(self.object_manager.dbm)

        self.ref_render = ref_render

    @property
    def object_instance(self) -> CmdbObject:
        """
        Object of the class CmdbObject that has already been instantiated.
        The data should come from the database and already be validated.
        This already happens when the object is instantiated.
        """
        return self._object_instance

    @object_instance.setter
    def object_instance(self, object_instance: CmdbObject):
        """
        Property setter for object_instance. The render only checks whether the passed object
        belongs to the correct class, not whether it is valid.
        """
        if not isinstance(object_instance, CmdbObject):
            raise ObjectInstanceError()
        else:
            self._object_instance = object_instance

    @property
    def type_instance(self) -> TypeModel:
        """
        Object of the class TypeModel that has already been instantiated.
        The data should come from the database and already be validated.
        This already happens when the object is instantiated.
        """
        return self._type_instance

    @type_instance.setter
    def type_instance(self, type_instance: TypeModel):
        """
        Property setter for type_instance. The render only checks whether the passed object
        belongs to the correct class, not whether it is valid.
        """
        if not isinstance(type_instance, TypeModel):
            raise TypeInstanceError()
        self._type_instance = type_instance

    def result(self) -> RenderResult:
        return self._generate_result()

    def _generate_result(self) -> RenderResult:
        render_result = RenderResult()
        try:
            render_result = self.__generate_object_information(render_result)
            render_result = self.__generate_type_information(render_result)
            render_result = self.__set_fields(render_result)
            render_result = self.__set_sections(render_result)
            render_result = self.__set_summaries(render_result)
            render_result = self.__set_external(render_result)
        except CMDBError as err:
            import traceback
            traceback.print_exc()
            raise RenderError(
                f'Error while generating a CMDBResult: {str(err)}')
        return render_result

    def __generate_object_information(
            self, render_result: RenderResult) -> RenderResult:
        try:
            author_name = self.user_manager.get(
                self.object_instance.author_id).get_display_name()
        except CMDBError:
            author_name = CmdbRender.AUTHOR_ANONYMOUS_NAME

        if self.object_instance.editor_id:
            try:
                editor_name = self.user_manager.get(
                    self.object_instance.editor_id).get_display_name()
            except CMDBError:
                editor_name = None
        else:
            editor_name = None

        render_result.object_information = {
            'object_id': self.object_instance.public_id,
            'creation_time': self.object_instance.creation_time,
            'last_edit_time': self.object_instance.last_edit_time,
            'author_id': self.object_instance.author_id,
            'author_name': author_name,
            'editor_id': self.object_instance.editor_id,
            'editor_name': editor_name,
            'active': self.object_instance.active,
            'version': self.object_instance.version
        }
        return render_result

    def __generate_type_information(
            self, render_result: RenderResult) -> RenderResult:
        try:
            author_name = self.user_manager.get(
                self.type_instance.author_id).get_display_name()
        except CMDBError as err:
            author_name = CmdbRender.AUTHOR_ANONYMOUS_NAME
        try:
            self.type_instance.render_meta.icon
        except KeyError:
            self.type_instance.render_meta.icon = ''
        render_result.type_information = {
            'type_id': self.type_instance.public_id,
            'type_name': self.type_instance.name,
            'type_label': self.type_instance.label,
            'creation_time': self.type_instance.creation_time,
            'author_id': self.type_instance.author_id,
            'author_name': author_name,
            'icon': self.type_instance.render_meta.icon,
            'active': self.type_instance.active,
            'version': self.type_instance.version,
            'acl': self.type_instance.acl.to_json(self.type_instance.acl)
        }
        return render_result

    def __set_fields(self, render_result: RenderResult) -> RenderResult:
        render_result.fields = self.__merge_fields_value()
        return render_result

    def __set_sections(self, render_result: RenderResult) -> RenderResult:
        try:
            render_result.sections = [
                section.to_json(section)
                for section in self.type_instance.render_meta.sections
            ]
        except (IndexError, ValueError):
            render_result.sections = []
        return render_result

    def __merge_field_content_section(self, field: dict, object_: CmdbObject):
        curr_field = [x for x in object_.fields
                      if x['name'] == field['name']][0]
        if curr_field['name'] == field['name'] and field.get('value'):
            field['default'] = field['value']
        field['value'] = curr_field['value']
        # handle dates that are stored as strings
        if field['type'] == 'date' and isinstance(field['value'],
                                                  str) and field['value']:
            field['value'] = parse(field['value'], fuzzy=True)

        if self.ref_render and field['type'] == 'ref' and field['value']:
            field['reference'] = self.__merge_references(field)
        return field

    def __merge_fields_value(self) -> List[dict]:
        """
        Checks all fields for references.
        Fields with references are extended by the property 'references'.
        All reference values are stored in the new property.
        """
        field_map = []
        for idx, section in enumerate(self.type_instance.render_meta.sections):
            if type(section) is TypeFieldSection and isinstance(
                    section, TypeFieldSection):
                for section_field in section.fields:
                    field = {}
                    try:
                        field = self.type_instance.get_field(section_field)
                        field = self.__merge_field_content_section(
                            field, self.object_instance)

                        if field['type'] == 'ref' and (not self.ref_render
                                                       or 'summaries'
                                                       not in field):
                            ref_field_name: str = field['name']
                            field = self.type_instance.get_field(
                                ref_field_name)
                            reference_id: int = self.object_instance.get_value(
                                ref_field_name)
                            field['value'] = reference_id
                            reference_object: CmdbObject = self.object_manager.get_object(
                                public_id=reference_id)
                            ref_type: TypeModel = self.type_manager.get(
                                reference_object.get_type_id())
                            field['reference'] = {
                                'type_id': ref_type.public_id,
                                'type_name': ref_type.name,
                                'type_label': ref_type.label,
                                'object_id': reference_id,
                                'summaries': []
                            }
                            for ref_section_field_name in ref_type.get_fields(
                            ):
                                ref_section_field = ref_type.get_field(
                                    ref_section_field_name['name'])
                                try:
                                    ref_field = self.__merge_field_content_section(
                                        ref_section_field, reference_object)
                                except (FileNotFoundError, ValueError,
                                        IndexError):
                                    continue
                                field['reference']['summaries'].append(
                                    ref_field)

                    except (ValueError, IndexError, FileNotFoundError,
                            ObjectManagerGetError):
                        field['value'] = None

                    field_map.append(field)

            elif type(section) is TypeReferenceSection and isinstance(
                    section, TypeReferenceSection):
                ref_field_name: str = f'{section.name}-field'
                ref_field = self.type_instance.get_field(ref_field_name)
                reference_id: int = self.object_instance.get_value(
                    ref_field_name)
                ref_field['value'] = reference_id
                try:
                    reference_object: CmdbObject = self.object_manager.get_object(
                        public_id=reference_id)
                except ObjectManagerGetError:
                    reference_object = None
                ref_type: TypeModel = self.type_manager.get(
                    section.reference.type_id)
                ref_section = ref_type.get_section(
                    section.reference.section_name)
                ref_field['references'] = {
                    'type_id': ref_type.public_id,
                    'type_name': ref_type.name,
                    'type_label': ref_type.label,
                    'fields': []
                }
                if not ref_section:
                    continue
                if not section.reference.selected_fields or len(
                        section.reference.selected_fields) == 0:
                    selected_ref_fields = ref_section.fields
                    section.reference.selected_fields = selected_ref_fields
                    self.type_instance.render_meta.sections[idx] = section
                else:
                    selected_ref_fields = [
                        f for f in ref_section.fields
                        if f in section.reference.selected_fields
                    ]
                for ref_section_field_name in selected_ref_fields:
                    ref_section_field = ref_type.get_field(
                        ref_section_field_name)
                    if reference_object:
                        try:
                            ref_section_field = self.__merge_field_content_section(
                                ref_section_field, reference_object)
                        except (FileNotFoundError, ValueError, IndexError,
                                ObjectManagerGetError):
                            continue
                    ref_field['references']['fields'].append(ref_section_field)
                field_map.append(ref_field)
        return field_map

    def __merge_references(self, current_field):

        # Initialise TypeReference
        reference = TypeReference(type_id=0,
                                  object_id=0,
                                  type_label='',
                                  line='')

        if current_field['value']:

            try:
                ref_object = self.object_manager.get_object(
                    int(current_field['value']),
                    user=self.render_user,
                    permission=AccessControlPermission.READ)
            except AccessDeniedError as err:
                return err.message
            except ObjectManagerGetError:
                return TypeReference.to_json(reference)

            try:
                ref_type = self.object_manager.get_type(
                    ref_object.get_type_id())

                _summary_fields = []
                _nested_summaries = self.type_instance.get_nested_summaries()
                _nested_summary_fields = ref_type.get_nested_summary_fields(
                    _nested_summaries)
                _nested_summary_line = ref_type.get_nested_summary_line(
                    _nested_summaries)

                reference.type_id = ref_type.get_public_id()
                reference.object_id = int(current_field['value'])
                reference.type_label = ref_type.label
                reference.icon = ref_type.get_icon()
                reference.prefix = ref_type.has_nested_prefix(
                    _nested_summaries)

                _summary_fields = _nested_summary_fields \
                    if (_nested_summary_line or _nested_summary_fields) else ref_type.get_summary().fields

                summaries = []
                summary_values = []
                for field in _summary_fields:
                    summary_value = str([
                        x for x in ref_object.fields
                        if x['name'] == field['name']
                    ][0]['value'])
                    summaries.append({
                        "value": summary_value,
                        "type": field.get('type')
                    })
                    summary_values.append(summary_value)
                reference.summaries = summaries

                try:
                    # fill the summary line with summaries value data
                    reference.line = _nested_summary_line
                    if not reference.line_requires_fields():
                        reference.summaries = []
                    if _nested_summary_line:
                        reference.fill_line(summary_values)
                except (TypeReferenceLineFillError, Exception):
                    pass

            except ObjectManagerGetError:
                return TypeReference.to_json(reference)

        return TypeReference.to_json(reference)

    def __set_summaries(self, render_result: RenderResult) -> RenderResult:
        # global summary list
        summary_list = []
        summary_line = ''
        default_line = f'{self.type_instance.label} #{self.object_instance.public_id}'
        if not self.type_instance.has_summaries():
            render_result.summaries = summary_list
            render_result.summary_line = default_line
            return render_result
        try:
            summary_list = self.type_instance.get_summary().fields
            render_result.summaries = summary_list
            first = True

            for line in summary_list:
                if first:
                    summary_line += f'{line["value"]}'
                    first = False
                else:
                    summary_line += f' | {line["value"]}'

            render_result.summary_line = summary_line
        except Exception:
            summary_line = default_line
        finally:
            render_result.summary_line = summary_line
        return render_result

    def __set_external(self, render_result: RenderResult) -> RenderResult:
        """
        get filled external links
        Returns:
            list of filled external links (TypeExternalLink)
        """
        # global external list
        external_list = []
        # checks if type has externals defined
        if not self.type_instance.has_externals():
            render_result.externals = []
        # loop over all externals
        for ext_link in self.type_instance.get_externals():
            # append all values for required field in this list
            field_list = []
            # if data are missing or empty append here
            missing_list = []
            try:
                # get TypeExternalLink definitions from type
                ext_link_instance = self.type_instance.get_external(
                    ext_link.name)
                # check if link requires data - regex check for {}
                if ext_link_instance.link_requires_fields():
                    # check if has fields
                    if not ext_link_instance.has_fields():
                        raise ValueError(field_list)
                    # for every field get the value data from object_instance
                    for ext_link_field in ext_link_instance.fields:
                        try:
                            if ext_link_field == 'object_id':
                                field_value = self.object_instance.public_id
                            else:
                                field_value = self.object_instance.get_value(
                                    ext_link_field)
                            if field_value is None or field_value == '':
                                # if value is empty or does not exists
                                raise ValueError(ext_link_field)
                            field_list.append(field_value)
                        except CMDBError:
                            # if error append missing data
                            missing_list.append(ext_link_instance)
                if len(missing_list) > 0:
                    raise RuntimeError(missing_list)
                try:
                    # fill the href with field value data
                    ext_link_instance.fill_href(field_list)
                except ValueError:
                    continue
            except (CMDBError, Exception):
                continue
            external_list.append(TypeExternalLink.to_json(ext_link_instance))
            render_result.externals = external_list
        return render_result