SLING-1678 Add support to disable built-in HTTP Basic Authentication Handler
SLING-1679 Use Apache Felix SCR Annotations (instead of @scr JavaDoc tags)
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@988016 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/sling/auth/core/impl/HttpBasicAuthenticationHandler.java b/src/main/java/org/apache/sling/auth/core/impl/HttpBasicAuthenticationHandler.java
index 4b3767f..1708907 100644
--- a/src/main/java/org/apache/sling/auth/core/impl/HttpBasicAuthenticationHandler.java
+++ b/src/main/java/org/apache/sling/auth/core/impl/HttpBasicAuthenticationHandler.java
@@ -25,6 +25,7 @@
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.codec.binary.Base64;
+import org.apache.sling.auth.core.spi.AbstractAuthenticationHandler;
import org.apache.sling.auth.core.spi.AuthenticationHandler;
import org.apache.sling.auth.core.spi.AuthenticationInfo;
import org.apache.sling.auth.core.spi.DefaultAuthenticationFeedbackHandler;
@@ -44,7 +45,7 @@
* an easy way for tools (like cURL) or libraries (like Apache HttpCLient) to
* preemptively authenticate with HTTP Basic authentication.
*/
-public class HttpBasicAuthenticationHandler extends
+class HttpBasicAuthenticationHandler extends
DefaultAuthenticationFeedbackHandler implements AuthenticationHandler {
private static final String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate";
@@ -56,47 +57,36 @@
/** default log */
private final Logger log = LoggerFactory.getLogger(getClass());
+ /** The realm to send back with the 401 response */
private final String realm;
- public HttpBasicAuthenticationHandler(final String realm) {
+ /**
+ * Whether this authentication handler is fully enabled and sends back 401
+ * responses from the
+ * {@link #requestCredentials(HttpServletRequest, HttpServletResponse)} and
+ * {@link #dropCredentials(HttpServletRequest, HttpServletResponse)}
+ * methods.
+ */
+ private final boolean fullSupport;
+
+ HttpBasicAuthenticationHandler(final String realm,
+ final boolean fullSupport) {
this.realm = realm;
+ this.fullSupport = fullSupport;
}
// ----------- AuthenticationHandler interface ----------------------------
/**
- * Extracts credential data from the request if at all contained. This check
- * is only based on the original request object, no URI translation has
- * taken place yet.
+ * Returns the credential present within in an HTTP Basic authentication
+ * header or <code>null</code> if no credentials are provided and the
+ * {@link AuthenticationHandler#REQUEST_LOGIN_PARAMETER} is neither set as a
+ * request parameter nor as a request attribute.
* <p>
- * The method returns any of the following values :
- * <table>
- * <tr>
- * <th>value
- * <th>description
- * </tr>
- * <tr>
- * <td><code>null</code>
- * <td>no user details were contained in the request
- * </tr>
- * <tr>
- * <td>{@link AuthenticationInfo#DOING_AUTH}
- * <td>the handler is in an ongoing authentication exchange with the client.
- * The request handling is terminated.
- * <tr>
- * <tr>
- * <td>valid credentials
- * <td>The user sent credentials.
- * </tr>
- * </table>
- * <p>
- * The method must not request credential information from the client, if
- * they are not found in the request.
- * <p>
- * Note : The implementation should pay special attention to the fact, that
- * 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.
+ * If the {@link AuthenticationHandler#REQUEST_LOGIN_PARAMETER} is set as a
+ * request parameter or request attribute, a 401 response is sent to the
+ * client and the method returns {@link AuthenticationInfo#DOING_AUTH} to
+ * indicate that the handler has started its own credentials requesting.
*
* @param request The request object containing the information for the
* authentication.
@@ -105,8 +95,8 @@
* @return A valid Credentials instance identifying the request user,
* DOING_AUTH if the handler is in an authentication trasaction with
* the client or null if the request does not contain authentication
- * information. In case of DOING_AUTH, the method must have sent a
- * response indicating that fact to the client.
+ * information. In case of DOING_AUTH, the method has sent back a
+ * 401 requesting the client to provide credentials.
*/
public AuthenticationInfo extractCredentials(HttpServletRequest request,
HttpServletResponse response) {
@@ -129,23 +119,29 @@
/**
* Called by the SlingAuthenticator.login method in case no other
* authentication handler was willing to request credentials from the
- * client. In this case this HTTP Basic authentication handler will
- * send back a {@link #sendUnauthorized(HttpServletResponse) 401 response}
- * to request HTTP Basic authentication from the client.
+ * client. In this case this HTTP Basic authentication handler will send
+ * back a {@link #sendUnauthorized(HttpServletResponse) 401 response} to
+ * request HTTP Basic authentication from the client if full support has
+ * been configured in the
+ * {@link #HttpBasicAuthenticationHandler(String, boolean) constructor}
*
* @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
+ * @return <code>true</code> if full support is enabled and the 401 response
+ * could be sent. If full support is not enabled <code>false</code>
+ * is always returned.
*/
public boolean requestCredentials(HttpServletRequest request,
HttpServletResponse response) {
- return sendUnauthorized(response);
+ return fullSupport ? sendUnauthorized(response) : false;
}
/**
* Sends a 401/UNATUHORIZED response if the request has an Authorization
* header and if this handler is configured to actually send this response
- * in response to a request to drop the credentials.
+ * in response to a request to drop the credentials; that is if full support
+ * has been enabled in the
+ * {@link #HttpBasicAuthenticationHandler(String, boolean) constructor}.
* <p>
* Note, that sending a 401/UNAUTHORIZED response is generally the only save
* means to remove HTTP Basic credentials from a browser's cache. Yet, the
@@ -154,26 +150,29 @@
*/
public void dropCredentials(HttpServletRequest request,
HttpServletResponse response) {
- if (request.getHeader(HEADER_AUTHORIZATION) != null) {
+ if (fullSupport && request.getHeader(HEADER_AUTHORIZATION) != null) {
sendUnauthorized(response);
}
}
/**
- * Returns true if the {@link #REQUEST_LOGIN_PARAMETER} parameter is set.
+ * Returns true if the {@link #REQUEST_LOGIN_PARAMETER} parameter or request
+ * attribute is set to any non-<code>null</code> value.
* <p>
- * This method always returns <code>true</code> if the parameter is set
- * regardless of its value because the client indicated it wanted to login
- * but no authentication handler was willing to actually handle this
- * request. So as a last fallback this handler request HTTP Basic
- * Credentials.
+ * This method always returns <code>true</code> if the parameter or request
+ * attribute is set regardless of its value because the client indicated it
+ * wanted to login but no authentication handler was willing to actually
+ * handle this request. So as a last fallback this handler request HTTP
+ * Basic Credentials.
*
+ * @param request The request object providing the parameter or attribute.
* @return <code>true</code> if the
- * {@link AuthenticationHandler#REQUEST_LOGIN_PARAMETER} is set to
- * any value.
+ * {@link AuthenticationHandler#REQUEST_LOGIN_PARAMETER} parameter
+ * or attribute is set to any value.
*/
private boolean isLoginRequested(HttpServletRequest request) {
- return request.getParameter(REQUEST_LOGIN_PARAMETER) != null;
+ return AbstractAuthenticationHandler.getAttributeOrParameter(request,
+ REQUEST_LOGIN_PARAMETER, null) != null;
}
/**
diff --git a/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java b/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java
index 12aa363..0a74fd0 100644
--- a/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java
+++ b/src/main/java/org/apache/sling/auth/core/impl/SlingAuthenticator.java
@@ -32,6 +32,15 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.PropertyOption;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.felix.scr.annotations.Services;
import org.apache.sling.api.auth.Authenticator;
import org.apache.sling.api.auth.NoAuthenticationHandlerException;
import org.apache.sling.api.resource.LoginException;
@@ -69,64 +78,41 @@
* <p>
* Currently this class does not support multiple handlers for any one request
* URL.
- * <p>
- *
- * @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.api.auth.Authenticator"
- * @scr.service interface="org.apache.sling.auth.core.AuthenticationSupport"
- * @scr.service interface="javax.servlet.ServletRequestListener"
- *
- * @scr.property name="service.vendor" value="The Apache Software Foundation"
*/
+@Component(name = "org.apache.sling.engine.impl.auth.SlingAuthenticator", label = "%auth.name", description = "%auth.description", metatype = true)
+@Services( { @Service(value = Authenticator.class),
+ @Service(value = AuthenticationSupport.class),
+ @Service(value = ServletRequestListener.class) })
+@Property(name = Constants.SERVICE_VENDOR, value = "The Apache Software Foundation")
public class SlingAuthenticator implements Authenticator,
AuthenticationSupport, ServletRequestListener {
/** default log */
private final Logger log = LoggerFactory.getLogger(SlingAuthenticator.class);
- /** @scr.property name="service.description" */
+ @Property(name = Constants.SERVICE_DESCRIPTION)
static final String DESCRIPTION = "Apache Sling Request Authenticator";
- /**
- * @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 cookie name */
private static final String DEFAULT_IMPERSONATION_COOKIE = "sling.sudo";
+ @Property(value = DEFAULT_IMPERSONATION_COOKIE)
+ public static final String PAR_IMPERSONATION_COOKIE_NAME = "auth.sudo.cookie";
+
/** The default impersonation parameter name */
private static final String DEFAULT_IMPERSONATION_PARAMETER = "sudo";
+ @Property(value = DEFAULT_IMPERSONATION_PARAMETER)
+ public static final String PAR_IMPERSONATION_PAR_NAME = "auth.sudo.parameter";
+
/** The default value for allowing anonymous access */
private static final boolean DEFAULT_ANONYMOUS_ALLOWED = true;
- /**
- * The name of the configuration property used to set the Realm of the
- * built-in HTTP Basic authentication handler.
- *
- * @scr.property valueRef="DEFAULT_REALM"
- */
- public static final String PAR_REALM_NAME = "auth.http.realm";
+ @Property(boolValue = DEFAULT_ANONYMOUS_ALLOWED)
+ public static final String PAR_ANONYMOUS_ALLOWED = "auth.annonymous";
+
+ @Property(cardinality = 2147483647)
+ private static final String PAR_AUTH_REQ = "sling.auth.requirements";
/**
* The default realm for the built-in HTTP Basic authentication handler.
@@ -134,13 +120,47 @@
private static final String DEFAULT_REALM = "Sling (Development)";
/**
+ * The name of the configuration property used to set the Realm of the
+ * built-in HTTP Basic authentication handler.
+ */
+ @Property(value = DEFAULT_REALM)
+ public static final String PAR_REALM_NAME = "auth.http.realm";
+
+ /**
+ * Value of the {@link #PAR_HTTP_AUTH} property to fully enable the built-in
+ * HTTP Authentication Handler (value is "enabled").
+ */
+ private static final String HTTP_AUTH_ENABLED = "enabled";
+
+ /**
+ * Value of the {@link #PAR_HTTP_AUTH} property to completely disable the
+ * built-in HTTP Authentication Handler (value is "disabled").
+ */
+ private static final String HTTP_AUTH_DISABLED = "disabled";
+
+ /**
+ * Value of the {@link #PAR_HTTP_AUTH} property to enable extracting the
+ * credentials if the HTTP Basic authentication header is present (value is
+ * "preemptive"). In <i>preemptive</i> mode, though, the
+ * <code>requestCredentials</code> and <code>dropCredentials</code> methods
+ * will not send back a 401 response.
+ */
+ private static final String HTTP_AUTH_PREEMPTIVE = "preemptive";
+
+ @Property(value = HTTP_AUTH_PREEMPTIVE, options = {
+ @PropertyOption(name = HTTP_AUTH_ENABLED, value = "Enabled"),
+ @PropertyOption(name = HTTP_AUTH_PREEMPTIVE, value = "Enabled (Preemptive)"),
+ @PropertyOption(name = HTTP_AUTH_DISABLED, value = "Disabled") })
+ private static final String PAR_HTTP_AUTH = "auth.http";
+
+ /**
* The name of the {@link AuthenticationInfo} property providing the option
* {@link org.apache.sling.auth.core.spi.AuthenticationFeedbackHandler}
* handler to be called back on login failure or success.
*/
private static final String AUTH_INFO_PROP_FEEDBACK_HANDLER = "$$sling.auth.AuthenticationFeedbackHandler$$";
- /** @scr.reference */
+ @Reference
private ResourceResolverFactory resourceResolverFactory;
private PathBasedHolderCache<AbstractAuthenticationHandlerHolder> authHandlerCache = new PathBasedHolderCache<AbstractAuthenticationHandlerHolder>();
@@ -187,6 +207,7 @@
// ---------- SCR integration
@SuppressWarnings("unused")
+ @Activate
private void activate(final BundleContext bundleContext,
final Map<String, Object> properties) {
modified(properties);
@@ -196,9 +217,10 @@
Hashtable<String, Object> props = new Hashtable<String, Object>();
props.put("felix.webconsole.label", plugin.getLabel());
props.put("felix.webconsole.title", plugin.getTitle());
- props.put("service.description",
+ props.put(Constants.SERVICE_DESCRIPTION,
"Sling Request Authenticator WebConsole Plugin");
- props.put("service.vendor", properties.get("service.vendor"));
+ props.put(Constants.SERVICE_VENDOR,
+ properties.get(Constants.SERVICE_VENDOR));
webConsolePlugin = bundleContext.registerService(
"javax.servlet.Servlet", plugin, props);
@@ -214,6 +236,7 @@
authInfoPostProcessorTracker.open();
}
+ @Modified
private void modified(Map<String, Object> properties) {
if (properties == null) {
properties = new HashMap<String, Object>();
@@ -272,10 +295,24 @@
// register as a service !
final String realm = OsgiUtil.toString(properties.get(PAR_REALM_NAME),
DEFAULT_REALM);
- httpBasicHandler = new HttpBasicAuthenticationHandler(realm);
+ final String http = OsgiUtil.toString(properties.get(PAR_HTTP_AUTH),
+ HTTP_AUTH_PREEMPTIVE);
+ if (HTTP_AUTH_DISABLED.equals(http)) {
+ httpBasicHandler = new HttpBasicAuthenticationHandler(realm, false) {
+ @Override
+ public AuthenticationInfo extractCredentials(
+ HttpServletRequest request, HttpServletResponse response) {
+ return null;
+ }
+ };
+ } else {
+ httpBasicHandler = new HttpBasicAuthenticationHandler(realm,
+ HTTP_AUTH_ENABLED.equals(http));
+ }
}
@SuppressWarnings("unused")
+ @Deactivate
private void deactivate(final BundleContext bundleContext) {
if (engineAuthHandlerTracker != null) {
engineAuthHandlerTracker.close();