Exemple #1
0
class LinkSchema(ma.Schema):
    class Meta:
        unknown = ma.EXCLUDE

    id = ma.fields.Integer(missing=None)
    paw = ma.fields.String()
    command = ma.fields.String()
    status = ma.fields.Integer(missing=-3)
    score = ma.fields.Integer(missing=0)
    jitter = ma.fields.Integer(missing=0)
    decide = ma.fields.DateTime(format='%Y-%m-%d %H:%M:%S')
    pin = ma.fields.Integer(missing=0)
    pid = ma.fields.String()
    facts = ma.fields.List(ma.fields.Nested(FactSchema()))
    relationships = ma.fields.List(ma.fields.Nested(RelationshipSchema()))
    used = ma.fields.List(ma.fields.Nested(FactSchema()))
    unique = ma.fields.String()
    collect = ma.fields.DateTime(format='%Y-%m-%d %H:%M:%S', default='')
    finish = ma.fields.String()
    ability = ma.fields.Nested(AbilitySchema())
    cleanup = ma.fields.Integer(missing=0)
    visibility = ma.fields.Nested(VisibilitySchema)
    host = ma.fields.String(missing=None)
    output = ma.fields.String()
    deadman = ma.fields.Boolean()

    @ma.pre_load()
    def fix_ability(self, link, **_):
        if 'ability' in link and isinstance(link['ability'], Ability):
            link_input = link.pop('ability')
            link['ability'] = link_input.schema.dump(link_input)
        return link

    @ma.post_load()
    def build_link(self, data, **_):
        return Link(**data)

    @ma.pre_dump()
    def prepare_link(self, data, **_):
        # temp - can be simplified with AbilitySchema
        data.executor = data.ability.executor if isinstance(
            data.ability, Ability) else data.ability['executor']
        return data
Exemple #2
0
class PlannerSchema(ma.Schema):
    planner_id = ma.fields.String(data_key='id')
    name = ma.fields.String()
    module = ma.fields.String()
    params = ma.fields.Dict()
    description = ma.fields.String()
    stopping_conditions = ma.fields.List(ma.fields.Nested(FactSchema()))
    ignore_enforcement_modules = ma.fields.List(ma.fields.String())

    @ma.post_load()
    def build_planner(self, data, **_):
        return Planner(**data)
Exemple #3
0
class PlannerSchema(ma.Schema):
    planner_id = ma.fields.String(data_key='id')
    name = ma.fields.String()
    module = ma.fields.String()
    params = ma.fields.Dict()
    description = ma.fields.String()
    stopping_conditions = ma.fields.List(ma.fields.Nested(FactSchema()))
    ignore_enforcement_modules = ma.fields.List(ma.fields.String())
    allow_repeatable_abilities = ma.fields.Boolean()

    @ma.post_load()
    def build_planner(self, data, **kwargs):
        return None if kwargs.get('partial') is True else Planner(**data)
Exemple #4
0
 async def get_facts(self, request: web.Request):
     knowledge_svc_handle = self._api_manager.knowledge_svc
     fact_data = await self._api_manager.extract_data(request)
     resp = []
     if fact_data:
         try:
             FactSchema(partial=True).load(fact_data)
             store = await knowledge_svc_handle.get_facts(criteria=fact_data)
             resp = await self._api_manager.verify_fact_integrity(store)
         except Exception as e:
             error_msg = f'Encountered issue retrieving fact {fact_data} - {e}'
             self.log.warning(error_msg)
             raise JsonHttpBadRequest(error_msg)
     return web.json_response(dict(found=resp))
