SEC-8: Introduction into CVS of contribution posted with the JIRA task and basic reformatting.

This commit is contained in:
Ben Alex 2005-11-03 12:32:19 +00:00
parent e9b1d9452f
commit eae6f81177
9 changed files with 743 additions and 0 deletions

View File

@ -63,5 +63,6 @@
<classpathentry kind="src" path="samples/annotations/src/main/resources"/>
<classpathentry kind="src" path="samples/annotations/src/test/java"/>
<classpathentry kind="src" path="samples/contacts-tiger/src/main/java"/>
<classpathentry kind="var" path="MAVEN_REPO/jcifs/jars/jcifs-1.2.6.jar"/>
<classpathentry kind="output" path="target/eclipseclasses"/>
</classpath>

View File

@ -310,6 +310,13 @@
<war.bundle>true</war.bundle>
</properties>
</dependency>
<dependency>
<groupId>jcifs</groupId>
<artifactId>jcifs</artifactId>
<version>1.2.6</version>
<type>jar</type>
<url>http://jcifs.samba.org/</url>
</dependency>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjrt</artifactId>

View File

@ -0,0 +1,121 @@
/* Copyright 2004, 2005 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sf.acegisecurity.providers.smb;
import jcifs.UniAddress;
import jcifs.smb.NtlmPasswordAuthentication;
import jcifs.smb.SmbAuthException;
import jcifs.smb.SmbException;
import jcifs.smb.SmbSession;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.AuthenticationException;
import net.sf.acegisecurity.AuthenticationServiceException;
import net.sf.acegisecurity.BadCredentialsException;
import net.sf.acegisecurity.providers.AuthenticationProvider;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* An {@link AuthenticationProvider} implementation that relies on <a
* href="http://www.jcifs.org">jcifs</a> in order to provide an authentication
* service on a Windows network. This implementation relies on a {@link
* #setAuthorizationProvider(AuthenticationProvider) delegate provider} in
* order for authorization information to be filled into the authorized {@link
* Authentication token}. Subclasses must implement the logic that {@link
* #getNtlmPasswordAuthentication(Authentication) extracts the jcifs }{@link
* NtlmPasswordAuthentication } object from the particular {@link
* Authentication } token implementation and the one that {@link
* #getDomainController(Authentication, NtlmPasswordAuthentication) extracts
* the domain controller address }.
*
* @author Davide Baroncelli
* @version $Id$
*/
public abstract class AbstractSmbAuthenticationProvider
implements AuthenticationProvider {
//~ Instance fields ========================================================
private AuthenticationProvider authorizationProvider;
private Log log = LogFactory.getLog(this.getClass());
//~ Methods ================================================================
/**
* DOCUMENT ME!
*
* @param authorizationProvider The {@link AuthenticationProvider } which
* will be contacted in order for it to fill authorization info in
* the (already authenticated) {@link Authentication } object that
* they will be passed.
*/
public void setAuthorizationProvider(
AuthenticationProvider authorizationProvider) {
this.authorizationProvider = authorizationProvider;
}
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
NtlmPasswordAuthentication ntlm = getNtlmPasswordAuthentication(authentication);
UniAddress dc = getDomainController(authentication, ntlm);
return performAuthentication(dc, ntlm, authentication);
}
protected abstract UniAddress getDomainController(
Authentication authentication,
NtlmPasswordAuthentication ntlmAuthentication);
protected abstract NtlmPasswordAuthentication getNtlmPasswordAuthentication(
Authentication authentication);
protected Authentication performAuthentication(UniAddress dc,
NtlmPasswordAuthentication ntlm, Authentication authentication) {
try {
// this performs authentication...
SmbSession.logon(dc, ntlm);
if (log.isDebugEnabled()) {
log.debug(ntlm + " successfully authenticated against " + dc);
}
// ...and this performs authorization.
Authentication authorizedResult = authorizationProvider
.authenticate(authentication);
return authorizedResult;
} catch (SmbException se) {
log.error(ntlm.getName() + ": 0x"
+ jcifs.util.Hexdump.toHexString(se.getNtStatus(), 8) + ": "
+ se);
if (se instanceof SmbAuthException) {
SmbAuthException sae = (SmbAuthException) se;
if (se.getNtStatus() == SmbAuthException.NT_STATUS_ACCESS_VIOLATION) {
throw new ChallengeExpiredException(sae.getMessage(), sae);
} else {
throw new BadCredentialsException(sae.getMessage(), sae);
}
} else {
throw new AuthenticationServiceException(se.getMessage(), se);
}
}
}
}

View File

@ -0,0 +1,37 @@
/* Copyright 2004, 2005 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sf.acegisecurity.providers.smb;
import net.sf.acegisecurity.AuthenticationException;
/**
* Thrown if
*
* @author Davide Baroncelli
* @version $Id$
*/
public class ChallengeExpiredException extends AuthenticationException {
//~ Constructors ===========================================================
public ChallengeExpiredException(String msg) {
super(msg);
}
public ChallengeExpiredException(String msg, Throwable t) {
super(msg, t);
}
}

