Merged V3.0 to HEAD

11829: Updated javadocs for RuntimeExec class
   11830: Updated and wired in Spring source
   11831: Fixed ETHREEOH-382: Can't run Lucene search via Node Browser
   11832: Added unit test for V3.0 rev 11535
   11834: Removed redundant TODO item
   11835: ETHREEOH-798 Double clicking OK on most pop-up dialogs in Share causing multiple requests to be sent - and errors generated for the user
   11836: Fix for a number of session based authentication and webscript authentication issues with NTLM from Share. Fixes ETHREEOH-806 and ETHREEOH-834 and first part of fix for ETHREEOH-789.
   11838: Sharepoint Protocol Support
   11843: Build fix
   11846: Refactor of the SSO web filters (NTLM and Kerberos) for web-client and WebDAV.
   11848: Added commented out entries for web-client and WebDAV Kerberos filter debugging.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@12483 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Kevin Roast
2008-12-18 11:51:12 +00:00
parent dd907e536f
commit 5e6181932f
7 changed files with 285 additions and 1000 deletions

View File

@@ -43,6 +43,9 @@ public abstract class AbstractAuthenticationFilter implements Filter
{
private static Log logger = LogFactory.getLog(AbstractAuthenticationFilter.class);
/** Request level marker to indicate that authentication should not be processed */
final static String NO_AUTH_REQUIRED = "alfNoAuthRequired";
/**
* Parse the Accept-Lanaguage HTTP header value

View File

@@ -64,10 +64,12 @@ import org.alfresco.jlan.server.auth.spnego.NegTokenTarg;
import org.alfresco.jlan.server.auth.spnego.OID;
import org.alfresco.jlan.server.auth.spnego.SPNEGO;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.SessionUser;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.NTLMMode;
import org.alfresco.repo.webdav.auth.BaseKerberosAuthenticationFilter;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
@@ -90,88 +92,26 @@ import org.springframework.web.context.support.WebApplicationContextUtils;
*
* @author GKSpencer
*/
public class KerberosAuthenticationFilter extends AbstractAuthenticationFilter implements Filter, CallbackHandler
public class KerberosAuthenticationFilter extends BaseKerberosAuthenticationFilter
{
// Debug logging
private static Log logger = LogFactory.getLog(KerberosAuthenticationFilter.class);
// Constants
//
// Default login configuration entry name
private static final String LoginConfigEntry = "AlfrescoHTTP";
// Locale object stored in the session
private static final String LOCALE = "locale";
public static final String MESSAGE_BUNDLE = "alfresco.messages.webclient";
// Debug logging
private static Log logger = LogFactory.getLog(KerberosAuthenticationFilter.class);
// Servlet context, required to get authentication service
private ServletContext m_context;
// File server configuration
private ServerConfigurationBean m_srvConfig;
// Various services required by the Kerberos authenticator
private AuthenticationService m_authService;
private AuthenticationComponent m_authComponent;
private PersonService m_personService;
private NodeService m_nodeService;
private TransactionService m_transactionService;
// Various services required by Kerberos authenticator
private ConfigService m_configService;
// Allow guest access
private boolean m_allowGuest;
// Login page address
private String m_loginPage;
// Random number generator used to generate challenge keys
private Random m_random = new Random(System.currentTimeMillis());
// Local server name, from either the file servers config or DNS host name
private String m_srvName;
// List of available locales (from the web-client configuration)
private List<String> m_languages;
// Kerberos settings
//
// Account name and password for server ticket
//
// The account name must be built from the HTTP server name, in the format :-
//
// HTTP/<server_name>@<realm>
private String m_accountName;
private String m_password;
// Kerberos realm and KDC address
private String m_krbRealm;
private String m_krbKDC;
// Login configuration entry name
private String m_loginEntryName = LoginConfigEntry;
// Server login context
private LoginContext m_loginContext;
// SPNEGO NegTokenInit blob, sent to the client in the SMB negotiate response
private byte[] m_negTokenInit;
/**
* Initialize the filter
*
@@ -180,77 +120,15 @@ public class KerberosAuthenticationFilter extends AbstractAuthenticationFilter i
*/
public void init(FilterConfig args) throws ServletException
{
// Save the servlet context, needed to get hold of the authentication service
// Call the base Kerberos filter initialization
super.init( args);
m_context = args.getServletContext();
// Setup the authentication context
WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(m_context);
ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
m_nodeService = serviceRegistry.getNodeService();
m_transactionService = serviceRegistry.getTransactionService();
m_authService = (AuthenticationService) ctx.getBean("AuthenticationService");
m_authComponent = (AuthenticationComponent) ctx.getBean("AuthenticationComponent");
m_personService = (PersonService) ctx.getBean("personService");
m_configService = (ConfigService) ctx.getBean("webClientConfigService");
m_srvConfig = (ServerConfigurationBean) ctx.getBean(ServerConfigurationBean.SERVER_CONFIGURATION);
// Check that the authentication component supports the required mode
if ( m_authComponent.getNTLMMode() != NTLMMode.MD4_PROVIDER &&
m_authComponent.getNTLMMode() != NTLMMode.PASS_THROUGH)
{
throw new ServletException("Required authentication mode not available");
}
// Get the local server name, try the file server config first
if ( m_srvConfig != null)
{
m_srvName = m_srvConfig.getServerName();
if ( m_srvName == null)
{
// CIFS server may not be running so the local server name has not been set, generate
// a server name
m_srvName = m_srvConfig.getLocalServerName(true) + "_A";
}
}
else
{
// Get the host name
try
{
// Get the local host name
m_srvName = InetAddress.getLocalHost().getHostName();
// Strip any domain name
int pos = m_srvName.indexOf(".");
if ( pos != -1)
m_srvName = m_srvName.substring(0, pos - 1);
}
catch (UnknownHostException ex)
{
// Log the error
if ( logger.isErrorEnabled())
logger.error("Kerberos filter, error getting local host name", ex);
}
}
// Check if the server name is valid
if ( m_srvName == null || m_srvName.length() == 0)
throw new ServletException("Failed to get local server name");
m_configService = (ConfigService)ctx.getBean("webClientConfigService");
// Get a list of the available locales
@@ -259,628 +137,86 @@ public class KerberosAuthenticationFilter extends AbstractAuthenticationFilter i
m_languages = config.getLanguages();
// Check if Kerberos is enabled, get the Kerberos KDC address
// Set hte login page address
String kdcAddress = args.getInitParameter("KDC");
if (kdcAddress != null && kdcAddress.length() > 0)
{
// Set the Kerberos KDC address
m_krbKDC = kdcAddress;
// Get the Kerberos realm
String krbRealm = args.getInitParameter("Realm");
if ( krbRealm != null && krbRealm.length() > 0)
{
// Set the Kerberos realm
m_krbRealm = krbRealm;
}
else
throw new ServletException("Kerberos realm not specified");
// Get the HTTP service account password
String srvPassword = args.getInitParameter("Password");
if ( srvPassword != null && srvPassword.length() > 0)
{
// Set the HTTP service account password
m_password = srvPassword;
}
else
throw new ServletException("HTTP service account password not specified");
// Get the login configuration entry name
String loginEntry = args.getInitParameter("LoginEntry");
if ( loginEntry != null)
{
if ( loginEntry.length() > 0)
{
// Set the login configuration entry name to use
m_loginEntryName = loginEntry;
}
else
throw new ServletException("Invalid login entry specified");
}
// Get the local host name
String localName = null;
try
{
localName = InetAddress.getLocalHost().getCanonicalHostName();
}
catch ( UnknownHostException ex)
{
throw new ServletException( "Failed to get local host name");
}
// Create a login context for the HTTP server service
try
{
// Login the HTTP server service
m_loginContext = new LoginContext( m_loginEntryName, this);
m_loginContext.login();
// DEBUG
if ( logger.isDebugEnabled())
logger.debug( "HTTP Kerberos login successful");
}
catch ( LoginException ex)
{
// Debug
if ( logger.isErrorEnabled())
logger.error("HTTP Kerberos web filter error", ex);
throw new ServletException("Failed to login HTTP server service");
}
// Get the HTTP service account name from the subject
Subject subj = m_loginContext.getSubject();
Principal princ = subj.getPrincipals().iterator().next();
m_accountName = princ.getName();
// DEBUG
if ( logger.isDebugEnabled())
logger.debug("Logged on using principal " + m_accountName);
// Create the Oid list for the SPNEGO NegTokenInit, include NTLMSSP for fallback
Vector<Oid> mechTypes = new Vector<Oid>();
mechTypes.add(OID.KERBEROS5);
mechTypes.add(OID.MSKERBEROS5);
// Build the SPNEGO NegTokenInit blob
try
{
// Build the mechListMIC principle
//
// Note: This field is not as specified
String mecListMIC = null;
StringBuilder mic = new StringBuilder();
mic.append( localName);
mic.append("$@");
mic.append( m_krbRealm);
mecListMIC = mic.toString();
// Build the SPNEGO NegTokenInit that contains the authentication types that the HTTP server accepts
NegTokenInit negTokenInit = new NegTokenInit(mechTypes, mecListMIC);
// Encode the NegTokenInit blob
m_negTokenInit = negTokenInit.encode();
}
catch (IOException ex)
{
// Debug
if ( logger.isErrorEnabled())
logger.error("Error creating SPNEGO NegTokenInit blob", ex);
throw new ServletException("Failed to create SPNEGO NegTokenInit blob");
}
}
setLoginPage( Application.getLoginPage(m_context));
}
/**
* Run the filter
*
* @param sreq ServletRequest
* @param sresp ServletResponse
* @param chain FilterChain
* @exception IOException
* @exception ServletException
/* (non-Javadoc)
* @see org.alfresco.repo.webdav.auth.BaseSSOAuthenticationFilter#createUserObject(java.lang.String, java.lang.String, org.alfresco.service.cmr.repository.NodeRef, java.lang.String)
*/
@Override
protected SessionUser createUserObject(String userName, String ticket, NodeRef personNode, String homeSpace) {
// Create a web client user object
User user = new User( userName, ticket, personNode);
user.setHomeSpaceId( homeSpace);
return user;
}
/* (non-Javadoc)
* @see org.alfresco.repo.webdav.auth.BaseNTLMAuthenticationFilter#onValidate(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpSession)
*/
public void doFilter(ServletRequest sreq, ServletResponse sresp, FilterChain chain) throws IOException,
ServletException
@Override
protected void onValidate(HttpServletRequest req, HttpSession session)
{
// Get the HTTP request/response/session
// Set the current locale from the Accept-Lanaguage header if available
HttpServletRequest req = (HttpServletRequest) sreq;
HttpServletResponse resp = (HttpServletResponse) sresp;
HttpSession httpSess = req.getSession(true);
// Check if there is an authorization header with an SPNEGO security blob
String authHdr = req.getHeader("Authorization");
boolean reqAuth = false;
if ( authHdr != null && authHdr.startsWith("Negotiate"))
reqAuth = true;
// Check if the user is already authenticated
User user = (User) httpSess.getAttribute(AuthenticationHelper.AUTHENTICATION_USER);
if ( user != null && reqAuth == false)
Locale userLocale = AbstractAuthenticationFilter.parseAcceptLanguageHeader(req, m_languages);
if (userLocale != null)
{
try
{
// Debug
if ( logger.isDebugEnabled())
logger.debug("User " + user.getUserName() + " validate ticket");
// Validate the user ticket
m_authService.validate( user.getTicket());
reqAuth = false;
// Set the current locale
I18NUtil.setLocale(Application.getLanguage(httpSess));
}
catch (AuthenticationException ex)
{
if ( logger.isErrorEnabled())
logger.error("Failed to validate user " + user.getUserName(), ex);
reqAuth = true;
}
session.setAttribute(LOCALE, userLocale);
session.removeAttribute(MESSAGE_BUNDLE);
}
// If the user has been validated and we do not require re-authentication then continue to
// the next filter
// Set the locale using the session
if ( reqAuth == false && user != null)
I18NUtil.setLocale(Application.getLanguage(session));
}
/* (non-Javadoc)
* @see org.alfresco.repo.webdav.auth.BaseNTLMAuthenticationFilter#onValidateFailed(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.servlet.http.HttpSession)
*/
@Override
protected void onValidateFailed(HttpServletRequest req, HttpServletResponse res, HttpSession session)
throws IOException
{
// Redirect to the login page if user validation fails
redirectToLoginPage(req, res);
}
/* (non-Javadoc)
* @see org.alfresco.repo.webdav.auth.BaseNTLMAuthenticationFilter#onLoginComplete(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@Override
protected boolean onLoginComplete(HttpServletRequest req, HttpServletResponse res)
throws IOException
{
// If the original URL requested was the login page then redirect to the browse view
if (req.getRequestURI().endsWith(getLoginPage()) == true)
{
// Debug
if (logger.isDebugEnabled())
logger.debug("Login page requested, redirecting to browse page");
if ( logger.isDebugEnabled())
logger.debug("Authentication not required, chaining ...");
// Chain to the next filter
chain.doFilter(sreq, sresp);
return;
}
// Check if the login page is being accessed, do not intercept the login page
if ( req.getRequestURI().endsWith(getLoginPage()) == true)
{
// Debug
if ( logger.isDebugEnabled())
logger.debug("Login page requested, chaining ...");
// Chain to the next filter
chain.doFilter( sreq, sresp);
return;
}
// Check if the browser is Opera, if so then display the login page as Opera does not
// support SPNEGO
String userAgent = req.getHeader("user-agent");
if ( userAgent != null && userAgent.indexOf("Opera ") != -1)
{
// Debug
if ( logger.isDebugEnabled())
logger.debug("Opera detected, redirecting to login page");
// Redirect to the login page
resp.sendRedirect(req.getContextPath() + "/faces" + getLoginPage());
return;
}
// Check the authorization header
if ( authHdr == null) {
// Debug
if ( logger.isDebugEnabled())
logger.debug("New Kerberos auth request from " + req.getRemoteHost() + " (" +
req.getRemoteAddr() + ":" + req.getRemotePort() + ")");
// Send back a request for SPNEGO authentication
resp.setHeader("WWW-Authenticate", "Negotiate");
resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
resp.flushBuffer();
// Redirect to the browse view
res.sendRedirect(req.getContextPath() + "/faces/jsp/browse/browse.jsp");
return false;
}
else
{
// Decode the received SPNEGO blob and validate
final byte[] spnegoByts = Base64.decodeBase64( authHdr.substring(10).getBytes());
// Check the received SPNEGO token type
int tokType = -1;
try
{
tokType = SPNEGO.checkTokenType( spnegoByts, 0, spnegoByts.length);
}
catch ( IOException ex)
{
}
// Check for a NegTokenInit blob
if ( tokType == SPNEGO.NegTokenInit)
{
// Parse the SPNEGO security blob to get the Kerberos ticket
NegTokenInit negToken = new NegTokenInit();
try
{
// Decode the security blob
negToken.decode( spnegoByts, 0, spnegoByts.length);
// Determine the authentication mechanism the client is using and logon
String oidStr = null;
if ( negToken.numberOfOids() > 0)
oidStr = negToken.getOidAt( 0).toString();
if ( oidStr != null && (oidStr.equals( OID.ID_MSKERBEROS5) || oidStr.equals(OID.ID_KERBEROS5)))
{
// Kerberos logon
if ( doKerberosLogon( negToken, req, resp, httpSess) != null)
{
// If the original URL requested was the login page then redirect to the browse view
if (req.getRequestURI().endsWith(getLoginPage()) == true)
{
// Debug
if ( logger.isDebugEnabled())
logger.debug("Login page requested, redirecting to browse page");
// Redirect to the browse view
resp.sendRedirect(req.getContextPath() + "/faces/jsp/browse/browse.jsp");
}
else
{
// Allow the user to access the requested page
chain.doFilter( req, resp);
}
}
else
{
// Send back a request for SPNEGO authentication
resp.setHeader("WWW-Authenticate", "Negotiate");
resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
resp.flushBuffer();
}
}
}
catch ( IOException ex)
{
// Log the error
if ( logger.isDebugEnabled())
logger.debug(ex);
}
}
else
{
// Unknown SPNEGO token type
if ( logger.isDebugEnabled())
logger.debug( "Unknown SPNEGO token type");
// Redirect to the login page
resp.sendRedirect(req.getContextPath() + "/faces" + getLoginPage());
}
return true;
}
}
/**
* Return the login page address
*
* @return String
/* (non-Javadoc)
* @see org.alfresco.repo.webdav.auth.BaseNTLMAuthenticationFilter#getLogger()
*/
private String getLoginPage()
@Override
final protected Log getLogger()
{
if (m_loginPage == null)
{
m_loginPage = Application.getLoginPage(m_context);
}
return m_loginPage;
}
/**
* Delete the servlet filter
*/
public void destroy()
{
}
/**
* JAAS callback handler
*
* @param callbacks Callback[]
* @exception IOException
* @exception UnsupportedCallbackException
*/
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException
{
// Process the callback list
for (int i = 0; i < callbacks.length; i++)
{
// Request for user name
if (callbacks[i] instanceof NameCallback)
{
NameCallback cb = (NameCallback) callbacks[i];
cb.setName( "");
}
// Request for password
else if (callbacks[i] instanceof PasswordCallback)
{
PasswordCallback cb = (PasswordCallback) callbacks[i];
cb.setPassword(m_password.toCharArray());
}
// Request for realm
else if (callbacks[i] instanceof RealmCallback)
{
RealmCallback cb = (RealmCallback) callbacks[i];
cb.setText(m_krbRealm);
}
else
{
throw new UnsupportedCallbackException(callbacks[i]);
}
}
}
/**
* Perform a Kerberos login and return an SPNEGO response
*
* @param negToken NegTokenInit
* @param req HttpServletRequest
* @param resp HttpServletResponse
* @param httpSess HttpSession
* @return NegTokenTarg
*/
private final NegTokenTarg doKerberosLogon( NegTokenInit negToken, HttpServletRequest req, HttpServletResponse resp, HttpSession httpSess)
{
// Authenticate the user
KerberosDetails krbDetails = null;
NegTokenTarg negTokenTarg = null;
UserTransaction tx = null;
try
{
// Run the session setup as a privileged action
SessionSetupPrivilegedAction sessSetupAction = new SessionSetupPrivilegedAction( m_accountName, negToken.getMechtoken());
Object result = Subject.doAs( m_loginContext.getSubject(), sessSetupAction);
if ( result != null)
{
// Access the Kerberos response
krbDetails = (KerberosDetails) result;
// Create the NegTokenTarg response blob
negTokenTarg = new NegTokenTarg( SPNEGO.AcceptCompleted, OID.KERBEROS5, krbDetails.getResponseToken());
// Check if the user has been authenticated, if so then setup the user environment
if ( negTokenTarg != null)
{
// Create a read transaction
tx = m_transactionService.getUserTransaction();
NodeRef homeSpaceRef = null;
User user = null;
String userName = null;
try
{
// Start the transaction
tx.begin();
// Setup User object and Home space ID etc.
NodeRef personNodeRef = m_personService.getPerson( krbDetails.getUserName());
// Use the system user to do the user name lookup
m_authComponent.setSystemUserAsCurrentUser();
// User name should match the uid in the person entry found
userName = (String) m_nodeService.getProperty(personNodeRef, ContentModel.PROP_USERNAME);
AuthenticationUtil.setCurrentUser( userName);
String currentTicket = m_authService.getCurrentTicket();
user = new User(userName, currentTicket, personNodeRef);
homeSpaceRef = (NodeRef) m_nodeService.getProperty( personNodeRef, ContentModel.PROP_HOMEFOLDER);
user.setHomeSpaceId(homeSpaceRef.getId());
// Commit
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 if(ex instanceof IOException)
{
throw (IOException)ex;
}
else if(ex instanceof ServletException)
{
throw (ServletException)ex;
}
else
{
throw new RuntimeException("Authentication setup failed", ex);
}
}
// Store the user
httpSess.setAttribute(AuthenticationHelper.AUTHENTICATION_USER, user);
httpSess.setAttribute(LoginBean.LOGIN_EXTERNAL_AUTH, Boolean.TRUE);
// Set the current locale from the Accept-Lanaguage header if available
Locale userLocale = parseAcceptLanguageHeader(req, m_languages);
if ( userLocale != null)
{
httpSess.setAttribute(LOCALE, userLocale);
httpSess.removeAttribute(MESSAGE_BUNDLE);
}
// Set the locale using the session
I18NUtil.setLocale(Application.getLanguage(httpSess));
// Debug
if ( logger.isDebugEnabled())
logger.debug("User " + userName + " logged on via Kerberos");
}
}
else
{
// Debug
if ( logger.isDebugEnabled())
logger.debug( "No SPNEGO response, Kerberos logon failed");
}
}
catch (Exception ex)
{
// Log the error
if ( logger.isDebugEnabled())
logger.debug("Kerberos logon error", ex);
}
// Return the response SPNEGO blob
return negTokenTarg;
}
/**
* Map the case insensitive logon name to the internal person object user name
*
* @param userName String
* @return String
*/
protected final String mapUserNameToPerson(String userName)
{
// Get the home folder for the user
UserTransaction tx = m_transactionService.getUserTransaction();
String personName = null;
try
{
tx.begin();
personName = m_personService.getUserIdentifier( userName);
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);
}
}
// Return the person name
return personName;
return logger;
}
}

