Added guest user support to FTP server/filesystem authenticator. Changed authentication to use the standard authentication classes so FTP should work with LDAP and Kerberos setups.

Fixed server session transaction rollback problem.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2308 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Gary Spencer
2006-02-06 17:50:25 +00:00
parent f4b281fc7e
commit 385d11b33a
5 changed files with 394 additions and 88 deletions

View File

@@ -344,6 +344,18 @@ public class FTPPath
return m_shareDev != null ? true : false; return m_shareDev != null ? true : false;
} }
/**
* Set the shared device
*
* @param share DiskSharedDevice
* @return boolean
*/
public final boolean setSharedDevice(DiskSharedDevice share)
{
m_shareDev = share;
return true;
}
/** /**
* Build an FTP path to the specified file * Build an FTP path to the specified file
* *

View File

@@ -31,15 +31,16 @@ import java.util.Enumeration;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import java.util.Vector; import java.util.Vector;
import javax.transaction.Status;
import javax.transaction.UserTransaction; import javax.transaction.UserTransaction;
import org.alfresco.filesys.server.SrvSession; import org.alfresco.filesys.server.SrvSession;
import org.alfresco.filesys.server.auth.ClientInfo; import org.alfresco.filesys.server.auth.ClientInfo;
import org.alfresco.filesys.server.auth.SrvAuthenticator;
import org.alfresco.filesys.server.auth.acl.AccessControl; import org.alfresco.filesys.server.auth.acl.AccessControl;
import org.alfresco.filesys.server.auth.acl.AccessControlManager; import org.alfresco.filesys.server.auth.acl.AccessControlManager;
import org.alfresco.filesys.server.core.SharedDevice; import org.alfresco.filesys.server.core.SharedDevice;
import org.alfresco.filesys.server.core.SharedDeviceList; import org.alfresco.filesys.server.core.SharedDeviceList;
import org.alfresco.filesys.server.filesys.AccessDeniedException;
import org.alfresco.filesys.server.filesys.AccessMode; import org.alfresco.filesys.server.filesys.AccessMode;
import org.alfresco.filesys.server.filesys.DiskDeviceContext; import org.alfresco.filesys.server.filesys.DiskDeviceContext;
import org.alfresco.filesys.server.filesys.DiskFullException; import org.alfresco.filesys.server.filesys.DiskFullException;
@@ -53,11 +54,22 @@ import org.alfresco.filesys.server.filesys.FileStatus;
import org.alfresco.filesys.server.filesys.NetworkFile; import org.alfresco.filesys.server.filesys.NetworkFile;
import org.alfresco.filesys.server.filesys.NotifyChange; import org.alfresco.filesys.server.filesys.NotifyChange;
import org.alfresco.filesys.server.filesys.SearchContext; import org.alfresco.filesys.server.filesys.SearchContext;
import org.alfresco.filesys.server.filesys.SrvDiskInfo;
import org.alfresco.filesys.server.filesys.TreeConnection; import org.alfresco.filesys.server.filesys.TreeConnection;
import org.alfresco.filesys.server.filesys.TreeConnectionHash; import org.alfresco.filesys.server.filesys.TreeConnectionHash;
import org.alfresco.filesys.smb.server.repo.ContentContext;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.transaction.TransactionService;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import com.sun.star.uno.RuntimeException;
/** /**
* FTP Server Session Class * FTP Server Session Class
* *
@@ -333,6 +345,9 @@ public class FTPSrvSession extends SrvSession implements Runnable
// Initialize the current working directory using the root path // Initialize the current working directory using the root path
m_cwd = new FTPPath(rootPath); m_cwd = new FTPPath(rootPath);
if ( rootPath.hasSharedDevice())
m_cwd.setSharedDevice( rootPath.getSharedDevice());
else
m_cwd.setSharedDevice(getShareList(), this); m_cwd.setSharedDevice(getShareList(), this);
} }
@@ -363,6 +378,14 @@ public class FTPSrvSession extends SrvSession implements Runnable
*/ */
protected final FTPPath generatePathForRequest(FTPRequest req, boolean filePath, boolean checkExists) protected final FTPPath generatePathForRequest(FTPRequest req, boolean filePath, boolean checkExists)
{ {
// Use the global share list for normal connections and the per session list for guest access
SharedDeviceList shareList = null;
if ( getClientInformation().isGuest())
shareList = getDynamicShareList();
else
shareList = getShareList();
// Convert the path to an FTP format path // Convert the path to an FTP format path
@@ -412,7 +435,7 @@ public class FTPSrvSession extends SrvSession implements Runnable
// Find the associated shared device // Find the associated shared device
if (ftpPath.setSharedDevice(getShareList(), this) == false) if (ftpPath.setSharedDevice( shareList, this) == false)
return null; return null;
} }
else else
@@ -439,7 +462,7 @@ public class FTPSrvSession extends SrvSession implements Runnable
// Remove the last directory from the path // Remove the last directory from the path
m_cwd.removeDirectory(); m_cwd.removeDirectory();
m_cwd.setSharedDevice(getShareList(), this); m_cwd.setSharedDevice( shareList, this);
return m_cwd; return m_cwd;
} }
else else
@@ -477,7 +500,7 @@ public class FTPSrvSession extends SrvSession implements Runnable
// Find the associated shared device, if not already set // Find the associated shared device, if not already set
if (ftpPath.hasSharedDevice() == false && ftpPath.setSharedDevice(getShareList(), this) == false) if (ftpPath.hasSharedDevice() == false && ftpPath.setSharedDevice( shareList, this) == false)
return null; return null;
} }
@@ -544,6 +567,11 @@ public class FTPSrvSession extends SrvSession implements Runnable
} }
catch (Exception ex) catch (Exception ex)
{ {
// DEBUG
if ( logger.isErrorEnabled())
logger.error("Error generating FTP path", ex);
ftpPath = null; ftpPath = null;
} }
} }
@@ -731,8 +759,7 @@ public class FTPSrvSession extends SrvSession implements Runnable
{ {
// Check if the client information has been set, this indicates a user // Check if the client information has been set, this indicates a user
// command has been // command has been received
// received
if (hasClientInformation() == false) if (hasClientInformation() == false)
{ {
@@ -743,42 +770,58 @@ public class FTPSrvSession extends SrvSession implements Runnable
// Check for an anonymous login, accept any password string // Check for an anonymous login, accept any password string
if (getClientInformation().isGuest()) ClientInfo cInfo = getClientInformation();
if (cInfo.isGuest())
{ {
if ( getFTPServer().allowAnonymous() == true)
{
// Authenticate as the guest user
// Save the anonymous login password string AuthenticationComponent authComponent = getServer().getConfiguration().getAuthenticationComponent();
cInfo.setUserName( authComponent.getGuestUserName());
}
else
{
// Return an access denied error
getClientInformation().setPassword(req.getArgument()); sendFTPResponse(530, "Access denied");
// Accept the login
setLoggedOn(true);
sendFTPResponse(230, "User logged in, proceed");
// DEBUG // DEBUG
if (logger.isDebugEnabled() && hasDebug(DBG_STATE)) if (logger.isDebugEnabled() && hasDebug(DBG_STATE))
logger.debug("Anonymous login, info=" + req.getArgument()); logger.debug("Anonymous logon not allowed");
// Close the connection
closeSession();
}
} }
// Validate the user // Get the client information and store the received plain text password
else cInfo.setPassword(req.getArgument());
// Use the normal authentication service as we have the plaintext password
AuthenticationService authService = getServer().getConfiguration().getAuthenticationService();
try
{ {
// Get the client information and store the received plain text
// password
getClientInformation().setPassword(req.getArgument());
// Authenticate the user // Authenticate the user
SrvAuthenticator auth = getServer().getConfiguration().getAuthenticator(); if ( cInfo.isGuest())
int access = auth.authenticateUserPlainText(getClientInformation(), this);
if (access == SrvAuthenticator.AUTH_ALLOW)
{ {
// Authenticate as the guest user
authService.authenticateAsGuest();
}
else
{
// Authenticate as a normal user
authService.authenticate( cInfo.getUserName(), cInfo.getPasswordAsCharArray());
}
// User successfully logged on // User successfully logged on
@@ -790,6 +833,62 @@ public class FTPSrvSession extends SrvSession implements Runnable
if (logger.isDebugEnabled() && hasDebug(DBG_STATE)) if (logger.isDebugEnabled() && hasDebug(DBG_STATE))
logger.debug("User " + getClientInformation().getUserName() + ", logon successful"); logger.debug("User " + getClientInformation().getUserName() + ", logon successful");
} }
catch (org.alfresco.repo.security.authentication.AuthenticationException ex)
{
// DEBUG
if ( logger.isDebugEnabled())
logger.debug("Logon failed", ex);
}
// Check if the logon was successful
if ( isLoggedOn() == true)
{
// If the user has successfully logged on to the FTP server then inform listeners
getFTPServer().sessionLoggedOn(this);
// If this is a guest logon then we need to set the root folder to the guest user home folder
// as guest is not allowed access to other areas
if ( cInfo.isGuest())
{
// Generate a dynamic share with the guest users home folder as the root
DiskSharedDevice guestShare = createHomeDiskShare( cInfo);
addDynamicShare( guestShare);
// Set the root path for the guest logon to the guest share
StringBuilder rootPath = new StringBuilder();
rootPath.append(FTP_SEPERATOR);
rootPath.append( guestShare.getName());
rootPath.append(FTP_SEPERATOR);
FTPPath guestRoot = null;
try
{
// Set the root path for this FTP session
guestRoot = new FTPPath( rootPath.toString());
guestRoot.setSharedDevice( guestShare);
setRootPath( guestRoot);
}
catch ( InvalidPathException ex)
{
if ( logger.isErrorEnabled())
logger.error("Error setting guest FTP root path", ex);
}
// DEBUG
if (logger.isDebugEnabled() && hasDebug(DBG_STATE))
logger.debug(" Using root path " + guestRoot);
}
}
else else
{ {
@@ -808,13 +907,6 @@ public class FTPSrvSession extends SrvSession implements Runnable
} }
} }
// If the user has successfully logged on to the FTP server then inform
// listeners
if (isLoggedOn())
getFTPServer().sessionLoggedOn(this);
}
/** /**
* Process a port command * Process a port command
* *
@@ -2045,9 +2137,19 @@ public class FTPSrvSession extends SrvSession implements Runnable
if (logger.isDebugEnabled() && hasDebug(DBG_FILEIO)) if (logger.isDebugEnabled() && hasDebug(DBG_FILEIO))
logger.debug(" Transfer complete, file closed"); logger.debug(" Transfer complete, file closed");
} }
catch( AccessDeniedException ex)
{
// DEBUG
if (logger.isDebugEnabled() && hasDebug(DBG_ERROR))
logger.debug(" Access denied", ex);
// Session does not have write access to the filesystem
sendFTPResponse(550, "Access denied");
}
catch (SocketException ex) catch (SocketException ex)
{ {
// DEBUG // DEBUG
if (logger.isDebugEnabled() && hasDebug(DBG_ERROR)) if (logger.isDebugEnabled() && hasDebug(DBG_ERROR))
@@ -2093,13 +2195,13 @@ public class FTPSrvSession extends SrvSession implements Runnable
if (logger.isDebugEnabled() && hasDebug(DBG_ERROR)) if (logger.isDebugEnabled() && hasDebug(DBG_ERROR))
logger.debug(" Error during transfer", ex); logger.debug(" Error during transfer", ex);
ex.printStackTrace();
// Indicate that there was an error during transmission of the file // Indicate that there was an error during transmission of the file
// data // data
sendFTPResponse(426, "Error during transmission"); sendFTPResponse(426, "Error during transmission");
} finally }
finally
{ {
// Close the network file // Close the network file
@@ -2216,6 +2318,17 @@ public class FTPSrvSession extends SrvSession implements Runnable
return; return;
} }
} }
catch (AccessDeniedException ex)
{
// DEBUG
if (logger.isDebugEnabled() && hasDebug(DBG_ERROR))
logger.debug(" Access denied", ex);
// Session does not have write access to the filesystem
sendFTPResponse(550, "Access denied");
}
catch (Exception ex) catch (Exception ex)
{ {
sendFTPResponse(450, "File action not taken"); sendFTPResponse(450, "File action not taken");
@@ -2444,7 +2557,8 @@ public class FTPSrvSession extends SrvSession implements Runnable
{ {
sendFTPResponse(450, "File action not taken"); sendFTPResponse(450, "File action not taken");
return; return;
} finally }
finally
{ {
// Clear the rename details // Clear the rename details
@@ -2859,8 +2973,14 @@ public class FTPSrvSession extends SrvSession implements Runnable
{ {
// The first level of directories are mapped to the available shares // The first level of directories are mapped to the available shares
// Guest users only see their own list of shares
SharedDeviceList shares = null;
if ( getClientInformation().isGuest())
shares = getDynamicShareList();
else
getShareList();
SharedDeviceList shares = getShareList();
if (shares != null) if (shares != null)
{ {
@@ -3043,9 +3163,10 @@ public class FTPSrvSession extends SrvSession implements Runnable
if (tree == null) if (tree == null)
{ {
// Create a new tree connection // Create a new tree connection, do not add dynamic shares to the connection cache
tree = new TreeConnection(share); tree = new TreeConnection(share);
if ( share.isTemporary() == false)
m_connections.addConnection(tree); m_connections.addConnection(tree);
// Set the access permission for the shared filesystem // Set the access permission for the shared filesystem
@@ -3068,6 +3189,73 @@ public class FTPSrvSession extends SrvSession implements Runnable
return tree; return tree;
} }
/**
* Create a disk share for the home folder
*
* @param client ClientInfo
* @return DiskSharedDevice
*/
private final DiskSharedDevice createHomeDiskShare(ClientInfo client)
{
// Check if the home folder has been set for the user
if ( client.hasHomeFolder() == false)
{
// Get the required services
NodeService nodeService = getServer().getConfiguration().getNodeService();
PersonService personService = getServer().getConfiguration().getPersonService();
TransactionService transService = getServer().getConfiguration().getTransactionService();
// Get the home folder for the user
UserTransaction tx = transService.getUserTransaction();
NodeRef homeSpaceRef = null;
try
{
tx.begin();
homeSpaceRef = (NodeRef) nodeService.getProperty( personService.getPerson(client.getUserName()),
ContentModel.PROP_HOMEFOLDER);
client.setHomeFolder( homeSpaceRef);
tx.commit();
}
catch (Throwable ex)
{
try
{
tx.rollback();
}
catch (Exception ex2)
{
logger.error("Failed to rollback transaction", ex2);
}
if(ex instanceof RuntimeException)
{
throw (RuntimeException)ex;
}
else
{
throw new RuntimeException("Failed to get home folder", ex);
}
}
}
// Create the disk driver and context
DiskInterface diskDrv = getServer().getConfiguration().getDiskInterface();
DiskDeviceContext diskCtx = new ContentContext("", "", client.getHomeFolder());
// Default the filesystem to look like an 80Gb sized disk with 90% free space
diskCtx.setDiskInformation(new SrvDiskInfo(2560, 64, 512, 2304));
// Create a temporary shared device for the users home directory
return new DiskSharedDevice( client.getUserName(), diskDrv, diskCtx, SharedDevice.Temporary);
}
/** /**
* Start the FTP session in a seperate thread * Start the FTP session in a seperate thread
*/ */
@@ -3351,16 +3539,13 @@ public class FTPSrvSession extends SrvSession implements Runnable
+ FTPCommand.getCommandName(ftpReq.isCommand()) + " in " + duration + "ms"); + FTPCommand.getCommandName(ftpReq.isCommand()) + " in " + duration + "ms");
} }
// Check for an active transaction, and commit it // Commit, or rollback, any active user transaction
if ( hasUserTransaction())
{
try try
{ {
// Commit the transaction // Commit or rollback the transaction
UserTransaction trans = getUserTransaction(); endTransaction();
trans.commit();
} }
catch ( Exception ex) catch ( Exception ex)
{ {
@@ -3369,7 +3554,6 @@ public class FTPSrvSession extends SrvSession implements Runnable
if ( logger.isDebugEnabled()) if ( logger.isDebugEnabled())
logger.debug("Error committing transaction", ex); logger.debug("Error committing transaction", ex);
} }
}
} // end while state } // end while state
} }

