Added guest account support to the CIFS server. Refactored authenticator code and moved

common code down into the base class.
Added configuration value to control if unknown users should be mapped to the guest account.
Fixed a couple of uncaught access denied exceptions in the CIFS protocol handler.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2317 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Gary Spencer
2006-02-07 20:04:16 +00:00
parent 5ea6e3d551
commit cc503b2ac8
5 changed files with 325 additions and 172 deletions

View File

@@ -17,32 +17,11 @@
package org.alfresco.filesys.server.auth;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
import javax.transaction.UserTransaction;
import net.sf.acegisecurity.Authentication;
import org.alfresco.config.ConfigElement;
import org.alfresco.filesys.server.SrvSession;
import org.alfresco.filesys.server.config.InvalidConfigurationException;
import org.alfresco.filesys.server.config.ServerConfiguration;
import org.alfresco.filesys.server.core.SharedDevice;
import org.alfresco.filesys.smb.server.SMBSrvSession;
import org.alfresco.filesys.util.DataPacker;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.MD4PasswordEncoder;
import org.alfresco.repo.security.authentication.MD4PasswordEncoderImpl;
import org.alfresco.repo.security.authentication.NTLMMode;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.transaction.TransactionService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.sun.star.uno.RuntimeException;
/**
* Alfresco Authenticator Class
@@ -57,32 +36,6 @@ import com.sun.star.uno.RuntimeException;
*/
public class AlfrescoAuthenticator extends SrvAuthenticator
{
// Logging
private static final Log logger = LogFactory.getLog("org.alfresco.smb.protocol.auth");
// Random number generator used to generate challenge keys
private Random m_random = new Random(System.currentTimeMillis());
// Server configuration
private ServerConfiguration m_config;
// Authentication component, used to access internal authentication functions
private AuthenticationComponent m_authComponent;
// MD4 hash decoder
private MD4PasswordEncoder m_md4Encoder = new MD4PasswordEncoderImpl();
// Various services required to get user information
private NodeService m_nodeService;
private PersonService m_personService;
private TransactionService m_transactionService;
/**
* Default Constructor
*
@@ -94,24 +47,6 @@ public class AlfrescoAuthenticator extends SrvAuthenticator
setEncryptedPasswords(true);
}
/**
* Authenticate the connection to a share
*
* @param client ClienInfo
* @param share SharedDevice
* @param pwd Share level password.
* @param sess Server session
* @return Authentication status.
*/
public int authenticateShareConnect(ClientInfo client, SharedDevice share, String pwd, SrvSession sess)
{
// Allow write access
//
// Main authentication is handled by authenticateUser()
return SrvAuthenticator.Writeable;
}
/**
* Authenticate a user
*
@@ -160,7 +95,7 @@ public class AlfrescoAuthenticator extends SrvAuthenticator
int authSts = AUTH_DISALLOW;
if ( client.isGuest())
if ( client.isGuest() || client.getUserName().equalsIgnoreCase(GUEST_USERNAME))
{
// Check if guest logons are allowed
@@ -169,18 +104,20 @@ public class AlfrescoAuthenticator extends SrvAuthenticator
// 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);
doGuestLogon( client, sess);
// Indicate logged on as guest
authSts = AUTH_GUEST;
// DEBUG
if ( logger.isDebugEnabled())
logger.debug("Authenticated user " + client.getUserName() + " sts=" + getStatusAsString(authSts));
// Return the guest status
return authSts;
}
// Check if MD4 or passthru mode is configured
@@ -191,6 +128,26 @@ public class AlfrescoAuthenticator extends SrvAuthenticator
authSts = doMD4UserAuthentication(client, sess, alg);
}
// Check if the logon status indicates a guest logon
if ( authSts == AUTH_GUEST)
{
// Only allow the guest logon if user mapping is enabled
if ( mapUnknownUserToGuest())
{
// Logon as guest, setup the security context
doGuestLogon( client, sess);
}
else
{
// Do not allow the guest logon
authSts = AUTH_DISALLOW;
}
}
// DEBUG
@@ -243,47 +200,6 @@ public class AlfrescoAuthenticator extends SrvAuthenticator
return key;
}
/**
* Search for the required user account details in the defined user list
*
* @param user String
* @return UserAccount
*/
public UserAccount getUserDetails(String user)
{
return null;
}
/**
* Initialize the authenticator
*
* @param config ServerConfiguration
* @param params ConfigElement
* @exception InvalidConfigurationException
*/
public void initialize(ServerConfiguration config, ConfigElement params) throws InvalidConfigurationException
{
// Save the server configuration so we can access the authentication component
m_config = config;
// Check that the required authentication classes are available
m_authComponent = m_config.getAuthenticationComponent();
if ( m_authComponent == null)
throw new InvalidConfigurationException("Authentication component not available");
if ( m_authComponent.getNTLMMode() != NTLMMode.MD4_PROVIDER)
throw new InvalidConfigurationException("Required authentication mode not available");
// Get hold of various services
m_nodeService = config.getNodeService();
m_personService = config.getPersonService();
m_transactionService = config.getTransactionService();
}
/**
* Perform MD4 user authentication
*
@@ -362,46 +278,4 @@ public class AlfrescoAuthenticator extends SrvAuthenticator
return allowGuest() ? SrvAuthenticator.AUTH_GUEST : SrvAuthenticator.AUTH_DISALLOW;
}
/**
* Get the home folder for the user
*
* @param client ClientInfo
*/
private final void getHomeFolderForUser(ClientInfo client)
{
// Get the home folder for the user
UserTransaction tx = m_transactionService.getUserTransaction();
NodeRef homeSpaceRef = null;
try
{
tx.begin();
homeSpaceRef = (NodeRef) m_nodeService.getProperty(m_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);
}
}
}
}

