def make_key(original_key):

        normalised = "".join(character.lower() for character in original_key
                             if character not in " _-',.()+&")

        if '?' in SanitiseASCII.encode(normalised):
            return normalised

        return SanitiseASCII.encode(normalised)
示例#2
0
    def make_key(original_key):

        original_key = original_key.replace('&', 'and')
        original_key = original_key.replace('+', 'and')

        normalised = "".join(character.lower() for character in original_key
                             if character not in " _-'’,.()")

        if '?' in SanitiseASCII.encode(normalised):
            return normalised

        return SanitiseASCII.encode(normalised)
示例#3
0
def useful_headers_after_request(response):
    response.headers.add("X-Frame-Options", "deny")
    response.headers.add("X-Content-Type-Options", "nosniff")
    response.headers.add("X-XSS-Protection", "1; mode=block")
    response.headers.add("Permissions-Policy", "interest-cohort=()")
    response.headers.add(
        "Content-Security-Policy",
        (
            "default-src 'self' {asset_domain} 'unsafe-inline';"
            "script-src 'self' {asset_domain} *.google-analytics.com *.googletagmanager.com 'unsafe-inline' 'unsafe-eval' data:;"
            "connect-src 'self' *.google-analytics.com;"
            "object-src 'self';"
            "style-src 'self' *.googleapis.com 'unsafe-inline';"
            "font-src 'self' {asset_domain} *.googleapis.com *.gstatic.com data:;"
            "img-src 'self' {asset_domain} *.google-analytics.com *.notifications.service.gov.uk data:;"  # noqa: E501
            "frame-src 'self' www.youtube.com;".format(
                asset_domain=current_app.config["ASSET_DOMAIN"], )),
    )
    if "Cache-Control" in response.headers:
        del response.headers["Cache-Control"]
    # Cache static assets (CSS, JS, images) for a long time
    # as they have unique hashes thanks to the asset
    # fingerprinter
    if asset_fingerprinter.is_static_asset(request.url):
        response.headers.add("Cache-Control",
                             "public, max-age=31536000, immutable")
    else:
        response.headers.add("Cache-Control",
                             "no-store, no-cache, private, must-revalidate")
    for key, value in response.headers:
        response.headers[key] = SanitiseASCII.encode(value)
    return response
示例#4
0
def upload_contact_list(service_id):
    form = CsvUploadForm()

    if form.validate_on_submit():
        try:
            upload_id = ContactList.upload(
                current_service.id,
                Spreadsheet.from_file_form(form).as_dict,
            )
            file_name_metadata = unicode_truncate(
                SanitiseASCII.encode(form.file.data.filename), 1600)
            ContactList.set_metadata(current_service.id,
                                     upload_id,
                                     original_file_name=file_name_metadata)
            return redirect(
                url_for(
                    '.check_contact_list',
                    service_id=service_id,
                    upload_id=upload_id,
                ))
        except (UnicodeDecodeError, BadZipFile, XLRDError):
            flash(
                'Could not read {}. Try using a different file format.'.format(
                    form.file.data.filename))
        except (XLDateError):
            flash((
                '{} contains numbers or dates that Notify cannot understand. '
                'Try formatting all columns as ‘text’ or export your file as CSV.'
            ).format(form.file.data.filename))

    return render_template(
        'views/uploads/contact-list/upload.html',
        form=form,
        allowed_file_extensions=Spreadsheet.ALLOWED_FILE_EXTENSIONS,
    )
def useful_headers_after_request(response):
    response.headers.add('X-Frame-Options', 'deny')
    response.headers.add('X-Content-Type-Options', 'nosniff')
    response.headers.add('X-XSS-Protection', '1; mode=block')
    response.headers.add(
        'Content-Security-Policy',
        (
            "default-src 'self' {asset_domain} 'unsafe-inline';"
            "script-src 'self' {asset_domain} *.google-analytics.com *.googletagmanager.com 'unsafe-inline' 'unsafe-eval' data:;"
            "connect-src 'self' *.google-analytics.com;"
            "object-src 'self';"
            "style-src 'self' *.googleapis.com 'unsafe-inline';"
            "font-src 'self' {asset_domain} *.googleapis.com *.gstatic.com data:;"
            "img-src 'self' {asset_domain} *.google-analytics.com *.notifications.service.gov.uk {logo_domain} notification-alpha-canada-ca-cdn.s3.amazonaws.com data:;"  # noqa: E501
            "frame-src 'self' www.youtube.com;".format(
                asset_domain=current_app.config['ASSET_DOMAIN'],
                logo_domain=get_logo_cdn_domain(),
            )))
    if 'Cache-Control' in response.headers:
        del response.headers['Cache-Control']
    response.headers.add('Cache-Control',
                         'no-store, no-cache, private, must-revalidate')
    for key, value in response.headers:
        response.headers[key] = SanitiseASCII.encode(value)
    return response
