Exemplo n.º 1
0
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
Exemplo n.º 3
0
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
Exemplo n.º 5
0
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)
Exemplo n.º 7
0
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
Exemplo n.º 8
0
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)
Exemplo n.º 9
0
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)
Exemplo n.º 10
0
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
Exemplo n.º 11
0
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
Exemplo n.º 12
0
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
Exemplo n.º 13
0
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,
    ))
Exemplo n.º 14
0
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)}
Exemplo n.º 16
0
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
Exemplo n.º 17
0
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)
Exemplo n.º 18
0
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
    )
Exemplo n.º 19
0
    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}
Exemplo n.º 20
0
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)
Exemplo n.º 21
0
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'
Exemplo n.º 23
0
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
Exemplo n.º 24
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)
Exemplo n.º 25
0
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
Exemplo n.º 26
0
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)
Exemplo n.º 27
0
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
Exemplo n.º 28
0
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
Exemplo n.º 29
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)
Exemplo n.º 30
0
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)
Exemplo n.º 31
0
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)
Exemplo n.º 32
0
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)}
Exemplo n.º 33
0
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)}
Exemplo n.º 34
0
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)
Exemplo n.º 35
0
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)
Exemplo n.º 36
0
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
Exemplo n.º 37
0
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
Exemplo n.º 38
0
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)
Exemplo n.º 39
0
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)
Exemplo n.º 40
0
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"
            )
Exemplo n.º 41
0
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)
Exemplo n.º 42
0
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)
Exemplo n.º 43
0
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"