mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-04-05 01:51:28 -04:00
We had been storing whether or not a file was valid in the S3 metadata, but using the query string of the URL to store the original filename and the page count. This meant that if you tried to view the preview letter page without the query string you would see a `500`. It was possible for this to happen if you were signed out of Notify while on the preview page - you would be redirected back to the preview page but without the query string, causing an error.
169 lines
5.4 KiB
Python
169 lines
5.4 KiB
Python
import base64
|
|
import uuid
|
|
from io import BytesIO
|
|
|
|
from flask import (
|
|
abort,
|
|
current_app,
|
|
flash,
|
|
redirect,
|
|
render_template,
|
|
request,
|
|
url_for,
|
|
)
|
|
from notifications_utils.pdf import pdf_page_count
|
|
from PyPDF2.utils import PdfReadError
|
|
from requests import RequestException
|
|
|
|
from app import current_service, notification_api_client, service_api_client
|
|
from app.extensions import antivirus_client
|
|
from app.main import main
|
|
from app.main.forms import PDFUploadForm
|
|
from app.s3_client.s3_letter_upload_client import (
|
|
get_letter_metadata,
|
|
get_letter_pdf_and_metadata,
|
|
get_transient_letter_file_location,
|
|
upload_letter_to_s3,
|
|
)
|
|
from app.template_previews import TemplatePreview, sanitise_letter
|
|
from app.utils import get_template, user_has_permissions
|
|
|
|
MAX_FILE_UPLOAD_SIZE = 2 * 1024 * 1024 # 2MB
|
|
|
|
|
|
@main.route("/services/<service_id>/uploads")
|
|
@user_has_permissions('send_messages')
|
|
def uploads(service_id):
|
|
return render_template('views/uploads/index.html')
|
|
|
|
|
|
@main.route("/services/<service_id>/upload-letter", methods=['GET', 'POST'])
|
|
@user_has_permissions('send_messages')
|
|
def upload_letter(service_id):
|
|
form = PDFUploadForm()
|
|
|
|
if form.validate_on_submit():
|
|
pdf_file_bytes = form.file.data.read()
|
|
original_filename = form.file.data.filename
|
|
|
|
virus_free = antivirus_client.scan(BytesIO(pdf_file_bytes))
|
|
if not virus_free:
|
|
return invalid_upload_error('Your file has failed the virus check')
|
|
|
|
if len(pdf_file_bytes) > MAX_FILE_UPLOAD_SIZE:
|
|
return invalid_upload_error('Your file must be smaller than 2MB')
|
|
|
|
try:
|
|
# TODO: get page count from the sanitise response once template preview handles malformed files nicely
|
|
page_count = pdf_page_count(BytesIO(pdf_file_bytes))
|
|
except PdfReadError:
|
|
current_app.logger.info('Invalid PDF uploaded for service_id: {}'.format(service_id))
|
|
return invalid_upload_error('Your file must be a valid PDF')
|
|
|
|
upload_id = uuid.uuid4()
|
|
file_location = get_transient_letter_file_location(service_id, upload_id)
|
|
|
|
try:
|
|
response = sanitise_letter(BytesIO(pdf_file_bytes))
|
|
response.raise_for_status()
|
|
except RequestException as ex:
|
|
if ex.response is not None and ex.response.status_code == 400:
|
|
status = 'invalid'
|
|
upload_letter_to_s3(
|
|
pdf_file_bytes,
|
|
file_location=file_location,
|
|
status=status,
|
|
page_count=page_count,
|
|
filename=original_filename)
|
|
else:
|
|
raise ex
|
|
else:
|
|
status = 'valid'
|
|
file_contents = base64.b64decode(response.json()['file'].encode())
|
|
upload_letter_to_s3(
|
|
file_contents,
|
|
file_location=file_location,
|
|
status=status,
|
|
page_count=page_count,
|
|
filename=original_filename)
|
|
|
|
return redirect(
|
|
url_for(
|
|
'main.uploaded_letter_preview',
|
|
service_id=current_service.id,
|
|
file_id=upload_id,
|
|
)
|
|
)
|
|
|
|
return render_template('views/uploads/choose-file.html', form=form)
|
|
|
|
|
|
def invalid_upload_error(message):
|
|
flash(message, 'dangerous')
|
|
return render_template('views/uploads/choose-file.html', form=PDFUploadForm()), 400
|
|
|
|
|
|
@main.route("/services/<service_id>/preview-letter/<file_id>")
|
|
@user_has_permissions('send_messages')
|
|
def uploaded_letter_preview(service_id, file_id):
|
|
metadata = get_letter_metadata(service_id, file_id)
|
|
original_filename = metadata.get('filename')
|
|
page_count = metadata.get('page_count')
|
|
status = metadata.get('status')
|
|
|
|
template_dict = service_api_client.get_precompiled_template(service_id)
|
|
|
|
template = get_template(
|
|
template_dict,
|
|
service_id,
|
|
letter_preview_url=url_for(
|
|
'.view_letter_upload_as_preview',
|
|
service_id=service_id,
|
|
file_id=file_id
|
|
),
|
|
page_count=page_count
|
|
)
|
|
|
|
return render_template(
|
|
'views/uploads/preview.html',
|
|
original_filename=original_filename,
|
|
template=template,
|
|
status=status,
|
|
file_id=file_id,
|
|
)
|
|
|
|
|
|
@main.route("/services/<service_id>/preview-letter-image/<file_id>")
|
|
@user_has_permissions('send_messages')
|
|
def view_letter_upload_as_preview(service_id, file_id):
|
|
pdf_file, metadata = get_letter_pdf_and_metadata(service_id, file_id)
|
|
|
|
page = request.args.get('page')
|
|
|
|
if metadata['status'] == 'invalid':
|
|
return TemplatePreview.from_invalid_pdf_file(pdf_file, page)
|
|
else:
|
|
return TemplatePreview.from_valid_pdf_file(pdf_file, page)
|
|
|
|
|
|
@main.route("/services/<service_id>/upload-letter/send", methods=['POST'])
|
|
@user_has_permissions('send_messages', restrict_admin_usage=True)
|
|
def send_uploaded_letter(service_id):
|
|
if not (current_service.has_permission('letter') and current_service.has_permission('upload_letters')):
|
|
abort(403)
|
|
|
|
file_id = request.form['file_id']
|
|
metadata = get_letter_metadata(service_id, file_id)
|
|
filename = metadata.get('filename')
|
|
|
|
if metadata.get('status') != 'valid':
|
|
abort(403)
|
|
|
|
notification_api_client.send_precompiled_letter(service_id, filename, file_id)
|
|
|
|
return redirect(url_for(
|
|
'.view_notification',
|
|
service_id=service_id,
|
|
notification_id=file_id,
|
|
))
|