示例#6
0
def check_messages(service_id, template_id, upload_id, row_index=2):

    data = _check_messages(service_id, template_id, upload_id, row_index)

    if (data['recipients'].too_many_rows or not data['count_of_recipients']
            or not data['recipients'].has_recipient_columns
            or data['recipients'].duplicate_recipient_column_headers
            or data['recipients'].missing_column_headers):
        return render_template('views/check/column-errors.html', **data)

    if data['row_errors']:
        return render_template('views/check/row-errors.html', **data)

    if (data['errors'] or data['trying_to_send_letters_in_trial_mode']):
        return render_template('views/check/column-errors.html', **data)

    data['original_file_name'] = SanitiseASCII.encode(
        data.get('original_file_name', ''))

    set_metadata_on_csv_upload(
        service_id,
        upload_id,
        notification_count=data['count_of_recipients'],
        template_id=str(template_id),
        valid=True,
        original_file_name=unicode_truncate(
            data['original_file_name'],
            1600,
        ),
    )

    return render_template('views/check/ok.html', **data)
示例#7
0
def useful_headers_after_request(response):
    response.headers.add('X-Frame-Options', 'deny')
    response.headers.add('X-Content-Type-Options', 'nosniff')
    response.headers.add('X-XSS-Protection', '1; mode=block')
    response.headers.add('Content-Security-Policy', (
        "default-src 'self' {asset_domain} 'unsafe-inline';"
        "script-src 'self' {asset_domain} *.google-analytics.com 'unsafe-inline' 'unsafe-eval' data:;"
        "connect-src 'self' *.google-analytics.com;"
        "object-src 'self';"
        "font-src 'self' {asset_domain} data:;"
        "img-src 'self' {asset_domain} *.tile.openstreetmap.org *.google-analytics.com"
        " *.notifications.service.gov.uk {logo_domain} data:;"
        "frame-src 'self' www.youtube-nocookie.com;".format(
            asset_domain=current_app.config['ASSET_DOMAIN'],
            logo_domain=get_logo_cdn_domain(),
        )))
    response.headers.add(
        'Link',
        ('<{asset_url}>; rel=dns-prefetch, <{asset_url}>; rel=preconnect'.
         format(asset_url=f'https://{current_app.config["ASSET_DOMAIN"]}')))
    if 'Cache-Control' in response.headers:
        del response.headers['Cache-Control']
    response.headers.add('Cache-Control',
                         'no-store, no-cache, private, must-revalidate')
    for key, value in response.headers:
        response.headers[key] = SanitiseASCII.encode(value)
    return response
def format_recipient(address):
    '''
    To format the recipient we need to:
    - remove new line characters
    - remove whitespace around the lines
    - join the address lines, separated by a comma
    - convert the string to ASCII (S3 metadata must be stored as ASCII)
    '''
    stripped_address_lines_no_trailing_commas = [
        line.lstrip().rstrip(' ,') for line in address.splitlines() if line
    ]
    one_line_address = ', '.join(stripped_address_lines_no_trailing_commas)

    return SanitiseASCII.encode(one_line_address)
