def validate_active_flag(request_body: bytes) -> None: """ Validate the active flag data given to the endpoint. Args: request_body: The body of the request. Raises: Fail: There is active flag data given to the endpoint which is not either a Boolean or NULL. """ if not request_body: return request_text = request_body.decode() if 'active_flag' not in json.loads(request_text): return active_flag = json.loads(request_text).get('active_flag') if active_flag is None or isinstance(active_flag, bool): return raise Fail(status_code=HTTPStatus.BAD_REQUEST)
def validate_width(request_body: bytes) -> None: """ Validate the width argument given to a VWS endpoint. Args: request_body: The body of the request. Raises: Fail: Width is given and is not a positive number. """ if not request_body: return request_text = request_body.decode() if 'width' not in json.loads(request_text): return width = json.loads(request_text).get('width') width_is_number = isinstance(width, numbers.Number) width_positive = width_is_number and width > 0 if not width_positive: raise Fail(status_code=HTTPStatus.BAD_REQUEST)
def validate_metadata_encoding(request_body: bytes) -> None: """ Validate that the given application metadata can be base64 decoded. Args: request_body: The body of the request. Raises: Fail: Application metadata is given and it cannot be base64 decoded. """ if not request_body: return request_text = request_body.decode() request_json = json.loads(request_text) if 'application_metadata' not in request_json: return application_metadata = request_json.get('application_metadata') if application_metadata is None: return try: decode_base64(encoded_data=application_metadata) except binascii.Error as exc: raise Fail(status_code=HTTPStatus.UNPROCESSABLE_ENTITY) from exc
def validate_json( request_body: bytes, request_method: str, ) -> None: """ Validate that there is either no JSON given or the JSON given is valid. Args: request_body: The body of the request. request_method: The HTTP method of the request. Raises: UnnecessaryRequestBody: A request body was given for an endpoint which does not require one. Fail: The request body includes invalid JSON. """ if not request_body: return if request_method not in (POST, PUT): raise UnnecessaryRequestBody try: json.loads(request_body.decode()) except JSONDecodeError as exc: raise Fail(status_code=HTTPStatus.BAD_REQUEST) from exc
def validate_date_header_given(request_headers: Dict[str, str]) -> None: """ Validate the date header is given to a VWS endpoint. Args: request_headers: The headers sent with the request. Raises: Fail: The date is not given. """ if 'Date' in request_headers: return raise Fail(status_code=HTTPStatus.BAD_REQUEST)
def validate_auth_header_has_signature( request_headers: Dict[str, str], ) -> None: """ Validate the authorization header includes a signature. Args: request_headers: The headers sent with the request. Raises: Fail: The "Authorization" header does not include a signature. """ header = request_headers['Authorization'] if header.count(':') == 1 and header.split(':')[1]: return raise Fail(status_code=HTTPStatus.BAD_REQUEST)
def validate_date_format(request_headers: Dict[str, str]) -> None: """ Validate the format of the date header given to a VWS endpoint. Args: request_headers: The headers sent with the request. Raises: Fail: The date is in the wrong format. """ date_header = request_headers['Date'] date_format = '%a, %d %b %Y %H:%M:%S GMT' try: datetime.datetime.strptime(date_header, date_format) except ValueError as exc: raise Fail(status_code=HTTPStatus.BAD_REQUEST) from exc
def validate_access_key_exists( request_headers: Dict[str, str], databases: Set[VuforiaDatabase], ) -> None: """ Validate the authorization header includes an access key for a database. Args: request_headers: The headers sent with the request. databases: All Vuforia databases. Raises: Fail: The access key does not match a given database. """ header = request_headers['Authorization'] first_part, _ = header.split(':') _, access_key = first_part.split(' ') for database in databases: if access_key == database.server_access_key: return raise Fail(status_code=HTTPStatus.BAD_REQUEST)
def validate_name_length(request_body: bytes) -> None: """ Validate the length of the name argument given to a VWS endpoint. Args: request_body: The body of the request. Raises: Fail: A name is given and it is not a between 1 and 64 characters in length. """ if not request_body: return request_text = request_body.decode() if 'name' not in json.loads(request_text): return name = json.loads(request_text)['name'] if name and len(name) < 65: return raise Fail(status_code=HTTPStatus.BAD_REQUEST)
def validate_name_type(request_body: bytes) -> None: """ Validate the type of the name argument given to a VWS endpoint. Args: request_body: The body of the request. Raises: Fail: A name is given and it is not a string. """ if not request_body: return request_text = request_body.decode() if 'name' not in json.loads(request_text): return name = json.loads(request_text)['name'] if isinstance(name, str): return raise Fail(status_code=HTTPStatus.BAD_REQUEST)
def validate_metadata_type(request_body: bytes) -> None: """ Validate that the given application metadata is a string or NULL. Args: request_body: The body of the request. Raises: Fail: Application metadata is given and it is not a string or NULL. """ if not request_body: return request_text = request_body.decode() request_json = json.loads(request_text) if 'application_metadata' not in request_json: return application_metadata = request_json.get('application_metadata') if application_metadata is None or isinstance(application_metadata, str): return raise Fail(status_code=HTTPStatus.BAD_REQUEST)
def validate_image_data_type(request_body: bytes) -> None: """ Validate that the given image data is a string. Args: request_body: The body of the request. Raises: Fail: Image data is given and it is not a string. """ if not request_body: return request_text = request_body.decode() if 'image' not in json.loads(request_text): return image = json.loads(request_text).get('image') if isinstance(image, str): return raise Fail(status_code=HTTPStatus.BAD_REQUEST)
def validate_image_encoding(request_body: bytes) -> None: """ Validate that the given image data can be base64 decoded. Args: request_body: The body of the request. Raises: Fail: Image data is given and it cannot be base64 decoded. """ if not request_body: return request_text = request_body.decode() if 'image' not in json.loads(request_text): return image = json.loads(request_text).get('image') try: decode_base64(encoded_data=image) except binascii.Error as exc: raise Fail(status_code=HTTPStatus.UNPROCESSABLE_ENTITY) from exc
def update_target(target_id: str) -> Response: """ Update a target. Fake implementation of https://library.vuforia.com/articles/Solution/How-To-Use-the-Vuforia-Web-Services-API.html#How-To-Update-a-Target """ # We do not use ``request.get_json(force=True)`` because this only works # when the content type is given as ``application/json``. request_json = json.loads(request.data) databases = get_all_databases() database = get_database_matching_server_keys( request_headers=dict(request.headers), request_body=request.data, request_method=request.method, request_path=request.path, databases=databases, ) assert isinstance(database, VuforiaDatabase) [target] = [ target for target in database.targets if target.target_id == target_id ] if target.status != TargetStatuses.SUCCESS.value: raise TargetStatusNotSuccess update_values = {} if 'width' in request_json: update_values['width'] = request_json['width'] if 'active_flag' in request_json: active_flag = request_json['active_flag'] if active_flag is None: raise Fail(status_code=HTTPStatus.BAD_REQUEST) update_values['active_flag'] = active_flag if 'application_metadata' in request_json: application_metadata = request_json['application_metadata'] if application_metadata is None: raise Fail(status_code=HTTPStatus.BAD_REQUEST) update_values['application_metadata'] = application_metadata if 'name' in request_json: name = request_json['name'] update_values['name'] = name if 'image' in request_json: image = request_json['image'] update_values['image'] = image target_manager_base_url = os.environ['TARGET_MANAGER_BASE_URL'] put_url = ( f'{target_manager_base_url}/databases/{database.database_name}/' f'targets/{target_id}' ) requests.put(url=put_url, json=update_values) date = email.utils.formatdate(None, localtime=False, usegmt=True) headers = { 'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Server': 'nginx', 'Date': date, } body = { 'result_code': ResultCodes.SUCCESS.value, 'transaction_id': uuid.uuid4().hex, } return Response( status=HTTPStatus.OK, response=json_dump(body), headers=headers, )
def update_target( self, request: _RequestObjectProxy, context: _Context, ) -> str: """ Update a target. Fake implementation of https://library.vuforia.com/articles/Solution/How-To-Use-the-Vuforia-Web-Services-API.html#How-To-Update-a-Target """ try: run_services_validators( request_headers=request.headers, request_body=request.body, request_method=request.method, request_path=request.path, databases=self._target_manager.databases, ) except ValidatorException as exc: context.headers = exc.headers context.status_code = exc.status_code return exc.response_text database = get_database_matching_server_keys( request_headers=request.headers, request_body=request.body, request_method=request.method, request_path=request.path, databases=self._target_manager.databases, ) assert isinstance(database, VuforiaDatabase) target_id = request.path.split('/')[-1] target = database.get_target(target_id=target_id) body: Dict[str, str] = {} date = email.utils.formatdate(None, localtime=False, usegmt=True) if target.status != TargetStatuses.SUCCESS.value: exception = TargetStatusNotSuccess() context.headers = exception.headers context.status_code = exception.status_code return exception.response_text width = request.json().get('width', target.width) name = request.json().get('name', target.name) active_flag = request.json().get('active_flag', target.active_flag) application_metadata = request.json().get( 'application_metadata', target.application_metadata, ) image_value = target.image_value if 'image' in request.json(): image_value = base64.b64decode(request.json()['image']) if 'active_flag' in request.json() and active_flag is None: fail_exception = Fail(status_code=HTTPStatus.BAD_REQUEST) context.headers = fail_exception.headers context.status_code = fail_exception.status_code return fail_exception.response_text if ( 'application_metadata' in request.json() and application_metadata is None ): fail_exception = Fail(status_code=HTTPStatus.BAD_REQUEST) context.headers = fail_exception.headers context.status_code = fail_exception.status_code return fail_exception.response_text # In the real implementation, the tracking rating can stay the same. # However, for demonstration purposes, the tracking rating changes but # when the target is updated. available_values = list(set(range(6)) - {target.tracking_rating}) processed_tracking_rating = random.choice(available_values) gmt = ZoneInfo('GMT') last_modified_date = datetime.datetime.now(tz=gmt) new_target = dataclasses.replace( target, name=name, width=width, active_flag=active_flag, application_metadata=application_metadata, image_value=image_value, processed_tracking_rating=processed_tracking_rating, last_modified_date=last_modified_date, ) database.targets.remove(target) database.targets.add(new_target) body = { 'result_code': ResultCodes.SUCCESS.value, 'transaction_id': uuid.uuid4().hex, } body_json = json_dump(body) context.headers = { 'Connection': 'keep-alive', 'Content-Type': 'application/json', 'Server': 'nginx', 'Date': date, 'Content-Length': str(len(body_json)), } return body_json