Return-Path: Delivered-To: apmail-sling-commits-archive@www.apache.org Received: (qmail 19802 invoked from network); 15 Oct 2009 14:10:03 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.3) by minotaur.apache.org with SMTP; 15 Oct 2009 14:10:03 -0000 Received: (qmail 71436 invoked by uid 500); 15 Oct 2009 14:10:03 -0000 Delivered-To: apmail-sling-commits-archive@sling.apache.org Received: (qmail 71384 invoked by uid 500); 15 Oct 2009 14:10:03 -0000 Mailing-List: contact commits-help@sling.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@sling.apache.org Delivered-To: mailing list commits@sling.apache.org Received: (qmail 71375 invoked by uid 99); 15 Oct 2009 14:10:03 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 15 Oct 2009 14:10:03 +0000 X-ASF-Spam-Status: No, hits=-3.6 required=5.0 tests=AWL,BAYES_00 X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 15 Oct 2009 14:09:59 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 770E52388907; Thu, 15 Oct 2009 14:09:39 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r825501 - in /sling/trunk/bundles/engine: ./ src/main/java/org/apache/sling/engine/auth/ src/main/java/org/apache/sling/engine/impl/auth/ Date: Thu, 15 Oct 2009 14:09:39 -0000 To: commits@sling.apache.org From: fmeschbe@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20091015140939.770E52388907@eris.apache.org> Author: fmeschbe Date: Thu Oct 15 14:09:38 2009 New Revision: 825501 URL: http://svn.apache.org/viewvc?rev=825501&view=rev Log: SLING-1155 Provide Authenticator.logout method and define new interface for authentication handlers to support logging out Added: sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/auth/AuthenticationHandler2.java (with props) sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/auth/AuthenticationHandlerHolder.java (with props) sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/auth/LogoutServlet.java (with props) Modified: sling/trunk/bundles/engine/pom.xml sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/auth/AuthenticationHandler.java sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/auth/Authenticator.java sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/auth/LoginServlet.java sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/auth/SlingAuthenticator.java Modified: sling/trunk/bundles/engine/pom.xml URL: http://svn.apache.org/viewvc/sling/trunk/bundles/engine/pom.xml?rev=825501&r1=825500&r2=825501&view=diff ============================================================================== --- sling/trunk/bundles/engine/pom.xml (original) +++ sling/trunk/bundles/engine/pom.xml Thu Oct 15 14:09:38 2009 @@ -55,9 +55,9 @@ - org.apache.sling.engine; - org.apache.sling.engine.auth; - org.apache.sling.engine.servlets;version=${pom.version} + org.apache.sling.engine;version=2.0.6, + org.apache.sling.engine.auth;version=2.1, + org.apache.sling.engine.servlets;version=2.0.6 org.apache.sling.engine.impl, Modified: sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/auth/AuthenticationHandler.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/auth/AuthenticationHandler.java?rev=825501&r1=825500&r2=825501&view=diff ============================================================================== --- sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/auth/AuthenticationHandler.java (original) +++ sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/auth/AuthenticationHandler.java Thu Oct 15 14:09:38 2009 @@ -31,11 +31,18 @@ public interface AuthenticationHandler { /** + * The name under which an implementation of this interface must be + * registered to be used as an authentication handler. + * @since 2.1 + */ + static final String SERVICE_NAME = "org.apache.sling.engine.auth.AuthenticationHandler"; + + /** * An authentication handler is associated with url paths. If the handler is * not configured with a path, it is regarded as inactive. If the handler * should be used for all requests, the path should be '/'. */ - String PATH_PROPERTY = "path"; + static final String PATH_PROPERTY = "path"; /** * Extracts credential data from the request if at all contained. @@ -71,7 +78,7 @@ * attribute. If the service is registered with multiple path values, the * value of the path request attribute may be used to implement * specific handling. - * + * * @param request The request object containing the information for the * authentication. * @param response The response object which may be used to send the @@ -100,7 +107,7 @@ * attribute. If the service is registered with multiple path values, the * value of the path request attribute may be used to implement * specific handling. - * + * * @param request The request object. * @param response The response object to which to send the request. * @return true if the handler is able to end an authentication Added: sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/auth/AuthenticationHandler2.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/auth/AuthenticationHandler2.java?rev=825501&view=auto ============================================================================== --- sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/auth/AuthenticationHandler2.java (added) +++ sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/auth/AuthenticationHandler2.java Thu Oct 15 14:09:38 2009 @@ -0,0 +1,54 @@ +/* + * 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.sling.engine.auth; + +import java.io.IOException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * The AuthenticationHandler interface defines the service API used + * by the authentication implementation to support plugin various ways of + * extracting credentials from the request. + *