def useful_headers_after_request(response):
    response.headers.add('X-Frame-Options', 'deny')
    response.headers.add('X-Content-Type-Options', 'nosniff')
    response.headers.add('X-XSS-Protection', '1; mode=block')
    response.headers.add('Content-Security-Policy', (
        "default-src 'self' 'unsafe-inline';"
        "script-src 'self' *.google-analytics.com 'unsafe-inline' 'unsafe-eval' data:;"
        "connect-src 'self' *.google-analytics.com;"
        "object-src 'self';"
        "font-src 'self' data:;"
        "img-src 'self' *.google-analytics.com *.notifications.service.gov.uk {} data:;"
        "frame-src www.youtube.com;".format(get_cdn_domain())))
    if 'Cache-Control' in response.headers:
        del response.headers['Cache-Control']
    response.headers.add('Cache-Control',
                         'no-store, no-cache, private, must-revalidate')
    for key, value in response.headers:
        response.headers[key] = SanitiseASCII.encode(value)
    return response
示例#10
0
def check_messages(service_id, template_id, upload_id, row_index=2):

    data = _check_messages(service_id, template_id, upload_id, row_index)

    if (
        data['recipients'].too_many_rows or
        not data['count_of_recipients'] or
        not data['recipients'].has_recipient_columns or
        data['recipients'].duplicate_recipient_column_headers or
        data['recipients'].missing_column_headers or
        data['sent_previously']
    ):
        return render_template('views/check/column-errors.html', **data)

    if data['row_errors']:
        return render_template('views/check/row-errors.html', **data)

    if (
        data['errors'] or
        data['trying_to_send_letters_in_trial_mode']
    ):
        return render_template('views/check/column-errors.html', **data)

    data['original_file_name'] = SanitiseASCII.encode(data.get('original_file_name', ''))

    metadata_kwargs = {
        'notification_count': data['count_of_recipients'],
        'template_id': str(template_id),
        'valid': True,
        'original_file_name': unicode_truncate(
            data['original_file_name'],
            1600,
        ),
    }

    if session.get('sender_id'):
        metadata_kwargs['sender_id'] = session['sender_id']

    set_metadata_on_csv_upload(service_id, upload_id, **metadata_kwargs)

    return render_template('views/check/ok.html', **data)
示例#11
0
def check_messages(service_id, template_id, upload_id, row_index=2):

    data = _check_messages(service_id, template_id, upload_id, row_index)

    if (data["recipients"].too_many_rows or not data["count_of_recipients"]
            or not data["recipients"].has_recipient_columns
            or data["recipients"].duplicate_recipient_column_headers
            or data["recipients"].missing_column_headers
            or data["sent_previously"]):
        return render_template("views/check/column-errors.html", **data)

    if data["row_errors"]:
        return render_template("views/check/row-errors.html", **data)

    if data["errors"] or data["trying_to_send_letters_in_trial_mode"]:
        return render_template("views/check/column-errors.html", **data)

    data["original_file_name"] = SanitiseASCII.encode(
        data.get("original_file_name", ""))

    metadata_kwargs = {
        "notification_count": data["count_of_recipients"],
        "template_id": str(template_id),
        "valid": True,
        "original_file_name": unicode_truncate(
            data["original_file_name"],
            1600,
        ),
    }

    if session.get("sender_id"):
        metadata_kwargs["sender_id"] = session["sender_id"]

    set_metadata_on_csv_upload(service_id, upload_id, **metadata_kwargs)

    return render_template("views/check/ok.html", **data)
示例#12
0
def test_encode_string(content, expected):
    assert SanitiseSMS.encode(content) == expected
    assert SanitiseASCII.encode(content) == expected
示例#13
0
def test_encode_chars_different_between_ascii_and_sms(char, expected_sms, expected_ascii):
    assert SanitiseSMS.encode_char(char) == expected_sms
    assert SanitiseASCII.encode_char(char) == expected_ascii
