backoffice: fix access to uploaded file in file widget (#70194)
This commit is contained in:
parent
00230b311b
commit
36110d8082
|
@ -8,6 +8,7 @@ import zipfile
|
|||
|
||||
import pytest
|
||||
import responses
|
||||
from webtest import Upload
|
||||
|
||||
import wcs.qommon.storage as st
|
||||
from wcs import fields
|
||||
|
@ -4877,3 +4878,55 @@ def test_backoffice_dispatch_single_user(pub, user_template):
|
|||
assert formdata.workflow_roles == {'_foobar': [user.roles[0]]}
|
||||
|
||||
assert 'button_a_button' in resp.text
|
||||
|
||||
|
||||
def test_backoffice_workflow_form_file_access(pub):
|
||||
FormDef.wipe()
|
||||
Workflow.wipe()
|
||||
|
||||
role = pub.role_class(name='xxx1')
|
||||
role.store()
|
||||
|
||||
user = create_superuser(pub)
|
||||
user.roles.append(role.id)
|
||||
user.store()
|
||||
|
||||
wf = Workflow(name='test')
|
||||
status = wf.add_status('New', 'st1')
|
||||
next_status = wf.add_status('Next', 'st2')
|
||||
|
||||
status.items = []
|
||||
display_form = status.add_action('form', id='_display_form')
|
||||
display_form.by = ['_receiver']
|
||||
display_form.varname = 'blah'
|
||||
display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
|
||||
display_form.formdef.fields = [
|
||||
fields.FileField(id='1', label='test', type='file', varname='file'),
|
||||
fields.StringField(id='2', label='test2', type='string', required=True),
|
||||
]
|
||||
|
||||
jump = status.add_action('jumponsubmit', id='_jump')
|
||||
jump.status = next_status.id
|
||||
|
||||
wf.store()
|
||||
|
||||
formdef = FormDef()
|
||||
formdef.name = 'test'
|
||||
formdef.workflow_id = wf.id
|
||||
formdef.workflow_roles = {'_receiver': role.id}
|
||||
formdef.fields = []
|
||||
formdef.store()
|
||||
|
||||
formdef.data_class().wipe()
|
||||
|
||||
formdata = formdef.data_class()()
|
||||
formdata.just_created()
|
||||
formdata.store()
|
||||
|
||||
app = login(get_app(pub))
|
||||
resp = app.get(formdata.get_url(backoffice=True))
|
||||
resp.form['fblah_1$file'] = Upload('test3.txt', b'foobar3', 'text/plain')
|
||||
resp = resp.form.submit('submit')
|
||||
# it will fail on the equired string field; this allows testing
|
||||
# the temporary file URL.
|
||||
assert resp.click('test3.txt').body == b'foobar3'
|
||||
|
|
|
@ -41,7 +41,7 @@ from wcs.conditions import Condition
|
|||
from wcs.formdata import FormData
|
||||
from wcs.formdef import FormDef
|
||||
from wcs.forms.backoffice import FormDefUI
|
||||
from wcs.forms.common import FormStatusPage
|
||||
from wcs.forms.common import FormdefDirectoryBase, FormStatusPage
|
||||
from wcs.roles import logged_users_role
|
||||
from wcs.variables import LazyFieldVar, LazyList
|
||||
from wcs.workflows import ActionsTracingEvolutionPart, WorkflowStatusItem, item_classes, template_on_formdata
|
||||
|
@ -752,7 +752,7 @@ class ManagementDirectory(Directory):
|
|||
return FormPage(component)
|
||||
|
||||
|
||||
class FormPage(Directory):
|
||||
class FormPage(FormdefDirectoryBase):
|
||||
do_not_call_in_templates = True
|
||||
_q_exports = [
|
||||
'',
|
||||
|
@ -2926,7 +2926,7 @@ class FormPage(Directory):
|
|||
except KeyError:
|
||||
raise errors.TraversalError()
|
||||
|
||||
return FormBackOfficeStatusPage(self.formdef, filled)
|
||||
return FormBackOfficeStatusPage(self.formdef, filled, parent_view=self)
|
||||
|
||||
def live(self):
|
||||
return FormBackofficeEditPage(self.formdef.url_name).live()
|
||||
|
@ -2951,6 +2951,7 @@ class FormBackOfficeStatusPage(FormStatusPage):
|
|||
'action',
|
||||
'live',
|
||||
'inspect',
|
||||
'tempfile',
|
||||
('inspect-tool', 'inspect_tool'),
|
||||
('download-as-zip', 'download_as_zip'),
|
||||
('lateral-block', 'lateral_block'),
|
||||
|
|
|
@ -941,3 +941,48 @@ class FormStatusPage(Directory, FormTemplateMixin):
|
|||
return f._q_index()
|
||||
|
||||
raise errors.AccessForbiddenError()
|
||||
|
||||
|
||||
class FormdefDirectoryBase(Directory):
|
||||
user = None
|
||||
|
||||
def tempfile(self):
|
||||
get_request().ignore_session = True
|
||||
self.check_access()
|
||||
if self.user and not self.user.id == get_session().user:
|
||||
self.check_receiver()
|
||||
try:
|
||||
t = get_request().form['t']
|
||||
tempfile = get_session().get_tempfile(t)
|
||||
except KeyError:
|
||||
raise errors.TraversalError()
|
||||
if tempfile is None:
|
||||
raise errors.TraversalError()
|
||||
response = get_response()
|
||||
|
||||
# force potential HTML upload to be used as-is (not decorated with theme)
|
||||
# and with minimal permissions
|
||||
response.filter = {}
|
||||
response.set_header(
|
||||
'Content-Security-Policy',
|
||||
'default-src \'none\'; img-src %s;' % get_request().build_absolute_uri(),
|
||||
)
|
||||
|
||||
if tempfile['content_type']:
|
||||
response.set_content_type(tempfile['content_type'])
|
||||
else:
|
||||
response.set_content_type('application/octet-stream')
|
||||
if tempfile['charset']:
|
||||
response.set_charset(tempfile['charset'])
|
||||
|
||||
if get_request().form.get('thumbnail') == '1':
|
||||
try:
|
||||
thumbnail = misc.get_thumbnail(
|
||||
get_session().get_tempfile_path(t), content_type=tempfile['content_type']
|
||||
)
|
||||
except misc.ThumbnailError:
|
||||
pass
|
||||
else:
|
||||
response.set_content_type('image/png')
|
||||
return thumbnail
|
||||
return get_session().get_tempfile_content(t).get_file_pointer().read()
|
||||
|
|
|
@ -37,7 +37,7 @@ from wcs.categories import Category
|
|||
from wcs.fields import MissingBlockFieldError, SetValueError
|
||||
from wcs.formdata import Evolution, FormData
|
||||
from wcs.formdef import FormDef
|
||||
from wcs.forms.common import FormStatusPage, FormTemplateMixin
|
||||
from wcs.forms.common import FormdefDirectoryBase, FormStatusPage, FormTemplateMixin
|
||||
from wcs.qommon.admin.texts import TextsDirectory
|
||||
from wcs.qommon.form import get_selection_error_text
|
||||
from wcs.qommon.storage import Equal, NothingToUpdate
|
||||
|
@ -256,7 +256,7 @@ class TrackingCodesDirectory(Directory):
|
|||
return TrackingCodeDirectory(component, self.formdef)
|
||||
|
||||
|
||||
class FormPage(Directory, FormTemplateMixin):
|
||||
class FormPage(FormdefDirectoryBase, FormTemplateMixin):
|
||||
_q_exports = [
|
||||
'',
|
||||
'tempfile',
|
||||
|
@ -312,7 +312,7 @@ class FormPage(Directory, FormTemplateMixin):
|
|||
def go_to_backoffice(self):
|
||||
return redirect(self.formdef.get_admin_url())
|
||||
|
||||
def check_role(self):
|
||||
def check_access(self):
|
||||
if self.formdef.roles:
|
||||
if not self.user:
|
||||
raise errors.AccessUnauthorizedError()
|
||||
|
@ -941,7 +941,7 @@ class FormPage(Directory, FormTemplateMixin):
|
|||
self._pages = None
|
||||
|
||||
def _q_index(self):
|
||||
self.check_role()
|
||||
self.check_access()
|
||||
authentication_context_check_result = self.check_authentication_context()
|
||||
if authentication_context_check_result:
|
||||
return authentication_context_check_result
|
||||
|
@ -1676,47 +1676,6 @@ class FormPage(Directory, FormTemplateMixin):
|
|||
break
|
||||
return redirect(url or '.')
|
||||
|
||||
def tempfile(self):
|
||||
get_request().ignore_session = True
|
||||
self.check_role()
|
||||
if self.user and not self.user.id == get_session().user:
|
||||
self.check_receiver()
|
||||
try:
|
||||
t = get_request().form['t']
|
||||
tempfile = get_session().get_tempfile(t)
|
||||
except KeyError:
|
||||
raise errors.TraversalError()
|
||||
if tempfile is None:
|
||||
raise errors.TraversalError()
|
||||
response = get_response()
|
||||
|
||||
# force potential HTML upload to be used as-is (not decorated with theme)
|
||||
# and with minimal permissions
|
||||
response.filter = {}
|
||||
response.set_header(
|
||||
'Content-Security-Policy',
|
||||
'default-src \'none\'; img-src %s;' % get_request().build_absolute_uri(),
|
||||
)
|
||||
|
||||
if tempfile['content_type']:
|
||||
response.set_content_type(tempfile['content_type'])
|
||||
else:
|
||||
response.set_content_type('application/octet-stream')
|
||||
if tempfile['charset']:
|
||||
response.set_charset(tempfile['charset'])
|
||||
|
||||
if get_request().form.get('thumbnail') == '1':
|
||||
try:
|
||||
thumbnail = misc.get_thumbnail(
|
||||
get_session().get_tempfile_path(t), content_type=tempfile['content_type']
|
||||
)
|
||||
except misc.ThumbnailError:
|
||||
pass
|
||||
else:
|
||||
response.set_content_type('image/png')
|
||||
return thumbnail
|
||||
return get_session().get_tempfile_content(t).get_file_pointer().read()
|
||||
|
||||
def validating(self, data, page_error_messages=None):
|
||||
self.on_validation_page = True
|
||||
get_request().view_name = 'validation'
|
||||
|
|
Loading…
Reference in New Issue