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 21:50:13 UTC
svn commit: r1001053 - in /sling/trunk/bundles/auth:
core/src/main/java/org/apache/sling/auth/core/impl/
core/src/main/java/org/apache/sling/auth/core/spi/
form/src/main/java/org/apache/sling/auth/form/impl/
Author: fmeschbe
Date: Fri Sep 24 19:50:12 2010
New Revision: 1001053
URL: http://svn.apache.org/viewvc?rev=1001053&view=rev
Log:
SLING-1428 Implement generalized support for validating credentials supplied by a request using the j_validate request parameter.
Modified:
sling/trunk/bundles/auth/core/src/main/java/org/apache/sling/auth/core/impl/HttpBasicAuthenticationHandler.java
sling/trunk/bundles/auth/core/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java
sling/trunk/bundles/auth/core/src/main/java/org/apache/sling/auth/core/spi/AbstractAuthenticationHandler.java
sling/trunk/bundles/auth/form/src/main/java/org/apache/sling/auth/form/impl/FormAuthenticationHandler.java
Modified: sling/trunk/bundles/auth/core/src/main/java/org/apache/sling/auth/core/impl/HttpBasicAuthenticationHandler.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/auth/core/src/main/java/org/apache/sling/auth/core/impl/HttpBasicAuthenticationHandler.java?rev=1001053&r1=1001052&r2=1001053&view=diff
==============================================================================
--- sling/trunk/bundles/auth/core/src/main/java/org/apache/sling/auth/core/impl/HttpBasicAuthenticationHandler.java (original)
+++ sling/trunk/bundles/auth/core/src/main/java/org/apache/sling/auth/core/impl/HttpBasicAuthenticationHandler.java Fri Sep 24 19:50:12 2010
@@ -224,7 +224,7 @@ class HttpBasicAuthenticationHandler ext
* @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()) {
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=1001053&r1=1001052&r2=1001053&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 19:50:12 2010
@@ -405,7 +405,7 @@ public class SlingAuthenticator implemen
if (authInfo != null) {
handleLoginFailure(request, response, authInfo.getUser(), e);
} else {
- handleLoginFailure(request, response, "<null>", e);
+ handleLoginFailure(request, response, "<null>", e);
}
return false;
}
@@ -695,9 +695,17 @@ public class SlingAuthenticator implemen
}
+ // client requested validation, which succeeds, thus send
+ // success response and close the resolver
+ if (AbstractAuthenticationHandler.isValidateRequest(request)) {
+ AbstractAuthenticationHandler.sendValid(response);
+ resolver.close();
+ return false;
+ }
+
// no redirect desired, so continue processing by first setting
// the request attributes and then returning true
- setAttributes(resolver, authInfo.getAuthType(), request);
+ setAttributes(resolver, authInfo.getAuthType(), request);
return true;
} catch (LoginException re) {
@@ -845,34 +853,95 @@ public class SlingAuthenticator implemen
}
/**
- * 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 a credentials validation request (see
+ * {@link AbstractAuthenticationHandler#isValidateRequest(HttpServletRequest)}
+ * ) a 403/FORBIDDEN response is sent back.</li>
+ * <li>If the request is not considered a
+ * {@link #isBrowserRequest(HttpServletRequest) browser 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. If HTTP
+ * Basic Authentication is completely switched of a 403/FORBIDDEN response
+ * is sent back instead.</li>
+ * <li>If the request is considered an
+ * {@link #isAjaxRequest(HttpServletRequest) Ajax request} a 403/FORBIDDIN
+ * response is simply sent back because we assume an Ajax requestor cannot
+ * properly handle any request for credentials graciously.</li>
+ * <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>
+ * If a 403/FORBIDDEN response is sent back 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. To actually send the response
+ * the
+ * {@link AbstractAuthenticationHandler#sendInvalid(HttpServletRequest, HttpServletResponse)}
+ * method is called.
+ * <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>
+ *
+ * @param request The current request
+ * @param response The response to send the credentials request (or access
+ * denial to)
+ * @see AbstractAuthenticationHandler#isValidateRequest(HttpServletRequest)
+ * @see #isBrowserRequest(HttpServletRequest)
+ * @see #isAjaxRequest(HttpServletRequest)
+ * @see AbstractAuthenticationHandler#sendInvalid(HttpServletRequest,
+ * HttpServletResponse)
*/
private void doLogin(HttpServletRequest request,
HttpServletResponse response) {
+ if (!AbstractAuthenticationHandler.isValidateRequest(request)) {
try {
login(request, response);
+ return;
} catch (IllegalStateException ise) {
log.error("doLogin: Cannot login: Response already committed");
+ return;
} 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");
- try {
- response.sendError(HttpServletResponse.SC_FORBIDDEN,
- "Cannot login");
- } catch (IOException ioe) {
- log.error("doLogin: Failed sending 403 status", ioe);
+ }
}
+ // 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 or because it is a failed
+ // credential validation
+
+ // ensure a failure reason
+ if (request.getAttribute(AuthenticationHandler.FAILURE_REASON) == null) {
+ request.setAttribute(AuthenticationHandler.FAILURE_REASON,
+ "Mandatory authentication is not possible");
}
+
+ AbstractAuthenticationHandler.sendInvalid(request, response);
}
/**
Modified: sling/trunk/bundles/auth/core/src/main/java/org/apache/sling/auth/core/spi/AbstractAuthenticationHandler.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/auth/core/src/main/java/org/apache/sling/auth/core/spi/AbstractAuthenticationHandler.java?rev=1001053&r1=1001052&r2=1001053&view=diff
==============================================================================
--- sling/trunk/bundles/auth/core/src/main/java/org/apache/sling/auth/core/spi/AbstractAuthenticationHandler.java (original)
+++ sling/trunk/bundles/auth/core/src/main/java/org/apache/sling/auth/core/spi/AbstractAuthenticationHandler.java Fri Sep 24 19:50:12 2010
@@ -41,6 +41,31 @@ public abstract class AbstractAuthentica
DefaultAuthenticationFeedbackHandler implements AuthenticationHandler {
/**
+ * The name of the request parameter indicating that the submitted username
+ * and password should just be checked and a status code be set for success
+ * (200/OK) or failure (403/FORBIDDEN).
+ *
+ * @see #isValidateRequest(HttpServletRequest)
+ * @see #sendValid(HttpServletResponse)
+ * @see #sendInvalid(HttpServletResponse, Object)
+ * @since 1.0.2 (Bundle version 1.0.4)
+ */
+ private static final String PAR_J_VALIDATE = "j_validate";
+
+ /**
+ * The name of the request header set by the
+ * {@link #sendInvalid(HttpServletResponse, Object)} method if the provided
+ * credentials cannot be used for login.
+ * <p>
+ * This header may be inspected by clients for a reason why the request
+ * failed.
+ *
+ * @see #sendInvalid(HttpServletResponse, Object)
+ * @since 1.0.2 (Bundle version 1.0.4)
+ */
+ private static final String X_REASON = "X-Reason";
+
+ /**
* Returns the value of the named request attribute or parameter as a string
* as follows:
* <ol>
@@ -231,4 +256,62 @@ public abstract class AbstractAuthentica
// not set or not a non-empty string
return null;
}
+
+ /**
+ * Returns <code>true</code> if the the client just asks for validation of
+ * submitted username/password credentials.
+ * <p>
+ * This implementation returns <code>true</code> if the request parameter
+ * {@link #PAR_J_VALIDATE} is set to <code>true</code> (case-insensitve). If
+ * the request parameter is not set or to any value other than
+ * <code>true</code> this method returns <code>false</code>.
+ *
+ * @param request The request to provide the parameter to check
+ * @return <code>true</code> if the {@link #PAR_J_VALIDATE} parameter is set
+ * to <code>true</code>.
+ * @since 1.0.2 (Bundle version 1.0.4)
+ */
+ public static boolean isValidateRequest(final HttpServletRequest request) {
+ return "true".equalsIgnoreCase(request.getParameter(PAR_J_VALIDATE));
+ }
+
+ /**
+ * Sends a 200/OK response to a credential validation request.
+ *
+ * @param response The response object
+ * @since 1.0.2 (Bundle version 1.0.4)
+ */
+ public static void sendValid(final HttpServletResponse response) {
+ try {
+ response.setStatus(HttpServletResponse.SC_OK);
+ response.flushBuffer();
+ } catch (IOException ioe) {
+ // TODO: log.error("Failed to send 200/OK response", ioe);
+ }
+ }
+
+ /**
+ * Sends a 403/FORBIDDEN response to a credential validation request
+ * providing the given reason as the value of the {@link #X_REASON} header.
+ *
+ * @param response The response object
+ * @param reason The reason to set on the header; not expected to be
+ * <code>null</code>
+ * @since 1.0.2 (Bundle version 1.0.4)
+ */
+ public static void sendInvalid(final HttpServletRequest request,
+ final HttpServletResponse response) {
+ try {
+ response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+
+ Object reason = request.getAttribute(AuthenticationHandler.FAILURE_REASON);
+ if (reason != null) {
+ response.setHeader(X_REASON, reason.toString());
+ }
+
+ response.flushBuffer();
+ } catch (IOException ioe) {
+ // TODO: log.error("Failed to send 403/Forbidden response", ioe);
+ }
+ }
}
Modified: sling/trunk/bundles/auth/form/src/main/java/org/apache/sling/auth/form/impl/FormAuthenticationHandler.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/auth/form/src/main/java/org/apache/sling/auth/form/impl/FormAuthenticationHandler.java?rev=1001053&r1=1001052&r2=1001053&view=diff
==============================================================================
--- sling/trunk/bundles/auth/form/src/main/java/org/apache/sling/auth/form/impl/FormAuthenticationHandler.java (original)
+++ sling/trunk/bundles/auth/form/src/main/java/org/apache/sling/auth/form/impl/FormAuthenticationHandler.java Fri Sep 24 19:50:12 2010
@@ -235,13 +235,6 @@ public class FormAuthenticationHandler e
private static final String PAR_J_PASSWORD = "j_password";
/**
- * The name of the form submission parameter indicating that the submitted
- * username and password should just be checked and a status code be set for
- * success (200/OK) or failure (403/FORBIDDEN).
- */
- private static final String PAR_J_VALIDATE = "j_validate";
-
- /**
* Key in the AuthenticationInfo map which contains the domain on which the
* auth cookie should be set.
*/
@@ -252,18 +245,6 @@ public class FormAuthenticationHandler e
*/
private static final long MINUTES = 60L * 1000L;
- /**
- * The name of the request header set by
- * {@link #authenticationFailed(HttpServletRequest, HttpServletResponse, AuthenticationInfo)}
- * 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.
- *
- * @see #authenticationFailed(HttpServletRequest, HttpServletResponse, AuthenticationInfo)
- */
- private static final String X_REASON = "X-Reason";
-
/** default log */
private final Logger log = LoggerFactory.getLogger(getClass());
@@ -339,15 +320,11 @@ public class FormAuthenticationHandler e
// so that the invalid cookie isn't present on the authN
// operation.
authStorage.clear(request, response);
- if (this.loginAfterExpire) {
+ if (this.loginAfterExpire || isValidateRequest(request)) {
// signal the requestCredentials method a previous login
// failure
request.setAttribute(FAILURE_REASON, FormReason.TIMEOUT);
info = AuthenticationInfo.FAIL_AUTH;
- } else if (isValidateRequest(request)) {
- // send 403 response and terminate the request
- sendInvalid(response, FormReason.TIMEOUT);
- info = AuthenticationInfo.DOING_AUTH;
}
}
}
@@ -451,17 +428,8 @@ public class FormAuthenticationHandler e
// clear authentication data from Cookie or Http Session
authStorage.clear(request, response);
- if (isValidateRequest(request)) {
-
- // just validated the credentials to be invalid
- sendInvalid(response, FormReason.INVALID_CREDENTIALS);
-
- } else {
-
- // signal the requestCredentials method a previous login failure
- request.setAttribute(FAILURE_REASON, FormReason.INVALID_CREDENTIALS);
-
- }
+ // signal the reason for login failure
+ request.setAttribute(FAILURE_REASON, FormReason.INVALID_CREDENTIALS);
}
/**
@@ -489,14 +457,7 @@ public class FormAuthenticationHandler e
refreshAuthData(request, response, authInfo);
final boolean result;
- if (isValidateRequest(request)) {
-
- sendValid(response);
-
- // terminate request, all done
- result = true;
-
- } else if (DefaultAuthenticationFeedbackHandler.handleRedirect(
+ if (DefaultAuthenticationFeedbackHandler.handleRedirect(
request, response)) {
// terminate request, all done in the default handler
@@ -550,56 +511,6 @@ public class FormAuthenticationHandler e
}
/**
- * Returns <code>true</code> if the the client just asks for validation of
- * submitted username/password credentials.
- * <p>
- * This implementation returns <code>true</code> if the request parameter
- * {@link #PAR_J_VALIDATE} is set to <code>true</code> (case-insensitve). If
- * the request parameter is not set or to any value other than
- * <code>true</code> this method returns <code>false</code>.
- *
- * @param request The request to provide the parameter to check
- * @return <code>true</code> if the {@link #PAR_J_VALIDATE} parameter is set
- * to <code>true</code>.
- */
- private boolean isValidateRequest(final HttpServletRequest request) {
- return "true".equalsIgnoreCase(request.getParameter(PAR_J_VALIDATE));
- }
-
- /**
- * Sends a 200/OK response to a credential validation request.
- *
- * @param response The response object
- */
- private void sendValid(final HttpServletResponse response) {
- try {
- response.setStatus(200);
- response.flushBuffer();
- } catch (IOException ioe) {
- log.error("Failed to send 200/OK response", ioe);
- }
- }
-
- /**
- * Sends a 403/FORBIDDEN response to a credential validation request
- * providing the given reason as the value of the {@link #X_REASON} header.
- *
- * @param response The response object
- * @param reason The reason to set on the header; not expected to be
- * <code>null</code>
- */
- private void sendInvalid(final HttpServletResponse response,
- final FormReason reason) {
- try {
- response.setStatus(HttpServletResponse.SC_FORBIDDEN);
- response.setHeader(X_REASON, reason.toString());
- response.flushBuffer();
- } catch (IOException ioe) {
- log.error("Failed to send 403/Forbidden response", ioe);
- }
- }
-
- /**
* Ensures the authentication data is set (if not set yet) and the expiry
* time is prolonged (if auth data already existed).
* <p>
@@ -666,8 +577,11 @@ public class FormAuthenticationHandler e
// make sure, that the request is redirected after successful
// authentication, otherwise the request may be processed
// as a POST request to the j_security_check page (unless
- // the j_validate parameter is set)
- setLoginResourceAttribute(request, request.getContextPath());
+ // the j_validate parameter is set); but only if this is not
+ // a validation request
+ if (!isValidateRequest(request)) {
+ setLoginResourceAttribute(request, request.getContextPath());
+ }
}
}