View File

@@ -28,33 +28,20 @@ import java.io.IOException;
import java.util.List;
import java.util.Locale;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.transaction.UserTransaction;
import org.alfresco.config.ConfigService;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.jlan.server.auth.ntlm.NTLM;
import org.alfresco.jlan.server.auth.ntlm.NTLMMessage;
import org.alfresco.jlan.server.auth.ntlm.Type1NTLMMessage;
import org.alfresco.jlan.server.auth.ntlm.Type3NTLMMessage;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.SessionUser;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.repo.webdav.auth.BaseNTLMAuthenticationFilter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.LoginBean;
import org.alfresco.web.bean.repository.User;
import org.alfresco.web.config.LanguagesConfigElement;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.context.WebApplicationContext;
@@ -77,9 +64,6 @@ public class NTLMAuthenticationFilter extends BaseNTLMAuthenticationFilter
// Various services required by NTLM authenticator
private ConfigService m_configService;
// Login page address
private String m_loginPage;
// List of available locales (from the web-client configuration)
private List<String> m_languages;
@@ -95,150 +79,32 @@ public class NTLMAuthenticationFilter extends BaseNTLMAuthenticationFilter
super.init(args);
// Setup the authentication context
WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(m_context);
m_configService = (ConfigService)ctx.getBean("webClientConfigService");
// Get a list of the available locales
LanguagesConfigElement config = (LanguagesConfigElement) m_configService.
getConfig("Languages").getConfigElement(LanguagesConfigElement.CONFIG_ELEMENT_ID);
m_languages = config.getLanguages();
setLoginPage( Application.getLoginPage(m_context));
}
/**
* Run the filter
*
* @param sreq ServletRequest
* @param sresp ServletResponse
* @param chain FilterChain
* @exception IOException
* @exception ServletException
*/
public void doFilter(ServletRequest sreq, ServletResponse sresp, FilterChain chain) throws IOException,
ServletException
{
// Get the HTTP request/response/session
HttpServletRequest req = (HttpServletRequest) sreq;
HttpServletResponse resp = (HttpServletResponse) sresp;
HttpSession httpSess = req.getSession(true);
/* (non-Javadoc)
* @see org.alfresco.repo.webdav.auth.BaseSSOAuthenticationFilter#createUserObject(java.lang.String, java.lang.String, org.alfresco.service.cmr.repository.NodeRef, java.lang.String)
*/
@Override
protected SessionUser createUserObject(String userName, String ticket, NodeRef personNode, String homeSpace) {
// Check if there is an authorization header with an NTLM security blob
String authHdr = req.getHeader(AUTHORIZATION);
boolean reqAuth = (authHdr != null && authHdr.startsWith(AUTH_NTLM));
// Create a web client user object
// Check if the user is already authenticated
User user = (User) httpSess.getAttribute(AuthenticationHelper.AUTHENTICATION_USER);
if (user != null && reqAuth == false)
{
try
{
if (logger.isDebugEnabled())
logger.debug("User " + user.getUserName() + " validate ticket");
User user = new User( userName, ticket, personNode);
user.setHomeSpaceId( homeSpace);
// Validate the user ticket
m_authService.validate(user.getTicket());
reqAuth = false;
// Set the current locale
I18NUtil.setLocale(Application.getLanguage(httpSess));
}
catch (AuthenticationException ex)
{
if (logger.isErrorEnabled())
logger.error("Failed to validate user " + user.getUserName(), ex);
reqAuth = true;
}
}
// If the user has been validated and we do not require re-authentication then continue to
// the next filter
if (reqAuth == false && user != null)
{
if (logger.isDebugEnabled())
logger.debug("Authentication not required, chaining ...");
// Chain to the next filter
chain.doFilter(sreq, sresp);
return;
}
// Check if the login page is being accessed, do not intercept the login page
if (req.getRequestURI().endsWith(getLoginPage()) == true)
{
if (logger.isDebugEnabled())
logger.debug("Login page requested, chaining ...");
// Chain to the next filter
chain.doFilter( sreq, sresp);
return;
}
// Check if the browser is Opera, if so then display the login page as Opera does not
// support NTLM and displays an error page if a request to use NTLM is sent to it
String userAgent = req.getHeader("user-agent");
if (userAgent != null && userAgent.indexOf("Opera ") != -1)
{
if (logger.isDebugEnabled())
logger.debug("Opera detected, redirecting to login page");
redirectToLoginPage(req, resp);
return;
}
// Check the authorization header
if (authHdr == null)
{
if (logger.isDebugEnabled())
logger.debug("New NTLM auth request from " + req.getRemoteHost() + " (" +
req.getRemoteAddr() + ":" + req.getRemotePort() + ")");
// Send back a request for NTLM authentication
restartLoginChallenge(resp, httpSess);
}
else
{
// Decode the received NTLM blob and validate
final byte[] ntlmByts = Base64.decodeBase64(authHdr.substring(5).getBytes());
int ntlmTyp = NTLMMessage.isNTLMType(ntlmByts);
if (ntlmTyp == NTLM.Type1)
{
// Process the type 1 NTLM message
Type1NTLMMessage type1Msg = new Type1NTLMMessage(ntlmByts);
processType1(type1Msg, req, resp, httpSess);
}
else if (ntlmTyp == NTLM.Type3)
{
// Process the type 3 NTLM message
Type3NTLMMessage type3Msg = new Type3NTLMMessage(ntlmByts);
processType3(type3Msg, req, resp, httpSess, chain);
}
else
{
if (logger.isDebugEnabled())
logger.debug("NTLM blob not handled, redirecting to login page.");
redirectToLoginPage(req, resp);
}
}
}
/**
* Redirect to the login page
*/
protected void redirectToLoginPage(HttpServletRequest req, HttpServletResponse res) throws IOException
{
res.sendRedirect(req.getContextPath() + "/faces" + getLoginPage());
}
/* (non-Javadoc)
* @see org.alfresco.repo.webdav.auth.BaseNTLMAuthenticationFilter#getSessionUser(javax.servlet.http.HttpSession)
*/
@Override
protected SessionUser getSessionUser(HttpSession session)
{
return (SessionUser)session.getAttribute(AuthenticationHelper.AUTHENTICATION_USER);
}
return user;
}
/* (non-Javadoc)
* @see org.alfresco.repo.webdav.auth.BaseNTLMAuthenticationFilter#onValidate(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpSession)
@@ -247,7 +113,8 @@ public class NTLMAuthenticationFilter extends BaseNTLMAuthenticationFilter
protected void onValidate(HttpServletRequest req, HttpSession session)
{
// Set the current locale from the Accept-Lanaguage header if available
Locale userLocale = AbstractAuthenticationFilter.parseAcceptLanguageHeader(req, m_languages);
Locale userLocale = AbstractAuthenticationFilter.parseAcceptLanguageHeader(req, m_languages);
if (userLocale != null)
{
session.setAttribute(LOCALE, userLocale);
@@ -255,6 +122,7 @@ public class NTLMAuthenticationFilter extends BaseNTLMAuthenticationFilter
}
// Set the locale using the session
I18NUtil.setLocale(Application.getLanguage(session));
}
@@ -266,76 +134,8 @@ public class NTLMAuthenticationFilter extends BaseNTLMAuthenticationFilter
throws IOException
{
// Redirect to the login page if user validation fails
redirectToLoginPage(req, res);
}
/* (non-Javadoc)
* @see org.alfresco.repo.webdav.auth.BaseNTLMAuthenticationFilter#createUserEnvironment(javax.servlet.http.HttpSession, java.lang.String)
*/
@Override
protected SessionUser createUserEnvironment(HttpSession session, String userName)
throws IOException, ServletException
{
SessionUser user = null;
UserTransaction tx = m_transactionService.getUserTransaction();
try
{
tx.begin();
// Setup User object and Home space ID etc.
NodeRef personNodeRef = m_personService.getPerson(userName);
// Use the system user context to do the user lookup
m_authComponent.setCurrentUser(m_authComponent.getSystemUserName());
// User name should match the uid in the person entry found
m_authComponent.setSystemUserAsCurrentUser();
userName = (String) m_nodeService.getProperty(personNodeRef, ContentModel.PROP_USERNAME);
m_authComponent.setCurrentUser(userName);
String currentTicket = m_authService.getCurrentTicket();
user = new User(userName, currentTicket, personNodeRef);
NodeRef homeSpaceRef = (NodeRef) m_nodeService.getProperty(personNodeRef, ContentModel.PROP_HOMEFOLDER);
((User)user).setHomeSpaceId(homeSpaceRef.getId());
tx.commit();
}
catch (Throwable ex)
{
try
{
tx.rollback();
}
catch (Exception err)
{
logger.error("Failed to rollback transaction", err);
}
if (ex instanceof RuntimeException)
{
throw (RuntimeException)ex;
}
else if (ex instanceof IOException)
{
throw (IOException)ex;
}
else if (ex instanceof ServletException)
{
throw (ServletException)ex;
}
else
{
throw new RuntimeException("Authentication setup failed", ex);
}
}
// Store the user on the session
session.setAttribute(AuthenticationHelper.AUTHENTICATION_USER, user);
session.setAttribute(LoginBean.LOGIN_EXTERNAL_AUTH, Boolean.TRUE);
return user;
redirectToLoginPage(req, res);
}
/* (non-Javadoc)
@@ -361,21 +161,6 @@ public class NTLMAuthenticationFilter extends BaseNTLMAuthenticationFilter
}
}
/**
* Return the login page address
*
* @return String
*/
private String getLoginPage()
{
if (m_loginPage == null)
{
m_loginPage = Application.getLoginPage(m_context);
}
return m_loginPage;
}
/* (non-Javadoc)
* @see org.alfresco.repo.webdav.auth.BaseNTLMAuthenticationFilter#getLogger()
*/