Exemple #5
0
class LinkSchema(ma.Schema):
    class Meta:
        unknown = ma.EXCLUDE

    id = ma.fields.String(missing='')
    paw = ma.fields.String()
    command = ma.fields.String()
    status = ma.fields.Integer(missing=-3)
    score = ma.fields.Integer(missing=0)
    jitter = ma.fields.Integer(missing=0)
    decide = ma.fields.DateTime(format='%Y-%m-%d %H:%M:%S')
    pin = ma.fields.Integer(missing=0)
    pid = ma.fields.String()
    facts = ma.fields.List(ma.fields.Nested(FactSchema()))
    relationships = ma.fields.List(ma.fields.Nested(RelationshipSchema()))
    used = ma.fields.List(ma.fields.Nested(FactSchema()))
    unique = ma.fields.String()
    collect = ma.fields.DateTime(format='%Y-%m-%d %H:%M:%S', default='')
    finish = ma.fields.String()
    ability = ma.fields.Nested(AbilitySchema())
    executor = ma.fields.Nested(ExecutorSchema())
    cleanup = ma.fields.Integer(missing=0)
    visibility = ma.fields.Nested(VisibilitySchema())
    host = ma.fields.String(missing=None)
    output = ma.fields.String()
    deadman = ma.fields.Boolean()
    agent_reported_time = ma.fields.DateTime(format='%Y-%m-%d %H:%M:%S',
                                             missing=None)

    @ma.pre_load()
    def fix_ability(self, link, **_):
        if 'ability' in link and isinstance(link['ability'], Ability):
            ability = link.pop('ability')
            link['ability'] = ability.schema.dump(ability)
        return link

    @ma.pre_load()
    def fix_executor(self, link, **_):
        if 'executor' in link and isinstance(link['executor'], Executor):
            executor = link.pop('executor')
            link['executor'] = executor.schema.dump(executor)
        return link

    @ma.pre_load()
    def remove_properties(self, data, **_):
        data.pop('unique', None)
        data.pop('decide', None)
        data.pop('pid', None)
        data.pop('facts', None)
        data.pop('collect', None)
        data.pop('finish', None)
        data.pop('visibility', None)
        data.pop('output', None)
        data.pop('used.unique', None)
        return data

    @ma.post_load()
    def build_link(self, data, **kwargs):
        return None if kwargs.get('partial') is True else Link(**data)

    @ma.post_dump()
    def prepare_dump(self, data, **_):
        if data.get('agent_reported_time', None) is None:
            data.pop('agent_reported_time', None)
        return data
Exemple #6
0
 async def _find_and_verify_facts(self, fact_data: dict):
     knowledge_svc_handle = self._api_manager.knowledge_svc
     FactSchema(partial=True).load(fact_data)
     store = await knowledge_svc_handle.get_facts(criteria=fact_data)
     return await self._api_manager.verify_fact_integrity(store)
