Attachment Validation API
Reference
The Attachment Validation API turns an attachment down on the server, after the bytes have been received and before the wiki saves them. Client-Side Attachment Validation spares a reader the upload of a file that would be refused; this API is what a REST client running no JavaScript still has to pass.
xwiki-platform-attachment-validation-api declares the roles below and xwiki-platform-attachment-validation-default implements them and ships the two validation steps. Both come with the default flavor. The Attachment Validation Application, xwiki-platform-attachment-validation-ui, is a consumer of these roles and not their container. Sources: http://www.github.com/xwiki/xwiki-platform/tree/master/xwiki-platform-core/xwiki-platform-attachment/xwiki-platform-attachment-validation.
Roles
| Type | Package | Members | Purpose |
|---|---|---|---|
| AttachmentValidator | org.xwiki.attachment.validation | validateAttachment() | The entry point. Code about to write an attachment calls this one method, and a thrown AttachmentValidationException is the refusal. |
| AttachmentValidationStep | org.xwiki.attachment.validation | validate() | One aspect of the attachment each. |
| AttachmentAccessWrapper | org.xwiki.attachment | getSize(), getInputStream(), getFileName() | What both roles are handed. A plain interface, not a component role: XWiki core implements it for a servlet Part (PartAttachmentAccessWrapper) and for an XWikiAttachment, and so does code validating something of its own. |
| AttachmentValidationException | org.xwiki.attachment.validation | Four payload fields, below | The checked exception both roles throw. |
| AttachmentValidationConfiguration | org.xwiki.attachment.validation | See the Attachment Validation Configuration API | The configured limits the shipped steps read. |
@Role
public interface AttachmentValidator
{
void validateAttachment(AttachmentAccessWrapper wrapper) throws AttachmentValidationException;
}
@Role
public interface AttachmentValidationStep
{
void validate(AttachmentAccessWrapper wrapper) throws AttachmentValidationException;
}Anything writing an attachment is expected to call validateAttachment first. XWiki's own paths already do: FileUploadUtils and FileUploadPlugin while an upload is parsed, BaseAttachmentsResource over REST, DefaultTemporaryAttachmentSessionsManager for the temporary attachments the editors hold.
The Order Steps Run In
The validator calls the size step, then the mimetype step, both hardcoded, and only then the remaining AttachmentValidationStep components, skipping those two. Size is first so that an oversized file is refused before anything pays for reading it. A step of your own always runs after both, in an order no annotation controls today.
The Shipped Steps
| Step | Hint | HTTP status | Translation key | Refuses |
|---|---|---|---|---|
| File size | size | 413 | attachment.validation.filesize.rejected | A file larger than the maximum attachment size of the Page, which it carries as its single translation parameter. |
| Mimetype | mimetype | 415 | attachment.validation.mimetype.rejected | A file whose mimetype the allow and block lists turn down, both of which it carries as translation parameters. |
The mimetype is detected with Apache Tika, from the stream and the file name, never from what the browser declared; a detection failure leaves an empty mimetype. The file is refused when an allow list is configured and the mimetype matches none of its patterns, or when a block list is configured and it matches one. An empty list counts as not configured.
A pattern with no * is an exact match against the lower-cased mimetype. The first *, and only the first, is a joker: what precedes it must be a prefix of the mimetype and what follows it a suffix, so image/* and */pdf work. It is neither a glob nor a regular expression.
Those values are administered elsewhere: see Set the Maximum Attachment Size and Restrict Attachments by Mimetype.
Refusing an Attachment
AttachmentValidationException carries the whole rejection, not just a message. Its four payload fields are what the upload paths turn into an answer, so a step that leaves them empty produces an untranslated, mis-statused rejection.
| Member | Carries |
|---|---|
| getHttpStatus() | The response status, 413 and 415 for the shipped steps. |
| getTranslationKey() | The key of the message shown to the reader; the message itself is never in the exception. |
| getTranslationParameters() | The values that key is rendered with, as a List<Object>. |
| getContextMessage() | A key put in the XWiki context; only the size step sets one. |
UploadAction puts them where the upload template finds them; AttachmentValidationExceptionMapper, a JAX-RS ExceptionMapper, answers a REST upload with a JSON body of message, translationKey and translationParameters under that same status.
When the Module Is Not Installed
The implementation is optional. XWiki core registers VoidAttachmentValidator, a no-op, at the lower priority 2000, so DefaultAttachmentValidator takes over where xwiki-platform-attachment-validation-default is installed and the role still resolves where it is not. A call that throws nothing is therefore no proof that the file was examined. The Attachment Validation Configuration API answers the same absence its own way.