You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by fm...@apache.org on 2010/09/24 22:03:24 UTC
svn commit: r1001056 - in /sling/trunk/bundles/auth/core: SLING-1745.patch
src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java
Author: fmeschbe
Date: Fri Sep 24 20:03:24 2010
New Revision: 1001056
URL: http://svn.apache.org/viewvc?rev=1001056&view=rev
Log:
SLING-1745 No redirect to login form for AJAX requests (403 instead)
SLING-1400 Use 401 if possible for failed authentication of non-browser requests (fallback to 403)
Added:
sling/trunk/bundles/auth/core/SLING-1745.patch
Modified:
sling/trunk/bundles/auth/core/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java
Added: sling/trunk/bundles/auth/core/SLING-1745.patch
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/auth/core/SLING-1745.patch?rev=1001056&view=auto
==============================================================================
--- sling/trunk/bundles/auth/core/SLING-1745.patch (added)
+++ sling/trunk/bundles/auth/core/SLING-1745.patch Fri Sep 24 20:03:24 2010
@@ -0,0 +1,171 @@
+Index: src/main/java/org/apache/sling/auth/core/impl/HttpBasicAuthenticationHandler.java
+===================================================================
+--- src/main/java/org/apache/sling/auth/core/impl/HttpBasicAuthenticationHandler.java (Revision 998797)
++++ src/main/java/org/apache/sling/auth/core/impl/HttpBasicAuthenticationHandler.java (Arbeitskopie)
+@@ -224,7 +224,7 @@
+ * @return <code>true</code> if the 401/UNAUTHORIZED method has successfully
+ * been sent.
+ */
+- private boolean sendUnauthorized(HttpServletResponse response) {
++ boolean sendUnauthorized(HttpServletResponse response) {
+
+ if (response.isCommitted()) {
+
+Index: src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java
+===================================================================
+--- src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java (Revision 998936)
++++ src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java (Arbeitskopie)
+@@ -47,6 +47,7 @@
+ import org.apache.sling.api.resource.LoginException;
+ import org.apache.sling.api.resource.ResourceResolver;
+ import org.apache.sling.api.resource.ResourceResolverFactory;
++import org.apache.sling.api.servlets.HttpConstants;
+ import org.apache.sling.auth.core.AuthenticationSupport;
+ import org.apache.sling.auth.core.impl.engine.EngineAuthenticationHandlerHolder;
+ import org.apache.sling.auth.core.spi.AbstractAuthenticationHandler;
+@@ -161,6 +162,32 @@
+ */
+ private static final String AUTH_INFO_PROP_FEEDBACK_HANDLER = "$$sling.auth.AuthenticationFeedbackHandler$$";
+
++ /**
++ * Request header commonly set by Ajax Frameworks to indicate the request is
++ * posted as an Ajax request. The value set is expected to be
++ * {@link #XML_HTTP_REQUEST}.
++ * <p>
++ * This header is known to be set by JQuery, ExtJS and Prototype. Other
++ * client-side JavaScript framework most probably also set it.
++ */
++ private static final String X_REQUESTED_WITH = "X-Requested-With";
++
++ /**
++ * The expected value of the {@link #X_REQUESTED_WITH} request header to
++ * identify a request as an Ajax request.
++ */
++ private static final String XML_HTTP_REQUEST = "XMLHttpRequest";
++
++ /**
++ * The name of the request header set by the
++ * {@link #doLogin(HttpServletRequest, HttpServletResponse)} if instead of
++ * requesting credentials from the client a 403/FORBIDDEN response is sent.
++ * <p>
++ * This header may be inspected by clients for a reason why the request
++ * failed.
++ */
++ private static final String X_REASON = "X-Reason";
++
+ @Reference
+ private ResourceResolverFactory resourceResolverFactory;
+
+@@ -842,17 +869,72 @@
+ }
+
+ /**
+- * Calls the {@link #login(HttpServletRequest, HttpServletResponse)} method
+- * catching declared exceptions of that method and cleanly handling and
+- * logging them. Particularly if no authentication handler is available to
+- * request credentials a 403/FORBIDDEN response is sent back to the client.
++ * Tries to request credentials from the client. The following mechanisms
++ * are implemented by this method:
++ * <ul>
++ * <li>If the request is an <code>OPTIONS</code> request and the HTTP Basic
++ * Authentication Handler is at least enabled for preemptive credentials
++ * processing, a 401/UNAUTHORIZED response is sent back. This helps
++ * implementing HTTP Basic authentication with WebDAV clients which send an
++ * OPTIONS request as a very first request to find out about the WebDAV
++ * capabilities of a supposed WebDAV server. If HTTP Basic Authentication is
++ * completely switched of a 403/FORBIDDEN response is sent back instead.</li>
++ * <li>If the request is an Ajax request a 403/FORBIDDIN response is simply
++ * sent back because we assume an Ajax requestor cannot properly handle any
++ * request for credentials graciously. To help any Ajax caller to identify
++ * the problem the {@link #X_REASON} header is set to a either the value of
++ * the {@link AuthenticationHandler#FAILURE_REASON} request attribute or to
++ * some generic description describing the reason. Ajax requests are
++ * identified by the {@link #X_REQUESTED_WITH} request header set to
++ * {@link #XML_HTTP_REQUEST} which is done by most (if not all) modern Ajax
++ * frameworks like JQuery, ExtJS, Prototype, etc.
++ * <li>Otherwise the {@link #login(HttpServletRequest, HttpServletResponse)}
++ * method is called to try to find and call an authentication handler to
++ * request credentials from the client. If none is available or willing to
++ * request credentials, a 403/FORBIDDEN response is also sent back to the
++ * client.</li>
++ * </ul>
++ * <p>
++ * This method is called in three situations:
++ * <ul>
++ * <li>If the request contains no credentials but anonymous login is not
++ * allowed</li>
++ * <li>If the request contains credentials but getting the Resource Resolver
++ * using the provided credentials fails</li>
++ * <li>If the selected authentication handler indicated any presented
++ * credentials are not valid</li>
++ * </ul>
+ */
+ private void doLogin(HttpServletRequest request,
+ HttpServletResponse response) {
+
++ String failureReason = null;
++ if (HttpConstants.METHOD_OPTIONS.equals(request.getMethod())) {
++
++ // Presumably this is WebDAV. If HTTP Basic is fully enabled or
++ // enabled for preemptive credential support, we just request
++ // HTTP Basic credentials. Otherwise (HTTP Basic is fully
++ // switched off, 403 is sent back)
++ if (httpBasicHandler != null) {
++ httpBasicHandler.sendUnauthorized(response);
++ return;
++ }
++
++ } else if (XML_HTTP_REQUEST.equals(request.getHeader(X_REQUESTED_WITH))) {
++
++ // this is an Ajax request, don't try to request credentials
++ // but fail the request with an optional reason response header
++ Object jReason = request.getAttribute(AuthenticationHandler.FAILURE_REASON);
++ if (jReason != null) {
++ failureReason = jReason.toString();
++ }
++
++ } else {
++
+ try {
+
+ login(request, response);
++ return;
+
+ } catch (IllegalStateException ise) {
+
+@@ -860,17 +942,32 @@
+
+ } catch (NoAuthenticationHandlerException nahe) {
+
++ /*
++ * Don't set the failureReason for missing authentication
++ * handlers to not disclose this setup information.
++ */
++
+ log.error("doLogin: Cannot login: No AuthenticationHandler available to handle the request");
+
++ }
++ }
++
++ // if we are here, we cannot redirect to the login form because it is
++ // an XHR request or because there is no authentication handler willing
++ // request credentials from the client.
++
++ if (failureReason == null) {
++ failureReason = "Mandatory authentication is not possible";
++ }
++
+ try {
+- response.sendError(HttpServletResponse.SC_FORBIDDEN,
+- "Cannot login");
++ response.setHeader(X_REASON, failureReason);
++ response.sendError(HttpServletResponse.SC_FORBIDDEN, failureReason);
+ } catch (IOException ioe) {
+ log.error("doLogin: Failed sending 403 status", ioe);
+ }
+
+ }
+- }
+
+ /**
+ * Sets the request attributes required by the OSGi HttpContext interface
Modified: sling/trunk/bundles/auth/core/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/auth/core/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java?rev=1001056&r1=1001055&r2=1001056&view=diff
==============================================================================
--- sling/trunk/bundles/auth/core/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java (original)
+++ sling/trunk/bundles/auth/core/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java Fri Sep 24 20:03:24 2010
@@ -161,6 +161,34 @@ public class SlingAuthenticator implemen
*/
private static final String AUTH_INFO_PROP_FEEDBACK_HANDLER = "$$sling.auth.AuthenticationFeedbackHandler$$";
+ /**
+ * Request header commonly set by Ajax Frameworks to indicate the request is
+ * posted as an Ajax request. The value set is expected to be
+ * {@link #XML_HTTP_REQUEST}.
+ * <p>
+ * This header is known to be set by JQuery, ExtJS and Prototype. Other
+ * client-side JavaScript framework most probably also set it.
+ *
+ * @see #isAjaxRequest(HttpServletRequest)
+ */
+ private static final String X_REQUESTED_WITH = "X-Requested-With";
+
+ /**
+ * The expected value of the {@link #X_REQUESTED_WITH} request header to
+ * identify a request as an Ajax request.
+ *
+ * @see #isAjaxRequest(HttpServletRequest)
+ */
+ private static final String XML_HTTP_REQUEST = "XMLHttpRequest";
+
+ /**
+ * The name of the <code>Accept</code> header which must not exists to
+ * consider a request an initial WebDAV request.
+ *
+ * @see #isBrowserRequest(HttpServletRequest)
+ */
+ private static final String HEADER_ACCEPT = "Accept";
+
@Reference
private ResourceResolverFactory resourceResolverFactory;
@@ -908,24 +936,42 @@ public class SlingAuthenticator implemen
HttpServletResponse response) {
if (!AbstractAuthenticationHandler.isValidateRequest(request)) {
- try {
+ if (isBrowserRequest(request)) {
- login(request, response);
- return;
+ if (!isAjaxRequest(request)) {
+ try {
- } catch (IllegalStateException ise) {
+ login(request, response);
+ return;
- log.error("doLogin: Cannot login: Response already committed");
- return;
+ } catch (IllegalStateException ise) {
- } catch (NoAuthenticationHandlerException nahe) {
+ log.error("doLogin: Cannot login: Response already committed");
+ return;
- /*
- * Don't set the failureReason for missing authentication
- * handlers to not disclose this setup information.
- */
+ } catch (NoAuthenticationHandlerException nahe) {
- log.error("doLogin: Cannot login: No AuthenticationHandler available to handle the request");
+ /*
+ * Don't set the failureReason for missing
+ * authentication handlers to not disclose this setup
+ * information.
+ */
+
+ log.error("doLogin: Cannot login: No AuthenticationHandler available to handle the request");
+
+ }
+ }
+
+ } else {
+
+ // Presumably this is WebDAV. If HTTP Basic is fully enabled or
+ // enabled for preemptive credential support, we just request
+ // HTTP Basic credentials. Otherwise (HTTP Basic is fully
+ // switched off, 403 is sent back)
+ if (httpBasicHandler != null) {
+ httpBasicHandler.sendUnauthorized(response);
+ return;
+ }
}
}
@@ -945,10 +991,37 @@ public class SlingAuthenticator implemen
}
/**
+ * Determine if this request comes from a web browser which accepts
+ * anything.
+ *
+ * @param request The current request
+ * @return <code>true</code> if the request can be considered a browser
+ * request.
+ */
+ private boolean isBrowserRequest(final HttpServletRequest request) {
+ return request.getHeader(HEADER_ACCEPT) != null;
+ }
+
+ /**
+ * Returns <code>true</code> if the request is to be considered an AJAX
+ * request placed using the <code>XMLHttpRequest</code> browser host object.
+ * Currently a request is considered an AJAX request if the client sends the
+ * <i>X-Requested-With</i> request header set to <code>XMLHttpRequest</code>
+ * .
+ *
+ * @param request The current request
+ * @return <code>true</code> if the request can be considered an AJAX
+ * request.
+ */
+ private boolean isAjaxRequest(final HttpServletRequest request) {
+ return XML_HTTP_REQUEST.equals(request.getHeader(X_REQUESTED_WITH));
+ }
+
+ /**
* Sets the request attributes required by the OSGi HttpContext interface
* specification for the <code>handleSecurity</code> method. In addition the
- * {@link SlingAuthenticator#REQUEST_ATTRIBUTE_RESOLVER} request attribute is
- * set to the ResourceResolver.
+ * {@link SlingAuthenticator#REQUEST_ATTRIBUTE_RESOLVER} request attribute
+ * is set to the ResourceResolver.
*/
private void setAttributes(final ResourceResolver resolver, final String authType,
final HttpServletRequest request) {