Exemple #7
0
class FactApi(BaseObjectApi):
    def __init__(self, services):
        super().__init__(description='adversary',
                         obj_class=Fact,
                         schema=FactSchema,
                         ram_key='facts',
                         id_property='adversary_id',
                         auth_svc=services['auth_svc'])
        self._api_manager = FactApiManager(
            data_svc=services['data_svc'],
            file_svc=services['file_svc'],
            knowledge_svc=services['knowledge_svc'])

    def add_routes(self, app: web.Application):
        router = app.router
        router.add_get('/facts', self.get_facts)
        router.add_get('/relationships', self.get_relationships)
        router.add_get('/facts/{operation_id}', self.get_facts_by_operation_id)
        router.add_get('/relationships/{operation_id}',
                       self.get_relationships_by_operation_id)
        router.add_post('/facts', self.add_facts)
        router.add_post('/relationships', self.add_relationships)
        router.add_delete('/facts', self.delete_facts)
        router.add_delete('/relationships', self.delete_relationships)
        router.add_patch('/facts', self.update_facts)
        router.add_patch('/relationships', self.update_relationships)

    @aiohttp_apispec.docs(
        tags=['facts'],
        summary='Retrieve Facts',
        description=
        'Retrieve facts by criteria. Use fields from the `FactSchema` in the request '
        'body to filter retrieved facts.')
    @aiohttp_apispec.querystring_schema(BaseGetAllQuerySchema)
    @aiohttp_apispec.response_schema(
        FactSchema(many=True, partial=True),
        description=
        'Returns a list of matching facts, dumped in `FactSchema` format.')
    async def get_facts(self, request: web.Request):
        fact_data = await self._api_manager.extract_data(request)
        resp = []
        if fact_data:
            try:
                resp = await self._find_and_verify_facts(fact_data)
            except Exception as e:
                error_msg = f'Encountered issue retrieving fact {fact_data} - {e}'
                self.log.warning(error_msg)
                raise JsonHttpBadRequest(error_msg)
        return web.json_response(dict(found=resp))

    @aiohttp_apispec.docs(
        tags=['facts'],
        summary='Retrieve Facts by operation id',
        description=
        'Retrieves facts associated with an operation. Returned facts will either be '
        'user-generated facts or learned facts.')
    @aiohttp_apispec.querystring_schema(BaseGetAllQuerySchema)
    @aiohttp_apispec.response_schema(
        FactSchema(many=True, partial=True),
        description=
        'Returns a list of facts associated with operation_id, dumped in '
        '`FactSchema` format.')
    async def get_facts_by_operation_id(self, request: web.Request):
        operation_id = request.match_info.get('operation_id')
        fact_data = {'source': operation_id}
        resp = []
        if fact_data:
            try:
                resp = await self._find_and_verify_facts(fact_data)
            except Exception as e:
                error_msg = f'Encountered issue retrieving facts associated with operation {operation_id} - {e}'
                self.log.warning(error_msg)
                raise JsonHttpBadRequest(error_msg)
        return web.json_response(dict(found=resp))

    @aiohttp_apispec.docs(
        tags=['relationships'],
        summary="Retrieve Relationships",
        description=
        'Retrieve relationships by criteria. Use fields from the `RelationshipSchema` in '
        'the request body to filter retrieved relationships.')
    @aiohttp_apispec.response_schema(
        RelationshipSchema,
        description='Returns a list of matching relationships, dumped in '
        '`RelationshipSchema` format.')
    @aiohttp_apispec.querystring_schema(BaseGetAllQuerySchema)
    @aiohttp_apispec.response_schema(
        RelationshipSchema(many=True, partial=True))
    async def get_relationships(self, request: web.Request):
        relationship_data = await self._api_manager.extract_data(request)
        resp = []
        if relationship_data:
            try:
                resp = await self._find_and_verify_relationships(
                    relationship_data)
            except Exception as e:
                error_msg = f'Encountered issue retrieving relationship {relationship_data} - {e}'
                self.log.warning(error_msg)
                raise JsonHttpBadRequest(error_msg)
        return web.json_response(dict(found=resp))

    @aiohttp_apispec.docs(
        tags=['relationships'],
        summary="Retrieve Relationships by operation id",
        description=
        'Retrieve relationships associated with an operation. Returned relationships '
        'will be either user-generated relationships or learned relationships.'
    )
    @aiohttp_apispec.response_schema(
        RelationshipSchema,
        description='Returns a list of matching relationships, dumped in '
        '`RelationshipSchema` format.')
    @aiohttp_apispec.querystring_schema(BaseGetAllQuerySchema)
    @aiohttp_apispec.response_schema(
        RelationshipSchema(many=True, partial=True))
    async def get_relationships_by_operation_id(self, request: web.Request):
        operation_id = request.match_info.get('operation_id')
        relationship_data = {'origin': operation_id}
        resp = []
        if relationship_data:
            try:
                resp = await self._find_and_verify_relationships(
                    relationship_data)
            except Exception as e:
                error_msg = f'Encountered issue retrieving relationships associated with operation' \
                            f' {operation_id} - {e}'
                self.log.warning(error_msg)
                raise JsonHttpBadRequest(error_msg)
        return web.json_response(dict(found=resp))

    @aiohttp_apispec.docs(
        tags=['facts'],
        summary='Create a Fact',
        description=
        'Create a new fact using the format provided in the `FactSchema`.')
    @aiohttp_apispec.request_schema(FactSchema)
    @aiohttp_apispec.response_schema(
        FactSchema,
        description='Returns the created fact, dumped in `FactSchema` format.')
    async def add_facts(self, request: web.Request):
        knowledge_svc_handle = self._api_manager.knowledge_svc
        fact_data = await self._api_manager.extract_data(request)
        try:
            new_fact = Fact.load(fact_data)
            if 'source' not in fact_data:
                new_fact.source = WILDCARD_STRING
            new_fact.origin_type = OriginType.USER
            await self._api_manager.verify_operation_state(new_fact)
            await knowledge_svc_handle.add_fact(new_fact)
            store = await knowledge_svc_handle.get_facts(
                criteria=dict(trait=new_fact.trait,
                              value=new_fact.value,
                              source=new_fact.source,
                              origin_type=OriginType.USER))
            resp = await self._api_manager.verify_fact_integrity(store)
            return web.json_response(dict(added=resp))
        except Exception as e:
            error_msg = f'Encountered issue saving fact {fact_data} - {e}'
            self.log.warning(error_msg)
            raise JsonHttpBadRequest(error_msg)

    @aiohttp_apispec.docs(
        tags=['relationships'],
        summary='Create a Relationship',
        description='Create a new relationship using the format provided in the '
        '`RelationshipSchema`.')
    @aiohttp_apispec.request_schema(RelationshipSchema)
    @aiohttp_apispec.response_schema(
        RelationshipSchema,
        description=
        'Returns the created relationship, dumped in `RelationshipSchema` '
        'format.')
    async def add_relationships(self, request: web.Request):
        knowledge_svc_handle = self._api_manager.knowledge_svc
        relationship_data = await self._api_manager.extract_data(request)
        try:
            origin_target = WILDCARD_STRING
            new_relationship = Relationship.load(relationship_data)
            if 'origin' in relationship_data:
                origin_target = relationship_data['origin']
            else:
                new_relationship.origin = origin_target
            shorthand = new_relationship.shorthand
            new_relationship.source.relationships = [shorthand]
            new_relationship.source.source = origin_target
            new_relationship.source.origin_type = OriginType.USER
            if 'target' in relationship_data:
                new_relationship.target.source = origin_target
                new_relationship.target.origin_type = OriginType.USER
                new_relationship.target.relationships = [shorthand]
                await knowledge_svc_handle.add_fact(new_relationship.target)
            await knowledge_svc_handle.add_fact(new_relationship.source)
            await knowledge_svc_handle.add_relationship(new_relationship)

            store = await knowledge_svc_handle.get_relationships(
                criteria=dict(source=new_relationship.source,
                              edge=new_relationship.edge if 'edge' in
                              relationship_data else None,
                              target=new_relationship.target if 'target' in
                              relationship_data else None,
                              origin=origin_target))
            resp = await self._api_manager.verify_relationship_integrity(store)
            return web.json_response(dict(added=resp))
        except Exception as e:
            error_msg = f'Encountered issue saving relationship {relationship_data} - {e}'
            self.log.warning(error_msg)
            raise JsonHttpBadRequest(error_msg)

    @aiohttp_apispec.docs(
        tags=['facts'],
        summary='Delete One or More Facts',
        description=
        'Delete facts using the format provided in the `FactSchema`. '
        'This will delete all facts that match the criteria specified in the payload.'
    )
    @aiohttp_apispec.request_schema(FactSchema(partial=True))
    @aiohttp_apispec.response_schema(
        FactSchema,
        description='Returns the deleted fact(s), dumped in `FactSchema` format.'
    )
    async def delete_facts(self, request: web.Request):
        knowledge_svc_handle = self._api_manager.knowledge_svc
        fact_data = await self._api_manager.extract_data(request)
        if fact_data:
            try:
                store = await knowledge_svc_handle.get_facts(criteria=fact_data
                                                             )
                await knowledge_svc_handle.delete_fact(criteria=fact_data)
                resp = await self._api_manager.verify_fact_integrity(store)
                return web.json_response(dict(removed=resp))
            except Exception as e:
                self.log.warning(
                    f'Encountered issue removing fact {fact_data} - {e}')
        raise JsonHttpBadRequest('Invalid fact data was provided.')

    @aiohttp_apispec.docs(
        tags=['relationships'],
        summary='Delete One or More Relationships',
        description=
        ('Delete relationships using the format provided in the RelationshipSchema. '
         'This will delete all relationships that match the criteria specified in the '
         'payload.'))
    @aiohttp_apispec.response_schema(
        RelationshipSchema,
        description=
        'Returns the deleted relationship(s), dumped in RelationshipSchema '
        'format.')
    @aiohttp_apispec.request_schema(RelationshipSchema(partial=True))
    async def delete_relationships(self, request: web.Request):
        knowledge_svc_handle = self._api_manager.knowledge_svc
        relationship_data = await self._api_manager.extract_data(request)
        if relationship_data:
            try:
                store = await knowledge_svc_handle.get_relationships(
                    criteria=relationship_data)
                await knowledge_svc_handle.delete_relationship(
                    criteria=relationship_data)
                resp = await self._api_manager.verify_relationship_integrity(
                    store)
                return web.json_response(dict(removed=resp))
            except Exception as e:
                self.log.warning(
                    f'Encountered issue removing relationship {relationship_data} - {e}'
                )
        raise JsonHttpBadRequest('Invalid relationship data was provided.')

    @aiohttp_apispec.docs(
        tags=['facts'],
        summary='Update One or More Facts',
        description=
        ('Update existing facts using the format provided in the `FactSchema`. '
         'This will update all facts that match the criteria specified in the payload.'
         ))
    @aiohttp_apispec.request_schema(FactUpdateRequestSchema(partial=True))
    @aiohttp_apispec.response_schema(
        FactSchema,
        description='Returns the updated fact(s), dumped in `FactSchema` format.'
    )
    async def update_facts(self, request: web.Request):
        knowledge_svc_handle = self._api_manager.knowledge_svc
        fact_data = await self._api_manager.extract_data(request)
        if 'criteria' in fact_data and 'updates' in fact_data:
            try:
                await knowledge_svc_handle.update_fact(
                    criteria=fact_data['criteria'],
                    updates=fact_data['updates'])
                temp = await self._api_manager.copy_object(
                    fact_data['criteria'])
                for k in fact_data['updates']:
                    temp[k] = fact_data['updates'][k]
                store = await knowledge_svc_handle.get_facts(criteria=temp)
                resp = await self._api_manager.verify_fact_integrity(store)
                return web.json_response(dict(updated=resp))
            except Exception as e:
                error_msg = f'Encountered issue updating fact {fact_data} - {e}'
                self.log.warning(error_msg)
                raise JsonHttpBadRequest(error_msg)
        raise JsonHttpBadRequest(
            "Need a 'criteria' to match on and 'updates' to apply.")

    @aiohttp_apispec.docs(
        tags=['relationships'],
        summary='Update One or More Relationships',
        description=
        ('Update existing relationships using the format provided in the '
         '`RelationshipSchema`. This will update all relationships that match the '
         'criteria specified in the payload.'))
    @aiohttp_apispec.request_schema(RelationshipUpdateSchema(partial=True))
    @aiohttp_apispec.response_schema(
        RelationshipSchema,
        description=
        'Returns the updated relationship(s), dumped in `RelationshipSchema` '
        'format.')
    async def update_relationships(self, request: web.Request):
        knowledge_svc_handle = self._api_manager.knowledge_svc
        relationship_data = await self._api_manager.extract_data(request)
        if 'criteria' in relationship_data and 'updates' in relationship_data:
            try:
                await knowledge_svc_handle.update_relationship(
                    criteria=relationship_data['criteria'],
                    updates=relationship_data['updates'])
                temp = await self._api_manager.copy_object(
                    relationship_data['criteria'])
                for k in relationship_data['updates']:
                    if isinstance(relationship_data['updates'][k], dict):
                        handle = dict()
                        if k in relationship_data['criteria'] and \
                                isinstance(relationship_data['criteria'][k], dict):
                            handle = relationship_data['criteria'][k]
                        for j in relationship_data['updates'][k]:
                            handle[j] = relationship_data['updates'][k][j]
                        temp[k] = handle
                    else:
                        temp[k] = relationship_data['updates'][k]
                store = await knowledge_svc_handle.get_relationships(
                    criteria=temp)
                resp = await self._api_manager.verify_relationship_integrity(
                    store)
                return web.json_response(dict(updated=resp))
            except Exception as e:
                error_msg = f'Encountered issue updating relationship {relationship_data} - {e}'
                self.log.warning(error_msg)
                raise JsonHttpBadRequest(error_msg)
        raise JsonHttpBadRequest(
            "Need a 'criteria' to match on and 'updates' to apply.")

    async def _find_and_verify_facts(self, fact_data: dict):
        knowledge_svc_handle = self._api_manager.knowledge_svc
        FactSchema(partial=True).load(fact_data)
        store = await knowledge_svc_handle.get_facts(criteria=fact_data)
        return await self._api_manager.verify_fact_integrity(store)

    async def _find_and_verify_relationships(self, relationship_data):
        knowledge_svc_handle = self._api_manager.knowledge_svc
        RelationshipSchema(partial=True).load(relationship_data)
        store = await knowledge_svc_handle.get_relationships(
            criteria=relationship_data)
        return await self._api_manager.verify_relationship_integrity(store)