View File

@ -0,0 +1,86 @@
/* Copyright 2004, 2005 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sf.acegisecurity.providers.smb;
import jcifs.UniAddress;
import jcifs.smb.NtlmPasswordAuthentication;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.GrantedAuthority;
import net.sf.acegisecurity.providers.AbstractAuthenticationToken;
/**
* {@link Authentication } implementation for NTLM smb authentication.
*
* @author Davide Baroncelli
* @version $Id$
*
* @see net.sf.acegisecurity.ui.ntlm.NtlmProcessingFilter
* @see net.sf.acegisecurity.providers.smb.SmbNtlmAuthenticationProvider
*/
public class NtlmAuthenticationToken extends AbstractAuthenticationToken {
//~ Instance fields ========================================================
private NtlmPasswordAuthentication ntlmPasswordAuthentication;
private transient UniAddress domainController;
private GrantedAuthority[] authorities;
private boolean authenticated;
//~ Constructors ===========================================================
public NtlmAuthenticationToken(
NtlmPasswordAuthentication ntlmPasswordAuthentication,
UniAddress domainController) {
this.ntlmPasswordAuthentication = ntlmPasswordAuthentication;
this.domainController = domainController;
}
//~ Methods ================================================================
public void setAuthenticated(boolean isAuthenticated) {
this.authenticated = isAuthenticated;
}
public boolean isAuthenticated() {
return authenticated;
}
public void setAuthorities(GrantedAuthority[] authorities) {
this.authorities = authorities;
}
public GrantedAuthority[] getAuthorities() {
return authorities;
}
public Object getCredentials() {
return ntlmPasswordAuthentication.getPassword();
}
public UniAddress getDomainController() {
return domainController;
}
public NtlmPasswordAuthentication getNtlmPasswordAuthentication() {
return ntlmPasswordAuthentication;
}
public Object getPrincipal() {
return ntlmPasswordAuthentication.getUsername();
}
}

View File

@ -0,0 +1,97 @@
/* Copyright 2004, 2005 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sf.acegisecurity.providers.smb;
import jcifs.Config;
import jcifs.UniAddress;
import jcifs.smb.NtlmPasswordAuthentication;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.BadCredentialsException;
import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import java.net.UnknownHostException;
/**
* Provides authentication of a basic {@link
* UsernamePasswordAuthenticationToken } on a ntlm domain via smb/cifs.
*
* @author Davide Baroncelli
* @version $Id$
*/
public class SmbBasicAuthenticationProvider
extends AbstractSmbAuthenticationProvider {
//~ Instance fields ========================================================
String domainController;
//~ Methods ================================================================
public void setDomainController(String domainController) {
this.domainController = domainController;
}
public boolean supports(Class authentication) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
protected UniAddress getDomainController(Authentication authentication,
NtlmPasswordAuthentication ntlmAuthentication) {
try {
if (domainController == null) {
domainController = Config.getProperty("jcifs.smb.client.domain");
}
String domain = domainController;
if (domain == null) {
domain = ntlmAuthentication.getDomain();
}
UniAddress dc = UniAddress.getByName(domain, true);
return dc;
} catch (UnknownHostException uhe) {
throw new BadCredentialsException(
"no host could be found for the name "
+ ntlmAuthentication.getDomain(), uhe);
}
}
protected NtlmPasswordAuthentication getNtlmPasswordAuthentication(
Authentication authentication) {
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
String username = token.getPrincipal().toString();
String password = (String) token.getCredentials();
int index = username.indexOf('\\');
if (index == -1) {
index = username.indexOf('/');
}
// if domain is null then the jcifs default is used
// (this is set through the "jcifs.smb.client.domain" Config property)
String domain = (index != -1) ? username.substring(0, index) : null;
username = (index != -1) ? username.substring(index + 1) : username;
NtlmPasswordAuthentication ntlm = new NtlmPasswordAuthentication(domain,
username, password);
return ntlm;
}
}

View File