+ * Authentication handlers implementing this interface should still be + * registered with the {@link AuthenticationHandler#SERVICE authentication + * handler service name}. Internally, the + * {@link Authenticator#logout(HttpServletRequest, HttpServletResponse)} method + * will identify the authentication handler appropriately. + * + * @since 2.1 + */ +public interface AuthenticationHandler2 extends AuthenticationHandler { + + /** + * Drops any credential and authentication details from the request and asks + * the client to do the same. + * + * @param request The request object. + * @param response The response object to which to send the request. + * @throws IOException If an error occurrs asking the client to drop any + * authentication traces. + * @since 2.1 + */ + void dropAuthentication(HttpServletRequest request, + HttpServletResponse response) throws IOException; + +} Propchange: sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/auth/AuthenticationHandler2.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/auth/AuthenticationHandler2.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Rev Url Modified: sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/auth/Authenticator.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/auth/Authenticator.java?rev=825501&r1=825500&r2=825501&view=diff ============================================================================== --- sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/auth/Authenticator.java (original) +++ sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/auth/Authenticator.java Thu Oct 15 14:09:38 2009 @@ -31,7 +31,7 @@ * This interface is not intended to be implemented by applications but may be * used to initiate the authentication process form a request processing servlet * or script. - * + * * @since 2.0.4 */ public interface Authenticator { @@ -49,13 +49,34 @@ *

* After this method has finished, request processing should be terminated * and the response be considered committed and finished. - * + * * @param request The object representing the client request. * @param response The object representing the response to the client. * @throws NoAuthenticationHandlerException If no authentication handler * claims responsibility to authenticate the request. * @throws IllegalStateException If the response has already been committed. */ - public void login(HttpServletRequest request, HttpServletResponse response); + void login(HttpServletRequest request, HttpServletResponse response); + /** + * Finds an {@link AuthenticationHandler2} for the given request and call + * its + * {@link AuthenticationHandler2#dropAuthentication(HttpServletRequest, HttpServletResponse)} + * method to drop authentication credentials for the client to logout from + * Sling. + *

+ * This method must be called on an uncommitted response since the + * implementation may want to reset the response to restart the + * authentication process with a clean response. If the response is already + * committed an IllegalStateException is thrown. + *

+ * After this method has finished, request processing should be terminated + * and the response be considered committed and finished. + * + * @param request The object representing the client request. + * @param response The object representing the response to the client. + * @throws IllegalStateException If the response has already been committed. + * @since 2.1 + */ + void logout(HttpServletRequest request, HttpServletResponse response); } Added: sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/auth/AuthenticationHandlerHolder.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/auth/AuthenticationHandlerHolder.java?rev=825501&view=auto ============================================================================== --- sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/auth/AuthenticationHandlerHolder.java (added) +++ sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/auth/AuthenticationHandlerHolder.java Thu Oct 15 14:09:38 2009 @@ -0,0 +1,144 @@ +/* + * 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.sling.engine.impl.auth; + +import java.io.IOException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.sling.engine.RequestUtil; +import org.apache.sling.engine.auth.AuthenticationHandler; +import org.apache.sling.engine.auth.AuthenticationHandler2; +import org.apache.sling.engine.auth.AuthenticationInfo; + +/** + * The AuthenticationHandlerHolder class represents an + * authentication handler service in the internal data structure of the + * {@link SlingAuthenticator}. + * + * @since 2.1 + */ +final class AuthenticationHandlerHolder implements AuthenticationHandler2, + Comparable { + + // full path of the service registration + private final String fullPath; + + // file path part of the service registration full path + final String path; + + // host element of the service registration full path + final String host; + + // protocol element of the service registration full path + final String protocol; + + // the actual authentication handler + private final AuthenticationHandler handler; + + AuthenticationHandlerHolder(final String fullPath, + final AuthenticationHandler handler) { + + String path = fullPath; + String host = ""; + String protocol = ""; + + // check for protocol prefix in the full path + if (path.startsWith("http://") || path.startsWith("https://")) { + int idxProtocolEnd = path.indexOf("://"); + protocol = path.substring(0, idxProtocolEnd); + path = path.substring(idxProtocolEnd + 1); + } + + // check for host prefix in the full path + if (path.startsWith("//")) { + int idxHostEnd = path.indexOf("/", 2); + idxHostEnd = idxHostEnd == -1 ? path.length() : idxHostEnd; + + if (path.length() > 2) { + host = path.substring(2, idxHostEnd); + if (idxHostEnd < path.length()) { + path = path.substring(idxHostEnd); + } else { + path = "/"; + } + } else { + path = "/"; + } + } + + // assign the fields + this.fullPath = fullPath; + this.path = path; + this.host = host; + this.protocol = protocol; + this.handler = handler; + } + + public AuthenticationInfo authenticate(HttpServletRequest request, + HttpServletResponse response) { + + final Object oldPathAttr = setPath(request); + try { + return handler.authenticate(request, response); + } finally { + resetPath(request, oldPathAttr); + } + + } + + public boolean requestAuthentication(HttpServletRequest request, + HttpServletResponse response) throws IOException { + final Object oldPathAttr = setPath(request); + try { + return handler.requestAuthentication(request, response); + } finally { + resetPath(request, oldPathAttr); + } + } + + public void dropAuthentication(HttpServletRequest request, + HttpServletResponse response) throws IOException { + + if (handler instanceof AuthenticationHandler2) { + final Object oldPathAttr = setPath(request); + try { + final AuthenticationHandler2 handler2 = (AuthenticationHandler2) handler; + handler2.dropAuthentication(request, response); + } finally { + resetPath(request, oldPathAttr); + } + } + } + + public int compareTo(AuthenticationHandlerHolder other) { + return other.path.compareTo(path); + } + + private Object setPath(final HttpServletRequest request) { + return RequestUtil.setRequestAttribute(request, + AuthenticationHandler.PATH_PROPERTY, fullPath); + } + + private void resetPath(final HttpServletRequest request, Object oldValue) { + RequestUtil.setRequestAttribute(request, + AuthenticationHandler.PATH_PROPERTY, oldValue); + } +} \ No newline at end of file Propchange: sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/auth/AuthenticationHandlerHolder.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/auth/AuthenticationHandlerHolder.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Rev Url Modified: sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/auth/LoginServlet.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/auth/LoginServlet.java?rev=825501&r1=825500&r2=825501&view=diff ============================================================================== --- sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/auth/LoginServlet.java (original) +++ sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/auth/LoginServlet.java Thu Oct 15 14:09:38 2009 @@ -31,12 +31,11 @@ import org.slf4j.LoggerFactory; /** - * The LoginServlet lets the Authenticator - * do the login. - * + * The LoginServlet lets the Authenticator do the login. + * * @scr.component metatype="no" * @scr.service interface="javax.servlet.Servlet" - * @scr.property name="service.description" value="HTTP Header Login Servlet" + * @scr.property name="service.description" value="Authenticator Login Servlet" * @scr.property name="service.vendor" value="The Apache Software Foundation" * @scr.property name="sling.servlet.methods" values.0="GET" values.1="POST" */ @@ -51,15 +50,36 @@ /** @scr.reference cardinality="0..1" policy="dynamic" */ private Authenticator authenticator; - /** The servlet is registered on this path, and the authenticator allows - * any requests to that path, without authentication - * @scr.property name="sling.servlet.paths" */ + /** + * The servlet is registered on this path, and the authenticator allows any + * requests to that path, without authentication + * + * @scr.property name="sling.servlet.paths" + */ public static final String LOGIN_SERVLET_PATH = "/system/sling/login"; @Override - protected void doGet(SlingHttpServletRequest request, + protected void service(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException { + // if the request is logged in and the resource is not set (such + // as when requesting /system/sling/login from the browser with the + // browser sending credentials) or the resource is set to the login + // servlet as a result of authenticating after providing credentials + // through the login servlet), redirect to root now assuming we are + // authenticated. + if (request.getAuthType() != null) { + final String resourcePath = request.getParameter("resource"); + if (isSelf(resourcePath)) { + String redirectTarget = request.getContextPath() + "/"; + log.warn( + "doGet: Redirecting to {} to prevent login loop for resource {}", + redirectTarget, resourcePath); + response.sendRedirect(redirectTarget); + return; + } + } + Authenticator authenticator = this.authenticator; if (authenticator != null) { try { @@ -72,17 +92,25 @@ log.error("doGet: No AuthenticationHandler to login registered"); } } else { - log.error("doGet: Authenticator service missing, cannot request authentication"); + log.error("doGet: Authenticator service missing, cannot login"); } // fall back to forbid access response.sendError(HttpServletResponse.SC_FORBIDDEN, "Cannot login"); } - @Override - protected void doPost(SlingHttpServletRequest request, - SlingHttpServletResponse response) throws IOException { - response.sendRedirect(request.getRequestURI()); - } + private boolean isSelf(final String resourcePath) { + // no resource, assume self + if (resourcePath == null) { + return true; + } + // login servlet is addressed + if (resourcePath.startsWith(LOGIN_SERVLET_PATH)) { + return true; + } + + // not a prefix + return false; + } } Added: sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/auth/LogoutServlet.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/auth/LogoutServlet.java?rev=825501&view=auto ============================================================================== --- sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/auth/LogoutServlet.java (added) +++ sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/auth/LogoutServlet.java Thu Oct 15 14:09:38 2009 @@ -0,0 +1,77 @@ +/* + * 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.sling.engine.impl.auth; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.SlingHttpServletResponse; +import org.apache.sling.api.servlets.SlingAllMethodsServlet; +import org.apache.sling.engine.auth.Authenticator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The LogoutServlet lets the Authenticator + * do the logout. + * + * @scr.component metatype="no" + * @scr.service interface="javax.servlet.Servlet" + * @scr.property name="service.description" value="Authenticator Logout Servlet" + * @scr.property name="service.vendor" value="The Apache Software Foundation" + * @scr.property name="sling.servlet.methods" values.0="GET" values.1="POST" + * + * @since 2.1 + */ +public class LogoutServlet extends SlingAllMethodsServlet { + + /** serialization UID */ + private static final long serialVersionUID = -1L; + + /** default log */ + private final Logger log = LoggerFactory.getLogger(getClass()); + + /** @scr.reference cardinality="0..1" policy="dynamic" */ + private Authenticator authenticator; + + /** The servlet is registered on this path. + * @scr.property name="sling.servlet.paths" */ + public static final String LOGIN_SERVLET_PATH = "/system/sling/logout"; + + @Override + protected void service(SlingHttpServletRequest request, + SlingHttpServletResponse response) { + + Authenticator authenticator = this.authenticator; + if (authenticator != null) { + try { + authenticator.logout(request, response); + return; + } catch (IllegalStateException ise) { + log.error("service: Response already committed, cannot logout"); + return; + } + } + + log.error("service: Authenticator service missing, cannot logout"); + + // well, we don't really have something to say here, do we ? + response.setStatus(HttpServletResponse.SC_NO_CONTENT); + } +} Propchange: sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/auth/LogoutServlet.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/auth/LogoutServlet.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Rev Url Modified: sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/auth/SlingAuthenticator.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/auth/SlingAuthenticator.java?rev=825501&r1=825500&r2=825501&view=diff ============================================================================== --- sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/auth/SlingAuthenticator.java (original) +++ sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/auth/SlingAuthenticator.java Thu Oct 15 14:09:38 2009 @@ -20,8 +20,7 @@ import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; +import java.util.Arrays; import java.util.Dictionary; import java.util.HashMap; import java.util.Hashtable; @@ -40,7 +39,6 @@ import org.apache.sling.commons.osgi.OsgiUtil; import org.apache.sling.engine.EngineConstants; -import org.apache.sling.engine.RequestUtil; import org.apache.sling.engine.auth.AuthenticationHandler; import org.apache.sling.engine.auth.AuthenticationInfo; import org.apache.sling.engine.auth.Authenticator; @@ -122,7 +120,7 @@ private int authHandlerTrackerCount; - private Map> authHandlerCache; + private Map> authHandlerCache; /** The name of the impersonation parameter */ private String sudoParameterName; @@ -255,24 +253,21 @@ throw new IllegalStateException("Response already committed"); } - AuthenticationHandlerInfo[] handlerInfos = findApplicableAuthenticationHandlers(request); + AuthenticationHandlerHolder[] handlerInfos = findApplicableAuthenticationHandlers(request); boolean done = false; for (int i = 0; !done && i < handlerInfos.length; i++) { if ( request.getPathInfo().startsWith(handlerInfos[i].path) ) { log.debug( - "requestAuthentication: requesting authentication using handler: {}", + "login: requesting authentication using handler: {}", handlerInfos[i]); - Object oldPathAttr = RequestUtil.setRequestAttribute(request, AuthenticationHandler.PATH_PROPERTY, handlerInfos[i].fullPath); try { - done = handlerInfos[i].handler.requestAuthentication(request, response); + done = handlerInfos[i].requestAuthentication(request, response); } catch (IOException ioe) { log.error( - "requestAuthentication: Failed sending authentication request through handler " + "login: Failed sending authentication request through handler " + handlerInfos[i] + ", access forbidden", ioe); done = true; - } finally { - RequestUtil.setRequestAttribute(request, AuthenticationHandler.PATH_PROPERTY, oldPathAttr); } } } @@ -280,12 +275,43 @@ // no handler could send an authentication request, throw if (!done) { log.info( - "requestAuthentication: No handler for request ({} handlers available)", + "login: No handler for request ({} handlers available)", handlerInfos.length); throw new NoAuthenticationHandlerException(); } } + /** + * Logs out the user calling all applicable + * {@link org.apache.sling.engine.auth.AuthenticationHandler2} + * authentication handlers. + * + * @since 2.1 + */ + public void logout(HttpServletRequest request, HttpServletResponse response) { + + // ensure the response is not committed yet + if (response.isCommitted()) { + throw new IllegalStateException("Response already committed"); + } + + AuthenticationHandlerHolder[] handlerInfos = findApplicableAuthenticationHandlers(request); + for (int i = 0; i < handlerInfos.length; i++) { + if (request.getPathInfo().startsWith(handlerInfos[i].path)) { + log.debug("logout: dropping authentication using handler: {}", + handlerInfos[i]); + + try { + handlerInfos[i].dropAuthentication(request, response); + } catch (IOException ioe) { + log.error( + "logout: Failed dropping authentication through handler " + + handlerInfos[i], ioe); + } + } + } + } + // ----------- ManagedService interface ----------------------------------- @SuppressWarnings("unchecked") @@ -335,13 +361,13 @@ return repo; } - private static Map> EMPTY_PROTOCOL_MAP = new HashMap>(); - private static AuthenticationHandlerInfo[] EMPTY_INFO = new AuthenticationHandlerInfo[0]; + private static Map> EMPTY_PROTOCOL_MAP = new HashMap>(); + private static AuthenticationHandlerHolder[] EMPTY_INFO = new AuthenticationHandlerHolder[0]; - private AuthenticationHandlerInfo[] findApplicableAuthenticationHandlers(HttpServletRequest request) { - Map> byProtocolMap = getAuthenticationHandlers(); + private AuthenticationHandlerHolder[] findApplicableAuthenticationHandlers(HttpServletRequest request) { + Map> byProtocolMap = getAuthenticationHandlers(); - Map byHostMap = byProtocolMap.get(request.getScheme()); + Map byHostMap = byProtocolMap.get(request.getScheme()); if (byHostMap == null) { byHostMap = byProtocolMap.get(""); } @@ -349,7 +375,7 @@ String hostname = request.getServerName() + (request.getServerPort() != 80 && request.getServerPort() != 443 ? ":" + request.getServerPort() : ""); - AuthenticationHandlerInfo[] infos = null; + AuthenticationHandlerHolder[] infos = null; if ( byHostMap != null ) { infos = byHostMap.get(hostname); if (infos == null) { @@ -363,14 +389,14 @@ return EMPTY_INFO; } - private Map> getAuthenticationHandlers() { + private Map> getAuthenticationHandlers() { if (authHandlerCache == null || authHandlerTrackerCount < authHandlerTracker.getTrackingCount()) { final ServiceReference[] services = authHandlerTracker.getServiceReferences(); if ( services == null || services.length == 0 ) { this.authHandlerCache = EMPTY_PROTOCOL_MAP; } else { - final Map>> byProtocolMap = new HashMap>>(); + final Map>> byProtocolMap = new HashMap>>(); int regPathCount = 0; for (int i = 0; i < services.length; i++) { @@ -381,48 +407,21 @@ for(int m = 0; m < paths.length; m++) { if ( paths[m] != null && paths[m].length() > 0 ) { - String fullPath = paths[m]; - String path = fullPath; - String host = ""; - String protocol = ""; - - if(path.startsWith("http://") || path.startsWith("https://")) { - int idxProtocolEnd = path.indexOf("://"); - protocol = path.substring(0,idxProtocolEnd); - path = path.substring(idxProtocolEnd + 1); - } - - if (path.startsWith("//")) { - int idxHostEnd = path.indexOf("/",2); - idxHostEnd = idxHostEnd == -1 ? path.length() : idxHostEnd; - - if(path.length() > 2) { - host = path.substring(2,idxHostEnd); - if(idxHostEnd < path.length()) { - path = path.substring(idxHostEnd); - } else { - path="/"; - } - } else { - path="/"; - } - } - - AuthenticationHandlerInfo newInfo = new AuthenticationHandlerInfo(fullPath, path, host, protocol, handler); + final AuthenticationHandlerHolder holder = new AuthenticationHandlerHolder(paths[m], handler); - Map> byHostMap = byProtocolMap.get(protocol); + Map> byHostMap = byProtocolMap.get(holder.protocol); if(byHostMap == null) { - byHostMap = new HashMap>(); - byProtocolMap.put(protocol, byHostMap); + byHostMap = new HashMap>(); + byProtocolMap.put(holder.protocol, byHostMap); } - List byPathList = byHostMap.get(host); + List byPathList = byHostMap.get(holder.host); if(byPathList == null) { - byPathList = new ArrayList(); - byHostMap.put(host, byPathList); + byPathList = new ArrayList(); + byHostMap.put(holder.host, byPathList); } - byPathList.add(newInfo); + byPathList.add(holder); regPathCount++; } } @@ -431,25 +430,21 @@ if ( regPathCount == 0 ) { authHandlerCache = EMPTY_PROTOCOL_MAP; } else { - authHandlerCache = new HashMap>(); + authHandlerCache = new HashMap>(); - for(Map.Entry>> protocolEntry : byProtocolMap.entrySet()) { - Map> hostMap = protocolEntry.getValue(); + for(Map.Entry>> protocolEntry : byProtocolMap.entrySet()) { + Map> hostMap = protocolEntry.getValue(); - Map finalHostMap = authHandlerCache.get(protocolEntry.getKey()); + Map finalHostMap = authHandlerCache.get(protocolEntry.getKey()); if(finalHostMap == null) { - finalHostMap = new HashMap(); + finalHostMap = new HashMap(); authHandlerCache.put(protocolEntry.getKey(), finalHostMap); } - for(Map.Entry> hostEntry : hostMap.entrySet()) { - List pathList = hostEntry.getValue(); - - Collections.sort(pathList, AuthenticationHandlerInfoComparator.SINGLETON); - - final AuthenticationHandlerInfo[] authInfos = - pathList.toArray(new AuthenticationHandlerInfo[pathList.size()]); - + for (Map.Entry> hostEntry : hostMap.entrySet()) { + final List pathList = hostEntry.getValue(); + final AuthenticationHandlerHolder[] authInfos = pathList.toArray(new AuthenticationHandlerHolder[pathList.size()]); + Arrays.sort(authInfos); finalHostMap.put(hostEntry.getKey(), authInfos); } } @@ -471,21 +466,13 @@ pathInfo = "/"; } - AuthenticationHandlerInfo[] local = findApplicableAuthenticationHandlers(request); + AuthenticationHandlerHolder[] local = findApplicableAuthenticationHandlers(request); for (int i = 0; i < local.length; i++) { if ( pathInfo.startsWith(local[i].path) ) { - Object oldPathAttr = RequestUtil.setRequestAttribute(request, - AuthenticationHandler.PATH_PROPERTY, local[i].fullPath); - try { - final AuthenticationInfo authInfo = local[i].handler.authenticate( - request, response); - if (authInfo != null) { - return authInfo; - } - } finally { - RequestUtil.setRequestAttribute(request, - AuthenticationHandler.PATH_PROPERTY, oldPathAttr); - + final AuthenticationInfo authInfo = local[i].authenticate( + request, response); + if (authInfo != null) { + return authInfo; } } } @@ -698,35 +685,4 @@ // return the session return session; } - - protected static final class AuthenticationHandlerInfo { - public final String fullPath; - public final String path; - public final String host; - public final String protocol; - public final AuthenticationHandler handler; - - public AuthenticationHandlerInfo(final String fullPath, final String p, final String host, final String protocol, final AuthenticationHandler h) { - this.fullPath = fullPath; - this.path = p; - this.host = host; - this.protocol = protocol; - this.handler = h; - } - } - - protected static final class AuthenticationHandlerInfoComparator implements Comparator { - - public static final AuthenticationHandlerInfoComparator SINGLETON = new AuthenticationHandlerInfoComparator(); - /** - * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) - */ - public int compare(AuthenticationHandlerInfo arg0, - AuthenticationHandlerInfo arg1) { - return arg0.path.compareTo(arg1.path) * -1; - } - - } - - }