Return-Path: Delivered-To: apmail-sling-commits-archive@www.apache.org Received: (qmail 93117 invoked from network); 6 Jan 2010 07:55:47 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.3) by minotaur.apache.org with SMTP; 6 Jan 2010 07:55:47 -0000 Received: (qmail 17009 invoked by uid 500); 6 Jan 2010 07:55:47 -0000 Delivered-To: apmail-sling-commits-archive@sling.apache.org Received: (qmail 16952 invoked by uid 500); 6 Jan 2010 07:55:47 -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 16943 invoked by uid 99); 6 Jan 2010 07:55:47 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 06 Jan 2010 07:55:47 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED 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; Wed, 06 Jan 2010 07:55:36 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 1F1212388A2D; Wed, 6 Jan 2010 07:55:14 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r896345 [2/3] - in /sling/trunk/bundles/commons/auth: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/sling/ src/main/java/org/apache/sling/commons/ src/main/java/org/apache/sling/commo... Date: Wed, 06 Jan 2010 07:55:12 -0000 To: commits@sling.apache.org From: fmeschbe@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20100106075514.1F1212388A2D@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Added: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/impl/SlingAuthenticator.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/impl/SlingAuthenticator.java?rev=896345&view=auto ============================================================================== --- sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/impl/SlingAuthenticator.java (added) +++ sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/impl/SlingAuthenticator.java Wed Jan 6 07:55:01 2010 @@ -0,0 +1,906 @@ +/* + * 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.commons.auth.impl; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +import javax.jcr.Credentials; +import javax.jcr.LoginException; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.SimpleCredentials; +import javax.servlet.ServletRequest; +import javax.servlet.ServletRequestEvent; +import javax.servlet.ServletRequestListener; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.commons.auth.AuthenticationSupport; +import org.apache.sling.commons.auth.Authenticator; +import org.apache.sling.commons.auth.NoAuthenticationHandlerException; +import org.apache.sling.commons.auth.impl.engine.EngineAuthenticationHandlerHolder; +import org.apache.sling.commons.auth.spi.AuthenticationHandler; +import org.apache.sling.commons.auth.spi.AuthenticationInfo; +import org.apache.sling.commons.osgi.OsgiUtil; +import org.apache.sling.jcr.api.SlingRepository; +import org.apache.sling.jcr.api.TooManySessionsException; +import org.apache.sling.jcr.resource.JcrResourceResolverFactory; +import org.osgi.framework.AllServiceListener; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceEvent; +import org.osgi.framework.ServiceListener; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.http.HttpContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The SlingAuthenticator class is the default implementation for + * handling authentication. This class supports : + *
    + *
  • Support for login sessions where session ids are exchanged with cookies + *
  • Support for multiple authentication handlers, which must implement the + * {@link AuthenticationHandler} interface. + *
  • + *

    + * Currently this class does not support multiple handlers for any one request + * URL. + *

    + * Clients of this class use {@link #authenticate} method to create a + * {@link AuthenticationInfo} for the handling of the request. This method uses + * any of the handlers to extract the user information from the request. Next an + * object is created for this user information. If no user information is + * contained in the request (according to the handler), the anonymous info is + * used. + *

    + * + * @scr.component name="org.apache.sling.engine.impl.auth.SlingAuthenticator" + * label="%auth.name" description="%auth.description" + * modified="modified" immediate="true" Register for three + * services + * @scr.service interface="org.apache.sling.commons.auth.AuthenticationSupport" + * @scr.service interface="org.apache.sling.commons.auth.Authenticator" + * @scr.service interface="javax.servlet.ServletRequestListener" + * @scr.property name="service.description" value="Sling Request Authenticator" + * @scr.property name="service.vendor" value="The Apache Software Foundation" + * @scr.reference name="authHandler" + * interface="org.apache.sling.commons.auth.spi.AuthenticationHandler" + * policy="dynamic" cardinality="0..n" bind="bindAuthHandler" + * unbind="unbindAuthHandler" + * @scr.reference name="engineAuthHandler" + * interface="org.apache.sling.engine.auth.AuthenticationHandler" + * policy="dynamic" cardinality="0..n" + * bind="bindEngineAuthHandler" unbind="unbindEngineAuthHandler" + */ +public class SlingAuthenticator implements Authenticator, + AuthenticationSupport, ServletRequestListener { + + static final String REQUEST_ATTRIBUTE_SESSION = "javax.jcr.Session"; + + /** default log */ + private static final Logger log = LoggerFactory.getLogger(SlingAuthenticator.class); + + /** + * @scr.property valueRef="DEFAULT_IMPERSONATION_COOKIE" + */ + public static final String PAR_IMPERSONATION_COOKIE_NAME = "auth.sudo.cookie"; + + /** + * @scr.property valueRef="DEFAULT_IMPERSONATION_PARAMETER" + */ + public static final String PAR_IMPERSONATION_PAR_NAME = "auth.sudo.parameter"; + + /** + * @scr.property valueRef="DEFAULT_ANONYMOUS_ALLOWED" type="Boolean" + */ + public static final String PAR_ANONYMOUS_ALLOWED = "auth.annonymous"; + + /** + * @scr.property type="String" cardinality="+" + */ + private static final String PAR_AUTH_REQ = "sling.auth.requirements"; + + /** The default impersonation parameter name */ + private static final String DEFAULT_IMPERSONATION_PARAMETER = "sudo"; + + /** The default impersonation cookie name */ + private static final String DEFAULT_IMPERSONATION_COOKIE = "sling.sudo"; + + /** The default value for allowing anonymous access */ + private static final boolean DEFAULT_ANONYMOUS_ALLOWED = true; + + private static ArrayList EMPTY_INFO = new ArrayList(); + + /** @scr.reference */ + private SlingRepository repository; + + /** @scr.reference */ + private JcrResourceResolverFactory resourceResolverFactory; + + private PathBasedHolderCache authHandlerCache = new PathBasedHolderCache(); + + // package protected for access in inner class ... + PathBasedHolderCache authRequiredCache = new PathBasedHolderCache(); + + /** The name of the impersonation parameter */ + private String sudoParameterName; + + /** The name of the impersonation cookie */ + private String sudoCookieName; + + /** Cache control flag */ + private boolean cacheControl; + + /** Web Console Plugin service registration */ + private ServiceRegistration webConsolePlugin; + + /** + * The listener for services registered with "sling.auth.requirements" to + * update the internal authentication requirements + */ + private ServiceListener serviceListener; + + // ---------- SCR integration + + @SuppressWarnings("unused") + private void activate(final BundleContext bundleContext, + final Map properties) { + modified(properties); + + AuthenticatorWebConsolePlugin plugin = new AuthenticatorWebConsolePlugin( + this); + Hashtable props = new Hashtable(); + props.put("felix.webconsole.label", plugin.getLabel()); + props.put("felix.webconsole.title", plugin.getTitle()); + props.put("service.description", + "Sling Request Authenticator WebConsole Plugin"); + props.put("service.vendor", properties.get("service.vendor")); + + webConsolePlugin = bundleContext.registerService( + "javax.servlet.Servlet", plugin, props); + + serviceListener = SlingAuthenticatorServiceListener.createListener( + bundleContext, this); + } + + private void modified(Map properties) { + if (properties == null) { + properties = new HashMap(); + } + + String newCookie = (String) properties.get(PAR_IMPERSONATION_COOKIE_NAME); + if (newCookie == null || newCookie.length() == 0) { + newCookie = DEFAULT_IMPERSONATION_COOKIE; + } + if (!newCookie.equals(this.sudoCookieName)) { + log.info("Setting new cookie name for impersonation {} (was {})", + newCookie, this.sudoCookieName); + this.sudoCookieName = newCookie; + } + + String newPar = (String) properties.get(PAR_IMPERSONATION_PAR_NAME); + if (newPar == null || newPar.length() == 0) { + newPar = DEFAULT_IMPERSONATION_PARAMETER; + } + if (!newPar.equals(this.sudoParameterName)) { + log.info( + "Setting new parameter name for impersonation {} (was {})", + newPar, this.sudoParameterName); + this.sudoParameterName = newPar; + } + + authRequiredCache.clear(); + + boolean flag = OsgiUtil.toBoolean( + properties.get(PAR_ANONYMOUS_ALLOWED), DEFAULT_ANONYMOUS_ALLOWED); + authRequiredCache.addHolder(new AuthenticationRequirementHolder("/", + !flag)); + + String[] authReqs = OsgiUtil.toStringArray(properties.get(PAR_AUTH_REQ)); + if (authReqs != null) { + for (String authReq : authReqs) { + if (authReq != null && authReq.length() > 0) { + authRequiredCache.addHolder(AuthenticationRequirementHolder.fromConfig(authReq)); + } + } + } + + // don't require authentication for login/logout servlets + authRequiredCache.addHolder(new AuthenticationRequirementHolder( + LoginServlet.SERVLET_PATH, false)); + authRequiredCache.addHolder(new AuthenticationRequirementHolder( + LogoutServlet.SERVLET_PATH, false)); + } + + @SuppressWarnings("unused") + private void deactivate(final BundleContext bundleContext) { + if (serviceListener != null) { + bundleContext.removeServiceListener(serviceListener); + serviceListener = null; + } + + if (webConsolePlugin != null) { + webConsolePlugin.unregister(); + webConsolePlugin = null; + } + } + + // --------- AuthenticationSupport interface + + /** + * Checks the authentication contained in the request. This check is only + * based on the original request object, no URI translation has taken place + * yet. + *

    + * + * @param req The request object containing the information for the + * authentication. + * @param res The response object which may be used to send the information + * on the request failure to the user. + */ + public boolean handleSecurity(HttpServletRequest request, + HttpServletResponse response) { + + // 0. Nothing to do, if the session is also in the request + // this might be the case if the request is handled as a result + // of a servlet container include inside another Sling request + Object sessionAttr = request.getAttribute(REQUEST_ATTRIBUTE_RESOLVER); + if (sessionAttr instanceof ResourceResolver) { + log.debug("authenticate: Request already authenticated, nothing to do"); + return true; + } else if (sessionAttr != null) { + // warn and remove existing non-session + log.warn( + "authenticate: Overwriting existing ResourceResolver attribute ({})", + sessionAttr); + request.removeAttribute(REQUEST_ATTRIBUTE_RESOLVER); + } + + // 1. Ask all authentication handlers to try to extract credentials + AuthenticationInfo authInfo = getAuthenticationInfo(request, response); + + // 3. Check Credentials + if (authInfo == AuthenticationInfo.DOING_AUTH) { + + log.debug("authenticate: ongoing authentication in the handler"); + return false; + + } else if (authInfo == null) { + + log.debug("authenticate: no credentials in the request, anonymous"); + return getAnonymousSession(request, response); + + } else { + // try to connect + try { + log.debug("authenticate: credentials, trying to get a session"); + Session session = repository.login(authInfo.getCredentials(), + authInfo.getWorkspaceName()); + + // handle impersonation + session = handleImpersonation(request, response, session); + setAttributes(session, authInfo.getAuthType(), request); + + return true; + + } catch (RepositoryException re) { + + handleLoginFailure(request, response, re); + + } + + // end request + return false; + } + } + + // ---------- Authenticator interface + + /** + * Requests authentication information from the client. Returns + * true if the information has been requested and request + * processing can be terminated. Otherwise the request information could not + * be requested and the request should be terminated with a 403/FORBIDDEN + * response. + *

    + * Any response sent by the handler is also handled by the error handler + * infrastructure. + * + * @param request The request object + * @param response The response object to which to send the request + * @throws IllegalStateException If response is already committed + * @throws NoAuthenticationHandlerException If no authentication handler + * claims responsibility to authenticate the request. + */ + public void login(HttpServletRequest request, HttpServletResponse response) { + + // ensure the response is not committed yet + if (response.isCommitted()) { + throw new IllegalStateException("Response already committed"); + } + + // select path used for authentication handler selection + final ArrayList holderList = findApplicableAuthenticationHandlers(request); + final String path = getHandlerSelectionPath(request); + boolean done = false; + for (int i = 0; !done && i < holderList.size(); i++) { + final AbstractAuthenticationHandlerHolder holder = holderList.get(i); + if (path.startsWith(holder.path)) { + log.debug("login: requesting authentication using handler: {}", + holder); + + try { + done = holder.requestAuthentication(request, response); + } catch (IOException ioe) { + log.error( + "login: Failed sending authentication request through handler " + + holder + ", access forbidden", ioe); + done = true; + } + } + } + + // no handler could send an authentication request, throw + if (!done) { + log.info("login: No handler for request ({} handlers available)", + holderList.size()); + 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"); + } + + final ArrayList holderList = findApplicableAuthenticationHandlers(request); + final String path = getHandlerSelectionPath(request); + for (int i = 0; i < holderList.size(); i++) { + AbstractAuthenticationHandlerHolder holder = holderList.get(i); + if (path.startsWith(holder.path)) { + log.debug("logout: dropping authentication using handler: {}", + holder); + + try { + holder.dropAuthentication(request, response); + } catch (IOException ioe) { + log.error( + "logout: Failed dropping authentication through handler " + + holder, ioe); + } + } + } + } + + // ---------- ServletRequestListener + + public void requestInitialized(ServletRequestEvent sre) { + // don't care + } + + public void requestDestroyed(ServletRequestEvent sre) { + ServletRequest request = sre.getServletRequest(); + Object sessionAttr = request.getAttribute(SlingAuthenticatorSession.ATTR_NAME); + if (sessionAttr instanceof SlingAuthenticatorSession) { + ((SlingAuthenticatorSession) sessionAttr).logout(); + + request.removeAttribute(REQUEST_ATTRIBUTE_RESOLVER); + request.removeAttribute(REQUEST_ATTRIBUTE_SESSION); + request.removeAttribute(SlingAuthenticatorSession.ATTR_NAME); + } + } + + // ---------- WebConsolePlugin support + + ArrayList getAuthenticationHandler() { + return authHandlerCache.getHolders(); + } + + ArrayList getAuthenticationRequirements() { + return authRequiredCache.getHolders(); + } + + // ---------- internal + + private ArrayList findApplicableAuthenticationHandlers( + HttpServletRequest request) { + + final ArrayList infos = authHandlerCache.findApplicableHolder(request); + if (infos != null) { + return infos; + } + + return EMPTY_INFO; + } + + @SuppressWarnings("unused") + private void bindAuthHandler(final AuthenticationHandler handler, + Map properties) { + final String paths[] = OsgiUtil.toStringArray(properties.get(AuthenticationHandler.PATH_PROPERTY)); + if (paths != null && paths.length > 0) { + for (int m = 0; m < paths.length; m++) { + if (paths[m] != null && paths[m].length() > 0) { + final AuthenticationHandlerHolder holder = new AuthenticationHandlerHolder( + paths[m], handler); + authHandlerCache.addHolder(holder); + } + } + } + } + + @SuppressWarnings("unused") + private void unbindAuthHandler(AuthenticationHandler handler, + Map properties) { + final String paths[] = OsgiUtil.toStringArray(properties.get(AuthenticationHandler.PATH_PROPERTY)); + if (paths != null && paths.length > 0) { + for (int m = 0; m < paths.length; m++) { + if (paths[m] != null && paths[m].length() > 0) { + final AuthenticationHandlerHolder holder = new AuthenticationHandlerHolder( + paths[m], handler); + authHandlerCache.removeHolder(holder); + } + } + } + } + + @SuppressWarnings( { "unused", "deprecation" }) + private void bindEngineAuthHandler( + org.apache.sling.engine.auth.AuthenticationHandler handler, + Map properties) { + final String paths[] = OsgiUtil.toStringArray(properties.get(AuthenticationHandler.PATH_PROPERTY)); + if (paths != null && paths.length > 0) { + for (int m = 0; m < paths.length; m++) { + if (paths[m] != null && paths[m].length() > 0) { + final EngineAuthenticationHandlerHolder holder = new EngineAuthenticationHandlerHolder( + paths[m], handler); + authHandlerCache.addHolder(holder); + } + } + } + } + + @SuppressWarnings( { "unused", "deprecation" }) + private void unbindEngineAuthHandler( + org.apache.sling.engine.auth.AuthenticationHandler handler, + Map properties) { + final String paths[] = OsgiUtil.toStringArray(properties.get(AuthenticationHandler.PATH_PROPERTY)); + if (paths != null && paths.length > 0) { + for (int m = 0; m < paths.length; m++) { + if (paths[m] != null && paths[m].length() > 0) { + final EngineAuthenticationHandlerHolder holder = new EngineAuthenticationHandlerHolder( + paths[m], handler); + authHandlerCache.removeHolder(holder); + } + } + } + } + + private AuthenticationInfo getAuthenticationInfo( + HttpServletRequest request, HttpServletResponse response) { + + // Get the path used to select the authenticator, if the SlingServlet + // itself has been requested without any more info, this will be null + // and we assume the root (SLING-722) + String pathInfo = request.getPathInfo(); + if (pathInfo == null || pathInfo.length() == 0) { + pathInfo = "/"; + } + + ArrayList local = findApplicableAuthenticationHandlers(request); + for (int i = 0; i < local.size(); i++) { + AbstractAuthenticationHandlerHolder holder = local.get(i); + if (pathInfo.startsWith(holder.path)) { + final AuthenticationInfo authInfo = holder.authenticate( + request, response); + if (authInfo != null) { + return authInfo; + } + } + } + + // no handler found for the request .... + log.debug("getCredentials: no handler could extract credentials"); + return null; + } + + /** Try to acquire an anonymous Session */ + private boolean getAnonymousSession(HttpServletRequest req, + HttpServletResponse res) { + + // Get an anonymous session if allowed, or if we are handling + // a request for the login servlet + if (isAnonAllowed(req)) { + try { + Session session = repository.login(); + setAttributes(session, null, req); + return true; + } catch (RepositoryException re) { + // cannot login > fail login, do not try to authenticate + handleLoginFailure(req, res, re); + return false; + } + } + + // If we get here, anonymous access is not allowed: redirect + // to the login servlet + log.info("getAnonymousSession: Anonymous access not allowed by configuration - redirecting to login"); + login(req, res); + + // fallback to no session + return false; + } + + private boolean isAnonAllowed(HttpServletRequest request) { + + String pathInfo = request.getPathInfo(); + if (pathInfo == null || pathInfo.length() == 0) { + pathInfo = "/"; + } + + ArrayList holderList = authRequiredCache.findApplicableHolder(request); + if (holderList != null && !holderList.isEmpty()) { + for (int i = 0; i < holderList.size(); i++) { + AuthenticationRequirementHolder holder = holderList.get(i); + if (pathInfo.startsWith(holder.path)) { + return !holder.requiresAuthentication(); + } + } + } + + if (LoginServlet.SERVLET_PATH.equals(pathInfo)) { + return true; + } + + // fallback to anonymous not allowed (aka authentication required) + return false; + } + + private void handleLoginFailure(HttpServletRequest request, + HttpServletResponse response, Exception reason) { + + if (reason instanceof TooManySessionsException) { + + // to many users, send a 503 Service Unavailable + log.info("authenticate: Too many sessions for user: {}", + reason.getMessage()); + + try { + response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, + "SlingAuthenticator: Too Many Users"); + } catch (IOException ioe) { + log.error("authenticate: Cannot send status 503 to client", ioe); + } + + } else if (reason instanceof LoginException) { + + // request authentication information and send 403 (Forbidden) + // if no handler can request authentication information. + log.info("authenticate: Unable to authenticate: {}", + reason.getMessage()); + log.debug("authenticate", reason); + + login(request, response); + + } else { + + // general problem, send a 500 Internal Server Error + log.error("authenticate: Unable to authenticate", reason); + + try { + response.sendError( + HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + "SlingAuthenticator: data access error, reason=" + + reason.getClass().getSimpleName()); + } catch (IOException ioe) { + log.error("authenticate: Cannot send status 500 to client", ioe); + } + } + + } + + /** + * Sets the request attributes required by the OSGi HttpContext interface + * specification for the handleSecurity method. In addition the + * {@link SlingHttpContext#SESSION} request attribute is set with the JCR + * Session. + */ + private void setAttributes(final Session session, final String authType, + final HttpServletRequest request) { + + final ResourceResolver resolver = resourceResolverFactory.getResourceResolver(session); + final SlingAuthenticatorSession sas = new SlingAuthenticatorSession( + session); + + // HttpService API required attributes + request.setAttribute(HttpContext.REMOTE_USER, session.getUserID()); + request.setAttribute(HttpContext.AUTHENTICATION_TYPE, authType); + + // resource resolver for down-stream use + request.setAttribute(REQUEST_ATTRIBUTE_RESOLVER, resolver); + request.setAttribute(SlingAuthenticatorSession.ATTR_NAME, sas); + + // JCR session for backwards compatibility + request.setAttribute(REQUEST_ATTRIBUTE_SESSION, session); + + log.debug( + "ResourceResolver stored as request attribute: user={}, workspace={}", + session.getUserID(), session.getWorkspace().getName()); + } + + /** + * Sends the session cookie for the name session with the given age in + * seconds. This sends a Version 1 cookie. + * + * @param response The {@link DeliveryHttpServletResponse} on which to send + * back the cookie. + * @param name The name of the cookie to send. + * @param value The value of cookie. + * @param maxAge The maximum age of the cookie in seconds. Positive values + * are persisted on the browser for the indicated number of + * seconds, setting the age to 0 (zero) causes the cookie to be + * deleted in the browser and using a negative value defines a + * temporary cookie to be deleted when the browser exits. + * @param path The cookie path to use. If empty or null the + */ + private void sendCookie(HttpServletResponse response, String name, + String value, int maxAge, String path) { + + if (path == null || path.length() == 0) { + log.debug("sendCookie: Using root path ''/''"); + path = "/"; + } + + Cookie cookie = new Cookie(name, value); + cookie.setMaxAge(maxAge); + cookie.setPath(path); + response.addCookie(cookie); + + // Tell a potential proxy server that this cookie is uncacheable + if (this.cacheControl) { + response.addHeader("Cache-Control", "no-cache=\"Set-Cookie\""); + } + } + + /** + * Handles impersonation based on the request parameter for impersonation + * (see {@link #sudoParameterName}) and the current setting in the sudo + * cookie. + *

    + * If the sudo parameter is empty or missing, the current cookie setting for + * impersonation is used. Else if the parameter is -, the + * current cookie impersonation is removed and no impersonation will take + * place for this request. Else the parameter is assumed to contain the + * handle of a user page acceptable for the {@link Session#impersonate} + * method. + * + * @param req The {@link DeliveryHttpServletRequest} optionally containing + * the sudo parameter. + * @param res The {@link DeliveryHttpServletResponse} to send the + * impersonation cookie. + * @param session The real {@link Session} to optionally replace with an + * impersonated session. + * @return The impersonated session or the input session. + * @throws LoginException thrown by the {@link Session#impersonate} method. + * @throws ContentBusException thrown by the {@link Session#impersonate} + * method. + * @see Session#impersonate for details on the user configuration + * requirements for impersonation. + */ + private Session handleImpersonation(HttpServletRequest req, + HttpServletResponse res, Session session) throws LoginException, + RepositoryException { + + // the current state of impersonation + String currentSudo = null; + Cookie[] cookies = req.getCookies(); + if (cookies != null) { + for (int i = 0; currentSudo == null && i < cookies.length; i++) { + if (sudoCookieName.equals(cookies[i].getName())) { + currentSudo = cookies[i].getValue(); + } + } + } + + /** + * sudo parameter : empty or missing to continue to use the setting + * already stored in the session; or "-" to remove impersonationa + * altogether (also from the session); or the handle of a user page to + * impersonate as that user (if possible) + */ + String sudo = req.getParameter(this.sudoParameterName); + if (sudo == null || sudo.length() == 0) { + sudo = currentSudo; + } else if ("-".equals(sudo)) { + sudo = null; + } + + // sudo the session if needed + if (sudo != null && sudo.length() > 0) { + Credentials creds = new SimpleCredentials(sudo, new char[0]); + session = session.impersonate(creds); + } + // invariant: same session or successful impersonation + + // set the (new) impersonation + if (sudo != currentSudo) { + if (sudo == null) { + // Parameter set to "-" to clear impersonation, which was + // active due to cookie setting + + // clear impersonation + this.sendCookie(res, this.sudoCookieName, "", 0, + req.getContextPath()); + + } else if (currentSudo == null || !currentSudo.equals(sudo)) { + // Parameter set to a name. As the cookie is not set yet + // or is set to another name, send the cookie with current sudo + + // (re-)set impersonation + this.sendCookie(res, this.sudoCookieName, sudo, -1, + req.getContextPath()); + } + } + + // return the session + return session; + } + + /** + * Returns the path to be used to select the authentication handler to login + * or logout with. + *

    + * This method uses the {@link Authenticator#LOGIN_RESOURCE} request + * attribute. If this attribute is not set (or is not a string), the request + * path info is used. If this is not set either, or is the empty string, "/" + * is returned. + * + * @param request The request providing the request attribute or path info. + * @return The path as set by the request attribute or the path info or "/" + * if neither is set. + */ + private String getHandlerSelectionPath(HttpServletRequest request) { + final Object loginPathO = request.getAttribute(Authenticator.LOGIN_RESOURCE); + String path = (loginPathO instanceof String) + ? (String) loginPathO + : request.getPathInfo(); + if (path == null || path.length() == 0) { + path = "/"; + } + return path; + } + + private static class SlingAuthenticatorSession { + + static final String ATTR_NAME = "$$org.apache.sling.commons.auth.impl.SlingAuthenticatorSession$$"; + + private Session session; + + SlingAuthenticatorSession(final Session session) { + this.session = session; + } + + void logout() { + if (session != null) { + try { + // logout if session is still alive (and not logged out) + if (session.isLive()) { + session.logout(); + } + } catch (Throwable t) { + // TODO: log + } finally { + session = null; + } + } + } + + @Override + protected void finalize() { + logout(); + } + } + + private static class SlingAuthenticatorServiceListener implements + AllServiceListener { + + private final SlingAuthenticator authenticator; + + private final HashMap props = new HashMap(); + + static ServiceListener createListener(final BundleContext context, + final SlingAuthenticator authenticator) { + SlingAuthenticatorServiceListener listener = new SlingAuthenticatorServiceListener( + authenticator); + try { + final String filter = "(" + PAR_AUTH_REQ + "=*)"; + context.addServiceListener(listener,filter); + ServiceReference[] refs = context.getAllServiceReferences(null, filter); + if (refs != null) { + for (ServiceReference ref : refs) { + listener.addService(ref); + } + } + return listener; + } catch (InvalidSyntaxException ise) { + } + return null; + } + + private SlingAuthenticatorServiceListener( + final SlingAuthenticator authenticator) { + this.authenticator = authenticator; + } + + public void serviceChanged(ServiceEvent event) { + + // modification of service properties, unregistration of the + // service or service properties does not contain requirements + // property any longer (new event with type 8 added in OSGi Core + // 4.2) + if ((event.getType() & (ServiceEvent.MODIFIED + | ServiceEvent.UNREGISTERING | 8)) != 0) { + removeService(event.getServiceReference()); + } + + // add requirements for newly registered services and for + // updated services + if ((event.getType() & (ServiceEvent.REGISTERED | ServiceEvent.MODIFIED)) != 0) { + addService(event.getServiceReference()); + } + } + + private void addService(final ServiceReference ref) { + String[] authReqs = OsgiUtil.toStringArray(ref.getProperty(PAR_AUTH_REQ)); + for (String authReq : authReqs) { + if (authReq != null && authReq.length() > 0) { + authenticator.authRequiredCache.addHolder(AuthenticationRequirementHolder.fromConfig(authReq)); + } + } + props.put(ref.getProperty(Constants.SERVICE_ID), authReqs); + } + + private void removeService(final ServiceReference ref) { + String[] authReqs = props.remove(ref.getProperty(Constants.SERVICE_ID)); + for (String authReq : authReqs) { + if (authReq != null && authReq.length() > 0) { + authenticator.authRequiredCache.removeHolder(AuthenticationRequirementHolder.fromConfig(authReq)); + } + } + } + }; + +} Propchange: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/impl/SlingAuthenticator.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/impl/SlingAuthenticator.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Rev Url Added: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/impl/engine/EngineAuthenticationHandlerHolder.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/impl/engine/EngineAuthenticationHandlerHolder.java?rev=896345&view=auto ============================================================================== --- sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/impl/engine/EngineAuthenticationHandlerHolder.java (added) +++ sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/impl/engine/EngineAuthenticationHandlerHolder.java Wed Jan 6 07:55:01 2010 @@ -0,0 +1,96 @@ +/* + * 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.commons.auth.impl.engine; + +import java.io.IOException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.sling.commons.auth.impl.AbstractAuthenticationHandlerHolder; +import org.apache.sling.commons.auth.spi.AuthenticationInfo; +import org.apache.sling.engine.auth.AuthenticationHandler; + +/** + * The EngineAuthenticationHandlerHolder class represents an + * old-style Sling {@link AuthenticationHandler} service in the internal data + * structure of the + * {@link org.apache.sling.commons.auth.impl.SlingAuthenticator}. + */ +@SuppressWarnings("deprecation") +public final class EngineAuthenticationHandlerHolder extends + AbstractAuthenticationHandlerHolder { + + // the actual authentication handler + private final AuthenticationHandler handler; + + public EngineAuthenticationHandlerHolder(final String fullPath, + final AuthenticationHandler handler) { + super(fullPath); + this.handler = handler; + } + + public AuthenticationInfo doAuthenticate(HttpServletRequest request, + HttpServletResponse response) { + + org.apache.sling.engine.auth.AuthenticationInfo engineAuthInfo = handler.authenticate( + request, response); + if (engineAuthInfo == null) { + return null; + } else if (engineAuthInfo == org.apache.sling.engine.auth.AuthenticationInfo.DOING_AUTH) { + return AuthenticationInfo.DOING_AUTH; + } + + return new AuthenticationInfo(engineAuthInfo.getAuthType(), + engineAuthInfo.getCredentials(), engineAuthInfo.getWorkspaceName()); + + } + + public boolean doRequestAuthentication(HttpServletRequest request, + HttpServletResponse response) throws IOException { + return handler.requestAuthentication(request, response); + } + + public void doDropAuthentication(HttpServletRequest request, + HttpServletResponse response) { + // Engine AuthenticationHandler does not have this method + } + + @Override + public boolean equals(Object obj) { + + // equality is the base class equality (based on the fullpath) + // and the encapsulated holders being the same. + if (super.equals(obj)) { + if (obj.getClass() == getClass()) { + EngineAuthenticationHandlerHolder other = (EngineAuthenticationHandlerHolder) obj; + return other.handler == handler; + } + } + + // handlers are not the same, so the holders are not the same + return false; + } + + @Override + public String toString() { + return handler.toString() + " (Legacy API Handler)"; + } + +} \ No newline at end of file Propchange: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/impl/engine/EngineAuthenticationHandlerHolder.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/impl/engine/EngineAuthenticationHandlerHolder.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Rev Url Added: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/impl/engine/EngineSlingAuthenticator.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/impl/engine/EngineSlingAuthenticator.java?rev=896345&view=auto ============================================================================== --- sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/impl/engine/EngineSlingAuthenticator.java (added) +++ sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/impl/engine/EngineSlingAuthenticator.java Wed Jan 6 07:55:01 2010 @@ -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.commons.auth.impl.engine; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.sling.engine.auth.Authenticator; + +/** + * The EngineSlingAuthenticator class is a simple proxy service + * providing the old Sling Engine {@link Authenticator} service calling into the + * new standalone Apache Sling + * {@link org.apache.sling.commons.auth.Authenticator} service. + * + * @scr.component metatype="no" + * @scr.service interface="org.apache.sling.engine.auth.Authenticator" + * @scr.property name="service.description" + * value="Apache Sling Request Authenticator (Legacy Bridge)" + * @scr.property name="service.vendor" value="The Apache Software Foundation" + */ +@SuppressWarnings("deprecation") +public class EngineSlingAuthenticator implements Authenticator { + + /** + * @scr.reference + */ + private org.apache.sling.commons.auth.Authenticator slingAuthenticator; + + public void login(HttpServletRequest request, HttpServletResponse response) { + slingAuthenticator.login(request, response); + } + + public void logout(HttpServletRequest request, HttpServletResponse response) { + slingAuthenticator.logout(request, response); + } + +} Propchange: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/impl/engine/EngineSlingAuthenticator.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/impl/engine/EngineSlingAuthenticator.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Rev Url Added: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/spi/AuthenticationHandler.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/spi/AuthenticationHandler.java?rev=896345&view=auto ============================================================================== --- sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/spi/AuthenticationHandler.java (added) +++ sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/spi/AuthenticationHandler.java Wed Jan 6 07:55:01 2010 @@ -0,0 +1,143 @@ +/* + * 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.commons.auth.spi; + +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. + */ +public interface AuthenticationHandler { + + /** + * The name under which an implementation of this interface must be + * registered to be used as an authentication handler. + */ + static final String SERVICE_NAME = "org.apache.sling.commons.auth.AuthenticationHandler"; + + /** + * The name of the service registration property listing one or more URL + * paths for which the authentication handler is to be used. The property + * may be a single string value or an array of strings or a Collection of + * strings. + *

    + * Each string value may either be an absolute path (e.g. /content) or an + * absolute URL (e.g. http://thehost/content) to assign the authentication + * handler to authenticate request for a select virtual host. + *

    + * Authentication handlers without a path service registration + * property are ignored. + */ + static final String PATH_PROPERTY = "path"; + + /** + * Extracts credential data from the request if at all contained. + *

    + * The method returns any of the following values : + * + * + * + * + * + * + * + * + * + *
    value + * description + *
    null + * no user details were contained in the request or the handler is not + * capable or willing to extract credentials from the request + *
    {@link AuthenticationInfo#DOING_AUTH} + * the handler is in an ongoing authentication transaction with the + * client. Request processing should be aborted at this stage. + *
    AuthenticationInfo object + * The user sent credentials. The returned object contains the + * credentials as well as the type of authentication transmission employed. + *
    + *

    + * The method must not request credential information from the client, if + * they are not found in the request. + *

    + * The value of {@link #PATH_PROPERTY} service registration property value + * triggering this call is available as the path request + * 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 + * information on the request failure to the user. + * @return A valid AuthenticationInfo instance identifying the + * request user, {@link AuthenticationInfo#DOING_AUTH} if the + * handler is in an authentication transaction with the client or + * null if the request does not contain authentication information. + * In case of {@link AuthenticationInfo#DOING_AUTH}, the method must + * have sent a response indicating that fact to the client. + */ + AuthenticationInfo authenticate(HttpServletRequest request, + HttpServletResponse response); + + /** + * Requests authentication information from the client. Returns + * true if the information has been requested and request + * processing can be terminated normally. Otherwise the authorization + * information could not be requested. + *

    + * The HttpServletResponse.sendError methods should not be used + * by the implementation because these responses might be post-processed by + * the servlet container's error handling infrastructure thus preventing the + * correct operation of the authentication handler. To convey a HTTP + * response status the HttpServletResponse.setStatus method + * should be used. + *

    + * The value of {@link #PATH_PROPERTY} service registration property value + * triggering this call is available as the path request + * 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 + * inquiry for the given request. false otherwise. + * @throws IOException If an error occurrs sending the authentication + * inquiry to the client. + */ + boolean requestAuthentication(HttpServletRequest request, + HttpServletResponse response) throws IOException; + + /** + * 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. + */ + void dropAuthentication(HttpServletRequest request, + HttpServletResponse response) throws IOException; +} Propchange: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/spi/AuthenticationHandler.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/spi/AuthenticationHandler.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Rev Url Added: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/spi/AuthenticationInfo.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/spi/AuthenticationInfo.java?rev=896345&view=auto ============================================================================== --- sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/spi/AuthenticationInfo.java (added) +++ sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/spi/AuthenticationInfo.java Wed Jan 6 07:55:01 2010 @@ -0,0 +1,323 @@ +/* + * 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.commons.auth.spi; + +import java.util.HashMap; +import javax.jcr.Credentials; +import javax.jcr.SimpleCredentials; + +/** + * The AuthenticationInfo conveys any authentication credentials + * and/or details extracted by the + * {@link AuthenticationHandler#authenticate(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)} + * method from the request. + *

    + * {@link AuthenticationHandler} implementations must return instances of this + * class which may be constructed through any of the provided public + * constructors. + *

    + * Internally all values are stored in the map where some property names have + * special semantics and the data type of the properties are ensured by the + * {@link #put(String, Object)} method implementation. + */ +@SuppressWarnings("serial") +public class AuthenticationInfo extends HashMap { + + /** + * A special instance of this class which is returned by the + * {@link AuthenticationHandler#authenticate(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)} + * method to inform the caller, that a response has been sent to the client + * to request for credentials. + *

    + * If this value is returned, the request should be considered finished and + * no further actions should be taken on this request. + */ + public static final AuthenticationInfo DOING_AUTH = new AuthenticationInfo(); + + /** + * The name of the special property providing the authentication type + * provided by the {@link AuthenticationHandler}. This value must be + * supplied to one of the constructors and is ultimately used as the value + * of the HttpServletRequest.getAuthType method. + *

    + * This property is always present (and cannot be removed) in this map and + * is of String type. + */ + public static final String AUTH_TYPE = "sling.authType"; + + /** + * The name of the property providing the name of the user on whose behalf + * the request is being handled. This property is set by the + * {@link #AuthenticationInfo(String, String, char[], String)} constructor + * and may be null if this instance is created by either the + * {@link #AuthenticationInfo(String, Credentials)} or + * {@link #AuthenticationInfo(String, Credentials, String)} constructors. + *

    + * The type of this property, if present, is String. + */ + public static final String USER = "user.name"; + + /** + * The name of the property providing the password of the user on whose + * behalf the request is being handled. This property is set by the + * {@link #AuthenticationInfo(String, String, char[], String)} constructor + * and may be null if this instance is created by either the + * {@link #AuthenticationInfo(String, Credentials)} or + * {@link #AuthenticationInfo(String, Credentials, String)} constructors. + *

    + * The type of this property, if present, is char[]. + */ + public static final String PASSWORD = "user.password"; + + /** + * The name of the property providing the JCR credentials. These credentials + * are preset to the credentials given to the + * {@link #AuthenticationInfo(String, Credentials)} or + * {@link #AuthenticationInfo(String, Credentials, String)} constructors. If + * the {@link #AuthenticationInfo(String, String, char[], String)} + * constructor is used the credentials property is set to a JCR + * SimpleCredentials instance containing the user id and + * password passed to the constructor. + */ + public static final String CREDENTIALS = "user.jcr.credentials"; + + /** + * The name of the property providing the name of the JCR workspace to which + * the request should be connected. This property may be set by any of the + * constructors. If this property is not set, the user will be connected to + * a default workspace as defined by the JCR repository to which the request + * is connected. + *

    + * The type of this property, if present, is String. + */ + public static final String WORKSPACE = "user.jcr.workspace"; + + /** Creates an empty instance, used for the {@link #DOING_AUTH} constant */ + private AuthenticationInfo() { + super.put(AUTH_TYPE, "Authentication in Progress"); + } + + /** + * Creates an instance of this class with the given authentication type and + * credentials connecting to the default workspace as if the + * {@link #AuthenticationInfo(String, Credentials, String)} method would be + * called with a null workspace name. + * + * @param authType The authentication type, must not be null. + * @param credentials The credentials, must not be null. + * @throws NullPointerException if authType is + * null . + */ + public AuthenticationInfo(final String authType, + final Credentials credentials) { + this(authType, credentials, null); + } + + /** + * Creates an instance of this class with the given authentication type and + * credentials. + * + * @param authType The authentication type, must not be null. + * @param credentials The credentials, must not be null. + * @param workspaceName The name of the workspace to connect to, may be + * null to connect to the default workspace. + * @throws NullPointerException if authType is + * null . + */ + public AuthenticationInfo(final String authType, + final Credentials credentials, final String workspaceName) { + if (authType == null) { + throw new NullPointerException("authType"); + } + + super.put(AUTH_TYPE, authType); + putIfNotNull(CREDENTIALS, credentials); + putIfNotNull(WORKSPACE, workspaceName); + } + + /** + * Creates an instance of this class with the given authentication type and + * credentials. + * + * @param authType The authentication type, must not be null. + * @param credentials The credentials, must not be null. + * @param workspaceName The name of the workspace to connect to, may be + * null to connect to the default workspace. + * @throws NullPointerException if authType is + * null + */ + public AuthenticationInfo(final String authType, final String userId, + final char[] password, final String workspaceName) { + if (authType == null) { + throw new NullPointerException("authType"); + } + + super.put(AUTH_TYPE, authType); + putIfNotNull(USER, userId); + putIfNotNull(PASSWORD, password); + putIfNotNull(WORKSPACE, workspaceName); + + // also store credentials if the user id is not null/empty + if (userId != null && userId.length() > 0) { + final char[] pwd = (password == null) ? new char[0] : password; + super.put(CREDENTIALS, new SimpleCredentials(userId, pwd)); + } + } + + /** + * Returns the authentication type stored as the {@link #AUTH_TYPE} property + * in this map. This value is expected to never be null. + *

    + * If authentication is taking place through one of the standard ways, such + * as Basic or Digest, the return value is one of the predefined constants + * of the HttpServletRequest interface. Otherwise the value may + * be specific to the {@link AuthenticationHandler} implementation. + */ + public String getAuthType() { + return (String) get(AUTH_TYPE); + } + + /** + * Returns the user name stored as the {@link #USER} property or + * null if the user is not set in this map. + */ + public String getUser() { + return (String) get(USER); + } + + /** + * Returns the password stored as the {@link #PASSWORD} property or + * null if the password is not set in this map. + */ + public char[] getPassword() { + return (char[]) get(PASSWORD); + } + + /** + * Returns the workspace name stored as the {@link #WORKSPACE} property or + * null if the workspace name is not set in this map. + */ + public String getWorkspaceName() { + return (String) get(WORKSPACE); + } + + /** + * Returns the JCR credentials stored as the {@link #CREDENTIALS} property + * or null if the credentials are not set in this map. + */ + public Credentials getCredentials() { + return (Credentials) get(CREDENTIALS); + } + + /** + * Sets or resets a property with the given key to a new + * value. Some keys have special meanings and their values are + * required to have predefined as listed in the following table: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    {@link #AUTH_TYPE}String
    {@link #USER}String
    {@link #PASSWORD}char[]
    {@link #CREDENTIALS}javax.jcr.Credentials
    {@link #WORKSPACE}String
    + *

    + * If the value for the special key does not match the required type an + * IllegalArgumentException is thrown. + * + * @param key The name of the property to set + * @param value The value of the property which must be of a special type if + * the key designates one of the predefined + * properties. + * @return The value previously set for the given key. + * @throws IllegalArgumentException if key designates one of + * the special properties and the value does not + * have the correct type for the respective key. + */ + @Override + public Object put(final String key, final Object value) { + + if (AUTH_TYPE.equals(key) && !(value instanceof String)) { + throw new IllegalArgumentException(AUTH_TYPE + + " property must be a String"); + } + + if (USER.equals(key) && !(value instanceof String)) { + throw new IllegalArgumentException(USER + + " property must be a String"); + } + + if (PASSWORD.equals(key) && !(value instanceof char[])) { + throw new IllegalArgumentException(PASSWORD + + " property must be a char[]"); + } + + if (CREDENTIALS.equals(key) && !(value instanceof String)) { + throw new IllegalArgumentException(CREDENTIALS + + " property must be a javax.jcr.Credentials instance"); + } + + if (WORKSPACE.equals(key) && !(value instanceof String)) { + throw new IllegalArgumentException(WORKSPACE + + " property must be a String"); + } + + return super.put(key, value); + } + + /** + * Removes the entry with the given key and returns its former + * value (if existing). If the key is {@link #AUTH_TYPE} the + * value is not actually removed and null is always returned. + * + * @param key Removes the value associated with this key. + * @return The former value associated with the key. + */ + @Override + public Object remove(Object key) { + + // don't return the auth type from the map + if (!AUTH_TYPE.equals(key)) { + return null; + } + + return super.remove(key); + } + + // helper to only set the property if the value is not null + private void putIfNotNull(final String key, final Object value) { + if (value != null) { + super.put(key, value); + } + } +} Propchange: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/spi/AuthenticationInfo.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/commons/auth/spi/AuthenticationInfo.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Rev Url Added: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/engine/auth/AuthenticationHandler.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/engine/auth/AuthenticationHandler.java?rev=896345&view=auto ============================================================================== --- sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/engine/auth/AuthenticationHandler.java (added) +++ sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/engine/auth/AuthenticationHandler.java Wed Jan 6 07:55:01 2010 @@ -0,0 +1,117 @@ +/* + * 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. + * + * @deprecated use + * {@link org.apache.sling.commons.auth.spi.AuthenticationHandler} + * instead + */ +public interface 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"; + + /** + * Extracts credential data from the request if at all contained. + *

    + * The method returns any of the following values : + * + * + * + * + * + * + * + * + * + *
    value + * description + *
    null + * no user details were contained in the request or the handler is not + * capable or willing to extract credentials from the request + *
    {@link AuthenticationInfo#DOING_AUTH} + * the handler is in an ongoing authentication transaction with the + * client. Request processing should be aborted at this stage. + *
    AuthenticationInfo object + * The user sent credentials. The returned object contains the + * credentials as well as the type of authentication transmission employed. + *
    + *

    + * The method must not request credential information from the client, if + * they are not found in the request. + *

    + * The value of {@link #PATH_PROPERTY} service registration property value + * triggering this call is available as the path request + * 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 + * information on the request failure to the user. + * @return A valid AuthenticationInfo instance identifying the + * request user, {@link AuthenticationInfo#DOING_AUTH} if the + * handler is in an authentication transaction with the client or + * null if the request does not contain authentication information. + * In case of {@link AuthenticationInfo#DOING_AUTH}, the method must + * have sent a response indicating that fact to the client. + */ + AuthenticationInfo authenticate(HttpServletRequest request, + HttpServletResponse response); + + /** + * Requests authentication information from the client. Returns + * true if the information has been requested and request + * processing can be terminated normally. Otherwise the authorization + * information could not be requested. + *

    + * Any response sent by the handler though the sendError method + * is also handled by the error handler infrastructure. + *

    + * The value of {@link #PATH_PROPERTY} service registration property value + * triggering this call is available as the path request + * 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 + * inquiry for the given request. false otherwise. + * @throws IOException If an error occurrs sending the authentication + * inquiry to the client. + */ + boolean requestAuthentication(HttpServletRequest request, + HttpServletResponse response) throws IOException; +} Propchange: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/engine/auth/AuthenticationHandler.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/engine/auth/AuthenticationHandler.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Rev Url Added: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/engine/auth/AuthenticationInfo.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/engine/auth/AuthenticationInfo.java?rev=896345&view=auto ============================================================================== --- sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/engine/auth/AuthenticationInfo.java (added) +++ sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/engine/auth/AuthenticationInfo.java Wed Jan 6 07:55:01 2010 @@ -0,0 +1,117 @@ +/* + * 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 javax.jcr.Credentials; + +/** + * The AuthenticationInfo defines the data returned from the + * {@link AuthenticationHandler#authenticate(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)} + * method. + * + * @deprecated see {@link AuthenticationHandler} + */ +public class AuthenticationInfo { + + /** + * This object is returned by the + * {@link AuthenticationHandler#authenticate(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)} + * method to indicate an ongoing authentication transaction. + */ + public static final AuthenticationInfo DOING_AUTH = new AuthenticationInfo(); + + /** The type of authentication */ + private final String authType; + + /** The javax.jcr.Credentials extracted from the request */ + private final Credentials credentials; + + /** + * The name of the workspace this user is wishing to login, + * null means the default workspace. + */ + private final String workspaceName; + + /** Creates an empty instance, used for the {@link #DOING_AUTH} constants */ + private AuthenticationInfo() { + this(null, null, null); + } + + /** + * Creates an instance of this class with the given authentication type and + * credentials connecting to the default workspace as if the + * {@link #AuthenticationInfo(String, Credentials, String)} method would be + * called with a null workspace name. + * + * @param authType The authentication type, must not be null. + * @param credentials The credentials, must not be null. + * @see #getAuthType() + * @see #getCredentials() + */ + public AuthenticationInfo(String authType, Credentials credentials) { + this(authType, credentials, null); + } + + /** + * Creates an instance of this class with the given authentication type and + * credentials. + * + * @param authType The authentication type, must not be null. + * @param credentials The credentials, must not be null. + * @param workspaceName The name of the workspace to connect to, may be + * null to connect to the default workspace. + * @see #getAuthType() + * @see #getCredentials() + */ + public AuthenticationInfo(String authType, Credentials credentials, + String workspaceName) { + this.authType = authType; + this.credentials = credentials; + this.workspaceName = workspaceName; + } + + /** + * Returns type of authentication provisioning. + *

    + * If authentication is taking place through one of the standard ways, such + * as Basic or Digest, the return value is one of the predefined constants + * of the HttpServletRequest interface. Otherwise the value + * may be specific to the {@link AuthenticationHandler} implementation. + */ + public String getAuthType() { + return authType; + } + + /** + * Returns the credentials extracted from the client request to use for + * authentication. + */ + public Credentials getCredentials() { + return credentials; + } + + /** + * Returns the name of the workspace the user contained in this instance + * wishes to connect to. This may be null, in which case the + * user is connected to the default workspace. + */ + public String getWorkspaceName() { + return workspaceName; + } +} Propchange: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/engine/auth/AuthenticationInfo.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/engine/auth/AuthenticationInfo.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Rev Url Added: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/engine/auth/Authenticator.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/engine/auth/Authenticator.java?rev=896345&view=auto ============================================================================== --- sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/engine/auth/Authenticator.java (added) +++ sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/engine/auth/Authenticator.java Wed Jan 6 07:55:01 2010 @@ -0,0 +1,62 @@ +/* + * 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 javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * The Authenticator interface defines the service interface of the + * authenticator used by the Sling engine. This service provides a method to + * find an {@link AuthenticationHandler} and call its + * {@link AuthenticationHandler#requestAuthentication(HttpServletRequest, HttpServletResponse)} + * method. + *

    + * 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 + * @deprecated use {@link org.apache.sling.commons.auth.Authenticator} instead + */ +public interface Authenticator { + + /** + * Finds an {@link AuthenticationHandler} for the given request and call its + * {@link AuthenticationHandler#requestAuthentication(HttpServletRequest, HttpServletResponse)} + * method to initiate an authentication process with the client to login to + * Sling. + *

    + * This method must be called on an uncommitted response since the + * implementation may want to reset the response to start 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 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); + +} Propchange: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/engine/auth/Authenticator.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/engine/auth/Authenticator.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Rev Url Added: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/engine/auth/NoAuthenticationHandlerException.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/engine/auth/NoAuthenticationHandlerException.java?rev=896345&view=auto ============================================================================== --- sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/engine/auth/NoAuthenticationHandlerException.java (added) +++ sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/engine/auth/NoAuthenticationHandlerException.java Wed Jan 6 07:55:01 2010 @@ -0,0 +1,44 @@ +/* + * 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 org.apache.sling.api.SlingException; + +/** + * The NoAuthenticationHandlerException is thrown to indicate that + * there is no {@link AuthenticationHandler} willing to handle the request. + *

    + * This exception is thrown without a message. The caller of the + * {@link Authenticator} method called is expected to immediately handle this + * exception and not to forward it up the call chain. + *

    + * This exception is not intended to be thrown by client code but is used by the + * {@link Authenticator} implementation to indicate, that no + * {@link AuthenticationHandler} is available to login. + * + * @deprecated see {@link Authenticator} + */ +@SuppressWarnings("serial") +public class NoAuthenticationHandlerException extends SlingException { + + public NoAuthenticationHandlerException() { + super(); + } + +} Propchange: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/engine/auth/NoAuthenticationHandlerException.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/bundles/commons/auth/src/main/java/org/apache/sling/engine/auth/NoAuthenticationHandlerException.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Rev Url