@ -0,0 +1,60 @@
/* Copyright 2004, 2005 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sf.acegisecurity.providers.smb;
import jcifs.UniAddress;
import jcifs.smb.NtlmPasswordAuthentication;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.ui.ntlm.NtlmProcessingFilter;
/**
* This class provides authentication through smb of {@link
* NtlmAuthenticationToken } (i.e. tokens obtained through the NTLM
* Authorization method by {@link NtlmProcessingFilter } ).
*
* @author Davide Baroncelli
* @version $Id$
*
* @see net.sf.acegisecurity.ui.ntlm.NtlmProcessingFilter
*/
public class SmbNtlmAuthenticationProvider
extends AbstractSmbAuthenticationProvider {
//~ Methods ================================================================
public boolean supports(Class authentication) {
return NtlmAuthenticationToken.class.isAssignableFrom(authentication);
}
protected UniAddress getDomainController(Authentication authentication,
NtlmPasswordAuthentication ntlmAuthentication) {
NtlmAuthenticationToken ntlmToken = (NtlmAuthenticationToken) authentication;
UniAddress dc = ntlmToken.getDomainController();
return dc;
}
protected NtlmPasswordAuthentication getNtlmPasswordAuthentication(
Authentication authentication) {
NtlmAuthenticationToken ntlmToken = (NtlmAuthenticationToken) authentication;
NtlmPasswordAuthentication ntlm = ntlmToken
.getNtlmPasswordAuthentication();
return ntlm;
}
}

View File

