def test_generate_hash(self): deed = Deed() deed.deed = {"effective_date": "2016-07-05 12:00:05", "property_address": "0 The Drive, This Town, This County, PL0 0TH"} deed.token = 'token' deed.status = 'status' deed_data_xml = '<deedData>Testing</deedData>' test = Deed().generate_hash(deed_data_xml.encode('utf-8')) self.assertEqual(test, '958b43b5b22169359e3ae383d7c5e0c2223dac44eb2a017ef04234d44420db0a')
def update_json_with_signature(deed_reference): data = request.get_json() deed = Deed().get_deed_system(deed_reference) incoming_xml = data['deed-xml'] incoming_xml_bytes = bytes(incoming_xml, encoding='utf-8') tree = etree.fromstring(incoming_xml_bytes) new_signature_element = tree.xpath( './/signatureSlots/borrower_signature[position()=%s]' % data['borrower-pos'])[0] # Replace the borrower's element within the deed object's deed_xml existing_deed_data_xml = etree.fromstring(deed.deed_xml) existing_signature_element = existing_deed_data_xml.xpath( './/signatureSlots/borrower_signature[position()=%s]' % data['borrower-pos'])[0] existing_signature_element.getparent().replace(existing_signature_element, new_signature_element) existing_deed_data_xml = etree.tostring(existing_deed_data_xml) esecurity_parent_element = b'<?xml version=\"1.0\" encoding=\"UTF-8\"?>' deed.deed_xml = esecurity_parent_element + existing_deed_data_xml # Save the deed_xml with the newly replaced borrower's element update_deed_signature_timestamp(deed, data['borrower-token'], data['datetime']) return jsonify({"status": "Successfully updated json with signature" }), status.HTTP_200_OK
def get_deed_internal(deed_reference): if 'application/pdf' in request.headers.get("Accept", ""): return create_deed_pdf( deed_pdf_adapter(deed_reference, use_system=True)), status.HTTP_200_OK deed = Deed().get_deed_system(deed_reference) return jsonify({"deed": deed.deed}), status.HTTP_200_OK
def issue_sms(deed_reference, borrower_token): deed_instance = Deed() deed = deed_instance.get_deed(deed_reference) esec_client = make_esec_client() if deed is None: application.app.logger.error( "Database Exception 404 for deed reference - %s" % deed_reference) abort(status.HTTP_404_NOT_FOUND) else: application.app.logger.info( "Signing deed for borrower_token %s against deed reference %s" % (borrower_token, deed_reference)) try: application.app.logger.info("getting existing XML") borrower = Borrower.get_by_token(borrower_token) if not borrower.esec_user_name: application.app.logger.info( "creating esec user for borrower[token:%s]", borrower.token) forenames = ' '.join( filter(bool, (borrower.forename, borrower.middlename))) user_id, status_code = esec_client.issue_sms( forenames, borrower.surname, deed.organisation_name, borrower.phonenumber) if status_code == 200: application.app.logger.info( "Created new esec user: %s for borrower[token:%s]", str(user_id.decode()), borrower.token) borrower.esec_user_name = user_id.decode() borrower.save() else: application.app.logger.error( "Unable to create new e-sec user for borrower [token:%s]", borrower.token) abort(status.HTTP_500_INTERNAL_SERVER_ERROR) else: result, status_code = esec_client.reissue_sms( borrower.esec_user_name) if status_code != 200: application.app.logger.error( "Unable to reissue new sms code for esec user: %s", borrower.esec_user_name) abort(status.HTTP_500_INTERNAL_SERVER_ERROR) except: msg = str(sys.exc_info()) application.app.logger.error( "Failed to issue auth code via sms: %s" % msg) abort(status.HTTP_500_INTERNAL_SERVER_ERROR) return status.HTTP_200_OK
def test_get_deed_json_adapter(self, mock_get_deed): deed = Deed() deed.deed = {} deed.token = 'token' deed.status = 'status' mock_get_deed.return_value = deed result = deed_json_adapter('1234') self.assertEqual(deed.token, result['deed']['token']) self.assertEqual(deed.status, result['deed']['status'])
def retrieve_signed_deed(): deed = Deed() result = deed.get_signed_deeds() if not result: return jsonify({ "message": "There are no deeds which have been fully signed" }), status.HTTP_404_NOT_FOUND else: return jsonify({"deeds": result}), status.HTTP_200_OK
def test_deed_pdf_adapter(self, mock_get_deed): deed = Deed() deed.deed = {"effective_date": "2016-07-05 12:00:05", "property_address": "0 The Drive, This Town, This County, PL0 0TH"} deed.token = 'token' deed.status = 'status' mock_get_deed.return_value = deed result = deed_pdf_adapter('1234') self.assertEqual(deed.token, result['token']) self.assertEqual(deed.status, result['status']) self.assertEqual('05/07/2016', result['effective_date']) self.assertEqual(['0 The Drive', 'This Town', 'This County', 'PL0 0TH'], result['property_address'])
def make_effective(deed_reference): credentials = Validation().validate_organisation_credentials() if credentials is None: return '', status.HTTP_401_UNAUTHORIZED deed = Deed() result = deed.get_deed(deed_reference) if result is None: application.app.logger.error("Deed with reference - %s not found" % str(deed_reference)) abort(status.HTTP_404_NOT_FOUND) else: deed_status = str(result.status) if deed_status == "ALL-SIGNED": check_result = Akuma.do_check(result.deed, "make effective", credentials['organisation_name'], credentials['organisation_locale'], result.token) application.app.logger.info("Check ID - Make Effective: " + check_result['id']) signed_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') make_deed_effective_date(result, signed_time) apply_registrar_signature(result, signed_time) return jsonify({"path": '/deed/' + str(result.token)}), status.HTTP_200_OK elif deed_status == "EFFECTIVE" or deed_status == "NOT-LR-SIGNED": errors = [] application.app.logger.error( "Deed with reference - %s is in %s status and can not be registrar signed" % (str(deed_reference), str(deed_status))) errors.append("This deed has already been made effective.") compiled_list = send_error_list(errors) return compiled_list else: errors = [] application.app.logger.error( "Deed with reference - %s is not fully signed and can not be registrar signed" % str(deed_reference)) errors.append( "This deed cannot be made effective as not all borrowers have signed the deed." ) compiled_list = send_error_list(errors) return compiled_list
def test_additional_columns(self): our_deed = requests.post(config.DEED_API_BASE_HOST + '/deed/', data=json.dumps(valid_deed), headers=self.webseal_headers) response_json = our_deed.json() response_path = response_json['path'].replace('/deed/', '') create_deed = Deed() deed_returned = create_deed.get_deed_system(response_path) self.assertIsInstance(deed_returned.created_date, datetime.datetime) self.assertEqual(deed_returned.payload_json, valid_deed)
def test_update_deed(self, mock_org_name, mock_json_doc, mock_updated_borrower, mock_update_md, mock_save_deed, mock_delete_orphans, mock_assign): new_deed = Deed() mock_org_name.return_value = 'A conveyancer' new_deed.organisation_id = "1" new_deed.organisation_name = mock_org_name mock_json_doc.return_value = DeedHelper._valid_initial_deed mock_updated_borrower.return_value = DeedHelper._valid_single_borrower_update_response res, msg = update_deed(new_deed, DeedHelper._json_doc) mock_assign.assert_called_with(new_deed, DeedHelper._update_deed_mock_response) self.assertTrue(res)
def test_update_deed_invalid(self, mock_org_name, mock_json_doc, mock_updated_borrower, mock_update_md, mock_save_deed): new_deed = Deed() mock_org_name.return_value = 'A conveyancer' new_deed.organisation_id = "1" new_deed.organisation_name = mock_org_name mock_json_doc.return_value = DeedHelper._valid_initial_deed mock_updated_borrower.return_value = DeedHelper._valid_single_borrower_update_response mock_update_md.return_value = None with app.app_context() as ac: ac.g.trace_id = None with app.test_request_context(): res, msg = update_deed(new_deed, DeedHelper._json_doc) self.assertFalse(res)
def create(): deed_json = request.get_json() error_count, error_message = validate_helper(deed_json) if error_count > 0: LOGGER.error("Schema validation 400_BAD_REQUEST") return error_message, status.HTTP_400_BAD_REQUEST else: try: deed = Deed() deed.token = Deed.generate_token() check_result = Akuma.do_check(deed_json, "create deed") LOGGER.info("Check ID: " + check_result['id']) organisation_credentials = process_organisation_credentials() if organisation_credentials: deed.organisation_id = organisation_credentials["O"][1] deed.organisation_name = organisation_credentials["O"][0] success, msg = update_deed(deed, deed_json, check_result['result']) if not success: LOGGER.error("Update deed 400_BAD_REQUEST") return msg, status.HTTP_400_BAD_REQUEST else: LOGGER.error("Unable to process headers") return "Unable to process headers", status.HTTP_401_UNAUTHORIZED if check_result['result'] != "A": LOGGER.error("Akuma endpoint 503_SERVICE_UNAVAILABLE") return abort(status.HTTP_503_SERVICE_UNAVAILABLE) return jsonify({"path": '/deed/' + str(deed.token) }), status.HTTP_201_CREATED except: msg = str(sys.exc_info()) LOGGER.error("Database Exception - %s" % msg) abort(status.HTTP_500_INTERNAL_SERVER_ERROR)
def auth_sms(deed, borrower_pos, user_id, borrower_auth_code, borrower_token): # pragma: no cover application.app.logger.info("Calling dm-esec-client to verify OTP code and sign the deed") element_id = 'deedData' borrower_path = "/dm-application/operativeDeed/signatureSlots" parameters = { 'borrower-pos': borrower_pos, 'element-id': element_id, 'borrowers-path': borrower_path, 'user-id': user_id, 'otp-code': borrower_auth_code, 'service-id': 1 } extra_parameters = { 'borrower-token': borrower_token, 'datetime': datetime.datetime.now().strftime("%d %B %Y %I:%M%p"), 'deed-id': deed.token } application.app.logger.info("Calling esec_client to hit validateSMSOTP...") request_url = config.ESEC_CLIENT_BASE_HOST + "/esec/auth_sms" resp = requests.post(request_url, params=parameters, data=deed.deed_xml) if resp.status_code == status.HTTP_401_UNAUTHORIZED: application.app.logger.info("Response status = %s" % resp.status_code) return jsonify({"status": "SMS Invalid"}), resp.status_code elif resp.status_code == status.HTTP_200_OK: application.app.logger.info("Response status = %s" % resp.status_code) deed_xml_to_send = deed.deed_xml.decode('utf-8') application.app.logger.info("Hashing deed prior to sending message to queue...") tree = etree.fromstring(deed.deed_xml) deed_data_xml = tree.xpath('.//deedData')[0] deed.deed_hash = Deed().generate_hash(etree.tostring(deed_data_xml)) deed.save() application.app.logger.info("Preparing to send message to the queue...") try: url = broker_url(config.RABBIT_HOST, config.EXCHANGE_USER, config.EXCHANGE_PASS, 5672, config.RABBIT_VHOST) with Emitter(url, config.EXCHANGE_NAME, config.ROUTING_KEYS) as emitter: emitter.send_message({'params': parameters, 'extra-parameters': extra_parameters, 'data': deed_xml_to_send}) application.app.logger.info("Message sent to the queue...") Borrower.update_borrower_signing_in_progress(borrower_token) return jsonify({"status": "Message successfully sent to the queue"}), status.HTTP_200_OK except Exception as e: application.app.logger.info('Error returned when trying to place an item on the queue: %s' % e) raise Exception else: application.app.logger.error("ESecurity Client Exception when trying to verify the OTP code") abort(status.HTTP_500_INTERNAL_SERVER_ERROR)
def test_save_make_effective(self): get_existing_deeds = requests.get(config.DEED_API_BASE_HOST + '/deed/retrieve-signed', headers=self.webseal_headers) get_existing_deeds = get_existing_deeds.json() create_deed = requests.post(config.DEED_API_BASE_HOST + '/deed/', data=json.dumps(valid_deed), headers=self.webseal_headers) self.assertEqual(create_deed.status_code, 201) response_json = create_deed.json() get_created_deed = requests.get(config.DEED_API_BASE_HOST + response_json["path"], headers=self.webseal_headers) self.assertEqual(get_created_deed.status_code, 200) created_deed = get_created_deed.json() code_payload = { "borrower_token": created_deed["deed"]["borrowers"][0]["token"] } request_code = requests.post(config.DEED_API_BASE_HOST + response_json["path"] + '/request-auth-code', data=json.dumps(code_payload), headers=self.webseal_headers) self.assertEqual(request_code.status_code, 200) sign_payload = { "borrower_token": created_deed["deed"]["borrowers"][0]["token"], "authentication_code": "aaaa" } sign_deed = requests.post(config.DEED_API_BASE_HOST + response_json["path"] + '/verify-auth-code', data=json.dumps(sign_payload), headers=self.webseal_headers) self.assertEqual(sign_deed.status_code, 200) get_deed_again = requests.get(config.DEED_API_BASE_HOST + response_json["path"], headers=self.webseal_headers) second_deed = get_deed_again.json() timer = time.time() + 60 while time.time( ) < timer and second_deed["deed"]["status"] != "ALL-SIGNED": get_deed_again = requests.get(config.DEED_API_BASE_HOST + response_json["path"], headers=self.webseal_headers) second_deed = get_deed_again.json() test_result = requests.get(config.DEED_API_BASE_HOST + '/deed/retrieve-signed', headers=self.webseal_headers) test_result = test_result.json() self.assertGreater(len(test_result["deeds"]), len(get_existing_deeds["deeds"])) make_effective = requests.post(config.DEED_API_BASE_HOST + response_json["path"] + '/make-effective', headers=self.webseal_headers) self.assertEqual(make_effective.status_code, 200) deed_model = Deed() result = deed_model.get_deed_system(response_json["path"].replace( "/deed/", "")) self.assertIsNotNone(result.deed_xml) mock_xml = etree.fromstring(result.deed_xml) for val in mock_xml.xpath("/dm-application/effectiveDate"): test_result = val.text self.assertIsNotNone(test_result)
def create(): deed = Deed() deed.token = Deed.generate_token() deed_json = request.get_json() validator = Validation() credentials = validator.validate_organisation_credentials() if credentials is None: return '', status.HTTP_401_UNAUTHORIZED deed.organisation_name = credentials['organisation_name'] schema_errors = validator.validate_payload(deed_json) if schema_errors: compiled_list = send_error_list(schema_errors) return compiled_list validate_title_number = validator.validate_title_number(deed_json) if validate_title_number != "title OK": errors = [] errors.append(validate_title_number) compiled_list = send_error_list(errors) return compiled_list # From here - errors are grouped error_list = [] validate_borrower_names, msg = validator.validate_borrower_names(deed_json) if not validate_borrower_names: error_list.append(msg) create_deed_akuma = validator.call_akuma( deed_json, deed.token, credentials['organisation_name'], credentials['organisation_locale'], deed_type="create deed") if create_deed_akuma["result"] == "Z": return jsonify({"message": "Unable to use this service. " "This might be because of technical difficulties or entries on the register not " "being suitable for digital applications. " "You will need to complete this transaction using a paper deed."}), \ status.HTTP_403_FORBIDDEN id_validate, msg = validator.validate_borrower_ids(deed_json) if not id_validate: error_list.append(msg) dob_validate, msg = validator.validate_dob(deed_json) if not dob_validate: error_list.append(msg) phone_validate, msg = validator.validate_phonenumbers(deed_json) if not phone_validate: error_list.append(msg) md_validate, msg = validator.validate_md_exists(deed_json['md_ref']) if not md_validate: error_list.append(msg) # Error List Print Out if len(error_list) > 0: compiled_list = send_error_list(error_list) return compiled_list success, msg = update_deed(deed, deed_json) if not success: application.app.logger.error("Create deed 400_BAD_REQUEST") return msg, status.HTTP_400_BAD_REQUEST else: application.app.logger.info("Deed has been created successfully.") return jsonify({"path": '/deed/' + str(deed.token)}), status.HTTP_201_CREATED
def test_model(self): test_deed = Deed() test_token = test_deed.generate_token() self.assertTrue(len(test_token) == 36)
def get_existing_deed_and_update(deed_reference): deed = Deed() deed_update_json = request.get_json() validator = Validation() credentials = validator.validate_organisation_credentials() if credentials is None: return '', status.HTTP_401_UNAUTHORIZED schema_errors = validator.validate_payload(deed_update_json) ids = [] for borrower in deed_update_json["borrowers"]: if 'id' in borrower: ids.append(borrower['id']) duplicates = [ item for item, count in collections.Counter(ids).items() if count > 1 ] if duplicates: schema_errors.append("A borrower ID must be unique to an individual.") if schema_errors: compiled_list = send_error_list(schema_errors) return compiled_list error_list = [] result_deed = deed.get_deed(deed_reference) if result_deed is None: error_list.append("There is no deed associated with - %s deed id." % str(deed_reference)) application.app.logger.error("Deed with reference - %s not found" % str(deed_reference)) return_error_list = send_error_list(error_list) return return_error_list # Deed Status checks if str(result_deed.status) != "DRAFT": error_list.append( "This deed is not in the correct state to be modified.") return_error_list = send_error_list(error_list) return return_error_list for borrower_id in ids: borrower_check = Borrower.get_by_id(borrower_id) if borrower_check is None or borrower_check.deed_token != deed_reference: error_list.append( "Borrowers provided do not match the selected deed.") return_error_list = send_error_list(error_list) return return_error_list validate_title_number = validator.validate_title_number(deed_update_json) if validate_title_number != "title OK": error_list.append(validate_title_number) return_error_list = send_error_list(error_list) return return_error_list # From here - errors are grouped error_list = [] validate_borrower_names, msg = validator.validate_borrower_names( deed_update_json) if not validate_borrower_names: error_list.append(msg) modify_deed_akuma = validator.call_akuma( deed_update_json, result_deed.token, credentials['organisation_name'], credentials['organisation_locale'], deed_type="modify deed") if modify_deed_akuma['result'] == "Z": return jsonify({"message": "Unable to use this service. " "This might be because of technical difficulties or entries on the register not " "being suitable for digital applications. " "You will need to complete this transaction using a paper deed."}), \ status.HTTP_403_FORBIDDEN dob_validate, msg = validator.validate_dob(deed_update_json) if not dob_validate: error_list.append(msg) phone_validate, msg = validator.validate_phonenumbers(deed_update_json) if not phone_validate: error_list.append(msg) md_validate, msg = validator.validate_md_exists(deed_update_json['md_ref']) if not md_validate: error_list.append(msg) # Error List Print Out if len(error_list) > 0: compiled_list = send_error_list(error_list) return compiled_list success, msg = update_deed(result_deed, deed_update_json) if not success: application.app.logger.error("Update deed 400_BAD_REQUEST") return msg, status.HTTP_400_BAD_REQUEST else: application.app.logger.info("Deed has been updated successfully.") return jsonify({"path": '/deed/' + str(deed_reference)}), status.HTTP_200_OK
def auth_sms(deed_reference, borrower_token, borrower_code): deed_instance = Deed() deed = deed_instance.get_deed(deed_reference) if deed is None: application.app.logger.error( "Database Exception 404 for deed reference - %s" % deed_reference) abort(status.HTTP_404_NOT_FOUND) else: application.app.logger.info( "Signing deed for borrower_token %s against deed reference %s" % (borrower_token, deed_reference)) signing_deed_akuma = Akuma.do_check(deed.deed, "borrower sign", deed.organisation_name, "", deed.token) application.app.logger.info("Check ID - Borrower SIGNING: " + signing_deed_akuma['id']) if signing_deed_akuma["result"] == "Z": application.app.logger.error("Failed to sign Mortgage document") return "Failed to sign Mortgage document" # check if XML already exist if deed.deed_xml is None: application.app.logger.info("Generating DEED_XML") deed_XML = convert_json_to_xml(deed.deed) deed.deed_xml = deed_XML.encode("utf-8") try: application.app.logger.info("getting existing XML") borrower_pos = deed.get_borrower_position(borrower_token) borrower = Borrower.get_by_token(borrower_token) esec_id = borrower.esec_user_name if esec_id: esec_client = make_esec_client() response, status_code = esec_client.auth_sms( deed, borrower_pos, esec_id, borrower_code, borrower_token) application.app.logger.info("auth_sms status code: %s", str(status_code)) if status_code == 200: return jsonify({"deed": deed.deed}), status.HTTP_200_OK else: application.app.logger.error( "Failed to authenticate sms code") return jsonify({ "status": "Failed to authenticate sms code" }), status.HTTP_401_UNAUTHORIZED else: application.app.logger.error( "Failed to sign Mortgage document - unable to create user") abort(status.HTTP_500_INTERNAL_SERVER_ERROR) except: msg = str(sys.exc_info()) application.app.logger.error( "Failed to sign Mortgage document: %s" % msg) abort(status.HTTP_500_INTERNAL_SERVER_ERROR) return jsonify({"deed": deed.deed}), status.HTTP_200_OK
def get_deed_hash(deed_reference): deed = Deed().get_deed_system(deed_reference) return jsonify({'hash': deed.deed_hash})
def get_deeds_by_status(status): deed = Deed() result = deed.get_deeds_by_status(status) return str(result)