Ensuring sanitized filenames on uploaded sports.injury.document even if files previously uploaded with illegal characters.
This commit is contained in:
parent
06b9c6d201
commit
fc8248ff2e
1 changed files with 37 additions and 4 deletions
|
|
@ -1,6 +1,8 @@
|
||||||
import logging
|
import logging
|
||||||
import base64
|
import base64
|
||||||
import io
|
import io
|
||||||
|
import re
|
||||||
|
import unicodedata
|
||||||
from odoo import http, fields, _
|
from odoo import http, fields, _
|
||||||
from odoo.exceptions import UserError, ValidationError
|
from odoo.exceptions import UserError, ValidationError
|
||||||
from odoo.http import request
|
from odoo.http import request
|
||||||
|
|
@ -249,6 +251,33 @@ class PatientInjuryPortal(CustomerPortal, AccessControlMixin):
|
||||||
return request.render('bemade_sports_clinic.portal_injury_created', values)
|
return request.render('bemade_sports_clinic.portal_injury_created', values)
|
||||||
|
|
||||||
# _check_access_to_injury method now inherited from AccessControlMixin
|
# _check_access_to_injury method now inherited from AccessControlMixin
|
||||||
|
|
||||||
|
def _sanitize_filename(self, name):
|
||||||
|
"""Return an ASCII-safe filename for HTTP headers/storage.
|
||||||
|
- Normalize unicode
|
||||||
|
- Remove path separators
|
||||||
|
- Strip non-ASCII characters
|
||||||
|
- Replace illegal chars with underscore
|
||||||
|
- Truncate to a reasonable length
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if not name:
|
||||||
|
return 'document'
|
||||||
|
# Normalize unicode then drop accents
|
||||||
|
norm = unicodedata.normalize('NFKD', str(name))
|
||||||
|
norm = norm.replace('/', '-').replace('\\', '-')
|
||||||
|
# Encode to ASCII, ignore non-ASCII
|
||||||
|
ascii_name = norm.encode('ascii', 'ignore').decode('ascii')
|
||||||
|
# Allow alnum, space, dot, dash, underscore only
|
||||||
|
ascii_name = re.sub(r'[^A-Za-z0-9._ -]', '_', ascii_name)
|
||||||
|
# Collapse whitespace and trim
|
||||||
|
ascii_name = re.sub(r'\s+', ' ', ascii_name).strip()
|
||||||
|
if not ascii_name:
|
||||||
|
ascii_name = 'document'
|
||||||
|
# Limit length
|
||||||
|
return ascii_name[:150]
|
||||||
|
except Exception:
|
||||||
|
return 'document'
|
||||||
|
|
||||||
@http.route(['/my/injury/edit'], type='http', auth='user', website=True)
|
@http.route(['/my/injury/edit'], type='http', auth='user', website=True)
|
||||||
def edit_injury_form(self, injury_id=None, **post):
|
def edit_injury_form(self, injury_id=None, **post):
|
||||||
|
|
@ -605,6 +634,7 @@ class PatientInjuryPortal(CustomerPortal, AccessControlMixin):
|
||||||
# Process the file
|
# Process the file
|
||||||
try:
|
try:
|
||||||
name = attachment.filename
|
name = attachment.filename
|
||||||
|
safe_file_name = self._sanitize_filename(name)
|
||||||
file_content = attachment.read()
|
file_content = attachment.read()
|
||||||
file_size = len(file_content)
|
file_size = len(file_content)
|
||||||
|
|
||||||
|
|
@ -620,7 +650,7 @@ class PatientInjuryPortal(CustomerPortal, AccessControlMixin):
|
||||||
'description': post.get('description', ''),
|
'description': post.get('description', ''),
|
||||||
'category': post.get('category', 'other'),
|
'category': post.get('category', 'other'),
|
||||||
'file_content': base64.b64encode(file_content),
|
'file_content': base64.b64encode(file_content),
|
||||||
'file_name': name,
|
'file_name': safe_file_name,
|
||||||
'created_by_id': request.env.user.id,
|
'created_by_id': request.env.user.id,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -649,11 +679,12 @@ class PatientInjuryPortal(CustomerPortal, AccessControlMixin):
|
||||||
raise request.not_found()
|
raise request.not_found()
|
||||||
|
|
||||||
# Return the file for download
|
# Return the file for download
|
||||||
|
safe_name = self._sanitize_filename(document.file_name)
|
||||||
return request.make_response(
|
return request.make_response(
|
||||||
base64.b64decode(document.file_content),
|
base64.b64decode(document.file_content),
|
||||||
headers=[
|
headers=[
|
||||||
('Content-Type', 'application/octet-stream'),
|
('Content-Type', 'application/octet-stream'),
|
||||||
('Content-Disposition', f'attachment; filename="{document.file_name}"'),
|
('Content-Disposition', f'attachment; filename="{safe_name}"'),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -668,11 +699,12 @@ class PatientInjuryPortal(CustomerPortal, AccessControlMixin):
|
||||||
self._check_access_to_patient(document.patient_id.id)
|
self._check_access_to_patient(document.patient_id.id)
|
||||||
except UserError:
|
except UserError:
|
||||||
raise request.not_found()
|
raise request.not_found()
|
||||||
|
safe_name = self._sanitize_filename(document.file_name)
|
||||||
return request.make_response(
|
return request.make_response(
|
||||||
base64.b64decode(document.file_content),
|
base64.b64decode(document.file_content),
|
||||||
headers=[
|
headers=[
|
||||||
('Content-Type', 'application/octet-stream'),
|
('Content-Type', 'application/octet-stream'),
|
||||||
('Content-Disposition', f'attachment; filename="{document.file_name}"'),
|
('Content-Disposition', f'attachment; filename="{safe_name}"'),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -698,6 +730,7 @@ class PatientInjuryPortal(CustomerPortal, AccessControlMixin):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
name = attachment.filename
|
name = attachment.filename
|
||||||
|
safe_file_name = self._sanitize_filename(name)
|
||||||
file_content = attachment.read()
|
file_content = attachment.read()
|
||||||
file_size = len(file_content)
|
file_size = len(file_content)
|
||||||
|
|
||||||
|
|
@ -713,7 +746,7 @@ class PatientInjuryPortal(CustomerPortal, AccessControlMixin):
|
||||||
'description': post.get('description', ''),
|
'description': post.get('description', ''),
|
||||||
'category': post.get('category', 'other'),
|
'category': post.get('category', 'other'),
|
||||||
'file_content': base64.b64encode(file_content),
|
'file_content': base64.b64encode(file_content),
|
||||||
'file_name': name,
|
'file_name': safe_file_name,
|
||||||
'created_by_id': request.env.user.id,
|
'created_by_id': request.env.user.id,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue