Index: xdocs/guides/index.xml =================================================================== --- xdocs/guides/index.xml (revision 537207) +++ xdocs/guides/index.xml (working copy) @@ -47,7 +47,8 @@
  • Guide to application servers configuration
  • Guide to configuring Jetspeed-2 security
  • Guide to using Jetspeed-2 single sign-on
  • -
  • Guide to defining user attributes (PLT.17 user information configuration)
  • +
  • Guide to using NTLM Authentication
  • +
  • Guide to defining user attributes (PLT.17 user information configuration)
  • Guide to using profilers
  • Guide to Aggregation
  • Guide to Subsites
  • Index: xdocs/guides/guide-ntlm.xml =================================================================== --- xdocs/guides/guide-ntlm.xml (revision 0) +++ xdocs/guides/guide-ntlm.xml (revision 0) @@ -0,0 +1,106 @@ + + + + + Guide to using NTLM Authentication + How to configure NTLM authentication with an optional fallback authentication method. + + + + + +
    +

    + NTLM Authentication can be used for Single Sign On (SSO) from a Windows client/browser. A nice explanation about NTLM can be found here. + Jetspeed-2 supports NTLM Authentication based on the jCIFS Servlet filter. With the approach described below + you can use NTLM Authentication with an optional fallback to the default active authentication and as such this solution can be used as a drop-in replacement. + A typical application for a fallback login method would be when users log on to an intranet from a different domain: these users can + be redirected to a login screen.
    + The solution below can also be used as a replacement for the default Security Valve: + if you don't configure the filters, then Jetspeed's default authorization will be applied. +

    +
    +
    +

    + Jetspeed-2 security configuration is explained + + here + + . +

    + +

    + The first step is to configure jCIFS NTLM HTTP Authentication, which is explained here. + You configure jCIFS as a filter in the web.xml of your webapp. Next, you must configure + a second Jetspeed servlet filter, which must be placed right after the jCIFS filter. An example configuration: +

    + + NtlmHttpFilter + jcifs.http.NtlmHttpFilter + + jcifs.smb.client.domain + SOME_DOMAIN + + + + + NtlmHttpServletRequestFilter + org.apache.jetspeed.security.impl.ntlm.NtlmHttpServletRequestFilter + + org.apache.jetspeed.security.ntlm.ignoreUrls + /login/login + + + + + NtlmHttpFilter + /* + + + + NtlmHttpServletRequestFilter + /* +]]> +
    + +

    + The above filters set the correct credentials on the request. To use these credentials, you + have to configure the org.apache.jetspeed.security.impl.ntlm.NtlmSecurityValve in the + Jetspeed pipelines you want to secure. This Valve can be used as a replacement for the default SecurityValveImpl. For explanation about how to set up pipelines, see + here. An example of how to configure the NtlmSecurityValve bean: +

    + + + + + + + SOME_DOMAIN + + true + + false +]]> +
    +
    + +
    \ No newline at end of file Index: components/portal/src/java/org/apache/jetspeed/security/impl/ntlm/NtlmSecurityValve.java =================================================================== --- components/portal/src/java/org/apache/jetspeed/security/impl/ntlm/NtlmSecurityValve.java (revision 0) +++ components/portal/src/java/org/apache/jetspeed/security/impl/ntlm/NtlmSecurityValve.java (revision 0) @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.jetspeed.security.impl.ntlm; + +import java.security.Principal; +import java.util.HashSet; +import java.util.Set; + +import javax.security.auth.Subject; + +import org.apache.commons.lang.StringUtils; +import org.apache.jetspeed.pipeline.PipelineException; +import org.apache.jetspeed.request.RequestContext; +import org.apache.jetspeed.security.SecurityException; +import org.apache.jetspeed.security.SecurityHelper; +import org.apache.jetspeed.security.User; +import org.apache.jetspeed.security.UserManager; +import org.apache.jetspeed.security.UserPrincipal; +import org.apache.jetspeed.security.impl.AbstractSecurityValve; +import org.apache.jetspeed.security.impl.UserPrincipalImpl; +import org.apache.jetspeed.statistics.PortalStatistics; +/** + * NTLMSecurityValve provides Subject creation based on the + * NTLM provided request.getRemoteUser() user name. When request.getRemoteUser() holds + * a valid value, then this user is authorized. Otherwise the username is retrieved + * from the Principal name in the request. In this way you can use NTLM authentication, with + * a fallback authentication method in case the user is not properly authenticated / authorized using + * NTLM. + * + * There are basically three authentication scenarios: + *
      + *
    1. + *

      The user is successfully authenticated and authorized by Ntml authentication

      + *

      A Subject is created, with Principal derived from the remoteUser value from Ntlm authentication

      + *
    2. + *
    3. + *

      The user is not authenticated by Ntlm, or the authenticated (can be NTLM or any other method) user cannot be authorized by Jetspeed.

      + *

      An anonymous Subject is created. The user can then be redirected to a login page for example.

      + *
    4. + *
    5. + *

      The user is authenticated by a (non-NTLM) authentication method, e.g. container-based form authentication.

      + *

      + * A subject is created based on the Principal name in the request. + *

      + *
    6. + *
    + * @author David Sean Taylor + * @author Randy Walter + * @author Scott T. Weaver + * @author Dennis Dam + * @version $Id$ + */ +public class NtlmSecurityValve extends AbstractSecurityValve +{ + private UserManager userMgr; + private PortalStatistics statistics; + private String networkDomain; + private boolean ntlmAuthRequired; + private boolean omitDomain; + + + /** + * @param userMgr A UserManager + * @param statistics Portal Statistics + * @param networkDomain The network domain is used in combination with the omitDomain flag. + * @param omitDomain If true, then the network domain is stripped from the remoteUser name. + * @param ntlmAuthRequired if true, then an exception is thrown when there is no valid remoteUser, + * or the remoteUser cannot be authorized. + * + */ + public NtlmSecurityValve(UserManager userMgr, String networkDomain, boolean omitDomain, boolean ntlmAuthRequired, PortalStatistics statistics) + { + this.userMgr = userMgr; + this.statistics = statistics; + this.networkDomain = networkDomain; + this.ntlmAuthRequired = ntlmAuthRequired; + this.omitDomain = omitDomain; + } + + public NtlmSecurityValve(UserManager userMgr, String networkDomain, boolean omitDomain, boolean ntlmAuthRequired){ + this(userMgr, networkDomain, omitDomain, ntlmAuthRequired, null); + } + + public String toString() + { + return "NtlmSecurityValve"; + } + + protected Principal getUserPrincipal(RequestContext context) throws Exception + { + Subject subject = getSubjectFromSession(context); + if (subject != null) + { + return SecurityHelper.getPrincipal(subject, UserPrincipal.class); + } + // otherwise return anonymous principal + return new UserPrincipalImpl(userMgr.getAnonymousUser()); + } + + protected Subject getSubject(RequestContext context) throws Exception + { + Subject subject = getSubjectFromSession(context); + // Get remote user name set by web container + String userName = context.getRequest().getRemoteUser(); + if ( userName == null ) + { + if (ntlmAuthRequired){ + throw new PipelineException("Authorization failed."); + } else if (context.getRequest().getUserPrincipal() != null){ + userName = context.getRequest().getUserPrincipal().getName(); + } + } else { + if (omitDomain && networkDomain != null){ + userName = StringUtils.stripStart( userName , networkDomain+"\\"); + } + } + + // check whether principal name stored in session subject equals the remote user name passed by the web container + if (subject != null) + { + Principal subjectUserPrincipal = SecurityHelper.getPrincipal(subject, UserPrincipal.class); + if ((subjectUserPrincipal == null) || !subjectUserPrincipal.getName().equals(userName)) + { + subject = null; + } + } + if ( subject == null ){ + if (userName != null){ + try + { + User user = userMgr.getUser(userName); + if ( user != null ) + { + subject = user.getSubject(); + } + } catch (SecurityException sex) + { + subject = null; + } + + if (subject == null && this.ntlmAuthRequired){ + throw new PipelineException("Authorization failed for user '"+userName+"'."); + } + } + if (subject == null){ + // create anonymous user + Principal userPrincipal = getUserPrincipal(context); + Set principals = new HashSet(); + principals.add(userPrincipal); + subject = new Subject(true, principals, new HashSet(), new HashSet()); + } + + // create a new statistics *user* session + if (statistics != null) + { + statistics.logUserLogin(context, 0); + } + // put IP address in session for logout + context.setSessionAttribute(IP_ADDRESS, context.getRequest().getRemoteAddr()); + } + + return subject; + } +} Property changes on: components\portal\src\java\org\apache\jetspeed\security\impl\ntlm\NtlmSecurityValve.java ___________________________________________________________________ Name: svn:keywords + Id Name: svn:eol-style + native Index: components/portal/src/java/org/apache/jetspeed/security/impl/ntlm/NtlmHttpServletRequestWrapper.java =================================================================== --- components/portal/src/java/org/apache/jetspeed/security/impl/ntlm/NtlmHttpServletRequestWrapper.java (revision 0) +++ components/portal/src/java/org/apache/jetspeed/security/impl/ntlm/NtlmHttpServletRequestWrapper.java (revision 0) @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.jetspeed.security.impl.ntlm; + +import java.security.Principal; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; + +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; + +/** + * NtlmHttpServletRequestWrapper should be used in combination with an Ntml authentication filter (jCIFS). + * This filter wraps the original request, setting the principal and remoteUser retrieved by Ntml + * authentication with the client. The wrapper Request sets the principal and remoteUser, regardless + * of the principal already present in the original request. This HttpServletRequestWrapper returns the principal + * from the original request when it's there, and otherwise returns the Ntml principal. When the + * the Ntml principal is actually returned can be influenced by a comma-separated list of servlet urls: + * only for these urls the Ntlm principal / remoteUser is ignored. + * @see NtlmHttpServletRequestFilter + * @author Dennis Dam + * @version $Id$ + */ +public class NtlmHttpServletRequestWrapper extends HttpServletRequestWrapper { + private Principal principal; + private String remoteUser; + + public NtlmHttpServletRequestWrapper(HttpServletRequest req, String ignoreNtmlUrls) { + super(req); + if (req instanceof HttpServletRequestWrapper){ + String[] urls = ignoreNtmlUrls != null ? StringUtils.split(ignoreNtmlUrls, ',') : new String[]{}; + String servletUrl = req.getServletPath(); + Principal reqPrincipal = req.getUserPrincipal(); + HttpServletRequest originalRequest = (HttpServletRequest)((HttpServletRequestWrapper) req).getRequest(); + /* + * Original request principal has precedence over Ntml authenticated principal. This is needed + * in the case that the Ntlm authenticated principal is not authorized by Jetspeed: a fallback login + * method can then be used. If Ntml authentication succeeds, then the principal from the + * original request will be null. + */ + if (originalRequest.getUserPrincipal() != null){ + principal = originalRequest.getUserPrincipal(); + } else + /* + * If no principal in the original request, take principal from Ntlm authentication, but + * only if the current servlet url is not in the ignore list. The last + * requirement is necessary when falling back to another authentication method, e.g. container-based + * form authentication: these authentication methods might only work if there is no + * principal in the request. + */ + if (!ArrayUtils.contains(urls,servletUrl) && reqPrincipal != null && req.getRemoteUser() != null){ + principal = reqPrincipal; + remoteUser = req.getRemoteUser(); + } + } else { + principal = super.getUserPrincipal(); + } + } + + public Principal getUserPrincipal() { + return principal; + } + + public String getRemoteUser() { + return remoteUser; + } + +} Property changes on: components\portal\src\java\org\apache\jetspeed\security\impl\ntlm\NtlmHttpServletRequestWrapper.java ___________________________________________________________________ Name: svn:keywords + Id Name: svn:eol-style + native Index: components/portal/src/java/org/apache/jetspeed/security/impl/ntlm/NtlmHttpServletRequestFilter.java =================================================================== --- components/portal/src/java/org/apache/jetspeed/security/impl/ntlm/NtlmHttpServletRequestFilter.java (revision 0) +++ components/portal/src/java/org/apache/jetspeed/security/impl/ntlm/NtlmHttpServletRequestFilter.java (revision 0) @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.jetspeed.security.impl.ntlm; + +import java.io.IOException; + +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; +/** + * NtlmHttpServletRequestFilter can be used in combination with an Ntml authentication filter (jCIFS). + * The NtlmHttpServletRequestFilter must be configured after the jCIFS filter in web.xml. The + * NtlmHttpServletRequestFilter wraps the jCIFS HttpServletRequest with a NtlmHttpServletRequestWrapper. + * This is done to control which principal / remoteUser is returned by the request. + * If a fallback authentication method is used (e.g. container-based form authentication) then you must + * use the filter param org.apache.jetspeed.security.ntlm.ignoreUrls in web.xml to specify the urls for + * which the Ntlm principal / remoteUser should be ignored. + * + * @see NtlmHttpServletRequestWrapper + * @author Dennis Dam + * @version $Id$ + */ +public class NtlmHttpServletRequestFilter implements Filter { + + private String ignoreNtlmUrls; + + public void destroy() { + } + + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, + ServletException { + HttpServletRequest req = (HttpServletRequest)request; + HttpServletResponse resp = (HttpServletResponse)response; + chain.doFilter( new NtlmHttpServletRequestWrapper( req, ignoreNtlmUrls ), resp ); + } + + public void init(FilterConfig config) throws ServletException { + ignoreNtlmUrls = config.getInitParameter("org.apache.jetspeed.security.ntlm.ignoreUrls"); + } + +} Property changes on: components\portal\src\java\org\apache\jetspeed\security\impl\ntlm\NtlmHttpServletRequestFilter.java ___________________________________________________________________ Name: svn:keywords + Id Name: svn:eol-style + native