Skip to content
Snippets Groups Projects
Commit 4e635935 authored by Manuel Leduc's avatar Manuel Leduc
Browse files

XWIKI-20454: Client side mimetype restriction check does not take into account the right document

- Attachment restrictions are now lazily loaded when the editor document is not the current document
parent 62335e9f
No related branches found
Tags 6.1.7
No related merge requests found
Showing
with 182 additions and 16 deletions
...@@ -157,6 +157,22 @@ ...@@ -157,6 +157,22 @@
</item> </item>
</differences> </differences>
</revapi.differences> </revapi.differences>
<revapi.differences>
<criticality>documented</criticality>
<justification>New methods on unstable code, allowing to get the mimetype configuration for a given document.</justification>
<differences>
<item>
<ignore>true</ignore>
<code>java.method.addedToInterface</code>
<new>method java.util.List&lt;java.lang.String&gt; org.xwiki.attachment.validation.AttachmentValidationConfiguration::getAllowedMimetypes(org.xwiki.model.reference.DocumentReference)</new>
</item>
<item>
<ignore>true</ignore>
<code>java.method.addedToInterface</code>
<new>method java.util.List&lt;java.lang.String&gt; org.xwiki.attachment.validation.AttachmentValidationConfiguration::getBlockerMimetypes(org.xwiki.model.reference.DocumentReference)</new>
</item>
</differences>
</revapi.differences>
</analysisConfiguration> </analysisConfiguration>
</configuration> </configuration>
</plugin> </plugin>
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
import java.util.List; import java.util.List;
import org.xwiki.component.annotation.Role; import org.xwiki.component.annotation.Role;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.stability.Unstable; import org.xwiki.stability.Unstable;
/** /**
...@@ -35,14 +36,32 @@ ...@@ -35,14 +36,32 @@
public interface AttachmentValidationConfiguration public interface AttachmentValidationConfiguration
{ {
/** /**
* @return the list of allowed attachment mimetypes. A joker (@code '*') can be used to match any media (e.g., * @return the list of allowed attachment mimetypes of the current document. A joker (@code '*') can be used to
* "image/png", "text/*") * match any media (e.g., "image/png", "text/*")
*/ */
List<String> getAllowedMimetypes(); List<String> getAllowedMimetypes();
/** /**
* @return the list of blocker attachment mimetype. A joker (@code '*') can be used to match any media (e.g., * @param documentReference the reference of a document
* "image/png", "text/*") * @return the list of allowed attachment mimetypes of the provided document. A joker (@code '*') can be used to
* match any media (e.g., * "image/png", "text/*")
* @since 14.10.2
* @since 15.0RC1
*/
List<String> getAllowedMimetypes(DocumentReference documentReference);
/**
* @return the list of blocker attachment mimetype of the current document. A joker (@code '*') can be used to match
* any media (e.g., "image/png", "text/*")
*/ */
List<String> getBlockerMimetypes(); List<String> getBlockerMimetypes();
/**
* @param documentReference the reference of a document
* @return the list of blocker attachment mimetypes of the provided document. A joker (@code '*') can be used to
* match any media (e.g., * "image/png", "text/*")
* @since 14.10.2
* @since 15.0RC1
*/
List<String> getBlockerMimetypes(DocumentReference documentReference);
} }
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
import org.xwiki.component.annotation.Component; import org.xwiki.component.annotation.Component;
import org.xwiki.component.manager.ComponentLookupException; import org.xwiki.component.manager.ComponentLookupException;
import org.xwiki.component.manager.ComponentManager; import org.xwiki.component.manager.ComponentManager;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.script.service.ScriptService; import org.xwiki.script.service.ScriptService;
import org.xwiki.stability.Unstable; import org.xwiki.stability.Unstable;
...@@ -54,8 +55,8 @@ public class AttachmentValidationScriptService implements ScriptService ...@@ -54,8 +55,8 @@ public class AttachmentValidationScriptService implements ScriptService
private Logger logger; private Logger logger;
/** /**
* @return the list of allowed attachment mimetypes. A joker (@code '*') can be used to match any media (e.g., * @return the list of allowed attachment mimetypes of the current document. A joker (@code '*') can be used to
* "image/png", "text/*") * match any media (e.g., "image/png", "text/*")
*/ */
public List<String> getAllowedMimetypes() public List<String> getAllowedMimetypes()
{ {
...@@ -65,8 +66,23 @@ public List<String> getAllowedMimetypes() ...@@ -65,8 +66,23 @@ public List<String> getAllowedMimetypes()
} }
/** /**
* @return the list of blocker attachment mimetype. A joker (@code '*') can be used to match any media (e.g., * @param documentReference the reference of a document
* "image/png", "text/*") * @return the list of allowed attachment mimetypes of the provided document. A joker (@code '*') can be used to
* match any media (e.g., "image/png", "text/*")
* @since 14.10.2
* @since 15.0RC1
*/
public List<String> getAllowedMimetypes(DocumentReference documentReference)
{
return getAttachmentValidationConfiguration()
.map(attachmentValidationConfiguration -> attachmentValidationConfiguration.getAllowedMimetypes(
documentReference))
.orElse(List.of());
}
/**
* @return the list of blocker attachment mimetype of the current document. A joker (@code '*') can be used to match
* any media (e.g., "image/png", "text/*")
*/ */
public List<String> getBlockerMimetypes() public List<String> getBlockerMimetypes()
{ {
...@@ -75,6 +91,21 @@ public List<String> getBlockerMimetypes() ...@@ -75,6 +91,21 @@ public List<String> getBlockerMimetypes()
.orElse(List.of()); .orElse(List.of());
} }
/**
* @param documentReference the reference of a document
* @return the list of blocker attachment mimetypes of the provided document. A joker (@code '*') can be used to
* match any media (e.g., "image/png", "text/*")
* @since 14.10.2
* @since 15.0RC1
*/
public List<String> getBlockerMimetypes(DocumentReference documentReference)
{
return getAttachmentValidationConfiguration()
.map(attachmentValidationConfiguration -> attachmentValidationConfiguration.getBlockerMimetypes(
documentReference))
.orElse(List.of());
}
private Optional<AttachmentValidationConfiguration> getAttachmentValidationConfiguration() private Optional<AttachmentValidationConfiguration> getAttachmentValidationConfiguration()
{ {
try { try {
......
...@@ -21,15 +21,24 @@ ...@@ -21,15 +21,24 @@
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.function.Supplier;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.slf4j.Logger;
import org.xwiki.attachment.validation.AttachmentValidationConfiguration; import org.xwiki.attachment.validation.AttachmentValidationConfiguration;
import org.xwiki.component.annotation.Component; import org.xwiki.component.annotation.Component;
import org.xwiki.configuration.ConfigurationSource; import org.xwiki.configuration.ConfigurationSource;
import org.xwiki.model.reference.DocumentReference;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.doc.XWikiDocument;
import static org.apache.commons.lang3.exception.ExceptionUtils.getRootCauseMessage;
import static org.xwiki.attachment.validation.internal.AttachmentMimetypeRestrictionClassDocumentInitializer.ALLOWED_MIMETYPES_FIELD; import static org.xwiki.attachment.validation.internal.AttachmentMimetypeRestrictionClassDocumentInitializer.ALLOWED_MIMETYPES_FIELD;
import static org.xwiki.attachment.validation.internal.AttachmentMimetypeRestrictionClassDocumentInitializer.BLOCKED_MIMETYPES_FIELD; import static org.xwiki.attachment.validation.internal.AttachmentMimetypeRestrictionClassDocumentInitializer.BLOCKED_MIMETYPES_FIELD;
...@@ -64,18 +73,36 @@ public class DefaultAttachmentValidationConfiguration implements AttachmentValid ...@@ -64,18 +73,36 @@ public class DefaultAttachmentValidationConfiguration implements AttachmentValid
@Named("xwikiproperties") @Named("xwikiproperties")
private ConfigurationSource xWikiPropertiesConfigurationSource; private ConfigurationSource xWikiPropertiesConfigurationSource;
@Inject
private Provider<XWikiContext> contextProvider;
@Inject
private Logger logger;
@Override @Override
public List<String> getAllowedMimetypes() public List<String> getAllowedMimetypes()
{ {
return getPropertyWithFallback(ALLOWED_MIMETYPES_FIELD, ATTACHMENT_MIMETYPE_ALLOW_LIST_PROPERTY); return getPropertyWithFallback(ALLOWED_MIMETYPES_FIELD, ATTACHMENT_MIMETYPE_ALLOW_LIST_PROPERTY);
} }
@Override
public List<String> getAllowedMimetypes(DocumentReference documentReference)
{
return wrapWithScope(documentReference, this::getAllowedMimetypes);
}
@Override @Override
public List<String> getBlockerMimetypes() public List<String> getBlockerMimetypes()
{ {
return getPropertyWithFallback(BLOCKED_MIMETYPES_FIELD, ATTACHMENT_MIMETYPE_BLOCK_LIST_PROPERTY); return getPropertyWithFallback(BLOCKED_MIMETYPES_FIELD, ATTACHMENT_MIMETYPE_BLOCK_LIST_PROPERTY);
} }
@Override
public List<String> getBlockerMimetypes(DocumentReference documentReference)
{
return wrapWithScope(documentReference, this::getBlockerMimetypes);
}
private List<String> getPropertyWithFallback(String attachmentXObjectProperty, String xWikiPropertiesProperty) private List<String> getPropertyWithFallback(String attachmentXObjectProperty, String xWikiPropertiesProperty)
{ {
return get(this.attachmentConfigurationSource, attachmentXObjectProperty) return get(this.attachmentConfigurationSource, attachmentXObjectProperty)
...@@ -99,4 +126,19 @@ private Optional<List<String>> get(ConfigurationSource attachmentConfigurationSo ...@@ -99,4 +126,19 @@ private Optional<List<String>> get(ConfigurationSource attachmentConfigurationSo
} }
return result; return result;
} }
private List<String> wrapWithScope(DocumentReference documentReference, Supplier<List<String>> supplier)
{
XWikiContext xWikiContext = this.contextProvider.get();
XWikiDocument oldDoc = xWikiContext.getDoc();
try {
xWikiContext.setDoc(xWikiContext.getWiki().getDocument(documentReference, xWikiContext));
return supplier.get();
} catch (XWikiException e) {
this.logger.warn("Failed to get document [{}]. Cause: [{}]", documentReference, getRootCauseMessage(e));
return List.of();
} finally {
xWikiContext.setDoc(oldDoc);
}
}
} }
...@@ -36,7 +36,12 @@ ...@@ -36,7 +36,12 @@
<minorEdit>false</minorEdit> <minorEdit>false</minorEdit>
<syntaxId>xwiki/2.1</syntaxId> <syntaxId>xwiki/2.1</syntaxId>
<hidden>true</hidden> <hidden>true</hidden>
<content/> <content>{{velocity wiki='false'}}
#set ($spaceReference = $services.model.resolveDocument($!request.documentReference).extractReference('SPACE'))
$jsontool.serialize({
"maxFileSize": $xwiki.getSpacePreferenceFor('upload_maxsize', $spaceReference)
})
{{/velocity}}</content>
<object> <object>
<name>XWiki.Attachment.Validation.Code.FileSizeValidation</name> <name>XWiki.Attachment.Validation.Code.FileSizeValidation</name>
<number>0</number> <number>0</number>
...@@ -146,9 +151,30 @@ ...@@ -146,9 +151,30 @@
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))}${sizes[i]}` return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))}${sizes[i]}`
} }
const configCache = {};
$(document).on('xwiki:actions:beforeUpload', (event, data) =&gt; { $(document).on('xwiki:actions:beforeUpload', (event, data) =&gt; {
const configElement = document.getElementById('attachment-validation-filesize-configuration'); let jsonString;
const config = JSON.parse(configElement.textContent); if (data.documentReference) {
if (!configCache[data.documentReference]) {
$.ajax({
url: new XWiki.Document(XWiki.Model.resolve('XWiki.Attachment.Validation.Code.FileSizeValidation',
XWiki.EntityType.DOCUMENT)).getURL('get', $.param({
outputSyntax: 'plain',
documentReference: data.documentReference
})),
async: false,
method: 'GET',
success: function (getData) {
configCache[data.documentReference] = getData;
}
})
}
jsonString = configCache[data.documentReference];
} else {
jsonString = document.getElementById('attachment-validation-filesize-configuration').textContent;
}
const config = JSON.parse(jsonString);
const maxFileSize = config.maxFileSize; const maxFileSize = config.maxFileSize;
const tooLarge = data.file.size &gt; maxFileSize; const tooLarge = data.file.size &gt; maxFileSize;
if (tooLarge) { if (tooLarge) {
......
...@@ -36,7 +36,12 @@ ...@@ -36,7 +36,12 @@
<minorEdit>false</minorEdit> <minorEdit>false</minorEdit>
<syntaxId>xwiki/2.1</syntaxId> <syntaxId>xwiki/2.1</syntaxId>
<hidden>true</hidden> <hidden>true</hidden>
<content/> <content>{{velocity wiki='false'}}
$jsontool.serialize({
"allowedMimetypes": $services.attachmentValidation.getAllowedMimetypes($!request.documentReference),
"blockerMimetypes": $services.attachmentValidation.getBlockerMimetypes($!request.documentReference)
})
{{/velocity}}</content>
<object> <object>
<name>XWiki.Attachment.Validation.Code.MimetypeValidation</name> <name>XWiki.Attachment.Validation.Code.MimetypeValidation</name>
<number>0</number> <number>0</number>
...@@ -147,9 +152,30 @@ ...@@ -147,9 +152,30 @@
}); });
} }
const configCache = {};
$(document).on('xwiki:actions:beforeUpload', function (event, data) { $(document).on('xwiki:actions:beforeUpload', function (event, data) {
const configElement = document.getElementById('attachment-validation-mimetypes-configuration'); let jsonString;
const config = JSON.parse(configElement.textContent); if (data.documentReference) {
if (!configCache[data.documentReference]) {
$.ajax({
url: new XWiki.Document(XWiki.Model.resolve('XWiki.Attachment.Validation.Code.MimetypeValidation',
XWiki.EntityType.DOCUMENT)).getURL('get', $.param({
outputSyntax: 'plain',
documentReference: data.documentReference
})),
async: false,
method: 'GET',
success: function (getData) {
configCache[data.documentReference] = getData;
}
})
}
jsonString = configCache[data.documentReference];
} else {
jsonString = document.getElementById('attachment-validation-mimetypes-configuration').textContent;
}
const config = JSON.parse(jsonString);
const mimeType = data.file.type.toLowerCase(); const mimeType = data.file.type.toLowerCase();
const allowedMimetypes = config.allowedMimetypes; const allowedMimetypes = config.allowedMimetypes;
const blockerMimetypes = config.blockerMimetypes; const blockerMimetypes = config.blockerMimetypes;
......
...@@ -233,6 +233,9 @@ define('imageSelector', ['jquery', 'modal', 'resource', 'l10n!imageSelector'], ...@@ -233,6 +233,9 @@ define('imageSelector', ['jquery', 'modal', 'resource', 'l10n!imageSelector'],
return { return {
open: createModal, open: createModal,
updateSelectedImageReferences: updateSelectedImageReferences, updateSelectedImageReferences: updateSelectedImageReferences,
createLoader: createLoader createLoader: createLoader,
getDocumentReference: function (element) {
return getDocumentReference(element.parents('.image-selector-modal').data('input'));
}
}; };
}); });
...@@ -145,7 +145,10 @@ ...@@ -145,7 +145,10 @@
const uploadedFile = element.find("input[type='file']").prop('files')[0]; const uploadedFile = element.find("input[type='file']").prop('files')[0];
const beforeUploadEvent = $.Event("xwiki:actions:beforeUpload"); const beforeUploadEvent = $.Event("xwiki:actions:beforeUpload");
$(document).trigger(beforeUploadEvent, {file: uploadedFile}); $(document).trigger(beforeUploadEvent, {
file: uploadedFile,
documentReference: imageSelector.getDocumentReference(element)
});
if (!beforeUploadEvent.isDefaultPrevented()) { if (!beforeUploadEvent.isDefaultPrevented()) {
element.addClass('loading'); element.addClass('loading');
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment