mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-06-30 18:15:39 +00:00
40164: Merged V4.0.2 to V4.1-BUG-FIX (4.1.1) - Partial merge of r39519 on V4.1 which included something from V4.1-BUG-FIX 39494: ALF-15170: Can't change folder permissions in Private or Public-moderated sites - Fix by Dmitry V 38899: ALF-15005: Merged V4.0-BUG-FIX to PATCHES/V4.0.2 37920: ALF-13816: Permission Denied on web-client browsing if parent does not inherit permissions - FileFolderService getNamePath() now performs toFileInfo() as SystemUser. 40167: Merged V4.1 (4.1.0) to V4.1-BUG-FIX (4.1.1) 40156: Adds defensive code to avoid situations where the parent can't be found (e.g. RM-474) 40144: ALF-11027: update URLs to deploy to our internal-snapshot Maven repo, so that GoogleDocs integration can take advantage of it 40121: Chinese: Translation update based on EN r40086 40120: Dutch: Translation update based on EN r40086 40119: Japanese: Translation update based on EN r40086 40118: Italian: Translation update based on EN r40086 40117: French: Translation update based on EN r40086 40116: Spanish: Translation update based on EN r40086 40115: German: Translation update based on EN r40086 40103: Unit test fix following removal of AVM file context. 40076: Fixes encoding error introduced in r40072. 40072: Error message string updates, based on feedback from Docs. 40069: ALF-15364 CloudSync: folder sync with lock on-premise - pulled new file is not auto-locked 40068: Java doc corrections. 40067: CloudSync: ALF-15287 - folder sync to a clashing name hierarchy causes file to be indirectly synced to the wrong place - was "An unsynced file which was synced indirectly will be synced in the documentLibrary" - plus unit test 40065: Removal of potential NullPointerException (not observed). 40063: Chinese: Translation updates from Gloria, based on EN r40019 40062: Dutch: Translation updates from Gloria, based on EN r40019 40061: Italian: Translation updates from Gloria, based on EN r40019 40060: French: Translation updates from Gloria, based on EN r40019 40059: Japanese: Translation updates from Gloria, based on EN r40019 40058: Spanish: Translation updates from Gloria, based on EN r40019 40057: German: Translation updates from Gloria, based on EN r40019 40054: CloudSync: add simple *folder* sync unit test ! 40053: ALF-15218 - AVM must not be visible by default in VFS mount points 40036: Addressing the important question raised in ALF-14950. I have decided that, as pointed out by MikeF, a sensible solution for handling secondary child-assocs that link nodes to synced folders would be to NOT sync those nodes. So I've made it such that new nodes are only added into a syncset if they are created as *primary* children of a synced folder. If they are secondary children, they are not added as members. Test case for same. 40019: Having fixed the TemporaryNodes @Rule in rev 40010 so that it handles checked-out test files gracefully, I'm removing Jan's workaround for a checked-out test file. 40017: During work for ALF-15296, I've fixed up the error handling during sync set creation. See ts doc: https://ts.alfresco.com/share/page/site/projectodin/document-details?nodeRef=workspace://SpacesStore/47bf11a3-8d30-4377-a373-1c1d3fba6018 Now Sync Set creation can fail if there are some problems with the provided direct member nodes. If there are problems with any indirect members, that will not cause the sync to fail during setup, but subsequent pushes to the Cloud will fail with appropriate error reporting. Exceptions during the creation of a syncset can now report multiple failed nodes. Rewrote the cannotCreateTwoSyncSetDefinitionsWithOverlappingLocalMembers() testcase to be creatingTwoSyncSetDefinitionsWithOverlappingLocalMembersShouldSilentlyDropTheOverlaps() Extra test cases for sync setup: syncing a list of ineligible nodes should throw an exception. Syncing a list of nodes, some of which are ineligible, should skip the ineligable ones. Syncing a folder containing already-synced child nodes and unwriteable child nodes should silently skip the already-synced, but include the unwriteable nodes as members (which will fail to sync). Various tidy-ups and comments to explain this behaviour to the next coder, which may be me. :) 40014: ALF-15218 - AVM must not be visible by default in VFS mount points 40010: Enhancement to TemporaryNodes JUnit @Rule so that it gracefully handles checked-out nodes during test code. 39996: ALF-14377: Need to decide what to do when SSD creator user is no longer valid 39986: Chinese: Translation updates from Gloria, based on EN r39671 39985: Dutch: Translation updates from Gloria, based on EN r39671 39984: Japanese: Translation updates from Gloria, based on EN r39671 39983: Italian: Translation updates from Gloria, based on EN r39671 39982: French: Translation updates from Gloria, based on EN r39671 39981: Spanish: Translation updates from Gloria, based on EN r39671 39980: German: Translation updates from Gloria, based on EN r39671 39957: CloudSync: minor - some info logging (applies to src + tgt) 39943: CloudSync: fix remainder of ALF-15130 - sync time for initial sync of working copy (+ unit test) 39942: ALF-14911 - Path shown in the version comments does not match the path shown in the sync status dialogue - first part share paths are now handled separatly 39933: ALF-14179: Merged PATCHES/V4.0.2 to V4.1 39929: ALF-15330: Merged V3.4-BUG-FIX to PATCHES/V4.0.2 39681: Fix CIFS hang on file open of MS Office document. ALF-13578, ALF-15092. Async packet queue not being cleared at end of request processing. 39924: CSS fixes for the sync panel 39918: Merged DEV to V4.1 39911: ALF-15263 : XAM: XAM Connector cannot be installed on Alfresco v4.1 39915: ALF-14908: Update Sync status dialogue to include the local root folder for indirectly synced nodes 39904: ALF-15041: Cloud Sync doesn't work with IE7 39903: ALF-15041: Cloud Sync doesn't work with IE7 39902: ALF-15041: Cloud Sync doesn't work with IE7 39883: CloudSync: fix ALF-15270 - moving directly synced node (on source) causes target node to be deleted - ... add related move file sync unit test 39876: ALF-15301: IE8 specific: It is impossible creating a new folder in a cloud target selection window/Cloud location 39864: ALF-15003 Sync failed for the file (where write access on source removed for sync owner) but no matter this file appears in Cloud 39863: ALF-15268: Cancel button disabled in the target selection window for new folder creation in Cloud 39860: ALF-14908: Update Sync status dialogue to include the local root folder for indirectly synced nodes. 39859: ALF-15249 Add a new method hasSyncSetDefintions, with unit test, and use this to skip the Sync Pull step on-premise if no SSDs have been defined on the local system 39847: ALF-15185: YUI Overlay Mozilla Scrollbar fix should be prevented in recent versions of Firefox too. 39826: ALF-14377 - Need to decide what to do when SSD creator user is no longer valid Implementation of "last gasp" unsync for the case where the sync set owner does not exist. 39820: ALF-15267: Edit online is available for locked on-premise copy and can unlock it ALF-15284: The actions "Upload New Version" and "Inline Edit" should not be available for a "synced" and "locked" node 39812: UI Bug Fixes: - ALF-15174: Uses already copied slingshot.properties file for message strings needed in both repo and Share - Cloud delete file message shouldn't refer to the cloud 39810: ALF-15221: Cloud Sync Status: Failure message when getting location is misleading 39767: CloudSync: fix ALF-15145 - Unsync of a folder (sync) that has a working copy file causes indirect icon to remain ... ... on working copy (until checkin or cancel checkout) 39748: CloudSync: fix compile error - failed manual "merge" (oops, sorry) 39747: ALF-14377 Need to decide what to do when SSD creator user is no longer valid - part 1 nodes are marked. 39735: CloudSync: ALF-14483 - multi-threaded SyncTracker - fix related to unit test failure (affecting CLOUD1 but not V4.1) - also add default (info) Sync log level 39729: Merged PATCHES/V4.0.2 to V4.1 39725: ALF-15176: Multi-threaded SOLR tracking suddenly stops leaving transactions unindexed - Debugged on environment provided by Antonio - If the last incomplete batch in a chunk consists entirely of empty transactions (or transactions consisting entirely of nodes updated in later transactions) it would loop forever 39687: Enterprise license for enterprise remote api project 39686: Replaced GPL license with enterprise licence. 39683: ALF-15162 - Unclear error message when delta syncing and the target is gone 39677: ALF-14903 Move the Sync allowed/not-allowed check, based on Network/Tenant, to AbstractCloudSyncAbstractWebScript, and have this used by the webscripts based on this too. Then, allow UnSync to proceed no matter what the tenant status, and add unit tests 39673: New unit test for adding checked out nodes into a sync set. 39670: Rework to cloud key license stuff - introduces a ValidLicenseEvent 39664: CloudSync: ALF-14483 - SyncTracker unit tests - simple file sync test - initially run with single-threaded push/pull (will require further investigation on cloud branch) 39653: ALF-15041: Cloud Sync doesn't work with IE7 39643: ALF-15220: Indirect sync and sync failed indicators appeared together on nodes during the demo 39641: ALF-15230 Refactor the lazy container creation code, to use the new SystemNodeUtils helper 39640: ALF-15231 Restore, with refactorings / making more general, the ability to lazy create the remote credentials container if bootstrap has not created it (eg on Cloud) 39639: ALF-15238: Bitrock Installer: Readme.txt should be updated - Alfresco version is 4.0 39633: CloudSync: ALF-14483 - SyncTracker - enable sync sets to be processed in parallel - wip default of 6 threads (3 to push / 3 to pull) 39616: CloudSync: CloudSync: ALF-14483 - SyncTracker - multiple syncs (to different tgt folders) with multiple files - make sure the multi-threaded tracker is excercised via automated unit tests 39613: Refactor of test code. SyncTrackerComponentTest now uses JUnit Rules to manage temporary content and to undo mocking out of services. 39607: CloudSync: ALF-14483 - SyncTracker - enable sync sets to be pushed in parallel - wip / default 3 threads 39606: CloudSync: minor log level adjustments - eg. info vs debug (vs trace) 39598: CloudSync: ALF-14655 - update simple file sync (to test multiple sync'd files) 39597: CloudSync: tweak a couple of unit tests - add "run id" to allow them to be locally re-runnable (in case of error) 39589: ALF-15098: Folder Picker causes JS error in the document details page 39587: ALF-15222: Cloud Folder picker: Newly created folder isn't selected 39580: CloudSync: fix Unsync - to ensure we don't leave sync aspects (such as failed, which reappears if src file is re-synced) - found by MF ... thanks - unit test added 39574: Cloud lacks a guest mode, so switch to unauthenticated for the sync mode fetch (user credentials may not be available) 39566: Allow the sync mode to be fetched by guest (it need not be hidden), then fetch from Share as guest, to avoid problems when fetching the sync mode if the current user's credentials have expired (otherwise it breaks the redirect back to the login) 39561: Merged THOR1 to V4.1 (try to say that in a hurry!) 39553: ALF-15149: Prevent an infinite loop in AlfrescoSolrEventListener.RemoveNullEntriesCacheMatch.updateCache() 39550: Synced folder shouldn't be filtered in the cloud folder picker. The user should be able to see them but not select them (the synced folder should be greyed out) 39548: CloudSync: ALF-14655 - add SyncTracker unit test for simple file sync - initial re-work of SyncTrackerComponentTest ... to make it easier to add more tests (and re-use test fragments) - add simple unit test for "file sync" (F4) => sync, push, pull, unsync ... - ... also fix potential SyncService unsync issue on target (found by new test) 39547: Bug fixes (spotted during demo): - Remove the "synced with errors" filter from dashlets and doclibrary for the cloud version - Remove the last '>' symbol in the info dialogue (path for "Cloud in the Location") 39546: Merged integrations/GoogleDocs/BRANCHES/OAUTH_V4.1 to BRANCHES/V4.1: 39504: [ALF-14926][GOOGLEDOCS-23] Merge OAuth2 Credentials Store Service 39516: Add missing license to OAuth2 Credentials Classes 39517: oAuth1 Credentials Store Service 39540: Some fixes to issues identified by findbugs. 39532: Revert cloud welcome dashlet text. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@40273 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
751 lines
27 KiB
Java
751 lines
27 KiB
Java
/*
|
|
* Copyright (C) 2005-2010 Alfresco Software Limited.
|
|
*
|
|
* This file is part of Alfresco
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
package org.alfresco.filesys;
|
|
|
|
import java.io.BufferedReader;
|
|
import java.io.ByteArrayInputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.InputStreamReader;
|
|
import java.io.PrintWriter;
|
|
import java.io.Reader;
|
|
import java.io.StringWriter;
|
|
|
|
import junit.framework.TestCase;
|
|
|
|
import org.alfresco.jlan.ftp.FTPConfigSection;
|
|
import org.alfresco.jlan.server.config.ServerConfigurationAccessor;
|
|
import org.alfresco.model.ContentModel;
|
|
import org.alfresco.repo.model.Repository;
|
|
import org.alfresco.repo.security.authentication.AuthenticationComponent;
|
|
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
|
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
|
import org.alfresco.repo.usage.ContentUsageImpl;
|
|
import org.alfresco.repo.usage.UserUsageTrackingComponent;
|
|
import org.alfresco.service.cmr.repository.NodeRef;
|
|
import org.alfresco.service.cmr.repository.NodeService;
|
|
import org.alfresco.service.cmr.security.MutableAuthenticationService;
|
|
import org.alfresco.service.cmr.security.PermissionService;
|
|
import org.alfresco.service.cmr.security.PersonService;
|
|
import org.alfresco.service.transaction.TransactionService;
|
|
import org.alfresco.util.ApplicationContextHelper;
|
|
import org.alfresco.util.PropertyMap;
|
|
import org.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.LogFactory;
|
|
import org.apache.commons.net.PrintCommandListener;
|
|
import org.apache.commons.net.ftp.FTPClient;
|
|
import org.apache.commons.net.ftp.FTPFile;
|
|
import org.apache.commons.net.ftp.FTPReply;
|
|
import org.springframework.context.ApplicationContext;
|
|
|
|
|
|
/**
|
|
* End to end JUNIT test of the FTP server
|
|
*
|
|
* Uses the commons-net ftp client library to connect to the
|
|
* Alfresco FTP server.
|
|
*/
|
|
public class FTPServerTest extends TestCase
|
|
|
|
{
|
|
private static Log logger = LogFactory.getLog(FTPServerTest.class);
|
|
|
|
private ApplicationContext applicationContext;
|
|
|
|
private final String USER_ADMIN="admin";
|
|
private final String PASSWORD_ADMIN="admin";
|
|
private final String USER_ONE = "FTPServerTestOne";
|
|
private final String USER_TWO = "FTPServerTestTwo";
|
|
private final String USER_THREE = "FTPServerTestThree";
|
|
private final String PASSWORD_ONE="Password01";
|
|
private final String PASSWORD_TWO="Password02";
|
|
private final String PASSWORD_THREE="Password03";
|
|
private final String HOSTNAME="localhost";
|
|
|
|
private NodeService nodeService;
|
|
private PersonService personService;
|
|
private MutableAuthenticationService authenticationService;
|
|
private AuthenticationComponent authenticationComponent;
|
|
private TransactionService transactionService;
|
|
private Repository repositoryHelper;
|
|
private PermissionService permissionService;
|
|
private FTPConfigSection ftpConfigSection;
|
|
|
|
@Override
|
|
protected void setUp() throws Exception
|
|
{
|
|
applicationContext = ApplicationContextHelper.getApplicationContext();
|
|
|
|
nodeService = (NodeService)applicationContext.getBean("nodeService");
|
|
personService = (PersonService)applicationContext.getBean("personService");
|
|
authenticationService = (MutableAuthenticationService)applicationContext.getBean("AuthenticationService");
|
|
authenticationComponent = (AuthenticationComponent)applicationContext.getBean("authenticationComponent");
|
|
transactionService = (TransactionService)applicationContext.getBean("transactionService");
|
|
repositoryHelper = (Repository)applicationContext.getBean("repositoryHelper");
|
|
permissionService = (PermissionService)applicationContext.getBean("permissionService");
|
|
ServerConfigurationAccessor fileServerConfiguration = (ServerConfigurationAccessor)applicationContext.getBean("fileServerConfiguration");
|
|
ftpConfigSection = (FTPConfigSection) fileServerConfiguration.getConfigSection( FTPConfigSection.SectionName);
|
|
|
|
assertNotNull("nodeService is null", nodeService);
|
|
assertNotNull("reporitoryHelper is null", repositoryHelper);
|
|
assertNotNull("personService is null", personService);
|
|
assertNotNull("authenticationService is null", authenticationService);
|
|
assertNotNull("authenticationComponent is null", authenticationComponent);
|
|
|
|
authenticationComponent.setSystemUserAsCurrentUser();
|
|
|
|
final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();
|
|
|
|
RetryingTransactionCallback<Void> createUsersCB = new RetryingTransactionCallback<Void>() {
|
|
|
|
@Override
|
|
public Void execute() throws Throwable
|
|
{
|
|
createUser(USER_ONE, PASSWORD_ONE, -1);
|
|
createUser(USER_TWO, PASSWORD_TWO, -1);
|
|
createUser(USER_THREE, PASSWORD_THREE, 30);
|
|
return null;
|
|
}
|
|
};
|
|
tran.doInTransaction(createUsersCB);
|
|
|
|
RetryingTransactionCallback<Void> createTestDirCB = new RetryingTransactionCallback<Void>() {
|
|
|
|
@Override
|
|
public Void execute() throws Throwable
|
|
{
|
|
{
|
|
NodeRef userOneHome = repositoryHelper.getUserHome(personService.getPerson(USER_ONE));
|
|
permissionService.setPermission(userOneHome, USER_TWO, PermissionService.CONTRIBUTOR, true);
|
|
permissionService.setPermission(userOneHome, USER_TWO, PermissionService.WRITE, true);
|
|
}
|
|
return null;
|
|
}
|
|
};
|
|
tran.doInTransaction(createTestDirCB, false, true);
|
|
}
|
|
|
|
protected void tearDown() throws Exception
|
|
{
|
|
// UserTransaction txn = transactionService.getUserTransaction();
|
|
// assertNotNull("transaction leaked", txn);
|
|
// txn.getStatus();
|
|
// txn.rollback();
|
|
}
|
|
|
|
/**
|
|
* Simple test that connects to the inbuilt ftp server and logs on
|
|
*
|
|
* @throws Exception
|
|
*/
|
|
public void testFTPConnect() throws Exception
|
|
{
|
|
logger.debug("Start testFTPConnect");
|
|
|
|
FTPClient ftp = connectClient();
|
|
try
|
|
{
|
|
int reply = ftp.getReplyCode();
|
|
|
|
if (!FTPReply.isPositiveCompletion(reply))
|
|
{
|
|
fail("FTP server refused connection.");
|
|
}
|
|
|
|
boolean login = ftp.login(USER_ADMIN, PASSWORD_ADMIN);
|
|
assertTrue("admin login not successful", login);
|
|
}
|
|
finally
|
|
{
|
|
ftp.disconnect();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Simple negative test that connects to the inbuilt ftp server and attempts to
|
|
* log on with the wrong password.
|
|
*
|
|
* @throws Exception
|
|
*/
|
|
public void testFTPConnectNegative() throws Exception
|
|
{
|
|
logger.debug("Start testFTPConnectNegative");
|
|
|
|
FTPClient ftp = connectClient();
|
|
|
|
try
|
|
{
|
|
int reply = ftp.getReplyCode();
|
|
|
|
if (!FTPReply.isPositiveCompletion(reply))
|
|
{
|
|
fail("FTP server refused connection.");
|
|
}
|
|
|
|
boolean login = ftp.login(USER_ADMIN, "garbage");
|
|
assertFalse("admin login successful", login);
|
|
|
|
// now attempt to list the files and check that the command does not
|
|
// succeed
|
|
FTPFile[] files = ftp.listFiles();
|
|
|
|
assertNotNull(files);
|
|
assertTrue(files.length == 0);
|
|
reply = ftp.getReplyCode();
|
|
|
|
assertTrue(FTPReply.isNegativePermanent(reply));
|
|
|
|
}
|
|
finally
|
|
{
|
|
ftp.disconnect();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test CWD for FTP server
|
|
*
|
|
* @throws Exception
|
|
*/
|
|
public void testCWD() throws Exception
|
|
{
|
|
logger.debug("Start testCWD");
|
|
|
|
FTPClient ftp = connectClient();
|
|
|
|
try
|
|
{
|
|
int reply = ftp.getReplyCode();
|
|
|
|
if (!FTPReply.isPositiveCompletion(reply))
|
|
{
|
|
fail("FTP server refused connection.");
|
|
}
|
|
|
|
boolean login = ftp.login(USER_ADMIN, PASSWORD_ADMIN);
|
|
assertTrue("admin login successful", login);
|
|
|
|
FTPFile[] files = ftp.listFiles();
|
|
reply = ftp.getReplyCode();
|
|
assertTrue(FTPReply.isPositiveCompletion(reply));
|
|
|
|
// expect /Alfresco directory
|
|
// /AVM directory - avm removed
|
|
assertTrue(files.length == 1);
|
|
|
|
boolean foundAVM=false;
|
|
boolean foundAlfresco=false;
|
|
for(FTPFile file : files)
|
|
{
|
|
logger.debug("file name=" + file.getName());
|
|
assertTrue(file.isDirectory());
|
|
|
|
if(file.getName().equalsIgnoreCase("AVM"))
|
|
{
|
|
foundAVM=true;
|
|
}
|
|
if(file.getName().equalsIgnoreCase("Alfresco"))
|
|
{
|
|
foundAlfresco=true;
|
|
}
|
|
}
|
|
// AVM mount point removed
|
|
//assertTrue(foundAVM);
|
|
assertTrue(foundAlfresco);
|
|
|
|
// Change to Alfresco Dir that we know exists
|
|
reply = ftp.cwd("/Alfresco");
|
|
assertTrue(FTPReply.isPositiveCompletion(reply));
|
|
|
|
// relative path with space char
|
|
reply = ftp.cwd("Data Dictionary");
|
|
assertTrue(FTPReply.isPositiveCompletion(reply));
|
|
|
|
// non existant absolute
|
|
reply = ftp.cwd("/Garbage");
|
|
assertTrue(FTPReply.isNegativePermanent(reply));
|
|
|
|
reply = ftp.cwd("/Alfresco/User Homes");
|
|
assertTrue(FTPReply.isPositiveCompletion(reply));
|
|
|
|
// Wild card
|
|
reply = ftp.cwd("/Alfresco/User*Homes");
|
|
assertTrue("unable to change to /Alfresco User*Homes/", FTPReply.isPositiveCompletion(reply));
|
|
|
|
// // Single char pattern match
|
|
// reply = ftp.cwd("/Alfre?co");
|
|
// assertTrue("Unable to match single char /Alfre?co", FTPReply.isPositiveCompletion(reply));
|
|
|
|
// two level folder
|
|
reply = ftp.cwd("/Alfresco/Data Dictionary");
|
|
assertTrue("unable to change to /Alfresco/Data Dictionary", FTPReply.isPositiveCompletion(reply));
|
|
|
|
// go up one
|
|
reply = ftp.cwd("..");
|
|
assertTrue("unable to change to ..", FTPReply.isPositiveCompletion(reply));
|
|
|
|
reply = ftp.pwd();
|
|
ftp.getStatus();
|
|
|
|
assertTrue("unable to get status", FTPReply.isPositiveCompletion(reply));
|
|
|
|
// check we are at the correct point in the tree
|
|
reply = ftp.cwd("Data Dictionary");
|
|
assertTrue(FTPReply.isPositiveCompletion(reply));
|
|
|
|
|
|
}
|
|
finally
|
|
{
|
|
ftp.disconnect();
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Test CRUD for FTP server
|
|
*
|
|
* @throws Exception
|
|
*/
|
|
public void testCRUD() throws Exception
|
|
{
|
|
final String PATH1 = "FTPServerTest";
|
|
final String PATH2 = "Second part";
|
|
|
|
logger.debug("Start testFTPCRUD");
|
|
|
|
FTPClient ftp = connectClient();
|
|
|
|
try
|
|
{
|
|
int reply = ftp.getReplyCode();
|
|
|
|
if (!FTPReply.isPositiveCompletion(reply))
|
|
{
|
|
fail("FTP server refused connection.");
|
|
}
|
|
|
|
boolean login = ftp.login(USER_ADMIN, PASSWORD_ADMIN);
|
|
assertTrue("admin login successful", login);
|
|
|
|
reply = ftp.cwd("/Alfresco/User Homes");
|
|
assertTrue(FTPReply.isPositiveCompletion(reply));
|
|
|
|
// Delete the root directory in case it was left over from a previous test run
|
|
try
|
|
{
|
|
ftp.removeDirectory(PATH1);
|
|
}
|
|
catch (IOException e)
|
|
{
|
|
// ignore this error
|
|
}
|
|
|
|
// make root directory
|
|
ftp.makeDirectory(PATH1);
|
|
ftp.cwd(PATH1);
|
|
|
|
// make sub-directory in new directory
|
|
ftp.makeDirectory(PATH2);
|
|
ftp.cwd(PATH2);
|
|
|
|
// List the files in the new directory
|
|
FTPFile[] files = ftp.listFiles();
|
|
assertTrue("files not empty", files.length == 0);
|
|
|
|
// Create a file
|
|
String FILE1_CONTENT_1="test file 1 content";
|
|
String FILE1_NAME = "testFile1.txt";
|
|
ftp.appendFile(FILE1_NAME , new ByteArrayInputStream(FILE1_CONTENT_1.getBytes("UTF-8")));
|
|
|
|
// Get the new file
|
|
FTPFile[] files2 = ftp.listFiles();
|
|
assertTrue("files not one", files2.length == 1);
|
|
|
|
InputStream is = ftp.retrieveFileStream(FILE1_NAME);
|
|
|
|
String content = inputStreamToString(is);
|
|
assertEquals("Content is not as expected", content, FILE1_CONTENT_1);
|
|
ftp.completePendingCommand();
|
|
|
|
// Update the file contents
|
|
String FILE1_CONTENT_2="That's how it is says Pooh!";
|
|
ftp.appendFile(FILE1_NAME , new ByteArrayInputStream(FILE1_CONTENT_2.getBytes("UTF-8")));
|
|
|
|
InputStream is2 = ftp.retrieveFileStream(FILE1_NAME);
|
|
|
|
String content2 = inputStreamToString(is2);
|
|
assertEquals("Content is not as expected", FILE1_CONTENT_2, content2);
|
|
ftp.completePendingCommand();
|
|
|
|
// now delete the file we have been using.
|
|
assertTrue (ftp.deleteFile(FILE1_NAME));
|
|
|
|
// negative test - file should have gone now.
|
|
assertFalse (ftp.deleteFile(FILE1_NAME));
|
|
|
|
}
|
|
finally
|
|
{
|
|
// clean up tree if left over from previous run
|
|
|
|
ftp.disconnect();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test of obscure path names in the FTP server
|
|
*
|
|
* RFC959 states that paths are constructed thus...
|
|
* <string> ::= <char> | <char><string>
|
|
* <pathname> ::= <string>
|
|
* <char> ::= any of the 128 ASCII characters except <CR> and <LF>
|
|
*
|
|
* So we need to check how high characters and problematic are encoded
|
|
*/
|
|
public void testPathNames() throws Exception
|
|
{
|
|
|
|
logger.debug("Start testPathNames");
|
|
|
|
FTPClient ftp = connectClient();
|
|
|
|
String PATH1="testPathNames";
|
|
|
|
try
|
|
{
|
|
int reply = ftp.getReplyCode();
|
|
|
|
if (!FTPReply.isPositiveCompletion(reply))
|
|
{
|
|
fail("FTP server refused connection.");
|
|
}
|
|
|
|
boolean login = ftp.login(USER_ADMIN, PASSWORD_ADMIN);
|
|
assertTrue("admin login successful", login);
|
|
|
|
reply = ftp.cwd("/Alfresco/User*Homes");
|
|
assertTrue(FTPReply.isPositiveCompletion(reply));
|
|
|
|
// Delete the root directory in case it was left over from a previous test run
|
|
try
|
|
{
|
|
ftp.removeDirectory(PATH1);
|
|
}
|
|
catch (IOException e)
|
|
{
|
|
// ignore this error
|
|
}
|
|
|
|
// make root directory for this test
|
|
boolean success = ftp.makeDirectory(PATH1);
|
|
assertTrue("unable to make directory:" + PATH1, success);
|
|
|
|
success = ftp.changeWorkingDirectory(PATH1);
|
|
assertTrue("unable to change to working directory:" + PATH1, success);
|
|
|
|
assertTrue("with a space", ftp.makeDirectory("test space"));
|
|
assertTrue("with exclamation", ftp.makeDirectory("space!"));
|
|
assertTrue("with dollar", ftp.makeDirectory("space$"));
|
|
assertTrue("with brackets", ftp.makeDirectory("space()"));
|
|
assertTrue("with hash curley brackets", ftp.makeDirectory("space{}"));
|
|
|
|
|
|
//Pound sign U+00A3
|
|
//Yen Sign U+00A5
|
|
//Capital Omega U+03A9
|
|
|
|
assertTrue("with pound sign", ftp.makeDirectory("pound \u00A3.world"));
|
|
assertTrue("with yen sign", ftp.makeDirectory("yen \u00A5.world"));
|
|
|
|
// Test steps that do not work
|
|
// assertTrue("with omega", ftp.makeDirectory("omega \u03A9.world"));
|
|
// assertTrue("with obscure ASCII chars", ftp.makeDirectory("?/.,<>"));
|
|
}
|
|
finally
|
|
{
|
|
// clean up tree if left over from previous run
|
|
|
|
ftp.disconnect();
|
|
}
|
|
|
|
|
|
}
|
|
|
|
/**
|
|
* Create a user other than "admin" who has access to a set of files.
|
|
*
|
|
* Create a folder containing test.docx as user one
|
|
* Update that file as user two.
|
|
* Check user one can see user two's changes.
|
|
*
|
|
* @throws Exception
|
|
*/
|
|
public void testTwoUserUpdate() throws Exception
|
|
{
|
|
logger.debug("Start testFTPConnect");
|
|
|
|
final String TEST_DIR="/Alfresco/User Homes/" + USER_ONE;
|
|
|
|
final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();
|
|
|
|
FTPClient ftpOne = connectClient();
|
|
FTPClient ftpTwo = connectClient();
|
|
try
|
|
{
|
|
int reply = ftpOne.getReplyCode();
|
|
|
|
if (!FTPReply.isPositiveCompletion(reply))
|
|
{
|
|
fail("FTP server refused connection.");
|
|
}
|
|
|
|
reply = ftpTwo.getReplyCode();
|
|
|
|
if (!FTPReply.isPositiveCompletion(reply))
|
|
{
|
|
fail("FTP server refused connection.");
|
|
}
|
|
|
|
boolean login = ftpOne.login(USER_ONE, PASSWORD_ONE);
|
|
assertTrue("user one login not successful", login);
|
|
|
|
login = ftpTwo.login(USER_TWO, PASSWORD_TWO);
|
|
assertTrue("user two login not successful", login);
|
|
|
|
boolean success = ftpOne.changeWorkingDirectory("Alfresco");
|
|
assertTrue("user one unable to cd to Alfreco", success);
|
|
success = ftpOne.changeWorkingDirectory("User*Homes");
|
|
assertTrue("user one unable to cd to User*Homes", success);
|
|
success = ftpOne.changeWorkingDirectory(USER_ONE);
|
|
assertTrue("user one unable to cd to " + USER_ONE, success);
|
|
|
|
success = ftpTwo.changeWorkingDirectory("Alfresco");
|
|
assertTrue("user two unable to cd to Alfreco", success);
|
|
success = ftpTwo.changeWorkingDirectory("User*Homes");
|
|
assertTrue("user two unable to cd to User*Homes", success);
|
|
success = ftpTwo.changeWorkingDirectory(USER_ONE);
|
|
assertTrue("user two unable to cd " + USER_ONE, success);
|
|
|
|
// Create a file as user one
|
|
String FILE1_CONTENT_1="test file 1 content";
|
|
String FILE1_NAME = "test.docx";
|
|
success = ftpOne.appendFile(FILE1_NAME , new ByteArrayInputStream(FILE1_CONTENT_1.getBytes("UTF-8")));
|
|
assertTrue("user one unable to append file", success);
|
|
|
|
// Update the file as user two
|
|
String FILE1_CONTENT_2="test file content updated";
|
|
success = ftpTwo.appendFile(FILE1_NAME , new ByteArrayInputStream(FILE1_CONTENT_2.getBytes("UTF-8")));
|
|
assertTrue("user two unable to append file", success);
|
|
|
|
// User one should read user2's content
|
|
InputStream is1 = ftpOne.retrieveFileStream(FILE1_NAME);
|
|
assertNotNull("is1 is null", is1);
|
|
String content1 = inputStreamToString(is1);
|
|
assertEquals("Content is not as expected", FILE1_CONTENT_2, content1);
|
|
ftpOne.completePendingCommand();
|
|
|
|
// User two should read user2's content
|
|
InputStream is2 = ftpTwo.retrieveFileStream(FILE1_NAME);
|
|
assertNotNull("is2 is null", is2);
|
|
String content2 = inputStreamToString(is2);
|
|
assertEquals("Content is not as expected", FILE1_CONTENT_2, content2);
|
|
ftpTwo.completePendingCommand();
|
|
logger.debug("Test finished");
|
|
|
|
}
|
|
finally
|
|
{
|
|
ftpOne.dele(TEST_DIR);
|
|
if(ftpOne != null)
|
|
{
|
|
ftpOne.disconnect();
|
|
}
|
|
if(ftpTwo != null)
|
|
{
|
|
ftpTwo.disconnect();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Test a quota failue exception over FTP.
|
|
* A file should not exist after a create and quota exception.
|
|
*/
|
|
public void testFtpQuotaAndFtp() throws Exception
|
|
{
|
|
// Enable usages
|
|
ContentUsageImpl contentUsage = (ContentUsageImpl)applicationContext.getBean("contentUsageImpl");
|
|
contentUsage.setEnabled(true);
|
|
contentUsage.init();
|
|
UserUsageTrackingComponent userUsageTrackingComponent = (UserUsageTrackingComponent)applicationContext.getBean("userUsageTrackingComponent");
|
|
userUsageTrackingComponent.setEnabled(true);
|
|
userUsageTrackingComponent.bootstrapInternal();
|
|
|
|
final String TEST_DIR="/Alfresco/User Homes/" + USER_THREE;
|
|
|
|
final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();
|
|
|
|
FTPClient ftpOne = connectClient();
|
|
try
|
|
{
|
|
int reply = ftpOne.getReplyCode();
|
|
|
|
if (!FTPReply.isPositiveCompletion(reply))
|
|
{
|
|
fail("FTP server refused connection.");
|
|
}
|
|
|
|
boolean login = ftpOne.login(USER_THREE, PASSWORD_THREE);
|
|
assertTrue("user one login not successful", login);
|
|
|
|
boolean success = ftpOne.changeWorkingDirectory("Alfresco");
|
|
assertTrue("user one unable to cd to Alfreco", success);
|
|
success = ftpOne.changeWorkingDirectory("User*Homes");
|
|
assertTrue("user one unable to cd to User*Homes", success);
|
|
success = ftpOne.changeWorkingDirectory(USER_THREE);
|
|
assertTrue("user one unable to cd to " + USER_THREE, success);
|
|
|
|
/**
|
|
* Create a file as user three which is bigger than the quota
|
|
*/
|
|
String FILE3_CONTENT_3="test file 3 content that needs to be greater than 100 bytes to result in a quota exception being thrown";
|
|
String FILE1_NAME = "test.docx";
|
|
|
|
// Should not be success
|
|
success = ftpOne.appendFile(FILE1_NAME , new ByteArrayInputStream(FILE3_CONTENT_3.getBytes("UTF-8")));
|
|
assertFalse("user one can ignore quota", success);
|
|
|
|
boolean deleted = ftpOne.deleteFile(FILE1_NAME);
|
|
assertFalse("quota exception expected", deleted);
|
|
|
|
logger.debug("test done");
|
|
|
|
}
|
|
finally
|
|
{
|
|
// Disable usages
|
|
contentUsage.setEnabled(false);
|
|
contentUsage.init();
|
|
userUsageTrackingComponent.setEnabled(false);
|
|
userUsageTrackingComponent.bootstrapInternal();
|
|
|
|
|
|
ftpOne.dele(TEST_DIR);
|
|
if(ftpOne != null)
|
|
{
|
|
ftpOne.disconnect();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Create a user with a small quota.
|
|
*
|
|
* Upload a file less than the quota.
|
|
*
|
|
* Upload a file greater than the quota.
|
|
*
|
|
* @throws Exception
|
|
*/
|
|
public void DISABLED_testQuota() throws Exception
|
|
{
|
|
fail("not yet implemented");
|
|
}
|
|
|
|
private FTPClient connectClient() throws IOException
|
|
{
|
|
FTPClient ftp = new FTPClient();
|
|
|
|
if(logger.isDebugEnabled())
|
|
{
|
|
ftp.addProtocolCommandListener(new PrintCommandListener(
|
|
new PrintWriter(System.out)));
|
|
}
|
|
ftp.connect(HOSTNAME, ftpConfigSection.getFTPPort());
|
|
return ftp;
|
|
}
|
|
|
|
/**
|
|
* Test quality utility to read an input stream into a string.
|
|
* @param is
|
|
* @return the content of the stream in a string.
|
|
* @throws IOException
|
|
*/
|
|
private String inputStreamToString(InputStream is) throws IOException
|
|
{
|
|
if (is != null)
|
|
{
|
|
StringWriter writer = new StringWriter();
|
|
|
|
char[] buffer = new char[1024];
|
|
try
|
|
{
|
|
Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
|
|
int n;
|
|
while ((n = reader.read(buffer)) != -1)
|
|
{
|
|
writer.write(buffer, 0, n);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
is.close();
|
|
}
|
|
is.close();
|
|
|
|
return writer.getBuffer().toString();
|
|
|
|
}
|
|
return "";
|
|
}
|
|
|
|
/**
|
|
* create a test user
|
|
* @param userName
|
|
* @param password
|
|
* @param quota
|
|
*/
|
|
private void createUser(String userName, String password, long quota)
|
|
{
|
|
if (this.authenticationService.authenticationExists(userName) == false)
|
|
{
|
|
this.authenticationService.createAuthentication(userName, password.toCharArray());
|
|
|
|
PropertyMap ppOne = new PropertyMap(4);
|
|
ppOne.put(ContentModel.PROP_USERNAME, userName);
|
|
ppOne.put(ContentModel.PROP_FIRSTNAME, "firstName");
|
|
ppOne.put(ContentModel.PROP_LASTNAME, "lastName");
|
|
ppOne.put(ContentModel.PROP_EMAIL, "email@email.com");
|
|
ppOne.put(ContentModel.PROP_JOBTITLE, "jobTitle");
|
|
|
|
if(quota > 0)
|
|
{
|
|
ppOne.put(ContentModel.PROP_SIZE_QUOTA, quota);
|
|
}
|
|
|
|
this.personService.createPerson(ppOne);
|
|
}
|
|
}
|
|
|
|
|
|
}
|