View File

@@ -18,6 +18,8 @@ package org.alfresco.filesys.server;
import java.net.InetAddress; import java.net.InetAddress;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction; import javax.transaction.UserTransaction;
import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.error.AlfrescoRuntimeException;
@@ -482,6 +484,24 @@ public abstract class SrvSession
if ( m_transaction != null) if ( m_transaction != null)
{ {
// Check if the current transaction is marked for rollback
try
{
if ( m_transaction.getStatus() == Status.STATUS_MARKED_ROLLBACK ||
m_transaction.getStatus() == Status.STATUS_ROLLEDBACK ||
m_transaction.getStatus() == Status.STATUS_ROLLING_BACK)
{
// Rollback the current transaction
m_transaction.rollback();
}
}
catch ( SystemException ex)
{
}
// Check if the transaction is a write transaction, if write has been requested // Check if the transaction is a write transaction, if write has been requested
if ( readOnly == false && m_readOnlyTrans == true) if ( readOnly == false && m_readOnlyTrans == true)
@@ -530,6 +550,48 @@ public abstract class SrvSession
return created; return created;
} }
/**
* End a transaction by either committing or rolling back
*
* @exception AlfrescoRuntimeException
*/
public final void endTransaction()
throws AlfrescoRuntimeException
{
// Check if there is an active transaction
if ( m_transaction != null)
{
try
{
// Commit or rollback the transaction
if ( m_transaction.getStatus() == Status.STATUS_MARKED_ROLLBACK)
{
// Transaction is marked for rollback
m_transaction.rollback();
}
else
{
// Commit the transaction
m_transaction.commit();
}
}
catch ( Exception ex)
{
throw new AlfrescoRuntimeException("Failed to end transaction", ex);
}
finally
{
// Clear the current transaction
m_transaction = null;
}
}
}
/** /**
* Determine if the session has an active transaction * Determine if the session has an active transaction
* *

View File

@@ -21,6 +21,8 @@ import java.util.Random;
import javax.transaction.UserTransaction; import javax.transaction.UserTransaction;
import net.sf.acegisecurity.Authentication;
import org.alfresco.config.ConfigElement; import org.alfresco.config.ConfigElement;
import org.alfresco.filesys.server.SrvSession; import org.alfresco.filesys.server.SrvSession;
import org.alfresco.filesys.server.config.InvalidConfigurationException; import org.alfresco.filesys.server.config.InvalidConfigurationException;
@@ -139,6 +141,9 @@ public class AlfrescoAuthenticator extends SrvAuthenticator
{ {
// Use the existing authentication token // Use the existing authentication token
if ( client.isGuest())
m_authComponent.setGuestUserAsCurrentUser();
else
m_authComponent.setCurrentUser(client.getUserName()); m_authComponent.setCurrentUser(client.getUserName());
// Debug // Debug
@@ -151,11 +156,36 @@ public class AlfrescoAuthenticator extends SrvAuthenticator
return client.getLogonType() != ClientInfo.LogonGuest ? AUTH_ALLOW : AUTH_GUEST; return client.getLogonType() != ClientInfo.LogonGuest ? AUTH_ALLOW : AUTH_GUEST;
} }
// Check if MD4 or passthru mode is configured // Check if this is a guest logon
int authSts = AUTH_DISALLOW; int authSts = AUTH_DISALLOW;
if ( m_authComponent.getNTLMMode() == NTLMMode.MD4_PROVIDER) if ( client.isGuest())
{
// Check if guest logons are allowed
if ( allowGuest() == false)
return AUTH_DISALLOW;
// Get a guest authentication token
m_authComponent.setGuestUserAsCurrentUser();
Authentication authToken = m_authComponent.getCurrentAuthentication();
client.setAuthenticationToken( authToken);
// Set the home folder for the guest user
getHomeFolderForUser( client);
// Indicate logged on as guest
authSts = AUTH_GUEST;
}
// Check if MD4 or passthru mode is configured
else if ( m_authComponent.getNTLMMode() == NTLMMode.MD4_PROVIDER)
{ {
// Perform local MD4 password check // Perform local MD4 password check

View File

@@ -129,6 +129,24 @@ public class ClientInfo
return null; return null;
} }
/**
* Return the password as a character array
*
* @return char[]
*/
public final char[] getPasswordAsCharArray()
{
char[] cpwd = null;
if (m_password != null)
{
String pwd = new String(m_password);
cpwd = new char[pwd.length()];
pwd.getChars(0, pwd.length(), cpwd, 0);
}
return cpwd;
}
/** /**
* Determine if the client has specified an ANSI password * Determine if the client has specified an ANSI password
* *