@ -0,0 +1,287 @@
/*
* LICENSE IS UNKNOWN (SEE TODO COMMENT LATER IN SOURCE CODE)
*/
package net.sf.acegisecurity.ui.ntlm;
import jcifs.Config;
import jcifs.UniAddress;
import jcifs.http.NtlmSsp;
import jcifs.smb.NtlmChallenge;
import jcifs.smb.NtlmPasswordAuthentication;
import jcifs.smb.SmbSession;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.AuthenticationException;
import net.sf.acegisecurity.AuthenticationManager;
import net.sf.acegisecurity.BadCredentialsException;
import net.sf.acegisecurity.context.SecurityContextHolder;
import net.sf.acegisecurity.intercept.web.AuthenticationEntryPoint;
import net.sf.acegisecurity.providers.smb.NtlmAuthenticationToken;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import java.io.IOException;
import java.util.Iterator;
import java.util.Properties;
import javax.servlet.Filter;
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;
/**
* A reimplementation of the jcifs NtlmHttpFilter suitable for use with the
* Acegi Security System.
*
* <p>
* This servlet Filter can be used to negotiate password hashes with MSIE
* clients using NTLM SSP. This is similar to <code>Authentication:
* BASIC</code> but weakly encrypted and without requiring the user to
* re-supply authentication credentials.
* </p>
*
* @author Davide Baroncelli
* @version $Id$
*/
public class NtlmProcessingFilter implements Filter, InitializingBean {
//~ Static fields/initializers =============================================
private static final String CHALLENGE_ATTR_NAME = "NtlmHttpChal";
//~ Instance fields ========================================================
private AuthenticationEntryPoint authenticationEntryPoint;
private AuthenticationManager authenticationManager;
// TODO: Verify licensing, as original contributor reported that large parts of this code where taken from jCifs NtmlHttpFilter: can this be re-licensed to APL (?).
private Log log = LogFactory.getLog(this.getClass());
private String defaultDomain;
private String domainController;
private boolean loadBalance;
//~ Methods ================================================================
/**
* DOCUMENT ME!
*
* @param authenticationEntryPoint The entry point that will be called if
* the "transparent" authentication fails for some reason: don't
* use the same {@link NtlmProcessingFilterEntryPoint} that is used
* in order to commence the NTLM authentication or the user's
* browser would probably loop
*/
public void setAuthenticationEntryPoint(
AuthenticationEntryPoint authenticationEntryPoint) {
this.authenticationEntryPoint = authenticationEntryPoint;
}
public void setAuthenticationManager(
AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
/**
* DOCUMENT ME!
*
* @param defaultDomain The domain that will be specified as part of the
* authentication credentials if not specified by the username.
*/
public void setDefaultDomain(String defaultDomain) {
this.defaultDomain = defaultDomain;
}
/**
* DOCUMENT ME!
*
* @param domainController The domain controller address: if not set the
* default domain is used.
*/
public void setDomainController(String domainController) {
this.domainController = domainController;
}
/**
* DOCUMENT ME!
*
* @param properties A {@link Properties} object whose properties with
* names starting with "jcifs." will be set into the jCifs {@link
* Config}.
*/
public void setJCifsProperties(Properties properties) {
for (Iterator iterator = properties.keySet().iterator();
iterator.hasNext();) {
String propertyName = (String) iterator.next();
String propertyValue = properties.getProperty(propertyName);
if (propertyName.startsWith("jcifs.")) {
if (log.isInfoEnabled()) {
log.info("setting jcifs property " + propertyName + ":"
+ propertyValue);
}
Config.setProperty(propertyName, propertyValue);
} else {
if (log.isInfoEnabled()) {
log.info("ignoring non-jcifs property " + propertyName
+ ":" + propertyValue);
}
}
}
}
public void setLoadBalance(boolean loadBalance) {
this.loadBalance = loadBalance;
}
public void afterPropertiesSet() throws Exception {
// Set jcifs properties we know we want; soTimeout and cachePolicy to 10min
Config.setProperty("jcifs.smb.client.soTimeout", "300000");
Config.setProperty("jcifs.netbios.cachePolicy", "1200");
if (domainController == null) {
domainController = defaultDomain;
}
if (defaultDomain != null) {
Config.setProperty("jcifs.smb.client.domain", defaultDomain);
}
Assert.notNull(authenticationEntryPoint,
"The authenticationEntryPoint property must be set before "
+ "NtlmProcessingFilter bean initialization");
Assert.notNull(authenticationManager,
"The authenticationManager property must be set before "
+ "NtlmProcessingFilter bean initialization");
}
public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
NtlmPasswordAuthentication ntlm = null;
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
try {
String msg = req.getHeader("Authorization");
UniAddress dc = null;
// the "basic" authentication case (both secure + insecure) originally in jcifs NtlmFilter has been
// refactored out, in order for it to be supported by the acegi BasicProcessingFilter +
// SmbNtlmAuthenticationProvider ( + SecureChannelProcessor) combination */
if ((msg != null) && (msg.startsWith("NTLM "))) {
if (log.isDebugEnabled()) {
log.debug("NTLM Authorization header received");
}
HttpSession ssn = req.getSession();
byte[] challenge;
if (loadBalance) {
NtlmChallenge chal = (NtlmChallenge) ssn.getAttribute(CHALLENGE_ATTR_NAME);
if (chal == null) {
chal = SmbSession.getChallengeForDomain();
if (log.isDebugEnabled()) {
log.debug(
"got load balanced challenge for domain: "
+ chal);
}
ssn.setAttribute(CHALLENGE_ATTR_NAME, chal);
}
dc = chal.dc;
challenge = chal.challenge;
} else {
// no challenge in session, here: the server itself keeps the challenge alive for a certain time
dc = UniAddress.getByName(domainController, true);
challenge = SmbSession.getChallenge(dc);
if (log.isDebugEnabled()) {
log.debug("domain controller is " + dc
+ ", challenge is " + challenge);
}
}
ntlm = NtlmSsp.authenticate(req, resp, challenge);
if (ntlm == null) {
if (log.isDebugEnabled()) {
log.debug(
"null ntlm authentication results: sending challenge to browser");
}
return; // this means we must send the challenge to the browser
} else {
if (log.isDebugEnabled()) {
log.debug("ntlm negotiation complete");
}
ssn.removeAttribute(CHALLENGE_ATTR_NAME); /* negotiation complete, remove the challenge object */
}
NtlmAuthenticationToken ntlmToken = newNtlmAuthenticationToken(ntlm,
dc);
Authentication authResult = authenticationManager.authenticate(ntlmToken);
if (log.isDebugEnabled()) {
log.debug("ntlm token authenticated ");
}
successfulAuthentication(req, resp, authResult);
}
} catch (AuthenticationException ae) {
unsuccessfulAuthentication(req, resp, ntlm, ae);
return;
}
chain.doFilter(request, response);
}
public void init(FilterConfig filterConfig) throws ServletException {}
protected NtlmAuthenticationToken newNtlmAuthenticationToken(
NtlmPasswordAuthentication ntlm, UniAddress dc) {
return new NtlmAuthenticationToken(ntlm, dc);
}
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response, Authentication authResult) {
if (log.isDebugEnabled()) {
log.debug("Authentication success: " + authResult.toString());
}
SecurityContextHolder.getContext().setAuthentication(authResult);
}
protected void unsuccessfulAuthentication(HttpServletRequest req,
HttpServletResponse resp, NtlmPasswordAuthentication ntlm,
AuthenticationException ae) throws IOException, ServletException {
if (log.isDebugEnabled()) {
log.debug("Authentication request for user: " + ntlm.getUsername()
+ " failed: " + ae.toString());
}
SecurityContextHolder.getContext().setAuthentication(null);
authenticationEntryPoint.commence(req, resp,
new BadCredentialsException(ae.getMessage(), ae));
}
}

View File

@ -0,0 +1,47 @@
/* Copyright 2004, 2005 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sf.acegisecurity.ui.ntlm;
import net.sf.acegisecurity.AuthenticationException;
import net.sf.acegisecurity.intercept.web.AuthenticationEntryPoint;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
/**
* An entry point for the NTLM authentication process.
*
* @author Davide Baroncelli
* @version $Id$
*/
public class NtlmProcessingFilterEntryPoint implements AuthenticationEntryPoint {
//~ Methods ================================================================
public void commence(ServletRequest request, ServletResponse response,
AuthenticationException authException)
throws IOException, ServletException {
HttpServletResponse resp = (HttpServletResponse) response;
resp.setHeader("WWW-Authenticate", "NTLM");
resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
resp.sendError(HttpServletResponse.SC_UNAUTHORIZED,
(authException != null) ? authException.getMessage() : "");
}
}