Index: xdocs/guides/index.xml =================================================================== --- xdocs/guides/index.xml (revision 537207) +++ xdocs/guides/index.xml (working copy) @@ -47,7 +47,8 @@
+ 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: +
+
+ 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:
+
The user is successfully authenticated and authorized by Ntml authentication
+ *A Subject is created, with Principal derived from the remoteUser value from Ntlm authentication
+ *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.
+ *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. + *
+ *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