View File

@@ -17,12 +17,34 @@
package org.alfresco.filesys.server.auth;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
import javax.transaction.UserTransaction;
import net.sf.acegisecurity.Authentication;
import org.alfresco.config.ConfigElement;
import org.alfresco.filesys.server.SrvSession;
import org.alfresco.filesys.server.config.InvalidConfigurationException;
import org.alfresco.filesys.server.config.ServerConfiguration;
import org.alfresco.filesys.server.core.SharedDevice;
import org.alfresco.filesys.server.filesys.DiskDeviceContext;
import org.alfresco.filesys.server.filesys.DiskInterface;
import org.alfresco.filesys.server.filesys.DiskSharedDevice;
import org.alfresco.filesys.server.filesys.SrvDiskInfo;
import org.alfresco.filesys.smb.server.repo.ContentContext;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.MD4PasswordEncoder;
import org.alfresco.repo.security.authentication.MD4PasswordEncoderImpl;
import org.alfresco.repo.security.authentication.NTLMMode;
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.LogFactory;
/**
* <p>
@@ -31,6 +53,9 @@ import org.alfresco.filesys.server.core.SharedDevice;
*/
public abstract class SrvAuthenticator
{
// Logging
protected static final Log logger = LogFactory.getLog("org.alfresco.smb.protocol.auth");
// Encryption algorithm types
@@ -61,6 +86,10 @@ public abstract class SrvAuthenticator
public static final int STANDARD_PASSWORD_LEN = 24;
// Default guest user name
protected static final String GUEST_USERNAME = "guest";
// Server access mode
private int m_accessMode = SHARE_MODE;
@@ -73,10 +102,38 @@ public abstract class SrvAuthenticator
private PasswordEncryptor m_encryptor = new PasswordEncryptor();
// Flag to enable/disable the guest account
// Flag to enable/disable the guest account, and control mapping of unknown users to the guest account
private boolean m_allowGuest;
private boolean m_mapToGuest;
// Default guest user name
private String m_guestUserName = GUEST_USERNAME;
// Random number generator used to generate challenge keys
protected Random m_random = new Random(System.currentTimeMillis());
// Server configuration
protected ServerConfiguration m_config;
// Authentication component, used to access internal authentication functions
protected AuthenticationComponent m_authComponent;
// MD4 hash decoder
protected MD4PasswordEncoder m_md4Encoder = new MD4PasswordEncoderImpl();
// Various services required to get user information
protected NodeService m_nodeService;
protected PersonService m_personService;
protected TransactionService m_transactionService;
protected AuthenticationService m_authenticationService;
/**
* Authenticate a connection to a share.
*
@@ -87,7 +144,14 @@ public abstract class SrvAuthenticator
* @return int Granted file permission level or disallow status if negative. See the
* FilePermission class.
*/
public abstract int authenticateShareConnect(ClientInfo client, SharedDevice share, String sharePwd, SrvSession sess);
public int authenticateShareConnect(ClientInfo client, SharedDevice share, String sharePwd, SrvSession sess)
{
// Allow write access
//
// Main authentication is handled by authenticateUser()
return SrvAuthenticator.Writeable;
}
/**
* Authenticate a user. A user may be granted full access, guest access or no access.
@@ -105,7 +169,10 @@ public abstract class SrvAuthenticator
* @param user String
* @return UserAccount
*/
public abstract UserAccount getUserDetails(String user);
public UserAccount getUserDetails(String user)
{
return null;
}
/**
* Authenticate a user using a plain text password.
@@ -150,6 +217,31 @@ public abstract class SrvAuthenticator
*/
public void initialize(ServerConfiguration config, ConfigElement params) throws InvalidConfigurationException
{
// Save the server configuration so we can access the authentication component
m_config = config;
// Check that the required authentication classes are available
m_authComponent = m_config.getAuthenticationComponent();
if ( m_authComponent == null)
throw new InvalidConfigurationException("Authentication component not available");
if ( m_authComponent.getNTLMMode() != NTLMMode.MD4_PROVIDER &&
m_authComponent.getNTLMMode() != NTLMMode.PASS_THROUGH)
throw new InvalidConfigurationException("Required authentication mode not available");
// Get hold of various services
m_nodeService = config.getNodeService();
m_personService = config.getPersonService();
m_transactionService = config.getTransactionService();
m_authenticationService = config.getAuthenticationService();
// Set the guest user name
setGuestUserName( m_authComponent.getGuestUserName());
}
/**
@@ -222,6 +314,26 @@ public abstract class SrvAuthenticator
return m_allowGuest;
}
/**
* Return the guest user name
*
* @return String
*/
public final String getGuestUserName()
{
return m_guestUserName;
}
/**
* Determine if unknown users should be mapped to the guest account
*
* @return boolean
*/
public final boolean mapUnknownUserToGuest()
{
return m_mapToGuest;
}
/**
* Set the access mode of the server.
*
@@ -252,6 +364,26 @@ public abstract class SrvAuthenticator
m_allowGuest = ena;
}
/**
* Set the guest user name
*
* @param guest String
*/
public final void setGuestUserName( String guest)
{
m_guestUserName = guest;
}
/**
* Enable/disable mapping of unknown users to the guest account
*
* @param ena Enable mapping of unknown users to the guest if true
*/
public final void setMapToGuest( boolean ena)
{
m_mapToGuest = ena;
}
/**
* Close the authenticator, perform any cleanup
*/
@@ -367,4 +499,87 @@ public abstract class SrvAuthenticator
return str;
}
/**
* Logon using the guest user account
*
* @param client ClientInfo
* @param sess SrvSession
*/
protected final void doGuestLogon( ClientInfo client, SrvSession sess)
{
// Get a guest authentication token
m_authenticationService.authenticateAsGuest();
Authentication authToken = m_authComponent.getCurrentAuthentication();
client.setAuthenticationToken( authToken);
// Set the home folder for the guest user
client.setUserName( getGuestUserName());
getHomeFolderForUser( client);
// Mark the client as being a guest logon
client.setGuest( true);
// Create a dynamic share for the guest user
// Create the disk driver and context
DiskInterface diskDrv = m_config.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
sess.addDynamicShare( new DiskSharedDevice( client.getUserName(), diskDrv, diskCtx, SharedDevice.Temporary));
}
/**
* Get the home folder for the user
*
* @param client ClientInfo
*/
protected final void getHomeFolderForUser(ClientInfo client)
{
// Get the home folder for the user
UserTransaction tx = m_transactionService.getUserTransaction();
NodeRef homeSpaceRef = null;
try
{
tx.begin();
homeSpaceRef = (NodeRef) m_nodeService.getProperty(m_personService.getPerson(client.getUserName()),
ContentModel.PROP_HOMEFOLDER);
client.setHomeFolder( homeSpaceRef);
tx.commit();
}
catch (Throwable ex)
{
try
{
tx.rollback();
}
catch (Throwable ex2)
{
logger.error("Failed to rollback transaction", ex2);
}
// Re-throw the exception
if (ex instanceof RuntimeException)
{
throw (RuntimeException) ex;
}
else
{
throw new RuntimeException("Error during execution of transaction.", ex);
}
}
}
}

