*/
public boolean getFireRequestListenersOnForwards();
+ /**
+ * Configures if a user presents authentication credentials, whether the
+ * context will process them when the request is for a non-protected
+ * resource.
+ */
+ public void setPreemptiveAuthentication(boolean enable);
+
+ /**
+ * Determines if a user presents authentication credentials, will the
+ * context will process them when the request is for a non-protected
+ * resource.
+ */
+ public boolean getPreemptiveAuthentication();
}
import java.io.IOException;
import java.security.Principal;
+import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import org.apache.catalina.Authenticator;
import org.apache.catalina.Container;
import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Manager;
import org.apache.catalina.Realm;
SecurityConstraint [] constraints
= realm.findSecurityConstraints(request, this.context);
- if ((constraints == null) /* &&
- (!Constants.FORM_METHOD.equals(config.getAuthMethod())) */ ) {
+ if (constraints == null && !context.getPreemptiveAuthentication()) {
if (log.isDebugEnabled())
log.debug(" Not subject to any constraint");
getNext().invoke(request, response);
// Make sure that constrained resources are not cached by web proxies
// or browsers as caching can provide a security hole
- if (disableProxyCaching &&
+ if (constraints != null && disableProxyCaching &&
// FIXME: Disabled for Mozilla FORM support over SSL
// (improper caching issue)
//!request.isSecure() &&
}
int i;
- // Enforce any user data constraint for this security constraint
- if (log.isDebugEnabled()) {
- log.debug(" Calling hasUserDataPermission()");
- }
- if (!realm.hasUserDataPermission(request, response,
- constraints)) {
+ if (constraints != null) {
+ // Enforce any user data constraint for this security constraint
if (log.isDebugEnabled()) {
- log.debug(" Failed hasUserDataPermission() test");
+ log.debug(" Calling hasUserDataPermission()");
+ }
+ if (!realm.hasUserDataPermission(request, response,
+ constraints)) {
+ if (log.isDebugEnabled()) {
+ log.debug(" Failed hasUserDataPermission() test");
+ }
+ /*
+ * ASSERT: Authenticator already set the appropriate
+ * HTTP status code, so we do not have to do anything special
+ */
+ return;
}
- /*
- * ASSERT: Authenticator already set the appropriate
- * HTTP status code, so we do not have to do anything special
- */
- return;
}
// Since authenticate modifies the response on failure,
// we have to check for allow-from-all first.
- boolean authRequired = true;
- for(i=0; i < constraints.length && authRequired; i++) {
- if(!constraints[i].getAuthConstraint()) {
- authRequired = false;
- } else if(!constraints[i].getAllRoles()) {
- String [] roles = constraints[i].findAuthRoles();
- if(roles == null || roles.length == 0) {
+ boolean authRequired;
+ if (constraints == null) {
+ authRequired = false;
+ } else {
+ authRequired = true;
+ for(i=0; i < constraints.length && authRequired; i++) {
+ if(!constraints[i].getAuthConstraint()) {
authRequired = false;
+ } else if(!constraints[i].getAllRoles()) {
+ String [] roles = constraints[i].findAuthRoles();
+ if(roles == null || roles.length == 0) {
+ authRequired = false;
+ }
}
}
}
-
+
+ if (!authRequired) {
+ authRequired =
+ request.getCoyoteRequest().getMimeHeaders().getValue(
+ "authorization") != null;
+ }
+
+ if (!authRequired) {
+ X509Certificate[] certs = (X509Certificate[]) request.getAttribute(
+ Globals.CERTIFICATES_ATTR);
+ authRequired = certs != null && certs.length > 0;
+ }
+
if(authRequired) {
if (log.isDebugEnabled()) {
log.debug(" Calling authenticate()");
}
- if (log.isDebugEnabled()) {
- log.debug(" Calling accessControl()");
- }
- if (!realm.hasResourcePermission(request, response,
- constraints,
- this.context)) {
+ if (constraints != null) {
if (log.isDebugEnabled()) {
- log.debug(" Failed accessControl() test");
+ log.debug(" Calling accessControl()");
+ }
+ if (!realm.hasResourcePermission(request, response,
+ constraints,
+ this.context)) {
+ if (log.isDebugEnabled()) {
+ log.debug(" Failed accessControl() test");
+ }
+ /*
+ * ASSERT: AccessControl method has already set the
+ * appropriate HTTP status code, so we do not have to do
+ * anything special
+ */
+ return;
}
- /*
- * ASSERT: AccessControl method has already set the
- * appropriate HTTP status code, so we do not have to do
- * anything special
- */
- return;
}
// Any and all specified constraints have been satisfied
*/
private Set<Servlet> createdServlets = new HashSet<Servlet>();
+ private boolean preemptiveAuthentication = false;
+
// ----------------------------------------------------- Context Properties
@Override
+ public boolean getPreemptiveAuthentication() {
+ return preemptiveAuthentication;
+ }
+
+
+ @Override
+ public void setPreemptiveAuthentication(boolean preemptiveAuthentication) {
+ this.preemptiveAuthentication = preemptiveAuthentication;
+ }
+
+
+ @Override
public void setFireRequestListenersOnForwards(boolean enable) {
fireRequestListenersOnForwards = enable;
}
protected synchronized void authenticatorConfig() {
LoginConfig loginConfig = context.getLoginConfig();
- if (loginConfig == null) {
- if (context.getIgnoreAnnotations()) {
- return;
- } else {
- // Not metadata-complete, need an authenticator to support
- // @ServletSecurity annotations
+
+ SecurityConstraint constraints[] = context.findConstraints();
+ if (context.getIgnoreAnnotations() &&
+ (constraints == null || constraints.length ==0) &&
+ !context.getPreemptiveAuthentication()) {
+ return;
+ } else {
+ if (loginConfig == null) {
+ // Not metadata-complete or security constraints present, need
+ // an authenticator to support @ServletSecurity annotations
+ // and/or constraints
loginConfig = DUMMY_LOGIN_CONFIG;
context.setLoginConfig(loginConfig);
}
assertEquals(403, rc);
}
+ public void testSecurityAnnotationsNoWebXmlLoginConfig() throws Exception {
+ // Setup Tomcat instance
+ Tomcat tomcat = getTomcatInstance();
+
+ File appDir = new File("test/webapp-3.0-servletsecurity2");
+ tomcat.addWebapp(null, "", appDir.getAbsolutePath());
+
+ tomcat.start();
+
+ ByteChunk bc = new ByteChunk();
+ int rc;
+ rc = getUrl("http://localhost:" + getPort() + "/protected.jsp",
+ bc, null, null);
+
+ assertNull(bc.toString());
+ assertEquals(403, rc);
+
+ rc = getUrl("http://localhost:" + getPort() + "/unprotected.jsp",
+ bc, null, null);
+
+ assertEquals(200, rc);
+ assertTrue(bc.toString().contains("00-OK"));
+ }
+
private void doTestSecurityAnnotationsAddServlet(boolean useCreateServlet)
throws Exception {
--- /dev/null
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ 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.
+-->
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
+ http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+ version="3.0"
+ metadata-complete="true">
+
+ <!--
+ WARNING:
+ For the unit tests to work correctly, no login-config may be configured in
+ the web.xml.
+ -->
+
+ <display-name>Tomcat Test Application</display-name>
+ <description>
+ Used as part of the Tomcat unit tests when a full web application is
+ required.
+ </description>
+
+ <security-constraint>
+ <auth-constraint/>
+ <web-resource-collection>
+ <url-pattern>/protected.jsp</url-pattern>
+ </web-resource-collection>
+ </security-constraint>
+</web-app>
\ No newline at end of file
--- /dev/null
+<%--
+ 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.
+--%>
+<html>
+ <head><title>Protected page</title></head>
+ <body>
+ <p>00-OK</p>
+ </body>
+</html>
+
--- /dev/null
+<%--
+ 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.
+--%>
+<html>
+ <head><title>Unprotected page</title></head>
+ <body>
+ <p>00-OK</p>
+ </body>
+</html>
+
When using parallel deployment, correctly handle the scenario when the
client sends multiple JSESSIONID cookies. (markt)
</fix>
+ <add>
+ <bug>12428</bug>: Add support (disabled by default) for preemptive
+ authentication. This can be configured per context. Based on a patch
+ suggested by Werner Donn. (markt)
+ </add>
<fix>
<bug>50929</bug>: When wrapping an exception, include the root cause.
Patch provided by sebb. (markt)
filenames used for either the .xml context file or the docBase.</p>
</attribute>
+ <attribute name="preemptiveAuthentication" required="false">
+ <p>When set to <code>true</code> and the user presents credentials for a
+ resource that is not protected by a security constraint, if the
+ authenticator supports preemptive authentication (the standard
+ authenticators provided with Tomcat do) then the user' credentials
+ will be processed. If not specified, the default of <code>false</code>is
+ used.
+ </p>
+ </attribute>
+
<attribute name="privileged" required="false">
<p>Set to <code>true</code> to allow this context to use container
servlets, like the manager servlet. Use of the <code>privileged</code>