Author: fmeschbe
Date: Thu Dec 3 14:36:37 2009
New Revision: 886796
URL: http://svn.apache.org/viewvc?rev=886796&view=rev
Log:
SLING-1220 Fix interaction between authentication handler and login form to
convey invalid credentials with a 403 status code instead of just resending the
login form with a status code of 200. Further fixes included:
* only send login request once (duplicate reference in the form)
* properly identify logged-in request (don't use AuthType since there
is at least one servlet container, which returns a non-null value
even if the servlet container does not authenticate the request)
Modified:
sling/trunk/bundles/extensions/httpauth/src/main/java/org/apache/sling/httpauth/impl/AuthorizationHeaderAuthenticationHandler.java
sling/trunk/bundles/extensions/httpauth/src/main/resources/org/apache/sling/httpauth/impl/LoginFormTemplate.html
Modified:
sling/trunk/bundles/extensions/httpauth/src/main/java/org/apache/sling/httpauth/impl/AuthorizationHeaderAuthenticationHandler.java
URL:
http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/httpauth/src/main/java/org/apache/sling/httpauth/impl/AuthorizationHeaderAuthenticationHandler.java?rev=886796&r1=886795&r2=886796&view=diff
==============================================================================
---
sling/trunk/bundles/extensions/httpauth/src/main/java/org/apache/sling/httpauth/impl/AuthorizationHeaderAuthenticationHandler.java
(original)
+++
sling/trunk/bundles/extensions/httpauth/src/main/java/org/apache/sling/httpauth/impl/AuthorizationHeaderAuthenticationHandler.java
Thu Dec 3 14:36:37 2009
@@ -32,6 +32,7 @@
import org.apache.sling.engine.auth.AuthenticationHandler;
import org.apache.sling.engine.auth.AuthenticationInfo;
import org.osgi.service.component.ComponentContext;
+import org.osgi.service.http.HttpContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -40,7 +41,7 @@
* the authorization steps based on the Authorization header of the HTTP
* request. This authenticator should eventually support both BASIC and DIGEST
* authentication methods.
- *
+ *
* @scr.component immediate="false" label="%auth.http.name"
* description="%auth.http.description"
* @scr.property name="service.description"
@@ -62,7 +63,7 @@
* in the {...@link #authenticate(HttpServletRequest, HttpServletResponse)}
* method if no credentials are present in the request (value is
* "sling:authRequestLogin").
- *
+ *
* @see #authenticate(HttpServletRequest, HttpServletResponse)
*/
static final String REQUEST_LOGIN_PARAMETER = "sling:authRequestLogin";
@@ -131,7 +132,7 @@
* the request may be for an included servlet, in which case the values for
* some URI specific values are contained in javax.servlet.include.*
request
* attributes.
- *
+ *
* @param request The request object containing the information for the
* authentication.
* @param response The response object which may be used to send the
@@ -162,7 +163,7 @@
/**
* Sends back the form to log into the system.
- *
+ *
* @param request The request object
* @param response The response object to which to send the request
* @return <code>true</code> is always returned by this handler
@@ -176,30 +177,55 @@
// reset the response
response.reset();
- response.setStatus(HttpServletResponse.SC_OK);
+ response.setHeader("Cache-Control", "no-cache");
- String form = getLoginForm();
+ if (isLoginRequested(request)) {
- if (form != null) {
-
- form = replaceVariables(form, "@@contextPath@@",
- request.getContextPath(), "/");
- form = replaceVariables(form, "@@authType@@",
- request.getAuthType(), "");
- form = replaceVariables(form, "@@user@@",
- request.getRemoteUser(), "");
-
- response.setContentType("text/html");
- response.setCharacterEncoding("UTF-8");
- response.getWriter().print(form);
+ // this is the ajax request for authentication which failed, we
+ // send back a 403/FORBIDDEN here to indicate failure
+ // Resoning:
+ // - using 401 would technically be correct but the browsers
+ // intercept these even for ajax requests. so sending 403
+ // indicates wrong credentials but allows processing without
+ // browser intervention
+ // - setting status only instead of using sendError prevents
+ // any error handling scripts from kicking in (and
+ // potentially modifying the result)
+ response.setStatus(HttpServletResponse.SC_FORBIDDEN);
} else {
-
- // have no form, so just send 401/UNATHORIZED for simple login
- sendUnauthorized(response);
-
+
+ response.setStatus(HttpServletResponse.SC_OK);
+
+ String form = getLoginForm();
+
+ if (form != null) {
+
+ form = replaceVariables(
+ form,
+ "@@loggedIn@@",
+
String.valueOf(request.getAttribute(HttpContext.AUTHENTICATION_TYPE) != null),
+ "false");
+ form = replaceVariables(form, "@@contextPath@@",
+ request.getContextPath(), "/");
+ form = replaceVariables(form, "@@authType@@",
+ request.getAuthType(), "");
+ form = replaceVariables(form, "@@user@@",
+ request.getRemoteUser(), "");
+
+ response.setContentType("text/html");
+ response.setCharacterEncoding("UTF-8");
+ response.getWriter().print(form);
+
+ } else {
+
+ // have no form, so just send 401/UNATHORIZED for simple
login
+ sendUnauthorized(response);
+
+ }
+
}
-
+
} else {
log.error("requestAuthentication: Response is committed, cannot
request authentication");
@@ -210,6 +236,14 @@
}
/**
+ * Returns true if the {...@link #REQUEST_LOGIN_PARAMETER} parameter is
set in
+ * the request.
+ */
+ private boolean isLoginRequested(HttpServletRequest request) {
+ return request.getParameter(REQUEST_LOGIN_PARAMETER) != null;
+ }
+
+ /**
* If the {...@link #REQUEST_LOGIN_PARAMETER} parameter is set this method
* sends status <code>401</code> (Unauthorized) with a
* <code>WWW-Authenticate</code> requesting standard HTTP header
@@ -220,7 +254,7 @@
* <code>false</code> is returned if the request parameter is not set, if
* the response is already committed or if an error occurred sending the
* status response. The latter two situations are logged as errors.
- *
+ *
* @param request The request object
* @param response The response object to which to send the request
* @return <code>true</code> if the 401/UNAUTHORIZED method has
successfully
@@ -232,7 +266,7 @@
// presume 401/UNAUTHORIZED has not been sent
boolean authenticationForced = false;
- if (request.getParameter(REQUEST_LOGIN_PARAMETER) != null) {
+ if (isLoginRequested(request)) {
if (!response.isCommitted()) {
@@ -243,25 +277,25 @@
log.error("forceAuthentication: Response is committed, cannot
request authentication");
}
-
+
} else {
-
+
log.debug(
"forceAuthentication: Not forcing authentication because
request parameter {} is not set",
REQUEST_LOGIN_PARAMETER);
-
+
}
// true if 401/UNAUTHORIZED has been sent, false otherwise
return authenticationForced;
}
-
+
/**
* Sends status <code>401</code> (Unauthorized) with a
* <code>WWW-Authenticate</code> requesting standard HTTP header
* authentication with the <code>Basic</code> scheme and the configured
* realm name.
- *
+ *
* @param response The response object to which to send the request
* @return <code>true</code> if the 401/UNAUTHORIZED method has
successfully
* been sent.
@@ -406,12 +440,12 @@
}
}
-
+
} else {
-
+
log.error("getLoginForm: Cannot access login form template at "
+ LOGIN_FORM_TEMPLATE);
-
+
}
}
@@ -422,7 +456,7 @@
* Replaces all occurrences in the <code>template</code> of the
* <code>key</code> (a regular expression) by the <code>value</code> or
* <code>defaultValue</code>.
- *
+ *
* @param template The template to replace occurences of key
* @param key The regular expression of the key to replace
* @param value The replacement value
Modified:
sling/trunk/bundles/extensions/httpauth/src/main/resources/org/apache/sling/httpauth/impl/LoginFormTemplate.html
URL:
http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/httpauth/src/main/resources/org/apache/sling/httpauth/impl/LoginFormTemplate.html?rev=886796&r1=886795&r2=886796&view=diff
==============================================================================
---
sling/trunk/bundles/extensions/httpauth/src/main/resources/org/apache/sling/httpauth/impl/LoginFormTemplate.html
(original)
+++
sling/trunk/bundles/extensions/httpauth/src/main/resources/org/apache/sling/httpauth/impl/LoginFormTemplate.html
Thu Dec 3 14:36:37 2009
@@ -58,7 +58,7 @@
<script>
// fix the display of login/logout
function onLoad() {
- if ('@@authType@@') {
+ if (@@loggedIn@@) {
document.getElementById("logout").style.display = "block";
} else {
document.getElementById("login").style.display = "block";
@@ -99,15 +99,24 @@
xmlhttp.abort();
}
+ // send a synchronous request
xmlhttp.open('POST', '@@contextPath@@?sling:authRequestLogin=1', false,
user, pass);
xmlhttp.send('');
+
+ // success if everything went smoothly
+ return xmlhttp.status == 200;
}
-
+
function loginuser() {
var user = document.forms['login'].usr.value;
var pass = document.forms['login'].pwd.value;
- sendRequest(user, pass);
- document.location = document.location
+ if (sendRequest(user, pass)) {
+ document.location = document.location;
+ } else {
+ alert("Wrong user name or password. Try again!");
+ sendRequest('__failed_login_user__', 'null');
+ }
+
return false;
}
@@ -122,7 +131,7 @@
document.execCommand('ClearAuthenticationCache');
} catch (e) {
- sendRequest('__forced_logout_user__', 'null');
+ sendRequest('__forced_logout_user__', 'null');
}
document.location = document.location
@@ -148,7 +157,7 @@
</tr>
<tr>
<td colspan='2' align='center'><input type='submit'
- value='Login' onClick='loginuser();' /></td>
+ value='Login' /></td>
</tr>
</table>
</form>