Allow documents to be uploaded from a public task - #5
Merged
Merged
Conversation
A public form could not accept files. Three things were in the way, of
which only two were missing.
Valtimo's upload components (valtimo-file, documenten-api-file) are
Angular components of the Valtimo front end, so the plain Form.io
renderer the public page uses drew nothing at all for them. They are now
rewritten to Form.io's own file component before the definition leaves
the server, which keeps the mapping in one testable place instead of in
the page.
There was no upload target an applicant could reach: Valtimo's
/api/v1/resource/temp is authenticated, and the public page has no
token. POST /api/v1/public-task/{publicTaskId}/attachment now parks the
file in temporary resource storage and answers with its resource id.
The third thing turned out to need nothing: the id travels back in the
submission, where Valtimo's UploadField raises a
TemporaryResourceSubmittedEvent for it, exactly as for an upload from a
task inside Valtimo. Whatever already handles those - a Documenten API
"store uploaded document" task, the S3 listener - keeps working without
knowing that a public task was involved.
The endpoint is unauthenticated by design, so what it accepts is bounded
rather than trusted. The bounds are configured per process link rather
than in application properties, so that an administrator sets them where
the rest of the task is configured: Maximum number of attachments,
Maximum file size and Accepted file types, each falling back to a
bounded default rather than to "no limit". They are read when the public
task is created and kept with it, so editing a process link does not
change the terms of a link that has already been sent out. An upload
field can narrow them further with Form.io's own Maximum File Size and
File Pattern, never widen them.
The attachment count is a conditional update on a counter rather than a
read followed by a write, so simultaneous uploads cannot both take the
last slot; a refused file gives its slot back. The type a file is held
to is the type its content is detected as, not the one the request
claims, so a renamed executable is still an executable. Filenames are
stripped of paths and of invisible characters - U+202E alone makes
"factuur<RLO>fdp.exe" read as "factuurexe.pdf" - before they reach
metadata that later becomes, for instance, a bestandsnaam in the
Documenten API. A submission may only point at files that this task
uploaded for this case.
Metadata on the process link is written on every file the task uploads,
for whatever picks the file up: the Documenten API builds the whole
document out of it and cannot default informatieobjecttype or titel,
which inside Valtimo are collected by a component the public page cannot
render. Its values are resolved against the case, so they can follow it
rather than being fixed per process link. Valtimo resolves placeholders
only in action properties that are strings, and this one is a map, so
the plugin resolves the values itself; the screen offers a value
selector for them.
Worth knowing when deploying this: temporary resources are purged after
valtimo.temporaryResourceStorage.retentionInMinutes, which defaults to
60. A public form is often left open longer than that, and an applicant
would lose their attachments before pressing submit.
spring.servlet.multipart.max-file-size caps every upload before the
plugin sees it, and valtimo.upload.accepted-mime-types is the
application-wide floor on file types. documentation/developer.md covers
these, along with what a replacement HTML template has to carry over.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Upload fields now work in the public form. A form containing a Valtimo File Upload, a Documenten API upload field or a plain Form.io file component renders a file picker, and the files an applicant chooses end up in the case the same way they would have from a task inside Valtimo. Nothing changes in the form definition.
How it fits together
filecomponent before the definition leaves the server.POST /api/v1/public-task/{publicTaskId}/attachmentparks the file in temporary resource storage and answers with its resource id.UploadFieldraises aTemporaryResourceSubmittedEvent— so a Documenten API "store uploaded document" task or the S3 listener keeps working without knowing a public task was involved.Bounding an unauthenticated endpoint
The limits live on the Create Public Task process link rather than in application properties, so an administrator sets them where the rest of the task is configured. Each falls back to a bounded default rather than to "no limit", and all are read when the task is created — editing the process link does not change the terms of a link already sent out. An upload field can narrow them with Form.io's own Maximum File Size and File Pattern, never widen them.
U+202Ealone makesfactuur<RLO>fdp.exeread asfactuurexe.pdf.Metadata
Metadata on the process link is written on every file the task uploads. The Documenten API builds the whole document out of it and cannot default
informatieobjecttypeortitel, which inside Valtimo are collected by a component the public page cannot render.Values are resolved against the case. Valtimo resolves placeholders only in action properties that are strings, and this one is a map, so
PluginServiceskipped it entirely and storedpv:...literally — the plugin now resolves the values itself, one at a time so an unresolvable value costs only its own key. The screen offers a value selector for the value column and a plain text field for the key.Review notes
documentation/plugin.mdis now written for people configuring the plugin in the Valtimo UI; developer and ops material moved todocumentation/developer.md.spring.servlet.multipart.max-file-sizecaps every upload before the plugin sees it,valtimo.upload.accepted-mime-typesis the application-wide floor on file types, and temporary resources are purged aftervaltimo.temporaryResourceStorage.retentionInMinutes(default 60) — a public form is often left open longer than that. All three are covered indeveloper.md.isCompletedByPublicTask, and there is no rate limiting in front of the upload endpoint — a leaked link is a cheap request that costs a form prefill per attempt.Backend tests (including the Postgres-backed ITs) and the Angular build pass. The stack was brought up and the public endpoints smoke-tested; the value selector was checked in the running admin UI.