/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see .
* #L%
*/
package org.alfresco.repo.webdav;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.Serializable;
import java.util.Collections;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.GUID;
import org.alfresco.util.TestWithUserUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
/**
* Tests for the {@link LockMethod} class.
*
* @author pavel.yurkevich
*/
public class LockMethodTest
{
private LockMethod lockMethod;
private PropFindMethod propFindMethod;
private ApplicationContext applicationContext = ApplicationContextHelper.getApplicationContext(new String[]
{
"classpath:alfresco/application-context.xml", "classpath:alfresco/web-scripts-application-context.xml",
"classpath:alfresco/remote-api-context.xml"
});
/**
* Services used by the tests
*/
private WebDAVHelper davHelper;
private TransactionService transactionService;
private AuthenticationComponent authenticationComponent;
private NodeService nodeService;
private MutableAuthenticationService authenticationService;
private PermissionService permissionService;
private ContentService contentService;
private FileFolderService fileFolderService;
/**
* Types and properties used by the tests
*/
private static final String CONTENT_1 = "This is some content";
private static final String TEST_FILE_NAME = "file" + GUID.generate();
private static final String TEST_NEW_FILE_NAME = "new_file" + GUID.generate();
private static final String TEST_FOLDER_NAME = "folder" + GUID.generate();
private static final String TEST_NEW_FOLDER_NAME = "new_folder" + GUID.generate();
/**
* Data used by the tests
*/
private NodeRef folderNodeRef;
private NodeRef fileNodeRef;
/**
* User details
*/
private String userName;
private static final String PWD = "password";
@Before
public void setUp() throws Exception
{
lockMethod = new LockMethod();
propFindMethod = new PropFindMethod();
this.transactionService = (TransactionService) applicationContext.getBean("TransactionService");
this.davHelper = (WebDAVHelper) applicationContext.getBean("webDAVHelper");
this.authenticationComponent = (AuthenticationComponent) applicationContext.getBean("authenticationComponent");
this.nodeService = (NodeService) applicationContext.getBean("NodeService");
this.authenticationService = (MutableAuthenticationService) applicationContext.getBean("authenticationService");
this.permissionService = (PermissionService) applicationContext.getBean("permissionService");
this.contentService = (ContentService) applicationContext.getBean("contentService");
this.fileFolderService = (FileFolderService) applicationContext.getBean("fileFolderService");
this.authenticationComponent.setSystemUserAsCurrentUser();
RetryingTransactionCallback createTestFileCallback = new RetryingTransactionCallback()
{
@Override
public Void execute() throws Throwable
{
NodeRef rootNodeRef = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
// Create and authenticate the user
userName = "webdavLockTest" + GUID.generate();
TestWithUserUtils.createUser(userName, PWD, rootNodeRef, nodeService, authenticationService);
permissionService.setPermission(rootNodeRef, userName, PermissionService.ALL_PERMISSIONS, true);
TestWithUserUtils.authenticateUser(userName, PWD, rootNodeRef, authenticationService);
// create test file in test folder
folderNodeRef = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("test"), ContentModel.TYPE_FOLDER,
Collections. singletonMap(ContentModel.PROP_NAME, TEST_FOLDER_NAME)).getChildRef();
fileNodeRef = nodeService.createNode(folderNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("test"), ContentModel.TYPE_CONTENT,
Collections. singletonMap(ContentModel.PROP_NAME, TEST_FILE_NAME)).getChildRef();
ContentWriter contentWriter = contentService.getWriter(fileNodeRef, ContentModel.PROP_CONTENT, true);
contentWriter.setMimetype("text/plain");
contentWriter.setEncoding("UTF-8");
contentWriter.putContent(CONTENT_1);
return null;
}
};
this.transactionService.getRetryingTransactionHelper().doInTransaction(createTestFileCallback);
}
@After
public void tearDown()
{
lockMethod = null;
propFindMethod = null;
// clear context for current user
this.authenticationComponent.clearCurrentSecurityContext();
// delete test store as system user
this.authenticationComponent.setSystemUserAsCurrentUser();
RetryingTransactionCallback deleteTestFolderCallback = new RetryingTransactionCallback()
{
@Override
public Void execute() throws Throwable
{
if (nodeService.exists(folderNodeRef))
{
nodeService.deleteNode(folderNodeRef);
}
return null;
}
};
this.transactionService.getRetryingTransactionHelper().doInTransaction(deleteTestFolderCallback);
}
@Test
public void testRefreshLock() throws Exception
{
MockHttpServletRequest lockRequest = new MockHttpServletRequest();
lockRequest.addHeader(WebDAV.HEADER_TIMEOUT, WebDAV.SECOND + 5);
lockRequest.setRequestURI("/" + TEST_FILE_NAME);
String content = "" +
"" +
"" + userName + "";
lockRequest.setContent(content.getBytes("UTF-8"));
lockMethod.setDetails(lockRequest, new MockHttpServletResponse(), davHelper, folderNodeRef);
lockMethod.parseRequestHeaders();
lockMethod.parseRequestBody();
RetryingTransactionCallback lockExecuteImplCallBack = new RetryingTransactionCallback()
{
@Override
public Void execute() throws Throwable
{
lockMethod.executeImpl();
return null;
}
};
// lock node for 5 seconds
this.transactionService.getRetryingTransactionHelper().doInTransaction(lockExecuteImplCallBack);
RetryingTransactionCallback getNodeLockInfoCallBack = new RetryingTransactionCallback()
{
@Override
public LockInfo execute() throws Throwable
{
return lockMethod.getNodeLockInfo(fileFolderService.getFileInfo(fileNodeRef));
}
};
// get lock info
LockInfo lockInfo = this.transactionService.getRetryingTransactionHelper().doInTransaction(getNodeLockInfoCallBack);
assertNotNull(lockInfo);
// wait for 1 second
Thread.sleep(1000);
String lockToken = fileNodeRef.getId() + WebDAV.LOCK_TOKEN_SEPERATOR + this.userName;
String lockHeaderValue = "(<" + WebDAV.OPAQUE_LOCK_TOKEN + lockToken + ">)";
lockRequest.addHeader(WebDAV.HEADER_IF, lockHeaderValue);
lockMethod.parseRequestHeaders();
// update lock for another 5 seconds
this.transactionService.getRetryingTransactionHelper().doInTransaction(lockExecuteImplCallBack);
// get updated lock info
LockInfo updatedLockInfo = this.transactionService.getRetryingTransactionHelper().doInTransaction(getNodeLockInfoCallBack);
assertNotNull(updatedLockInfo);
assertEquals("Lock owner should not change after lock refresh.", lockInfo.getOwner(), updatedLockInfo.getOwner());
assertEquals("Lock token should not change after lock refresh.", lockInfo.getExclusiveLockToken(), updatedLockInfo.getExclusiveLockToken());
assertFalse("Expires was not updated.", lockInfo.getExpires().equals(updatedLockInfo.getExpires()));
assertTrue("Expires was updated incorrectly.", lockInfo.getExpires().before(updatedLockInfo.getExpires()));
// prepare propfind method
MockHttpServletRequest propFindRequest = new MockHttpServletRequest();
propFindRequest.setRequestURI("/" + TEST_FILE_NAME);
propFindRequest.addHeader(WebDAV.HEADER_DEPTH, "1");
content = "" +
"";
propFindRequest.setContent(content.getBytes("UTF-8"));
MockHttpServletResponse propfindResponse = new MockHttpServletResponse();
propFindMethod.setDetails(propFindRequest, propfindResponse, davHelper, folderNodeRef);
propFindMethod.parseRequestHeaders();
propFindMethod.parseRequestBody();
RetryingTransactionCallback propfindExecuteImplCallBack = new RetryingTransactionCallback()
{
@Override
public Void execute() throws Throwable
{
propFindMethod.executeImpl();
return null;
}
};
// make propfind method call for locked resource
this.transactionService.getRetryingTransactionHelper().doInTransaction(propfindExecuteImplCallBack);
String response = propfindResponse.getContentAsString();
assertFalse("Propfind response should contain lock informarion.", response.indexOf("lockdiscovery") == -1);
// wait for lock expiration
Thread.sleep(6000);
propfindResponse = new MockHttpServletResponse();
propFindMethod.setDetails(propFindRequest, propfindResponse, davHelper, folderNodeRef);
// make another propfind call on resource with expired lock
this.transactionService.getRetryingTransactionHelper().doInTransaction(propfindExecuteImplCallBack);
response = propfindResponse.getContentAsString();
// check that lock information is not there for expired lock
assertTrue("Propfind response should not conatain information about expired lock", response.indexOf("lockdiscovery") == -1);
}
@Test
public void testMNT_10873() throws Exception
{
String fileName = TEST_FILE_NAME + GUID.generate();
final MockHttpServletRequest lockRequest = new MockHttpServletRequest();
MockHttpServletResponse lockResponse = new MockHttpServletResponse();
lockRequest.addHeader(WebDAV.HEADER_TIMEOUT, WebDAV.SECOND + 5);
// set request uri to point to non-existent file
lockRequest.setRequestURI("/" + fileName);
String content = "" +
"" +
"" + userName + "";
lockRequest.setContent(content.getBytes("UTF-8"));
lockMethod.setDetails(lockRequest, lockResponse, davHelper, folderNodeRef);
lockMethod.parseRequestHeaders();
lockMethod.parseRequestBody();
RetryingTransactionCallback lockExecuteImplCallBack = new RetryingTransactionCallback()
{
@Override
public Void execute() throws Throwable
{
lockMethod.executeImpl();
return null;
}
};
// lock node for 5 seconds
this.transactionService.getRetryingTransactionHelper().doInTransaction(lockExecuteImplCallBack);
assertEquals("Unexpected response status code.", HttpServletResponse.SC_CREATED, lockResponse.getStatus());
RetryingTransactionCallback getNodeRefCallback = new RetryingTransactionCallback()
{
@Override
public NodeRef execute() throws Throwable
{
return lockMethod.getNodeForPath(folderNodeRef, lockRequest.getRequestURI()).getNodeRef();
}
};
NodeRef nodeRef = this.transactionService.getRetryingTransactionHelper().doInTransaction(getNodeRefCallback);
assertTrue("NodeRef should exists.", nodeService.exists(nodeRef));
assertTrue("sys:webdavNoContent aspect should be applied on node.", nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WEBDAV_NO_CONTENT));
// sleep for 6 seconds to ensure that timer was triggered
Thread.sleep(6000);
assertFalse("File should note exist in repo any more.", nodeService.exists(nodeRef));
assertFalse("File should note exist in trashcan.", nodeService.exists(new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, nodeRef.getId())));
}
@Test
public void testMNT_11990() throws Exception
{
MockHttpServletRequest lockRequest = new MockHttpServletRequest();
lockRequest.addHeader(WebDAV.HEADER_TIMEOUT, WebDAV.SECOND + 3600);
lockRequest.addHeader(WebDAV.HEADER_IF, "(<" + WebDAV.makeLockToken(fileNodeRef, userName) + ">)");
lockRequest.setRequestURI("/" + TEST_FILE_NAME);
lockMethod.setDetails(lockRequest, new MockHttpServletResponse(), davHelper, folderNodeRef);
lockMethod.parseRequestHeaders();
lockMethod.parseRequestBody();
RetryingTransactionCallback lockExecuteImplCallBack = new RetryingTransactionCallback()
{
@Override
public Void execute() throws Throwable
{
try
{
lockMethod.executeImpl();
fail("Lock should not be refreshed for non-locked file.");
}
catch (WebDAVServerException e)
{
assertEquals(e.getHttpStatusCode(), HttpServletResponse.SC_BAD_REQUEST);
}
return null;
}
};
// try to refresh lock for non-locked node
this.transactionService.getRetryingTransactionHelper().doInTransaction(lockExecuteImplCallBack);
}
@Test
public void testMNT_12425() throws Exception
{
MockHttpServletRequest lockRequest = new MockHttpServletRequest();
MockHttpServletResponse lockResponse = new MockHttpServletResponse();
// refresh lock set to 1 hour
lockRequest.addHeader(WebDAV.HEADER_TIMEOUT, WebDAV.SECOND + 3600);
lockRequest.addHeader(WebDAV.HEADER_IF, "(<" + WebDAV.makeLockToken(fileNodeRef, userName) + ">)");
// specify path to non-existing file
lockRequest.setRequestURI("/" + TEST_NEW_FOLDER_NAME + "/" + TEST_NEW_FILE_NAME);
lockMethod.setDetails(lockRequest, lockResponse, davHelper, folderNodeRef);
lockMethod.parseRequestHeaders();
RetryingTransactionCallback lockExecuteImplCallBack = new RetryingTransactionCallback()
{
@Override
public Void execute() throws Throwable
{
try
{
lockMethod.executeImpl();
fail("Refresh lock for non-exisitent resource should fail.");
}
catch (WebDAVServerException e)
{
assertEquals(HttpServletResponse.SC_FORBIDDEN, e.getHttpStatusCode());
}
return null;
}
};
// try to lock non-existent file
this.transactionService.getRetryingTransactionHelper().doInTransaction(lockExecuteImplCallBack);
}
}