View File

@@ -0,0 +1,118 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.web.app.servlet;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.config.ConfigService;
import org.alfresco.util.URLDecoder;
import org.alfresco.web.scripts.Match;
import org.alfresco.web.scripts.RuntimeContainer;
import org.alfresco.web.scripts.Description.RequiredAuthentication;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
/**
* WebScript aware NTLM Authentication Filter Class.
*
* Takes into account the authentication setting in the descriptor for the webscript.
* If authentication is not required then simply chains. Otherwise will delegate
* back to the usual web-client NTLM filter code path.
*
* @author Kevin Roast
*/
public class WebScriptNTLMAuthenticationFilter extends NTLMAuthenticationFilter
{
private RuntimeContainer container;
/**
* Initialize the filter
*
* @param args FilterConfig
* @exception ServletException
*/
public void init(FilterConfig args) throws ServletException
{
super.init(args);
ApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(args.getServletContext());
ConfigService configService = (ConfigService)context.getBean("web.config");
String containerName = args.getInitParameter("container");
if (containerName == null)
{
containerName = "webscripts.container";
}
container = (RuntimeContainer)context.getBean(containerName);
}
/**
* Run the filter
*
* @param sreq ServletRequest
* @param sresp ServletResponse
* @param chain FilterChain
*
* @exception IOException
* @exception ServletException
*/
public void doFilter(ServletRequest sreq, ServletResponse sres, FilterChain chain)
throws IOException, ServletException
{
// Get the HTTP request/response
HttpServletRequest req = (HttpServletRequest)sreq;
HttpServletResponse res = (HttpServletResponse)sres;
// find a webscript match for the requested URI
String requestURI = req.getRequestURI();
String pathInfo = requestURI.substring((req.getContextPath() + req.getServletPath()).length());
if (getLogger().isDebugEnabled())
getLogger().debug("Processing request: " + requestURI + " SID:" + req.getSession().getId());
Match match = container.getRegistry().findWebScript(req.getMethod(), URLDecoder.decode(pathInfo));
if (match != null && match.getWebScript() != null)
{
// check the authentication required - if none then we don't want any of
// the filters down the chain to require any authentication checks
if (RequiredAuthentication.none == match.getWebScript().getDescription().getRequiredAuthentication())
{
if (getLogger().isDebugEnabled())
getLogger().debug("Found webscript with no authentication - set NO_AUTH_REQUIRED flag.");
req.setAttribute(AbstractAuthenticationFilter.NO_AUTH_REQUIRED, Boolean.TRUE);
}
}
super.doFilter(sreq, sres, chain);
}
}

