diff --git a/.classpath b/.classpath index 15e781231f..1b59dde815 100644 --- a/.classpath +++ b/.classpath @@ -63,5 +63,6 @@ + diff --git a/project.xml b/project.xml index 49f063d1fe..848b3841f9 100644 --- a/project.xml +++ b/project.xml @@ -310,6 +310,13 @@ true + + jcifs + jcifs + 1.2.6 + jar + http://jcifs.samba.org/ + aspectj aspectjrt diff --git a/sandbox/src/main/java/org/acegisecurity/providers/smb/AbstractSmbAuthenticationProvider.java b/sandbox/src/main/java/org/acegisecurity/providers/smb/AbstractSmbAuthenticationProvider.java new file mode 100644 index 0000000000..7c8d1866a9 --- /dev/null +++ b/sandbox/src/main/java/org/acegisecurity/providers/smb/AbstractSmbAuthenticationProvider.java @@ -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 jcifs 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); + } + } + } +} diff --git a/sandbox/src/main/java/org/acegisecurity/providers/smb/ChallengeExpiredException.java b/sandbox/src/main/java/org/acegisecurity/providers/smb/ChallengeExpiredException.java new file mode 100644 index 0000000000..698c8cac11 --- /dev/null +++ b/sandbox/src/main/java/org/acegisecurity/providers/smb/ChallengeExpiredException.java @@ -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); + } +} diff --git a/sandbox/src/main/java/org/acegisecurity/providers/smb/NtlmAuthenticationToken.java b/sandbox/src/main/java/org/acegisecurity/providers/smb/NtlmAuthenticationToken.java new file mode 100644 index 0000000000..a5721ca6a9 --- /dev/null +++ b/sandbox/src/main/java/org/acegisecurity/providers/smb/NtlmAuthenticationToken.java @@ -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(); + } +} diff --git a/sandbox/src/main/java/org/acegisecurity/providers/smb/SmbBasicAuthenticationProvider.java b/sandbox/src/main/java/org/acegisecurity/providers/smb/SmbBasicAuthenticationProvider.java new file mode 100644 index 0000000000..77adc25c70 --- /dev/null +++ b/sandbox/src/main/java/org/acegisecurity/providers/smb/SmbBasicAuthenticationProvider.java @@ -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; + } +} diff --git a/sandbox/src/main/java/org/acegisecurity/providers/smb/SmbNtlmAuthenticationProvider.java b/sandbox/src/main/java/org/acegisecurity/providers/smb/SmbNtlmAuthenticationProvider.java new file mode 100644 index 0000000000..077794ab27 --- /dev/null +++ b/sandbox/src/main/java/org/acegisecurity/providers/smb/SmbNtlmAuthenticationProvider.java @@ -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; + } +} diff --git a/sandbox/src/main/java/org/acegisecurity/ui/ntlm/NtlmProcessingFilter.java b/sandbox/src/main/java/org/acegisecurity/ui/ntlm/NtlmProcessingFilter.java new file mode 100644 index 0000000000..41b3c99d4f --- /dev/null +++ b/sandbox/src/main/java/org/acegisecurity/ui/ntlm/NtlmProcessingFilter.java @@ -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. + * + *

+ * This servlet Filter can be used to negotiate password hashes with MSIE + * clients using NTLM SSP. This is similar to Authentication: + * BASIC but weakly encrypted and without requiring the user to + * re-supply authentication credentials. + *

+ * + * @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)); + } +} diff --git a/sandbox/src/main/java/org/acegisecurity/ui/ntlm/NtlmProcessingFilterEntryPoint.java b/sandbox/src/main/java/org/acegisecurity/ui/ntlm/NtlmProcessingFilterEntryPoint.java new file mode 100644 index 0000000000..20baa26792 --- /dev/null +++ b/sandbox/src/main/java/org/acegisecurity/ui/ntlm/NtlmProcessingFilterEntryPoint.java @@ -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() : ""); + } +}