def test_delete_object(self, rest_api, example_object, full_access_user, none_access_user): # Test default route rest_api.post(f'{self.ROUTE_URL}/', json=CmdbObject.to_json(example_object)) default_response = rest_api.delete( f'{self.ROUTE_URL}/{example_object.public_id}') assert default_response.status_code == HTTPStatus.OK default_response = rest_api.post( f'{self.ROUTE_URL}/', json=CmdbObject.to_json(example_object)) assert default_response.status_code == HTTPStatus.OK # ACCESS OK access_update_types_response = rest_api.delete( f'{self.ROUTE_URL}/{example_object.public_id}', user=full_access_user) assert access_update_types_response.status_code != ( HTTPStatus.FORBIDDEN or HTTPStatus.UNAUTHORIZED) validate_response = rest_api.get( f'{self.ROUTE_URL}/{example_object.public_id}') assert validate_response.status_code == HTTPStatus.NOT_FOUND # ACCESS FORBIDDEN none_update_types_response = rest_api.delete( f'{self.ROUTE_URL}/{example_object.public_id}', user=none_access_user) assert none_update_types_response.status_code == HTTPStatus.FORBIDDEN # ACCESS UNAUTHORIZED un_get_types_response = rest_api.delete( f'{self.ROUTE_URL}/{example_object.public_id}', unauthorized=True) assert un_get_types_response.status_code == HTTPStatus.UNAUTHORIZED
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!')
def test_get_object(self, rest_api, example_object, full_access_user, none_access_user): default_response = rest_api.get( f'{self.ROUTE_URL}/{example_object.public_id}/{"native"}') assert default_response.status_code == HTTPStatus.OK # Response parsable response_dict = default_response.get_json() test_object = CmdbObject.from_data(response_dict) assert isinstance(test_object, CmdbObject) # Not Found not_found_response = rest_api.get(f'{self.ROUTE_URL}/{-1}') assert not_found_response.status_code == HTTPStatus.NOT_FOUND # ACCESS OK access_get_types_response = rest_api.get( f'{self.ROUTE_URL}/{example_object.public_id}', user=full_access_user) assert access_get_types_response.status_code != ( HTTPStatus.FORBIDDEN or HTTPStatus.UNAUTHORIZED) # ACCESS FORBIDDEN none_get_types_response = rest_api.get( f'{self.ROUTE_URL}/{example_object.public_id}', user=none_access_user) assert none_get_types_response.status_code == HTTPStatus.FORBIDDEN # ACCESS UNAUTHORIZED none_get_types_response = rest_api.get( f'{self.ROUTE_URL}/{example_object.public_id}', unauthorized=True) assert none_get_types_response.status_code == HTTPStatus.UNAUTHORIZED
def test_update_object(self, rest_api, example_object, full_access_user, none_access_user): example_object.editor_id = 1 example_object.creation_time = None # Test default default_response = rest_api.put( f'{self.ROUTE_URL}/{example_object.public_id}', json=CmdbObject.to_json(example_object)) assert default_response.status_code == HTTPStatus.ACCEPTED validate_response = rest_api.get( f'{self.ROUTE_URL}/{example_object.public_id}/{"native"}') assert validate_response.status_code == HTTPStatus.OK response_dict = validate_response.get_json() test_object = CmdbObject.from_data(response_dict) assert isinstance(test_object, CmdbObject) assert test_object.last_edit_time is not None # ACCESS OK access_update_types_response = rest_api.put( f'{self.ROUTE_URL}/{example_object.public_id}', json=CmdbObject.to_json(example_object), user=full_access_user) assert access_update_types_response.status_code != ( HTTPStatus.FORBIDDEN or HTTPStatus.UNAUTHORIZED) validate_response = rest_api.get( f'{self.ROUTE_URL}/{example_object.public_id}') assert validate_response.status_code == HTTPStatus.OK rest_api.delete(f'{self.ROUTE_URL}/{example_object.public_id}') # ACCESS FORBIDDEN none_update_types_response = rest_api.put( f'{self.ROUTE_URL}/{example_object.public_id}', json=CmdbObject.to_json(example_object), user=none_access_user) assert none_update_types_response.status_code == HTTPStatus.FORBIDDEN # ACCESS UNAUTHORIZED un_get_types_response = rest_api.put( f'{self.ROUTE_URL}/{example_object.public_id}', json=CmdbObject.to_json(example_object), unauthorized=True) assert un_get_types_response.status_code == HTTPStatus.UNAUTHORIZED example_object.public_id = 1 example_object.name = 'test'
def example_object(): return CmdbObject(public_id=1, type_id=1, status=True, creation_time=datetime.now(timezone.utc), author_id=1, active=True, fields=[], version='1.0.0')
def example_object(): return CmdbObject(public_id=1, type_id=1, status=True, creation_time=datetime.now(timezone.utc), author_id=1, active=True, fields=[{ "name": "dummy-field-1", "value": 'dummy-value' }, { "name": "dummy-field-2", "value": '' }], version='1.0.0')
def test_get_objects(self, rest_api, full_access_user, none_access_user): default_response = rest_api.get(f'{self.ROUTE_URL}/') assert default_response.status_code == HTTPStatus.OK # Response parsable response_dict = default_response.get_json() test_object_json = response_dict['results'][0] test_object = CmdbObject.from_data(test_object_json) assert len(response_dict['results']) == int( default_response.headers['X-Total-Count']) assert len(response_dict['results']) assert isinstance(test_object, CmdbObject) # Test filter filter_response = rest_api.get( f'{self.ROUTE_URL}/', query_string={'filter': dumps({'public_id': 1})}) assert filter_response.status_code == HTTPStatus.OK assert int(filter_response.headers['X-Total-Count']) == 1 # Test empty filter empty_filter_response = rest_api.get( f'{self.ROUTE_URL}/', query_string={'filter': dumps({'public_id': 2})}) assert empty_filter_response.status_code == HTTPStatus.OK assert int(empty_filter_response.headers['X-Total-Count']) == 0 # Test wrong filter wrong_filter_response = rest_api.get(f'{self.ROUTE_URL}/', query_string={'filter': '\xE9'}) assert wrong_filter_response.status_code == HTTPStatus.BAD_REQUEST # ACCESS OK access_get_types_response = rest_api.get(f'{self.ROUTE_URL}/', user=full_access_user) assert access_get_types_response.status_code != ( HTTPStatus.FORBIDDEN or HTTPStatus.UNAUTHORIZED) # ACCESS FORBIDDEN none_get_types_response = rest_api.get(f'{self.ROUTE_URL}/', user=none_access_user) assert none_get_types_response.status_code == HTTPStatus.FORBIDDEN # ACCESS UNAUTHORIZED none_get_types_response = rest_api.get(f'{self.ROUTE_URL}/', unauthorized=True) assert none_get_types_response.status_code == HTTPStatus.UNAUTHORIZED
def test_insert_object(self, rest_api, example_object, full_access_user): """ Prepare data for mass change with access control """ i = 0 while i < 3: i = i + 1 data = copy.copy(CmdbObject.to_json(example_object)) data['public_id'] = i data['active'] = i % 2 != 0 data['fields'][0]['value'] = f'dummy-value-{i}' access_insert_types_response = rest_api.post(f'{self.ROUTE_URL}/', json=data, user=full_access_user) assert access_insert_types_response.status_code != ( HTTPStatus.FORBIDDEN or HTTPStatus.UNAUTHORIZED) assert len( rest_api.get(f'{self.ROUTE_URL}/').get_json()['results']) == 3
def update_object(public_id: int, request_user: UserModel): object_ids = request.args.getlist('objectIDs') if len(object_ids) > 0: object_ids = list(map(int, object_ids)) else: object_ids = [public_id] update_ack = None for obj_id in object_ids: # get current object state try: current_object_instance = object_manager.get_object(obj_id) current_type_instance = object_manager.get_type( current_object_instance.get_type_id()) current_object_render_result = CmdbRender( object_instance=current_object_instance, type_instance=current_type_instance, render_user=request_user, object_manager=object_manager, ref_render=True).result() except ObjectManagerGetError as err: LOGGER.error(err) return abort(404) except RenderError as err: LOGGER.error(err) return abort(500) update_comment = '' # load put data try: # get data as str add_data_dump = json.dumps(request.json) # convert into python dict put_data = json.loads(add_data_dump, object_hook=object_hook) # check for comment try: put_data['public_id'] = obj_id put_data[ 'creation_time'] = current_object_instance.creation_time put_data['author_id'] = current_object_instance.author_id old_fields = list( map( lambda x: {k: v for k, v in x.items() if k in ['name', 'value']}, current_type_instance.get_fields())) new_fields = put_data['fields'] for item in new_fields: for old in old_fields: if item['name'] == old['name']: old['value'] = item['value'] put_data['fields'] = old_fields if 'active' not in put_data: put_data['active'] = current_object_instance.active if 'version' not in put_data: put_data['version'] = current_object_instance.version update_comment = put_data['comment'] del put_data['comment'] except (KeyError, IndexError, ValueError): update_comment = '' except TypeError as e: LOGGER.warning(e) return abort(400) # update edit time put_data['last_edit_time'] = datetime.now(timezone.utc) try: update_object_instance = CmdbObject(**put_data) except ObjectManagerUpdateError as err: LOGGER.error(err) return abort(400) # calc version changes = current_object_instance / update_object_instance if len(changes['new']) == 1: update_object_instance.update_version( update_object_instance.VERSIONING_PATCH) elif len(changes['new']) == len(update_object_instance.fields): update_object_instance.update_version( update_object_instance.VERSIONING_MAJOR) elif len(changes['new']) > (len(update_object_instance.fields) / 2): update_object_instance.update_version( update_object_instance.VERSIONING_MINOR) else: update_object_instance.update_version( update_object_instance.VERSIONING_PATCH) # insert object try: update_ack = object_manager.update_object( update_object_instance, request_user, AccessControlPermission.UPDATE) except ManagerGetError as err: return abort(404, err.message) except AccessDeniedError as err: return abort(403, err.message) except CMDBError as e: LOGGER.warning(e) return abort(500) try: # generate log log_data = { 'object_id': obj_id, 'version': current_object_render_result.object_information['version'], 'user_id': request_user.get_public_id(), 'user_name': request_user.get_display_name(), 'comment': update_comment, 'changes': changes, 'render_state': json.dumps(current_object_render_result, default=default).encode('UTF-8') } log_manager.insert(action=LogAction.EDIT, log_type=CmdbObjectLog.__name__, **log_data) except (CMDBError, LogManagerInsertError) as err: LOGGER.error(err) return make_response(update_ack)
def update_object(public_id: int, data: dict, request_user: UserModel): object_ids = request.args.getlist('objectIDs') if len(object_ids) > 0: object_ids = list(map(int, object_ids)) else: object_ids = [public_id] manager = ObjectManager(database_manager=current_app.database_manager, event_queue=current_app.event_queue) results: [dict] = [] failed = [] for obj_id in object_ids: # deep copy active_state = request.get_json().get('active', None) new_data = copy.deepcopy(data) try: current_object_instance = manager.get( obj_id, user=request_user, permission=AccessControlPermission.READ) current_type_instance = type_manager.get( current_object_instance.get_type_id()) current_object_render_result = CmdbRender( object_instance=current_object_instance, object_manager=object_manager, type_instance=current_type_instance, render_user=request_user).result() update_comment = '' try: # check for comment new_data['public_id'] = obj_id new_data[ 'creation_time'] = current_object_instance.creation_time new_data['author_id'] = current_object_instance.author_id new_data[ 'active'] = active_state if active_state else current_object_instance.active if 'version' not in data: new_data['version'] = current_object_instance.version old_fields = list( map( lambda x: {k: v for k, v in x.items() if k in ['name', 'value']}, current_object_render_result.fields)) new_fields = data['fields'] for item in new_fields: for old in old_fields: if item['name'] == old['name']: old['value'] = item['value'] new_data['fields'] = old_fields update_comment = data['comment'] del new_data['comment'] except (KeyError, IndexError, ValueError): update_comment = '' except TypeError as err: LOGGER.error( f'Error: {str(err.args)} Object: {json.dumps(new_data, default=default)}' ) failed.append( ResponseFailedMessage(error_message=str(err.args), status=400, public_id=obj_id, obj=new_data).to_dict()) continue # update edit time new_data['last_edit_time'] = datetime.now(timezone.utc) new_data['editor_id'] = request_user.public_id update_object_instance = CmdbObject( **json.loads(json.dumps(new_data, default=json_util.default), object_hook=object_hook)) # calc version changes = current_object_instance / update_object_instance if len(changes['new']) == 1: new_data['version'] = update_object_instance.update_version( update_object_instance.VERSIONING_PATCH) elif len(changes['new']) == len(update_object_instance.fields): new_data['version'] = update_object_instance.update_version( update_object_instance.VERSIONING_MAJOR) elif len( changes['new']) > (len(update_object_instance.fields) / 2): new_data['version'] = update_object_instance.update_version( update_object_instance.VERSIONING_MINOR) else: new_data['version'] = update_object_instance.update_version( update_object_instance.VERSIONING_PATCH) manager.update(obj_id, new_data, request_user, AccessControlPermission.UPDATE) results.append(new_data) # Generate log entry try: log_data = { 'object_id': obj_id, 'version': update_object_instance.get_version(), 'user_id': request_user.get_public_id(), 'user_name': request_user.get_display_name(), 'comment': update_comment, 'changes': changes, 'render_state': json.dumps(update_object_instance, default=default).encode('UTF-8') } log_manager.insert(action=LogAction.EDIT, log_type=CmdbObjectLog.__name__, **log_data) except (CMDBError, LogManagerInsertError) as err: LOGGER.error(err) except AccessDeniedError as err: LOGGER.error(err) return abort(403) except ObjectManagerGetError as err: LOGGER.error(err) failed.append( ResponseFailedMessage(error_message=err.message, status=400, public_id=obj_id, obj=new_data).to_dict()) continue except (ManagerGetError, ObjectManagerUpdateError) as err: LOGGER.error(err) failed.append( ResponseFailedMessage(error_message=err.message, status=404, public_id=obj_id, obj=new_data).to_dict()) continue except (CMDBError, RenderError) as e: LOGGER.warning(e) failed.append( ResponseFailedMessage(error_message=str(e.__repr__), status=500, public_id=obj_id, obj=new_data).to_dict()) continue api_response = UpdateMultiResponse(results=results, failed=failed, url=request.url, model=CmdbObject.MODEL) return api_response.make_response()
def test_insert_object(self, rest_api, example_object, full_access_user, none_access_user): # Test default default_response = rest_api.post( f'{self.ROUTE_URL}/', json=CmdbObject.to_json(example_object)) assert default_response.status_code == HTTPStatus.OK example_object.public_id = 2 example_object.type_id = 2 example_object.fields = [{ "name": "test-field", "value": 1, }] default_response = rest_api.post( f'{self.ROUTE_URL}/', json=CmdbObject.to_json(example_object)) assert default_response.status_code == HTTPStatus.OK validate_response = rest_api.get( f'{self.ROUTE_URL}/{example_object.public_id}') assert validate_response.status_code == HTTPStatus.OK double_check_response = rest_api.post( f'{self.ROUTE_URL}/', json=CmdbObject.to_json(example_object)) assert double_check_response.status_code == HTTPStatus.BAD_REQUEST # DELETE default rest_api.delete(f'{self.ROUTE_URL}/{example_object.public_id}') # ACCESS OK access_insert_types_response = rest_api.post( f'{self.ROUTE_URL}/', json=CmdbObject.to_json(example_object), user=full_access_user) assert access_insert_types_response.status_code != ( HTTPStatus.FORBIDDEN or HTTPStatus.UNAUTHORIZED) validate_response = rest_api.get( f'{self.ROUTE_URL}/{example_object.public_id}') assert validate_response.status_code == HTTPStatus.OK # DELETE default rest_api.delete(f'{self.ROUTE_URL}/{example_object.public_id}') # ACCESS FORBIDDEN forbidden_insert_types_response = rest_api.post( f'{self.ROUTE_URL}/', json=CmdbObject.to_json(example_object), user=none_access_user) assert forbidden_insert_types_response.status_code == HTTPStatus.FORBIDDEN validate_response = rest_api.get( f'{self.ROUTE_URL}/{example_object.public_id}') assert validate_response.status_code == HTTPStatus.NOT_FOUND # ACCESS UNAUTHORIZED un_insert_types_response = rest_api.post( f'{self.ROUTE_URL}/', json=CmdbObject.to_json(example_object), unauthorized=True) assert un_insert_types_response.status_code == HTTPStatus.UNAUTHORIZED validate_response = rest_api.get( f'{self.ROUTE_URL}/{example_object.public_id}') assert validate_response.status_code == HTTPStatus.NOT_FOUND example_object.public_id = 1