View File

@@ -1451,13 +1451,15 @@ public class ServerConfiguration
else
throw new AlfrescoRuntimeException("Invalid authenticator type, " + authType);
// Get the allow guest setting
// Get the allow guest and map unknown user to guest settings
boolean allowGuest = authElem.getChild("allowGuest") != null ? true : false;
boolean mapGuest = authElem.getChild("mapUnknownUserToGuest") != null ? true : false;
// Initialize and set the authenticator class
setAuthenticator(auth, authElem, allowGuest);
auth.setMapToGuest( mapGuest);
}
// Add the users

View File

@@ -16,6 +16,8 @@
*/
package org.alfresco.filesys.server.filesys;
import java.util.Enumeration;
import org.alfresco.config.ConfigElement;
import org.alfresco.filesys.server.SrvSession;
import org.alfresco.filesys.server.auth.InvalidUserException;
@@ -99,20 +101,33 @@ public class DefaultShareMapper implements ShareMapper
SharedDevice share = null;
// Find the required share by name/type. Use a case sensitive search first, if that fails
// use a case
// insensitive search.
// Search the sessions dynamic share list first
share = m_config.getShares().findShare(name, typ, false);
if (share == null)
{
// Try a case insensitive search for the required share
share = m_config.getShares().findShare(name, typ, true);
if ( sess.hasDynamicShares()) {
// Check if the required share exists in the sessions dynamic share list
share = sess.getDynamicShareList().findShare(name, typ, true);
}
// If we did not find a share then search the global share list
if ( share == null)
{
// Find the required share by name/type. Use a case sensitive search first, if that fails
// use a case insensitive search.
share = m_config.getShares().findShare(name, typ, false);
if (share == null)
{
// Try a case insensitive search for the required share
share = m_config.getShares().findShare(name, typ, true);
}
}
// Check if the share is available
if (share != null && share.getContext() != null && share.getContext().isAvailable() == false)
@@ -130,6 +145,30 @@ public class DefaultShareMapper implements ShareMapper
*/
public void deleteShares(SrvSession sess)
{
// Check if the session has any dynamic shares
if ( sess.hasDynamicShares() == false)
return;
// Delete the dynamic shares
SharedDeviceList shares = sess.getDynamicShareList();
Enumeration<SharedDevice> enm = shares.enumerateShares();
while ( enm.hasMoreElements()) {
// Get the current share from the list
SharedDevice shr = (SharedDevice) enm.nextElement();
// Close the shared device
shr.getContext().CloseContext();
}
// Clear the dynamic share list
shares.removeAllShares();
}
/**
@@ -149,6 +188,13 @@ public class DefaultShareMapper implements ShareMapper
SharedDeviceList shrList = new SharedDeviceList(m_config.getShares());
if ( sess != null && sess.hasDynamicShares()) {
// Add the per session dynamic shares
shrList.addShares(sess.getDynamicShareList());
}
// Remove unavailable shares from the list and return the list
if (allShares == false)