2
0
Эх сурвалжийг харах

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

Ben Alex 20 жил өмнө
parent
commit
eae6f81177

+ 1 - 0
.classpath

@@ -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>

+ 7 - 0
project.xml

@@ -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>

+ 121 - 0
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 <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);
+            }
+        }
+    }
+}

+ 37 - 0
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);
+    }
+}

+ 86 - 0
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();
+    }
+}

+ 97 - 0
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;
+    }
+}

+ 60 - 0
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;
+    }
+}

+ 287 - 0
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.
+ * 
+ * <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));
+    }
+}

+ 47 - 0
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() : "");
+    }
+}