Merged HEAD-BUG-FIX (4.3/Cloud) to HEAD (4.3/Cloud)

61350: Merged V4.2-BUG-FIX (4.2.2) to HEAD-BUG-FIX (Cloud/4.3)
      61114: Merged V4.1-BUG-FIX (4.1.8) to V4.2-BUG-FIX (4.2.2)
         61011: Merged DEV to V4.1-BUG-FIX (4.1.8)
            60967: MNT-10610: FilenameFilteringInterceptor.java readability
            MNT-10561 : If a folder is renamed with 8 characters that are within A-F and 0-9, the sys:temporary aspect is applied to it
            Implemented a JUnit test to simulate the issue.
            Added a check for a node to be without content in FilenameFilteringInterceptor.isNameOfTmporaryObject(String, NodeRef).
            Fixed code and JavaDoc formatting.


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@62410 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Alan Davis
2014-02-12 15:10:29 +00:00
parent 4faaf92018
commit 2a9600f26c
2 changed files with 181 additions and 147 deletions

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2005-2012 Alfresco Software Limited. * Copyright (C) 2005-2014 Alfresco Software Limited.
* *
* This file is part of Alfresco * This file is part of Alfresco
* *
@@ -46,6 +46,7 @@ import org.apache.commons.logging.LogFactory;
* An interceptor that intercepts FileFolderService methods, ensuring system, temporary and hidden files * An interceptor that intercepts FileFolderService methods, ensuring system, temporary and hidden files
* and paths are marked with the correct aspects. * and paths are marked with the correct aspects.
* *
* @author alex.mukha
*/ */
public class FilenameFilteringInterceptor implements MethodInterceptor public class FilenameFilteringInterceptor implements MethodInterceptor
{ {
@@ -219,158 +220,161 @@ public class FilenameFilteringInterceptor implements MethodInterceptor
public Object invoke(final MethodInvocation invocation) throws Throwable public Object invoke(final MethodInvocation invocation) throws Throwable
{ {
if (!enabled)
{
return invocation.proceed();
}
// execute and get the result // execute and get the result
String methodName = invocation.getMethod().getName(); String methodName = invocation.getMethod().getName();
Object ret = null; Object ret = null;
if(enabled) // do the invocation
if (methodName.startsWith("create"))
{ {
// do the invocation NodeRef nodeRef = (NodeRef)invocation.getArguments()[0];
if (methodName.startsWith("create")) String filename = (String)invocation.getArguments()[1];
if(getMode() == Mode.ENHANCED)
{ {
NodeRef nodeRef = (NodeRef)invocation.getArguments()[0]; if(systemPaths.isFiltered(filename))
String filename = (String)invocation.getArguments()[1]; {
// it's a system file/folder, create as system and allow full control to all authorities
ret = runAsSystem(invocation);
FileInfoImpl fileInfo = (FileInfoImpl)ret;
permissionService.setPermission(fileInfo.getNodeRef(), PermissionService.ALL_AUTHORITIES, PermissionService.FULL_CONTROL, true);
if(getMode() == Mode.ENHANCED) // it's always marked temporary and hidden
{ checkTemporaryAspect(true, fileInfo);
if(systemPaths.isFiltered(filename))
{
// it's a system file/folder, create as system and allow full control to all authorities
ret = runAsSystem(invocation);
FileInfoImpl fileInfo = (FileInfoImpl)ret;
permissionService.setPermission(fileInfo.getNodeRef(), PermissionService.ALL_AUTHORITIES, PermissionService.FULL_CONTROL, true);
// it's always marked temporary and hidden
checkTemporaryAspect(true, fileInfo);
hiddenAspect.hideNode(fileInfo, getSystemFileVisibilityMask(), false, false, false); hiddenAspect.hideNode(fileInfo, getSystemFileVisibilityMask(), false, false, false);
}
else
{
// it's not a temporary file/folder, create as normal
ret = invocation.proceed();
FileInfoImpl fileInfo = (FileInfoImpl)ret;
if(isSystemPath(nodeRef, filename))
{
// it's on a system path, check whether temporary, hidden and noindex aspects need to be applied
checkTemporaryAspect(true, fileInfo);
hiddenAspect.hideNode(fileInfo, getSystemFileVisibilityMask(), false, false, false);
}
else
{
// check whether it's a temporary or hidden file
FileInfo sourceInfo = (FileInfo)ret;
checkTemporaryAspect(isNameOfTmporaryObject(filename, sourceInfo.getNodeRef()), sourceInfo);
boolean isHidden = hiddenAspect.checkHidden(fileInfo, false, false);
if(isHidden && fileInfo instanceof FileInfoImpl)
{
((FileInfoImpl)fileInfo).setHidden(true);
}
}
}
} }
else else
{ {
// it's not a temporary file/folder, create as normal
ret = invocation.proceed(); ret = invocation.proceed();
FileInfoImpl fileInfo = (FileInfoImpl)ret; FileInfoImpl fileInfo = (FileInfoImpl)ret;
checkTemporaryAspect(isNameOfTmporaryObject(filename, fileInfo.getNodeRef()), fileInfo); if(isSystemPath(nodeRef, filename))
}
}
else if (methodName.startsWith("move"))
{
Object[] args = invocation.getArguments();
NodeRef sourceNodeRef = (NodeRef)args[0];
String newName = (String)args[args.length -1];
if(newName != null)
{
// Name is changing
// check against all the regular expressions
checkTemporaryAspect(isNameOfTmporaryObject(newName, sourceNodeRef), sourceNodeRef);
}
// now do the move
ret = invocation.proceed();
if(getMode() == Mode.ENHANCED)
{
hiddenAspect.checkHidden(sourceNodeRef, true, true);
}
}
else if (methodName.startsWith("copy"))
{
ret = invocation.proceed();
FileInfoImpl fileInfo = (FileInfoImpl) ret;
String filename = fileInfo.getName();
if (logger.isDebugEnabled())
{
logger.debug("Checking filename returned by " + methodName + ": " + filename);
}
// check against all the regular expressions
checkTemporaryAspect(isNameOfTmporaryObject(filename, fileInfo.getNodeRef()), fileInfo);
if(getMode() == Mode.ENHANCED)
{
boolean isHidden = hiddenAspect.checkHidden(fileInfo, true, true);
if(isHidden && fileInfo instanceof FileInfoImpl)
{ {
((FileInfoImpl)fileInfo).setHidden(true); // it's on a system path, check whether temporary, hidden and noindex aspects need to be applied
checkTemporaryAspect(true, fileInfo);
hiddenAspect.hideNode(fileInfo, getSystemFileVisibilityMask(), false, false, false);
} }
} else
/*
* TODO should these two calls be before the proceed? However its the same problem as create
* The node needs to be created before we can add aspects.
*/
}
else if (methodName.startsWith("rename"))
{
Object[] args = invocation.getArguments();
if(args != null && args.length == 2)
{
/**
* Expecting rename(NodeRef, newName)
*/
String newName = (String)args[1];
NodeRef sourceNodeRef = (NodeRef)args[0];
if (logger.isDebugEnabled())
{ {
logger.debug("Checking filename returned by " + methodName + ": " + newName); // check whether it's a temporary or hidden file
} FileInfo sourceInfo = (FileInfo)ret;
boolean isTmp = isTemporaryObject(filename, sourceInfo.getNodeRef());
// check against all the regular expressions checkTemporaryAspect(isTmp, sourceInfo);
checkTemporaryAspect(isNameOfTmporaryObject(newName, sourceNodeRef), sourceNodeRef); boolean isHidden = hiddenAspect.checkHidden(fileInfo, false, false);
if(isHidden && fileInfo instanceof FileInfoImpl)
ret = invocation.proceed();
if(getMode() == Mode.ENHANCED)
{
boolean isHidden = hiddenAspect.checkHidden(sourceNodeRef, true, true);
if(isHidden && ret instanceof FileInfoImpl)
{ {
((FileInfoImpl)ret).setHidden(true); ((FileInfoImpl)fileInfo).setHidden(true);
} }
} }
return ret;
}
else
{
/**
* expected rename(NodeRef, String) - got something else...
*/
throw new AlfrescoRuntimeException("FilenameFilteringInterceptor: unknown rename method");
} }
} }
else else
{ {
ret = invocation.proceed(); ret = invocation.proceed();
FileInfoImpl fileInfo = (FileInfoImpl)ret;
boolean isTmp = isTemporaryObject(filename, fileInfo.getNodeRef());
checkTemporaryAspect(isTmp, fileInfo);
}
}
else if (methodName.startsWith("move"))
{
Object[] args = invocation.getArguments();
NodeRef sourceNodeRef = (NodeRef)args[0];
String newName = (String)args[args.length -1];
if(newName != null)
{
// Name is changing
// check against all the regular expressions
boolean isTmp = isTemporaryObject(newName, sourceNodeRef);
checkTemporaryAspect(isTmp, sourceNodeRef);
}
// now do the move
ret = invocation.proceed();
if(getMode() == Mode.ENHANCED)
{
hiddenAspect.checkHidden(sourceNodeRef, true, true);
}
}
else if (methodName.startsWith("copy"))
{
ret = invocation.proceed();
FileInfoImpl fileInfo = (FileInfoImpl) ret;
String filename = fileInfo.getName();
if (logger.isDebugEnabled())
{
logger.debug("Checking filename returned by " + methodName + ": " + filename);
}
// check against all the regular expressions
boolean isTmp = isTemporaryObject(filename, fileInfo.getNodeRef());
checkTemporaryAspect(isTmp, fileInfo);
if(getMode() == Mode.ENHANCED)
{
boolean isHidden = hiddenAspect.checkHidden(fileInfo, true, true);
if(isHidden && fileInfo instanceof FileInfoImpl)
{
((FileInfoImpl)fileInfo).setHidden(true);
}
}
/*
* TODO should these two calls be before the proceed? However its the same problem as create
* The node needs to be created before we can add aspects.
*/
}
else if (methodName.startsWith("rename"))
{
Object[] args = invocation.getArguments();
if(args != null && args.length == 2)
{
/**
* Expecting rename(NodeRef, newName)
*/
String newName = (String)args[1];
NodeRef sourceNodeRef = (NodeRef)args[0];
if (logger.isDebugEnabled())
{
logger.debug("Checking filename returned by " + methodName + ": " + newName);
}
// check against all the regular expressions
boolean isTmp = isTemporaryObject(newName, sourceNodeRef);
checkTemporaryAspect(isTmp, sourceNodeRef);
ret = invocation.proceed();
if(getMode() == Mode.ENHANCED)
{
boolean isHidden = hiddenAspect.checkHidden(sourceNodeRef, true, true);
if(isHidden && ret instanceof FileInfoImpl)
{
((FileInfoImpl)ret).setHidden(true);
}
}
return ret;
}
else
{
/**
* expected rename(NodeRef, String) - got something else...
*/
throw new AlfrescoRuntimeException("FilenameFilteringInterceptor: unknown rename method");
} }
} }
else else
@@ -383,33 +387,45 @@ public class FilenameFilteringInterceptor implements MethodInterceptor
} }
/** /**
* Determines whether specified <code>name</code> matches any pattern of temporary file names. Also it checks special case of new XLS document creation in MacOS. See <a * Determines whether specified <code>name</code> matches any pattern of temporary file names.
* href="https://issues.alfresco.com/jira/browse/ALF-14078">ALF-14078</a> (comment added on 04-September-12 04:11 PM) for more details * <br>Also it checks special case of new XLS document creation in MacOS:
* <ul>
* <li>its name doesnt have extension (on MacOS, but it has .tmp extension on Windows);
* <li>length of its name equals to 8;
* <li>its name contains only hexadecimal digits (0-9, A-F);
* <li>it has Mimetype equal to application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.
* </ul>
* *
* @param name - {@link String} value which contains name of node * @param name - {@link String} value which contains name of node
* @param nodeRef - {@link NodeRef} instance of the node * @param nodeRef - {@link NodeRef} instance of the node
* @return {@link Boolean} value. <code>true</code> if <code>name</code> is name of temporary object including special case of XLSX in MacOS. <code>false</code> in other case * @return {@link boolean} value. <code>true</code> if <code>name</code> is name of temporary object.
*/ */
private boolean isNameOfTmporaryObject(String name, NodeRef nodeRef) private boolean isTemporaryObject(String name, NodeRef nodeRef)
{ {
boolean result = temporaryFiles.isFiltered(name); boolean isFiltered = temporaryFiles.isFiltered(name);
if (isFiltered)
if (!result)
{ {
// This pattern must be validated in conjunction with mimetype validation only! return true;
result = XSL_MACOS_TEMPORARY_FILENAME_FITLER.matcher(name).matches();
if (result && !name.startsWith(MACOS_TEMPORARY_FILE_NAME_PREFIX))
{
ContentReader contentReader = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT);
if (null != contentReader)
{
result = XLSX_MIMETYPE.equals(contentReader.getMimetype());
}
}
} }
// This pattern must be validated in conjunction with mimetype validation only!
boolean result = XSL_MACOS_TEMPORARY_FILENAME_FITLER.matcher(name).matches();
if (result && !name.startsWith(MACOS_TEMPORARY_FILE_NAME_PREFIX))
{
ContentReader contentReader = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT);
if (null != contentReader)
{
result = XLSX_MIMETYPE.equals(contentReader.getMimetype());
}
else
{
// MNT-10561
// We are unable to determine the mimetype so assume it's NOT temporary
result = false;
}
}
return result; return result;
} }
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2005-2012 Alfresco Software Limited. * Copyright (C) 2005-2014 Alfresco Software Limited.
* *
* This file is part of Alfresco * This file is part of Alfresco
* *
@@ -414,6 +414,24 @@ public class FileFolderServiceImplTest extends TestCase
assertNotNull("Folder info for new name is not present", checkInfo); assertNotNull("Folder info for new name is not present", checkInfo);
} }
/**
* Test for MNT-10561. Renames a folder to a name with the pattern "^[0-9,a-f]{8}$"
*/
public void testRenamePattern() throws Exception
{
FileInfo folderInfo = getByName(NAME_L0_FOLDER_A, true);
assertNotNull(folderInfo);
// rename normal
String newName = "abcd1234";
folderInfo = fileFolderService.rename(folderInfo.getNodeRef(), newName);
// check it
FileInfo checkInfo = getByName(NAME_L0_FOLDER_A, true);
assertNull("Folder info should have been renamed away", checkInfo);
checkInfo = getByName(newName, true);
assertNotNull("Folder info for new name is not present", checkInfo);
assertFalse(nodeService.getAspects(checkInfo.getNodeRef()).contains(ContentModel.ASPECT_TEMPORARY));
}
public void testRenameWithoutAssocQNameChange() throws Exception public void testRenameWithoutAssocQNameChange() throws Exception
{ {
FileInfo folderInfo = getByName(NAME_L0_FOLDER_A, true); FileInfo folderInfo = getByName(NAME_L0_FOLDER_A, true);