View File

@@ -27,6 +27,7 @@ package org.alfresco.web.bean.admin;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -39,6 +40,7 @@ import javax.faces.model.SelectItem;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.domain.PropertyValue;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.avm.AVMService;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
@@ -57,6 +59,7 @@ import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ISO9075;
import org.alfresco.web.app.servlet.DownloadContentServlet;
import org.alfresco.web.bean.repository.Repository;
@@ -105,6 +108,7 @@ public class AdminNodeBrowseBean implements Serializable
transient private DataModel avmStoreProps = null;
// supporting repository services
transient private TransactionService transactionService;
transient private NodeService nodeService;
transient private DictionaryService dictionaryService;
transient private SearchService searchService;
@@ -112,6 +116,23 @@ public class AdminNodeBrowseBean implements Serializable
transient private PermissionService permissionService;
transient private AVMService avmService;
/**
* @param transactionService transaction service
*/
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
private TransactionService getTransactionService()
{
if (transactionService == null)
{
transactionService = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getTransactionService();
}
return transactionService;
}
/**
* @param nodeService node service
*/
@@ -529,7 +550,7 @@ public class AdminNodeBrowseBean implements Serializable
*
* @return query languages
*/
public List getQueryLanguages()
public List<SelectItem> getQueryLanguages()
{
return queryLanguages;
}
@@ -667,30 +688,38 @@ public class AdminNodeBrowseBean implements Serializable
*/
public String submitSearch()
{
try
RetryingTransactionCallback<String> searchCallback = new RetryingTransactionCallback<String>()
{
if (queryLanguage.equals("noderef"))
public String execute() throws Throwable
{
// ensure node exists
NodeRef nodeRef = new NodeRef(query);
boolean exists = getNodeService().exists(nodeRef);
if (!exists)
if (queryLanguage.equals("noderef"))
{
throw new AlfrescoRuntimeException("Node " + nodeRef + " does not exist.");
// ensure node exists
NodeRef nodeRef = new NodeRef(query);
boolean exists = getNodeService().exists(nodeRef);
if (!exists)
{
throw new AlfrescoRuntimeException("Node " + nodeRef + " does not exist.");
}
setNodeRef(nodeRef);
return "node";
}
setNodeRef(nodeRef);
return "node";
}
else if (queryLanguage.equals("selectnodes"))
{
List<NodeRef> nodes = getSearchService().selectNodes(getNodeRef(), query, null, getNamespaceService(), false);
searchResults = new SearchResults(nodes);
else if (queryLanguage.equals("selectnodes"))
{
List<NodeRef> nodes = getSearchService().selectNodes(getNodeRef(), query, null, getNamespaceService(), false);
searchResults = new SearchResults(nodes);
return "search";
}
// perform search
searchResults = new SearchResults(getSearchService().query(getNodeRef().getStoreRef(), queryLanguage, query));
return "search";
}
};
// perform search
searchResults = new SearchResults(getSearchService().query(getNodeRef().getStoreRef(), queryLanguage, query));
return "search";
try
{
return getTransactionService().getRetryingTransactionHelper().doInTransaction(searchCallback, true);
}
catch (Throwable e)
{
@@ -724,6 +753,7 @@ public class AdminNodeBrowseBean implements Serializable
* @param name property name
* @param value property values
*/
@SuppressWarnings("unchecked")
public Property(QName name, Serializable value)
{
this.name = name;
@@ -740,19 +770,20 @@ public class AdminNodeBrowseBean implements Serializable
}
// handle multi/single values
// TODO: perhaps this is not the most efficient way - lots of list creations
List<Value> values = new ArrayList<Value>();
final List<Value> values;
if (value instanceof Collection)
{
Collection<Serializable> oldValues = (Collection<Serializable>) value;
values = new ArrayList<Value>(oldValues.size());
isCollection = true;
for (Serializable multiValue : (Collection<Serializable>) value)
for (Serializable multiValue : oldValues)
{
values.add(new Value(multiValue));
}
}
else
{
values.add(new Value(value));
values = Collections.singletonList(new Value(value));
}
this.values = new ListDataModel(values);
}

View File

@@ -1863,6 +1863,10 @@
<managed-bean-name>AdminNodeBrowseBean</managed-bean-name>
<managed-bean-class>org.alfresco.web.bean.admin.AdminNodeBrowseBean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<property-name>transactionService</property-name>
<value>#{TransactionService}</value>
</managed-property>
<managed-property>
<property-name>nodeService</property-name>
<value>#{NodeService}</value>

View File

@@ -100,6 +100,14 @@
-->
</filter>
<!-- For NTLM authentication support use the following filter, also see the filter-mapping section -->
<!--
<filter>
<filter-name>WebScript NTLM Authentication Filter</filter-name>
<filter-class>org.alfresco.web.app.servlet.WebScriptNTLMAuthenticationFilter</filter-class>
</filter>
-->
<filter>
<filter-name>WebDAV Authentication Filter</filter-name>
<filter-class>org.alfresco.repo.webdav.auth.AuthenticationFilter</filter-class>
@@ -177,12 +185,12 @@
</filter-mapping>
<filter-mapping>
<filter-name>Authentication Filter</filter-name>
<filter-name>WebScript NTLM Authentication Filter</filter-name>
<url-pattern>/wcservice/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Authentication Filter</filter-name>
<filter-name>WebScript NTLM Authentication Filter</filter-name>
<url-pattern>/wcs/*</url-pattern>
</filter-mapping>