示例#14
0
def send_messages(service_id, template_id):
    db_template = current_service.get_template_with_user_permission_or_403(
        template_id, current_user)

    email_reply_to = None
    sms_sender = None

    if db_template['template_type'] == 'email':
        email_reply_to = get_email_reply_to_address_from_session()
    elif db_template['template_type'] == 'sms':
        sms_sender = get_sms_sender_from_session()

    if db_template[
            'template_type'] not in current_service.available_template_types:
        return redirect(
            url_for('.action_blocked',
                    service_id=service_id,
                    notification_type=db_template['template_type'],
                    return_to='view_template',
                    template_id=template_id))

    template = get_template(
        db_template,
        current_service,
        show_recipient=True,
        letter_preview_url=url_for(
            'no_cookie.view_letter_template_preview',
            service_id=service_id,
            template_id=template_id,
            filetype='png',
            page_count=get_page_count_for_letter(db_template),
        ),
        email_reply_to=email_reply_to,
        sms_sender=sms_sender,
    )

    form = CsvUploadForm()
    if form.validate_on_submit():
        try:
            upload_id = s3upload(service_id,
                                 Spreadsheet.from_file_form(form).as_dict,
                                 current_app.config['AWS_REGION'])
            file_name_metadata = unicode_truncate(
                SanitiseASCII.encode(form.file.data.filename), 1600)
            set_metadata_on_csv_upload(service_id,
                                       upload_id,
                                       original_file_name=file_name_metadata)
            return redirect(
                url_for(
                    '.check_messages',
                    service_id=service_id,
                    upload_id=upload_id,
                    template_id=template.id,
                ))
        except (UnicodeDecodeError, BadZipFile, XLRDError):
            flash(
                'Could not read {}. Try using a different file format.'.format(
                    form.file.data.filename))
        except (XLDateError):
            flash((
                '{} contains numbers or dates that Notify cannot understand. '
                'Try formatting all columns as ‘text’ or export your file as CSV.'
            ).format(form.file.data.filename))

    column_headings = get_spreadsheet_column_headings_from_template(template)

    return render_template(
        'views/send.html',
        template=template,
        column_headings=list(ascii_uppercase[:len(column_headings)]),
        example=[column_headings,
                 get_example_csv_rows(template)],
        form=form,
        allowed_file_extensions=Spreadsheet.ALLOWED_FILE_EXTENSIONS)
示例#15
0
def test_encode_chars_different_between_ascii_gsm(char, expected_gsm, expected_ascii):
    assert SanitiseGSM.encode_char(char) == expected_gsm
    assert SanitiseASCII.encode_char(char) == expected_ascii
示例#16
0
def check_contact_list(service_id, upload_id):

    form = CsvUploadForm()

    contents = ContactList.download(service_id, upload_id)
    first_row = contents.splitlines()[0].strip().rstrip(
        ',') if contents else ''

    template_type = {
        'emailaddress': 'email',
        'phonenumber': 'sms',
    }.get(Columns.make_key(first_row))

    original_file_name = SanitiseASCII.encode(
        request.args.get('original_file_name', ''))

    recipients = RecipientCSV(
        contents,
        template=get_sample_template(template_type or 'sms'),
        whitelist=itertools.chain.from_iterable(
            [user.name, user.mobile_number, user.email_address]
            for user in current_service.active_users)
        if current_service.trial_mode else None,
        allow_international_sms=current_service.has_permission(
            'international_sms'),
        max_initial_rows_shown=50,
        max_errors_shown=50,
    )

    non_empty_column_headers = list(filter(None, recipients.column_headers))

    if len(non_empty_column_headers
           ) > 1 or not template_type or not recipients:
        return render_template(
            'views/uploads/contact-list/too-many-columns.html',
            recipients=recipients,
            original_file_name=original_file_name,
            template_type=template_type,
            form=form,
        )

    if recipients.too_many_rows or not len(recipients):
        return render_template(
            'views/uploads/contact-list/column-errors.html',
            recipients=recipients,
            original_file_name=original_file_name,
            form=form,
        )

    row_errors = get_errors_for_csv(recipients, template_type)
    if row_errors:
        return render_template(
            'views/uploads/contact-list/row-errors.html',
            recipients=recipients,
            original_file_name=original_file_name,
            row_errors=row_errors,
            form=form,
        )

    if recipients.has_errors:
        return render_template(
            'views/uploads/contact-list/column-errors.html',
            recipients=recipients,
            original_file_name=original_file_name,
            form=form,
        )

    metadata_kwargs = {
        'row_count': len(recipients),
        'valid': True,
        'original_file_name': unicode_truncate(
            original_file_name,
            1600,
        ),
        'template_type': template_type
    }

    ContactList.set_metadata(service_id, upload_id, **metadata_kwargs)

    return render_template(
        'views/uploads/contact-list/ok.html',
        recipients=recipients,
        original_file_name=original_file_name,
        upload_id=upload_id,
    )