Exemple #8
0
class FactApi(BaseObjectApi):
    def __init__(self, services):
        super().__init__(description='adversary', obj_class=Fact, schema=FactSchema, ram_key='facts',
                         id_property='adversary_id', auth_svc=services['auth_svc'])
        self._api_manager = FactApiManager(data_svc=services['data_svc'], file_svc=services['file_svc'],
                                           knowledge_svc=services['knowledge_svc'])

    def add_routes(self, app: web.Application):
        router = app.router
        router.add_get('/facts', self.get_facts)
        router.add_get('/relationships', self.get_relationships)
        router.add_post('/facts', self.add_facts)
        router.add_post('/relationships', self.add_relationships)
        router.add_delete('/facts', self.delete_facts)
        router.add_delete('/relationships', self.delete_relationships)
        router.add_patch('/facts', self.update_facts)
        router.add_patch('/relationships', self.update_relationships)

    @aiohttp_apispec.docs(tags=['facts'])
    @aiohttp_apispec.querystring_schema(BaseGetAllQuerySchema)
    @aiohttp_apispec.response_schema(FactSchema(many=True, partial=True))
    async def get_facts(self, request: web.Request):
        knowledge_svc_handle = self._api_manager.knowledge_svc
        fact_data = await self._api_manager.extract_data(request)
        if fact_data:
            try:
                store = await knowledge_svc_handle.get_facts(criteria=fact_data)
                resp = await self._api_manager.verify_fact_integrity(store)
                return web.json_response(dict(found=resp))
            except Exception as e:
                self.log.warning(f'Encountered issue retrieving fact {fact_data} - {e}')

    @aiohttp_apispec.docs(tags=['relationships'])
    @aiohttp_apispec.querystring_schema(BaseGetAllQuerySchema)
    @aiohttp_apispec.response_schema(RelationshipSchema(many=True, partial=True))
    async def get_relationships(self, request: web.Request):
        knowledge_svc_handle = self._api_manager.knowledge_svc
        relationship_data = await self._api_manager.extract_data(request)
        if relationship_data:
            try:
                store = await knowledge_svc_handle.get_relationships(criteria=relationship_data)
                resp = await self._api_manager.verify_relationship_integrity(store)
                return web.json_response(dict(found=resp))
            except Exception as e:
                self.log.warning(f'Encountered issue retrieving relationship {relationship_data} - {e}')

    @aiohttp_apispec.docs(tags=['facts'])
    @aiohttp_apispec.request_schema(FactSchema)
    @aiohttp_apispec.response_schema(FactSchema)
    async def add_facts(self, request: web.Request):
        knowledge_svc_handle = self._api_manager.knowledge_svc
        fact_data = await self._api_manager.extract_data(request)
        if fact_data:
            try:
                new_fact = Fact.load(fact_data)
                if 'source' not in fact_data:
                    new_fact.source = WILDCARD_STRING
                new_fact.source_type = OriginType.USER.name
                await knowledge_svc_handle.add_fact(new_fact)
                store = await knowledge_svc_handle.get_facts(criteria=dict(trait=fact_data['trait'],
                                                                           value=fact_data['value'],
                                                                           source=WILDCARD_STRING,
                                                                           source_type=OriginType.USER.name))
                resp = await self._api_manager.verify_fact_integrity(store)
                return web.json_response(dict(added=resp))
            except Exception as e:
                self.log.warning(f'Encountered issue saving fact {fact_data} - {e}')

    @aiohttp_apispec.docs(tags=['relationships'])
    @aiohttp_apispec.request_schema(RelationshipSchema)
    @aiohttp_apispec.response_schema(RelationshipSchema)
    async def add_relationships(self, request: web.Request):
        knowledge_svc_handle = self._api_manager.knowledge_svc
        relationship_data = await self._api_manager.extract_data(request)
        if relationship_data:
            try:
                origin_target = WILDCARD_STRING
                new_relationship = Relationship.load(relationship_data)
                if 'origin' in relationship_data:
                    origin_target = relationship_data['origin']
                else:
                    new_relationship.origin = origin_target
                shorthand = new_relationship.shorthand
                new_relationship.source.relationships = [shorthand]
                new_relationship.source.source = origin_target
                new_relationship.source.source_type = OriginType.USER.name
                if 'target' in relationship_data:
                    new_relationship.target.source = origin_target
                    new_relationship.target.source_type = OriginType.USER.name
                    new_relationship.target.relationships = [shorthand]
                    await knowledge_svc_handle.add_fact(new_relationship.target)
                await knowledge_svc_handle.add_fact(new_relationship.source)
                await knowledge_svc_handle.add_relationship(new_relationship)

                store = await knowledge_svc_handle.get_relationships(
                    criteria=dict(source=new_relationship.source,
                                  edge=new_relationship.edge if 'edge' in relationship_data else None,
                                  target=new_relationship.target if 'target' in relationship_data else None,
                                  origin=origin_target))
                resp = await self._api_manager.verify_relationship_integrity(store)
                return web.json_response(dict(added=resp))
            except Exception as e:
                self.log.warning(f'Encountered issue saving relationship {relationship_data} - {e}')

    @aiohttp_apispec.docs(tags=['facts'])
    @aiohttp_apispec.response_schema(FactSchema)
    async def delete_facts(self, request: web.Request):
        knowledge_svc_handle = self._api_manager.knowledge_svc
        fact_data = await self._api_manager.extract_data(request)
        if fact_data:
            try:
                store = await knowledge_svc_handle.get_facts(criteria=fact_data)
                await knowledge_svc_handle.delete_fact(criteria=fact_data)
                resp = await self._api_manager.verify_fact_integrity(store)
                return web.json_response(dict(removed=resp))
            except Exception as e:
                self.log.warning(f'Encountered issue removing fact {fact_data} - {e}')

    @aiohttp_apispec.docs(tags=['relationships'])
    @aiohttp_apispec.response_schema(RelationshipSchema)
    async def delete_relationships(self, request: web.Request):
        knowledge_svc_handle = self._api_manager.knowledge_svc
        relationship_data = await self._api_manager.extract_data(request)
        if relationship_data:
            try:
                store = await knowledge_svc_handle.get_relationships(criteria=relationship_data)
                await knowledge_svc_handle.delete_relationship(criteria=relationship_data)
                resp = await self._api_manager.verify_relationship_integrity(store)
                return web.json_response(dict(removed=resp))
            except Exception as e:
                self.log.warning(f'Encountered issue removing relationship {relationship_data} - {e}')

    @aiohttp_apispec.docs(tags=['facts'])
    @aiohttp_apispec.request_schema(FactSchema(partial=True))
    @aiohttp_apispec.response_schema(FactSchema)
    async def update_facts(self, request: web.Request):
        knowledge_svc_handle = self._api_manager.knowledge_svc
        fact_data = await self._api_manager.extract_data(request)
        if fact_data:
            try:
                if 'criteria' in fact_data and 'updates' in fact_data:
                    await knowledge_svc_handle.update_fact(criteria=fact_data['criteria'],
                                                           updates=fact_data['updates'])
                    temp = await self._api_manager.copy_object(fact_data['criteria'])
                    for k in fact_data['updates']:
                        temp[k] = fact_data['updates'][k]
                    store = await knowledge_svc_handle.get_facts(criteria=temp)
                    resp = await self._api_manager.verify_fact_integrity(store)
                    return web.json_response(dict(updated=resp))
                else:
                    return web.json_response(dict(error="Need a 'criteria' to match on and 'updates' to apply."))
            except Exception as e:
                self.log.warning(f'Encountered issue updating fact {fact_data} - {e}')

    @aiohttp_apispec.docs(tags=['relationships'])
    @aiohttp_apispec.request_schema(RelationshipSchema(partial=True))
    @aiohttp_apispec.response_schema(RelationshipSchema)
    async def update_relationships(self, request: web.Request):
        knowledge_svc_handle = self._api_manager.knowledge_svc
        relationship_data = await self._api_manager.extract_data(request)
        if relationship_data:
            try:
                if 'criteria' in relationship_data and 'updates' in relationship_data:
                    await knowledge_svc_handle.update_relationship(criteria=relationship_data['criteria'],
                                                                   updates=relationship_data['updates'])
                    temp = await self._api_manager.copy_object(relationship_data['criteria'])
                    for k in relationship_data['updates']:
                        if isinstance(relationship_data['updates'][k], dict):
                            handle = dict()
                            if k in relationship_data['criteria'] and \
                                    isinstance(relationship_data['criteria'][k], dict):
                                handle = relationship_data['criteria'][k]
                            for j in relationship_data['updates'][k]:
                                handle[j] = relationship_data['updates'][k][j]
                            temp[k] = handle
                        else:
                            temp[k] = relationship_data['updates'][k]
                    store = await knowledge_svc_handle.get_relationships(criteria=temp)
                    resp = await self._api_manager.verify_relationship_integrity(store)
                    return web.json_response(dict(updated=resp))
                else:
                    return web.json_response(dict(error="Need a 'criteria' to match on and 'updates' to apply."))
            except Exception as e:
                self.log.warning(f'Encountered issue updating relationship {relationship_data} - {e}')