def create_job(service_id): dao_fetch_service_by_id(service_id) data = request.get_json() data.update({ "service": service_id }) template = dao_get_template_by_id(data['template']) errors = unarchived_template_schema.validate({'archived': template.archived}) if errors: raise InvalidRequest(errors, status_code=400) data.update({"template_version": template.version}) job = job_schema.load(data).data if job.scheduled_for: job.job_status = JOB_STATUS_SCHEDULED dao_create_job(job) if job.job_status == JOB_STATUS_PENDING: process_job.apply_async([str(job.id)], queue="process-job") job_json = job_schema.dump(job).data job_json['statistics'] = [] return jsonify(data=job_json), 201
def post_email_notification(): form = validate(request.get_json(), post_email_request) service = services_dao.dao_fetch_service_by_id(api_user.service_id) check_service_message_limit(api_user.key_type, service) service_can_send_to_recipient(form["email_address"], api_user.key_type, service) template, template_with_content = __validate_template(form, service, EMAIL_TYPE) notification = persist_notification( template_id=template.id, template_version=template.version, recipient=form["email_address"], service_id=service.id, personalisation=form.get("personalisation", None), notification_type=EMAIL_TYPE, api_key_id=api_user.id, key_type=api_user.key_type, reference=form.get("reference"), ) send_notification_to_queue(notification, service.research_mode) resp = create_post_email_response_from_notification( notification=notification, content=str(template_with_content), subject=template_with_content.subject, email_from=service.email_from, url_root=request.url_root, ) return jsonify(resp), 201
def create_authorization_header(service_id=None, key_type=KEY_TYPE_NORMAL): if service_id: client_id = str(service_id) secrets = ApiKey.query.filter_by(service_id=service_id, key_type=key_type).all() if secrets: secret = secrets[0].secret else: service = dao_fetch_service_by_id(service_id) data = { 'service': service, 'name': uuid.uuid4(), 'created_by': service.created_by, 'key_type': key_type } api_key = ApiKey(**data) save_model_api_key(api_key) secret = api_key.secret else: client_id = current_app.config['ADMIN_CLIENT_USER_NAME'] secret = current_app.config['ADMIN_CLIENT_SECRET'] token = create_jwt_token(secret=secret, client_id=client_id) return 'Authorization', 'Bearer {}'.format(token)
def post_sms_notification(): form = validate(request.get_json(), post_sms_request) service = services_dao.dao_fetch_service_by_id(api_user.service_id) check_service_message_limit(api_user.key_type, service) service_can_send_to_recipient(form["phone_number"], api_user.key_type, service) template, template_with_content = __validate_template(form, service, SMS_TYPE) notification = persist_notification( template_id=template.id, template_version=template.version, recipient=form["phone_number"], service_id=service.id, personalisation=form.get("personalisation", None), notification_type=SMS_TYPE, api_key_id=api_user.id, key_type=api_user.key_type, reference=form.get("reference"), ) send_notification_to_queue(notification, service.research_mode) sms_sender = service.sms_sender if service.sms_sender else current_app.config.get("FROM_NUMBER") resp = create_post_sms_response_from_notification( notification, str(template_with_content), sms_sender, request.url_root ) return jsonify(resp), 201
def service_allowed_to_send_to(recipient, service, key_type, allow_guest_list_recipients=True): if key_type == KEY_TYPE_TEST: return True if key_type == KEY_TYPE_NORMAL and not service.restricted: return True # Revert back to the ORM model here so we can get some things which # aren’t in the serialised model service = dao_fetch_service_by_id(service.id) team_members = itertools.chain.from_iterable( [user.mobile_number, user.email_address] for user in service.users ) guest_list_members = [ member.recipient for member in service.guest_list if allow_guest_list_recipients ] if ( (key_type == KEY_TYPE_NORMAL and service.restricted) or (key_type == KEY_TYPE_TEAM) ): return allowed_to_send_to( recipient, itertools.chain( team_members, guest_list_members ) )
def send_sms_to_provider(notification): service = dao_fetch_service_by_id(notification.service_id) provider = provider_to_use(SMS_TYPE, notification.id) if notification.status == 'created': template_model = dao_get_template_by_id(notification.template_id, notification.template_version) template = SMSMessageTemplate( template_model.__dict__, values=notification.personalisation, prefix=service.name, sender=service.sms_sender ) if service.research_mode or notification.key_type == KEY_TYPE_TEST: send_sms_response.apply_async( (provider.get_name(), str(notification.id), notification.to), queue='research-mode' ) notification.billable_units = 0 else: provider.send_sms( to=validate_and_format_phone_number(notification.to), content=str(template), reference=str(notification.id), sender=service.sms_sender ) notification.billable_units = template.fragment_count notification.sent_at = datetime.utcnow() notification.sent_by = provider.get_name() notification.status = 'sending' dao_update_notification(notification) current_app.logger.info( "SMS {} sent to provider at {}".format(notification.id, notification.sent_at) ) delta_milliseconds = (datetime.utcnow() - notification.created_at).total_seconds() * 1000 statsd_client.timing("sms.total-time", delta_milliseconds)
def create_template(service_id): fetched_service = dao_fetch_service_by_id(service_id=service_id) # permissions needs to be placed here otherwise marshmallow will intefere with versioning permissions = fetched_service.permissions new_template = template_schema.load(request.get_json()).data if not service_has_permission(new_template.template_type, permissions): message = "Creating {} templates is not allowed".format( get_public_notify_type_text(new_template.template_type)) errors = {'template_type': [message]} raise InvalidRequest(errors, 403) new_template.service = fetched_service over_limit = _content_count_greater_than_limit(new_template.content, new_template.template_type) if over_limit: char_count_limit = SMS_CHAR_COUNT_LIMIT message = 'Content has a character count greater than the limit of {}'.format( char_count_limit) errors = {'content': [message]} raise InvalidRequest(errors, status_code=400) check_reply_to(service_id, new_template.reply_to, new_template.template_type) dao_create_template(new_template) return jsonify(data=template_schema.dump(new_template).data), 201
def populate_go_live(file_name): # 0 - count, 1- Link, 2- Service ID, 3- DEPT, 4- Service Name, 5- Main contact, # 6- Contact detail, 7-MOU, 8- LIVE date, 9- SMS, 10 - Email, 11 - Letters, 12 -CRM, 13 - Blue badge import csv print("Populate go live user and date") with open(file_name, 'r') as f: rows = csv.reader( f, quoting=csv.QUOTE_MINIMAL, skipinitialspace=True, ) print(next(rows)) # ignore header row for index, row in enumerate(rows): print(index, row) service_id = row[2] go_live_email = row[6] go_live_date = datetime.strptime(row[8], '%d/%m/%Y') + timedelta(hours=12) print(service_id, go_live_email, go_live_date) try: if go_live_email: go_live_user = get_user_by_email(go_live_email) else: go_live_user = None except NoResultFound: print("No user found for email address: ", go_live_email) continue try: service = dao_fetch_service_by_id(service_id) except NoResultFound: print("No service found for: ", service_id) continue service.go_live_user = go_live_user service.go_live_at = go_live_date dao_update_service(service)
def create_authorization_header(service_id=None, key_type=KEY_TYPE_NORMAL): if service_id: client_id = str(service_id) secrets = ApiKey.query.filter_by(service_id=service_id, key_type=key_type).all() if secrets: secret = secrets[0].secret else: service = dao_fetch_service_by_id(service_id) data = { "service": service, "name": uuid.uuid4(), "created_by": service.created_by, "key_type": key_type, } api_key = ApiKey(**data) save_model_api_key(api_key) secret = api_key.secret else: client_id = current_app.config["ADMIN_CLIENT_USER_NAME"] secret = current_app.config["ADMIN_CLIENT_SECRET"] token = create_jwt_token(secret=secret, client_id=client_id) return "Authorization", "Bearer {}".format(token)
def set_permissions(user_id, service_id): # TODO fix security hole, how do we verify that the user # who is making this request has permission to make the request. service_user = dao_get_service_user(user_id, service_id) user = service_user.user service = dao_fetch_service_by_id(service_id=service_id) data = request.get_json() validate(data, post_set_permissions_schema) permission_list = [ Permission(service_id=service_id, user_id=user_id, permission=p['permission']) for p in data['permissions'] ] permission_dao.set_user_service_permission(user, service, permission_list, _commit=True, replace=True) if 'folder_permissions' in data: folders = [ dao_get_template_folder_by_id_and_service_id( folder_id, service_id) for folder_id in data['folder_permissions'] ] service_user.folders = folders dao_update_service_user(service_user) return jsonify({}), 204
def create_broadcast_message(service_id): data = request.get_json() validate(data, create_broadcast_message_schema) service = dao_fetch_service_by_id(data['service_id']) user = get_user_by_id(data['created_by']) template = dao_get_template_by_id_and_service_id(data['template_id'], data['service_id']) personalisation = data.get('personalisation', {}) broadcast_message = BroadcastMessage( service_id=service.id, template_id=template.id, template_version=template.version, personalisation=personalisation, areas={"areas": data.get("areas", []), "simple_polygons": data.get("simple_polygons", [])}, status=BroadcastStatusType.DRAFT, starts_at=_parse_nullable_datetime(data.get('starts_at')), finishes_at=_parse_nullable_datetime(data.get('finishes_at')), created_by_id=user.id, content=template._as_utils_template_with_personalisation( personalisation ).content_with_placeholders_filled_in, ) dao_save_object(broadcast_message) return jsonify(broadcast_message.serialize()), 201
def update_service(service_id): req_json = request.get_json() fetched_service = dao_fetch_service_by_id(service_id) # Capture the status change here as Marshmallow changes this later service_going_live = fetched_service.restricted and not req_json.get( 'restricted', True) current_data = dict(service_schema.dump(fetched_service).data.items()) current_data.update(request.get_json()) service = service_schema.load(current_data).data org_type = req_json.get('organisation_type', None) if org_type: service.crown = org_type == 'central' if 'email_branding' in req_json: email_branding_id = req_json['email_branding'] service.email_branding = None if not email_branding_id else EmailBranding.query.get( email_branding_id) dao_update_service(service) if service_going_live: send_notification_to_service_users( service_id=service_id, template_id=current_app.config['SERVICE_NOW_LIVE_TEMPLATE_ID'], personalisation={ 'service_name': current_data['name'], 'message_limit': '{:,}'.format(current_data['message_limit']) }, include_user_fields=['name']) return jsonify(data=service_schema.dump(fetched_service).data), 200
def test_create_service_returns_service_with_default_permissions(notify_db_session): service = create_service(service_name='testing', email_from='testing', service_permissions=None) service = dao_fetch_service_by_id(service.id) _assert_service_permissions(service.permissions, ( SMS_TYPE, EMAIL_TYPE, LETTER_TYPE, INTERNATIONAL_SMS_TYPE, ))
def create_api_key(service_id=None): fetched_service = dao_fetch_service_by_id(service_id=service_id) valid_api_key = api_key_schema.load(request.get_json()).data valid_api_key.service = fetched_service save_model_api_key(valid_api_key) unsigned_api_key = get_unsigned_secret(valid_api_key.id) return jsonify(data=unsigned_api_key), 201
def send_one_off_notification(service_id, post_data): service = dao_fetch_service_by_id(service_id) template = dao_get_template_by_id_and_service_id( template_id=post_data['template_id'], service_id=service_id ) personalisation = post_data.get('personalisation', None) validate_template(template.id, personalisation, service, template.template_type) check_service_over_daily_message_limit(KEY_TYPE_NORMAL, service) validate_and_format_recipient( send_to=post_data['to'], key_type=KEY_TYPE_NORMAL, service=service, notification_type=template.template_type, allow_whitelisted_recipients=False, ) validate_created_by(service, post_data['created_by']) sender_id = post_data.get('sender_id', None) reply_to = get_reply_to_text( notification_type=template.template_type, sender_id=sender_id, service=service, template=template ) notification = persist_notification( template_id=template.id, template_version=template.version, template_postage=template.postage, recipient=post_data['to'], service=service, personalisation=personalisation, notification_type=template.template_type, api_key_id=None, key_type=KEY_TYPE_NORMAL, created_by_id=post_data['created_by'], reply_to_text=reply_to, reference=create_one_off_reference(template.template_type), ) queue_name = QueueNames.PRIORITY if template.process_type == PRIORITY else None if template.template_type == LETTER_TYPE and service.research_mode: _update_notification_status( notification, NOTIFICATION_DELIVERED, ) else: send_notification_to_queue( notification=notification, research_mode=service.research_mode, queue=queue_name, ) return {'id': str(notification.id)}
def create_template(service_id): fetched_service = dao_fetch_service_by_id(service_id=service_id) # permissions needs to be placed here otherwise marshmallow will interfere with versioning permissions = fetched_service.permissions template_json = validate(request.get_json(), post_create_template_schema) folder = validate_parent_folder(template_json=template_json) new_template = Template.from_json(template_json, folder) if not service_has_permission(new_template.template_type, permissions): message = "Creating {} templates is not allowed".format( get_public_notify_type_text(new_template.template_type)) errors = {'template_type': [message]} raise InvalidRequest(errors, 403) if not new_template.postage and new_template.template_type == LETTER_TYPE: new_template.postage = SECOND_CLASS new_template.service = fetched_service over_limit = _content_count_greater_than_limit(new_template.content, new_template.template_type) if over_limit: message = 'Content has a character count greater than the limit of {}'.format(SMS_CHAR_COUNT_LIMIT) errors = {'content': [message]} raise InvalidRequest(errors, status_code=400) check_reply_to(service_id, new_template.reply_to, new_template.template_type) dao_create_template(new_template) return jsonify(data=template_schema.dump(new_template).data), 201
def get_monthly_notification_stats(service_id): service = dao_fetch_service_by_id(service_id) try: return jsonify(data=dao_fetch_monthly_historical_stats_for_service( service.id, int(request.args.get('year', 'NaN')))) except ValueError: raise InvalidRequest('Year must be a number', status_code=400)
def send_notifications_on_mou_signed(organisation_id): organisation = dao_get_organisation_by_id(organisation_id) notify_service = dao_fetch_service_by_id(current_app.config['NOTIFY_SERVICE_ID']) def _send_notification(template_id, recipient, personalisation): template = dao_get_template_by_id(template_id) saved_notification = persist_notification( template_id=template.id, template_version=template.version, recipient=recipient, service=notify_service, personalisation=personalisation, notification_type=template.template_type, api_key_id=None, key_type=KEY_TYPE_NORMAL, reply_to_text=notify_service.get_default_reply_to_email_address() ) send_notification_to_queue(saved_notification, research_mode=False, queue=QueueNames.NOTIFY) personalisation = { 'mou_link': '{}/agreement/{}.pdf'.format( current_app.config['ADMIN_BASE_URL'], 'crown' if organisation.crown else 'non-crown' ), 'org_name': organisation.name, 'org_dashboard_link': '{}/organisations/{}'.format( current_app.config['ADMIN_BASE_URL'], organisation.id ), 'signed_by_name': organisation.agreement_signed_by.name, 'on_behalf_of_name': organisation.agreement_signed_on_behalf_of_name } # let notify team know something's happened _send_notification( current_app.config['MOU_NOTIFY_TEAM_ALERT_TEMPLATE_ID'], 'notify-support+{}@digital.cabinet-office.gov.uk'.format(current_app.config['NOTIFY_ENVIRONMENT']), personalisation ) if not organisation.agreement_signed_on_behalf_of_email_address: signer_template_id = 'MOU_SIGNER_RECEIPT_TEMPLATE_ID' else: signer_template_id = 'MOU_SIGNED_ON_BEHALF_SIGNER_RECEIPT_TEMPLATE_ID' # let the person who has been signed on behalf of know. _send_notification( current_app.config['MOU_SIGNED_ON_BEHALF_ON_BEHALF_RECEIPT_TEMPLATE_ID'], organisation.agreement_signed_on_behalf_of_email_address, personalisation ) # let the person who signed know - the template is different depending on if they signed on behalf of someone _send_notification( current_app.config[signer_template_id], organisation.agreement_signed_by.email_address, personalisation )
def get_dict(service_id): from app.schemas import service_schema service_dict = service_schema.dump( dao_fetch_service_by_id(service_id)).data db.session.commit() return {'data': service_dict}
def test_remove_permission_from_service_by_id_returns_service_with_correct_permissions( notify_db_session, permission_to_remove, permissions_remaining ): service = create_service(service_permissions=None) dao_remove_service_permission(service_id=service.id, permission=permission_to_remove) service = dao_fetch_service_by_id(service.id) _assert_service_permissions(service.permissions, permissions_remaining)
def test_create_service_by_id_adding_and_removing_letter_returns_service_without_letter(service_factory): service = service_factory.get('testing', email_from='testing') dao_remove_service_permission(service_id=service.id, permission=LETTER_TYPE) dao_add_service_permission(service_id=service.id, permission=LETTER_TYPE) service = dao_fetch_service_by_id(service.id) _assert_service_permissions(service.permissions, ( SMS_TYPE, EMAIL_TYPE, LETTER_TYPE, INTERNATIONAL_SMS_TYPE, UPLOAD_LETTERS, )) dao_remove_service_permission(service_id=service.id, permission=LETTER_TYPE) service = dao_fetch_service_by_id(service.id) _assert_service_permissions(service.permissions, ( SMS_TYPE, EMAIL_TYPE, INTERNATIONAL_SMS_TYPE, UPLOAD_LETTERS, ))
def test_deactivating_service_changes_name_and_email(client, sample_service): auth_header = create_authorization_header() client.post('/service/{}/archive'.format(sample_service.id), headers=[auth_header]) archived_service = dao_fetch_service_by_id(sample_service.id) assert archived_service.name == '_archived_2018-07-07_Sample service' assert archived_service.email_from == '_archived_2018-07-07_sample.service'
def get_detailed_service(service_id, today_only=False): service = dao_fetch_service_by_id(service_id) stats_fn = dao_fetch_todays_stats_for_service if today_only else dao_fetch_stats_for_service stats = stats_fn(service_id) service.statistics = statistics.format_statistics(stats) return detailed_service_schema.dump(service).data
def get_service_by_id(service_id): if request.args.get('detailed') == 'True': data = get_detailed_service(service_id, today_only=request.args.get('today_only') == 'True') else: fetched = dao_fetch_service_by_id(service_id) data = service_schema.dump(fetched).data return jsonify(data=data)
def request_to_go_live(service_id): fetched_service = dao_fetch_service_by_id(service_id=service_id) if fetched_service.restricted: send_notification_to_notify_support( current_app.config['REQUEST_TO_GO_LIVE_TEMPLATE_ID'], request.get_json()) return jsonify(), 200
def save_sms(self, service_id, notification_id, encrypted_notification, sender_id=None): notification = encryption.decrypt(encrypted_notification) service = dao_fetch_service_by_id(service_id) template = dao_get_template_by_id(notification["template"], version=notification["template_version"]) if sender_id: reply_to_text = dao_get_service_sms_senders_by_id( service_id, sender_id).sms_sender else: reply_to_text = template.get_reply_to_text() if not service_allowed_to_send_to(notification["to"], service, KEY_TYPE_NORMAL): current_app.logger.debug( "SMS {} failed as restricted service".format(notification_id)) return check_service_over_daily_message_limit(KEY_TYPE_NORMAL, service) try: # this task is used by two main things... process_job and process_sms_or_email_notification # if the data is not present in the encrypted data then fallback on whats needed for process_job saved_notification = persist_notification( notification_id=notification.get("id", notification_id), template_id=notification["template"], template_version=notification["template_version"], recipient=notification["to"], service=service, personalisation=notification.get("personalisation"), notification_type=SMS_TYPE, simulated=notification.get("simulated", None), api_key_id=notification.get("api_key", None), key_type=notification.get("key_type", KEY_TYPE_NORMAL), created_at=datetime.utcnow(), job_id=notification.get("job", None), job_row_number=notification.get("row_number", None), reply_to_text=reply_to_text, ) send_notification_to_queue( saved_notification, service.research_mode, queue=notification.get("queue") or template.queue_to_use(), ) current_app.logger.debug("SMS {} created at {} for job {}".format( saved_notification.id, saved_notification.created_at, notification.get("job", None), )) except SQLAlchemyError as e: handle_exception(self, notification, notification_id, e)
def link_service_to_organisation(organisation_id): data = request.get_json() validate(data, post_link_service_to_organisation_schema) service = dao_fetch_service_by_id(data["service_id"]) service.organisation = None dao_add_service_to_organisation(service, organisation_id) return "", 204
def test_removing_all_permission_returns_service_with_no_permissions(notify_db_session): service = create_service() dao_remove_service_permission(service_id=service.id, permission=SMS_TYPE) dao_remove_service_permission(service_id=service.id, permission=EMAIL_TYPE) dao_remove_service_permission(service_id=service.id, permission=LETTER_TYPE) dao_remove_service_permission(service_id=service.id, permission=INTERNATIONAL_SMS_TYPE) service = dao_fetch_service_by_id(service.id) assert len(service.permissions) == 0
def get_service_by_id(service_id): if request.args.get("detailed") == "True": data = get_detailed_service( service_id, today_only=request.args.get("today_only") == "True") else: fetched = dao_fetch_service_by_id(service_id) data = service_schema.dump(fetched).data return jsonify(data=data)
def save_letter( self, service_id, notification_id, encrypted_notification, ): notification = encryption.decrypt(encrypted_notification) # we store the recipient as just the first item of the person's address recipient = notification["personalisation"]["addressline1"] service = dao_fetch_service_by_id(service_id) template = dao_get_template_by_id(notification["template"], version=notification["template_version"]) check_service_over_daily_message_limit(KEY_TYPE_NORMAL, service) try: # if we don't want to actually send the letter, then start it off in SENDING so we don't pick it up status = NOTIFICATION_CREATED if not service.research_mode else NOTIFICATION_SENDING saved_notification = persist_notification( template_id=notification["template"], template_version=notification["template_version"], template_postage=template.postage, recipient=recipient, service=service, personalisation=notification["personalisation"], notification_type=LETTER_TYPE, api_key_id=notification.get("api_key", None), key_type=KEY_TYPE_NORMAL, created_at=datetime.utcnow(), job_id=notification["job"], job_row_number=notification["row_number"], notification_id=notification_id, reference=create_random_identifier(), reply_to_text=template.get_reply_to_text(), status=status, ) if not service.research_mode: send_notification_to_queue(saved_notification, service.research_mode) elif current_app.config["NOTIFY_ENVIRONMENT"] in [ "preview", "development" ]: research_mode_tasks.create_fake_letter_response_file.apply_async( (saved_notification.reference, ), queue=QueueNames.RESEARCH_MODE) else: update_notification_status_by_reference( saved_notification.reference, "delivered") current_app.logger.debug("Letter {} created at {}".format( saved_notification.id, saved_notification.created_at)) except SQLAlchemyError as e: handle_exception(self, notification, notification_id, e)
def save_letter( self, service_id, notification_id, encrypted_notification, ): notification = encryption.decrypt(encrypted_notification) postal_address = PostalAddress.from_personalisation( Columns(notification['personalisation'])) service = dao_fetch_service_by_id(service_id) template = dao_get_template_by_id(notification['template'], version=notification['template_version']) try: # if we don't want to actually send the letter, then start it off in SENDING so we don't pick it up status = NOTIFICATION_CREATED if not service.research_mode else NOTIFICATION_SENDING saved_notification = persist_notification( template_id=notification['template'], template_version=notification['template_version'], postage=postal_address.postage if postal_address.international else template.postage, recipient=postal_address.normalised, service=service, personalisation=notification['personalisation'], notification_type=LETTER_TYPE, api_key_id=None, key_type=KEY_TYPE_NORMAL, created_at=datetime.utcnow(), job_id=notification['job'], job_row_number=notification['row_number'], notification_id=notification_id, reference=create_random_identifier(), reply_to_text=template.get_reply_to_text(), status=status) if not service.research_mode: letters_pdf_tasks.get_pdf_for_templated_letter.apply_async( [str(saved_notification.id)], queue=QueueNames.CREATE_LETTERS_PDF) elif current_app.config['NOTIFY_ENVIRONMENT'] in [ 'preview', 'development' ]: research_mode_tasks.create_fake_letter_response_file.apply_async( (saved_notification.reference, ), queue=QueueNames.RESEARCH_MODE) else: update_notification_status_by_reference( saved_notification.reference, 'delivered') current_app.logger.debug("Letter {} created at {}".format( saved_notification.id, saved_notification.created_at)) except SQLAlchemyError as e: handle_exception(self, notification, notification_id, e)
def send_pdf_letter_notification(service_id, post_data): service = dao_fetch_service_by_id(service_id) check_service_has_permission(LETTER_TYPE, service.permissions) check_service_has_permission(UPLOAD_LETTERS, service.permissions) check_service_over_daily_message_limit(KEY_TYPE_NORMAL, service) validate_created_by(service, post_data["created_by"]) template = get_precompiled_letter_template(service.id) file_location = "service-{}/{}.pdf".format(service.id, post_data["file_id"]) try: letter = utils_s3download(current_app.config["TRANSIENT_UPLOADED_LETTERS"], file_location) except S3ObjectNotFound as e: current_app.logger.exception( "Letter {}.pdf not in transient {} bucket".format( post_data["file_id"], current_app.config["TRANSIENT_UPLOADED_LETTERS"] ) ) raise e # Getting the page count won't raise an error since admin has already checked the PDF is valid billable_units = get_page_count(letter.read()) personalisation = {"address_line_1": post_data["filename"]} # TODO: stop hard-coding postage as 'second' once we get postage from the admin notification = persist_notification( notification_id=post_data["file_id"], template_id=template.id, template_version=template.version, template_postage=template.postage, recipient=post_data["filename"], service=service, personalisation=personalisation, notification_type=LETTER_TYPE, api_key_id=None, key_type=KEY_TYPE_NORMAL, reference=create_one_off_reference(LETTER_TYPE), client_reference=post_data["filename"], created_by_id=post_data["created_by"], billable_units=billable_units, postage="second", ) upload_filename = get_letter_pdf_filename( notification.reference, notification.service.crown, is_scan_letter=False, postage=notification.postage, ) move_uploaded_pdf_to_letters_bucket(file_location, upload_filename) return {"id": str(notification.id)}
def send_one_off_notification(service_id, post_data): service = dao_fetch_service_by_id(service_id) template = dao_get_template_by_id_and_service_id(template_id=post_data["template_id"], service_id=service_id) personalisation = post_data.get("personalisation", None) validate_template(template.id, personalisation, service, template.template_type) check_service_over_daily_message_limit(KEY_TYPE_NORMAL, service) validate_and_format_recipient( send_to=post_data["to"], key_type=KEY_TYPE_NORMAL, service=service, notification_type=template.template_type, allow_safelisted_recipients=False, ) validate_created_by(service, post_data["created_by"]) sender_id = post_data.get("sender_id", None) reply_to = get_reply_to_text( notification_type=template.template_type, sender_id=sender_id, service=service, template=template, ) notification = persist_notification( template_id=template.id, template_version=template.version, template_postage=template.postage, recipient=post_data["to"], service=service, personalisation=personalisation, notification_type=template.template_type, api_key_id=None, key_type=KEY_TYPE_NORMAL, created_by_id=post_data["created_by"], reply_to_text=reply_to, reference=create_one_off_reference(template.template_type), ) if template.template_type == LETTER_TYPE and service.research_mode: _update_notification_status( notification, NOTIFICATION_DELIVERED, ) else: send_notification_to_queue( notification=notification, research_mode=service.research_mode, queue=template.queue_to_use(), ) return {"id": str(notification.id)}
def send_sms( self, service_id, notification_id, encrypted_notification, created_at, api_key_id=None, key_type=KEY_TYPE_NORMAL ): notification = encryption.decrypt(encrypted_notification) service = dao_fetch_service_by_id(service_id) if not service_allowed_to_send_to(notification["to"], service, key_type): current_app.logger.info("SMS {} failed as restricted service".format(notification_id)) return try: saved_notification = persist_notification( template_id=notification["template"], template_version=notification["template_version"], recipient=notification["to"], service_id=service.id, personalisation=notification.get("personalisation"), notification_type=SMS_TYPE, api_key_id=api_key_id, key_type=key_type, created_at=created_at, job_id=notification.get("job", None), job_row_number=notification.get("row_number", None), notification_id=notification_id, ) provider_tasks.deliver_sms.apply_async( [str(saved_notification.id)], queue="send-sms" if not service.research_mode else "research-mode" ) current_app.logger.info( "SMS {} created at {} for job {}".format(saved_notification.id, created_at, notification.get("job", None)) ) except SQLAlchemyError as e: if not get_notification_by_id(notification_id): # Sometimes, SQS plays the same message twice. We should be able to catch an IntegrityError, but it seems # SQLAlchemy is throwing a FlushError. So we check if the notification id already exists then do not # send to the retry queue. current_app.logger.error( "RETRY: send_sms notification for job {} row number {} and notification id {}".format( notification.get("job", None), notification.get("row_number", None), notification_id ) ) current_app.logger.exception(e) try: raise self.retry(queue="retry", exc=e) except self.MaxRetriesExceededError: current_app.logger.error( "RETRY FAILED: send_sms notification for job {} row number {} and notification id {}".format( notification.get("job", None), notification.get("row_number", None), notification_id ) ) current_app.logger.exception(e)
def delete_smtp_relay(service_id): service = dao_fetch_service_by_id(service_id) if service.smtp_user is not None: smtp_remove(service.smtp_user) service.smtp_user = None dao_update_service(service) return jsonify(True), 201 else: raise InvalidRequest(message="SMTP user does not exist", status_code=500)
def set_permissions(user_id, service_id): # TODO fix security hole, how do we verify that the user # who is making this request has permission to make the request. user = get_user_by_id(user_id=user_id) service = dao_fetch_service_by_id(service_id=service_id) permissions, errors = permission_schema.load(request.get_json(), many=True) for p in permissions: p.user = user p.service = service permission_dao.set_user_service_permission(user, service, permissions, _commit=True, replace=True) return jsonify({}), 204
def create_template(service_id): fetched_service = dao_fetch_service_by_id(service_id=service_id) new_template = template_schema.load(request.get_json()).data new_template.service = fetched_service new_template.content = _strip_html(new_template.content) over_limit = _content_count_greater_than_limit(new_template.content, new_template.template_type) if over_limit: char_count_limit = current_app.config.get('SMS_CHAR_COUNT_LIMIT') message = 'Content has a character count greater than the limit of {}'.format(char_count_limit) errors = {'content': [message]} raise InvalidRequest(errors, status_code=400) dao_create_template(new_template) return jsonify(data=template_schema.dump(new_template).data), 201
def send_email_to_provider(notification): service = dao_fetch_service_by_id(notification.service_id) provider = provider_to_use(EMAIL_TYPE, notification.id) if notification.status == 'created': template_dict = dao_get_template_by_id(notification.template_id, notification.template_version).__dict__ html_email = HTMLEmailTemplate( template_dict, values=notification.personalisation, **get_html_email_options(service) ) plain_text_email = PlainTextEmailTemplate( template_dict, values=notification.personalisation ) if service.research_mode or notification.key_type == KEY_TYPE_TEST: reference = str(create_uuid()) send_email_response.apply_async( (provider.get_name(), reference, notification.to), queue='research-mode' ) notification.billable_units = 0 else: from_address = '"{}" <{}@{}>'.format(service.name, service.email_from, current_app.config['NOTIFY_EMAIL_DOMAIN']) reference = provider.send_email( from_address, notification.to, plain_text_email.subject, body=str(plain_text_email), html_body=str(html_email), reply_to_address=service.reply_to_email_address, ) notification.reference = reference notification.sent_at = datetime.utcnow() notification.sent_by = provider.get_name(), notification.status = 'sending' dao_update_notification(notification) current_app.logger.info( "Email {} sent to provider at {}".format(notification.id, notification.sent_at) ) delta_milliseconds = (datetime.utcnow() - notification.created_at).total_seconds() * 1000 statsd_client.timing("email.total-time", delta_milliseconds)
def requires_auth(): auth_token = get_auth_token(request) try: client = get_token_issuer(auth_token) except TokenDecodeError as e: raise AuthError(e.message, 403) except TokenIssuerError: raise AuthError("Invalid token: iss not provided", 403) if client == current_app.config.get('ADMIN_CLIENT_USER_NAME'): g.service_id = current_app.config.get('ADMIN_CLIENT_USER_NAME') return handle_admin_key(auth_token, current_app.config.get('ADMIN_CLIENT_SECRET')) try: service = dao_fetch_service_by_id(client) except DataError: raise AuthError("Invalid token: service id is not the right data type", 403) except NoResultFound: raise AuthError("Invalid token: service not found", 403) if not service.api_keys: raise AuthError("Invalid token: service has no API keys", 403) if not service.active: raise AuthError("Invalid token: service is archived", 403) for api_key in service.api_keys: try: get_decode_errors(auth_token, api_key.unsigned_secret) except TokenDecodeError: continue if api_key.expiry_date: raise AuthError("Invalid token: API key revoked", 403) g.service_id = api_key.service_id _request_ctx_stack.top.api_user = api_key return else: # service has API keys, but none matching the one the user provided raise AuthError("Invalid token: signature, api token is not valid", 403)
def test_send_user_sms_code(notify_api, sample_user, sms_code_template, mocker, research_mode): """ Tests POST endpoint /user/<user_id>/sms-code """ with notify_api.test_request_context(): with notify_api.test_client() as client: if research_mode: notify_service = dao_fetch_service_by_id(current_app.config['NOTIFY_SERVICE_ID']) notify_service.research_mode = True dao_update_service(notify_service) data = json.dumps({}) auth_header = create_authorization_header() mocked = mocker.patch('app.user.rest.create_secret_code', return_value='11111') mocker.patch('app.celery.provider_tasks.deliver_sms.apply_async') resp = client.post( url_for('user.send_user_sms_code', user_id=sample_user.id), data=data, headers=[('Content-Type', 'application/json'), auth_header]) assert resp.status_code == 204 assert mocked.call_count == 1 assert VerifyCode.query.count() == 1 assert VerifyCode.query.first().check_code('11111') assert Notification.query.count() == 1 notification = Notification.query.first() assert notification.personalisation == {'verify_code': '11111'} assert notification.to == sample_user.mobile_number assert str(notification.service_id) == current_app.config['NOTIFY_SERVICE_ID'] app.celery.provider_tasks.deliver_sms.apply_async.assert_called_once_with( ([str(notification.id)]), queue="notify" )
def create_authorization_header(service_id=None, key_type=KEY_TYPE_NORMAL): if service_id: client_id = str(service_id) secrets = ApiKey.query.filter_by(service_id=service_id, key_type=key_type).all() if secrets: secret = secrets[0].unsigned_secret else: service = dao_fetch_service_by_id(service_id) data = { 'service': service, 'name': uuid.uuid4(), 'created_by': service.created_by, 'key_type': key_type } api_key = ApiKey(**data) save_model_api_key(api_key) secret = api_key.unsigned_secret else: client_id = current_app.config.get('ADMIN_CLIENT_USER_NAME') secret = current_app.config.get('ADMIN_CLIENT_SECRET') token = create_jwt_token(secret=secret, client_id=client_id) return 'Authorization', 'Bearer {}'.format(token)
def test_get_service_by_id_returns_none_if_no_service(notify_db): with pytest.raises(NoResultFound) as e: dao_fetch_service_by_id(str(uuid.uuid4())) assert "No row was found for one()" in str(e)
def test_get_service_by_id_returns_service(service_factory): service = service_factory.get("testing", email_from="testing") assert dao_fetch_service_by_id(service.id).name == "testing"