Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Wilkins2012-10-12 06:19:21 -0400
committerGreg Wilkins2012-10-12 06:19:21 -0400
commitfac580c374127acd8fc9b955f415cedd8b5ec6dc (patch)
treeabfe7aec8f782c512c47f8e7a71a319322ca39de /jetty-security
parent6c011025b67635004e3c3471a5315f2429755852 (diff)
parent7625f0b8b3a1ceced86b46f8e4c5722430323468 (diff)
downloadorg.eclipse.jetty.project-fac580c374127acd8fc9b955f415cedd8b5ec6dc.tar.gz
org.eclipse.jetty.project-fac580c374127acd8fc9b955f415cedd8b5ec6dc.tar.xz
org.eclipse.jetty.project-fac580c374127acd8fc9b955f415cedd8b5ec6dc.zip
Merge remote-tracking branch 'origin/jetty-8' into jetty-9
Conflicts: VERSION.txt example-async-rest/async-rest-jar/pom.xml example-async-rest/async-rest-webapp/pom.xml example-async-rest/pom.xml example-jetty-embedded/pom.xml jetty-aggregate/jetty-all-server/pom.xml jetty-aggregate/jetty-all/pom.xml jetty-aggregate/jetty-client/pom.xml jetty-aggregate/jetty-server/pom.xml jetty-aggregate/jetty-servlet/pom.xml jetty-aggregate/jetty-webapp/pom.xml jetty-aggregate/jetty-websocket/pom.xml jetty-aggregate/pom.xml jetty-ajp/src/main/java/org/eclipse/jetty/ajp/Ajp13Generator.java jetty-annotations/pom.xml jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AbstractDiscoverableAnnotationHandler.java jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletContainerInitializerListener.java jetty-annotations/src/main/java/org/eclipse/jetty/annotations/ServletSecurityAnnotationHandler.java jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotation.java jetty-annotations/src/main/java/org/eclipse/jetty/annotations/WebServletAnnotationHandler.java jetty-client/pom.xml jetty-continuation/pom.xml jetty-deploy/pom.xml jetty-distribution/pom.xml jetty-http-spi/pom.xml jetty-http/pom.xml jetty-http/src/main/java/org/eclipse/jetty/http/Generator.java jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java jetty-io/pom.xml jetty-io/src/main/java/org/eclipse/jetty/io/AbstractBuffer.java jetty-io/src/main/java/org/eclipse/jetty/io/Buffer.java jetty-io/src/main/java/org/eclipse/jetty/io/BufferUtil.java jetty-jaspi/pom.xml jetty-jmx/pom.xml jetty-jndi/pom.xml jetty-jndi/src/main/java/org/eclipse/jetty/jndi/ContextFactory.java jetty-jndi/src/test/java/org/eclipse/jetty/jndi/java/TestJNDI.java jetty-jsp/pom.xml jetty-monitor/pom.xml jetty-nested/pom.xml jetty-nosql/pom.xml jetty-osgi/jetty-osgi-boot-jsp/pom.xml jetty-osgi/jetty-osgi-boot-warurl/pom.xml jetty-osgi/jetty-osgi-boot/pom.xml jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiDeployer.java jetty-osgi/jetty-osgi-httpservice/pom.xml jetty-osgi/pom.xml jetty-osgi/test-jetty-osgi-context/pom.xml jetty-osgi/test-jetty-osgi-webapp/pom.xml jetty-osgi/test-jetty-osgi/pom.xml jetty-overlay-deployer/pom.xml jetty-plus/pom.xml jetty-policy/pom.xml jetty-rewrite/pom.xml jetty-runner/pom.xml jetty-security/pom.xml jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java jetty-security/src/main/java/org/eclipse/jetty/security/authentication/FormAuthenticator.java jetty-security/src/main/java/org/eclipse/jetty/security/authentication/LoginAuthenticator.java jetty-security/src/test/java/org/eclipse/jetty/security/ConstraintTest.java jetty-server/pom.xml jetty-server/src/main/java/org/eclipse/jetty/server/AbstractHttpConnection.java jetty-server/src/main/java/org/eclipse/jetty/server/AsyncHttpConnection.java jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java jetty-server/src/main/java/org/eclipse/jetty/server/Request.java jetty-server/src/main/java/org/eclipse/jetty/server/Response.java jetty-server/src/main/java/org/eclipse/jetty/server/bio/SocketConnector.java jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java jetty-server/src/test/java/org/eclipse/jetty/server/HttpURITest.java jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SelectChannelServerSslTest.java jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SslSocketServerTest.java jetty-server/src/test/resources/jetty-logging.properties jetty-servlet/pom.xml jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java jetty-servlets/pom.xml jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java jetty-servlets/src/main/java/org/eclipse/jetty/servlets/MultiPartFilter.java jetty-servlets/src/test/java/org/eclipse/jetty/servlets/MultipartFilterTest.java jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java jetty-spdy/pom.xml jetty-spdy/spdy-client/pom.xml jetty-spdy/spdy-core/pom.xml jetty-spdy/spdy-example-webapp/pom.xml jetty-spdy/spdy-http-server/pom.xml jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnection.java jetty-start/pom.xml jetty-start/src/test/java/org/eclipse/jetty/start/CommandLineBuilderTest.java jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java jetty-util-ajax/pom.xml jetty-util/pom.xml jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStream.java jetty-util/src/main/java/org/eclipse/jetty/util/log/StdErrLog.java jetty-util/src/test/java/org/eclipse/jetty/util/MultiPartInputStreamTest.java jetty-webapp/pom.xml jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java jetty-websocket/pom.xml jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketServletRFCTest.java jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/MessageSender.java jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/UnitGenerator.java jetty-xml/pom.xml pom.xml test-continuation/pom.xml test-jetty-nested/pom.xml test-jetty-servlet/pom.xml test-jetty-webapp/pom.xml test-jetty-webapp/src/main/webapp/WEB-INF/web.xml tests/pom.xml tests/test-integration/pom.xml tests/test-loginservice/pom.xml tests/test-sessions/pom.xml tests/test-sessions/test-hash-sessions/pom.xml tests/test-sessions/test-jdbc-sessions/pom.xml tests/test-sessions/test-mongodb-sessions/pom.xml tests/test-sessions/test-sessions-common/pom.xml tests/test-webapps/pom.xml tests/test-webapps/test-webapp-rfc2616/pom.xml
Diffstat (limited to 'jetty-security')
-rw-r--r--jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java435
-rw-r--r--jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java7
-rw-r--r--jetty-security/src/test/java/org/eclipse/jetty/security/ConstraintTest.java80
-rw-r--r--jetty-security/src/test/java/org/eclipse/jetty/security/SpecExampleConstraintTest.java315
4 files changed, 784 insertions, 53 deletions
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java
index ec998dd131..eb9235ab42 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/ConstraintSecurityHandler.java
@@ -19,15 +19,24 @@
package org.eclipse.jetty.security;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
+import javax.servlet.HttpConstraintElement;
+import javax.servlet.HttpMethodConstraintElement;
+import javax.servlet.ServletSecurityElement;
+import javax.servlet.annotation.ServletSecurity.EmptyRoleSemantic;
+import javax.servlet.annotation.ServletSecurity.TransportGuarantee;
+
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.PathMap;
import org.eclipse.jetty.server.HttpChannel;
@@ -36,18 +45,18 @@ import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.util.StringMap;
-import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.security.Constraint;
/* ------------------------------------------------------------ */
/**
* Handler to enforce SecurityConstraints. This implementation is servlet spec
- * 2.4 compliant and pre-computes the constraint combinations for runtime
+ * 3.0 compliant and pre-computes the constraint combinations for runtime
* efficiency.
*
*/
public class ConstraintSecurityHandler extends SecurityHandler implements ConstraintAware
{
+ private static final String OMISSION_SUFFIX = ".omission";
private static final String ALL_METHODS = "*";
private final List<ConstraintMapping> _constraintMappings= new CopyOnWriteArrayList<>();
private final Set<String> _roles = new CopyOnWriteArraySet<>();
@@ -55,6 +64,212 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
private boolean _strict = true;
/* ------------------------------------------------------------ */
+ /**
+ * @return
+ */
+ public static Constraint createConstraint()
+ {
+ return new Constraint();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param constraint
+ * @return
+ */
+ public static Constraint createConstraint(Constraint constraint)
+ {
+ try
+ {
+ return (Constraint)constraint.clone();
+ }
+ catch (CloneNotSupportedException e)
+ {
+ throw new IllegalStateException (e);
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Create a security constraint
+ *
+ * @param name
+ * @param authenticate
+ * @param roles
+ * @param dataConstraint
+ * @return
+ */
+ public static Constraint createConstraint (String name, boolean authenticate, String[] roles, int dataConstraint)
+ {
+ Constraint constraint = createConstraint();
+ if (name != null)
+ constraint.setName(name);
+ constraint.setAuthenticate(authenticate);
+ constraint.setRoles(roles);
+ constraint.setDataConstraint(dataConstraint);
+ return constraint;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param name
+ * @param element
+ * @return
+ */
+ public static Constraint createConstraint (String name, HttpConstraintElement element)
+ {
+ return createConstraint(name, element.getRolesAllowed(), element.getEmptyRoleSemantic(), element.getTransportGuarantee());
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param name
+ * @param rolesAllowed
+ * @param permitOrDeny
+ * @param transport
+ * @return
+ */
+ public static Constraint createConstraint (String name, String[] rolesAllowed, EmptyRoleSemantic permitOrDeny, TransportGuarantee transport)
+ {
+ Constraint constraint = createConstraint();
+
+ if (rolesAllowed == null || rolesAllowed.length==0)
+ {
+ if (permitOrDeny.equals(EmptyRoleSemantic.DENY))
+ {
+ //Equivalent to <auth-constraint> with no roles
+ constraint.setName(name+"-Deny");
+ constraint.setAuthenticate(true);
+ }
+ else
+ {
+ //Equivalent to no <auth-constraint>
+ constraint.setName(name+"-Permit");
+ constraint.setAuthenticate(false);
+ }
+ }
+ else
+ {
+ //Equivalent to <auth-constraint> with list of <security-role-name>s
+ constraint.setAuthenticate(true);
+ constraint.setRoles(rolesAllowed);
+ constraint.setName(name+"-RolesAllowed");
+ }
+
+ //Equivalent to //<user-data-constraint><transport-guarantee>CONFIDENTIAL</transport-guarantee></user-data-constraint>
+ constraint.setDataConstraint((transport.equals(TransportGuarantee.CONFIDENTIAL)?Constraint.DC_CONFIDENTIAL:Constraint.DC_NONE));
+ return constraint;
+ }
+
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param pathSpec
+ * @param constraintMappings
+ * @return
+ */
+ public static List<ConstraintMapping> getConstraintMappingsForPath(String pathSpec, List<ConstraintMapping> constraintMappings)
+ {
+ if (pathSpec == null || "".equals(pathSpec.trim()) || constraintMappings == null || constraintMappings.size() == 0)
+ return Collections.emptyList();
+
+ List<ConstraintMapping> mappings = new ArrayList<ConstraintMapping>();
+ for (ConstraintMapping mapping:constraintMappings)
+ {
+ if (pathSpec.equals(mapping.getPathSpec()))
+ {
+ mappings.add(mapping);
+ }
+ }
+ return mappings;
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /** Take out of the constraint mappings those that match the
+ * given path.
+ *
+ * @param pathSpec
+ * @param constraintMappings a new list minus the matching constraints
+ * @return
+ */
+ public static List<ConstraintMapping> removeConstraintMappingsForPath(String pathSpec, List<ConstraintMapping> constraintMappings)
+ {
+ if (pathSpec == null || "".equals(pathSpec.trim()) || constraintMappings == null || constraintMappings.size() == 0)
+ return Collections.emptyList();
+
+ List<ConstraintMapping> mappings = new ArrayList<ConstraintMapping>();
+ for (ConstraintMapping mapping:constraintMappings)
+ {
+ //Remove the matching mappings by only copying in non-matching mappings
+ if (!pathSpec.equals(mapping.getPathSpec()))
+ {
+ mappings.add(mapping);
+ }
+ }
+ return mappings;
+ }
+
+
+
+ /* ------------------------------------------------------------ */
+ /** Generate Constraints and ContraintMappings for the given url pattern and ServletSecurityElement
+ *
+ * @param name
+ * @param pathSpec
+ * @param securityElement
+ * @return
+ */
+ public static List<ConstraintMapping> createConstraintsWithMappingsForPath (String name, String pathSpec, ServletSecurityElement securityElement)
+ {
+ List<ConstraintMapping> mappings = new ArrayList<ConstraintMapping>();
+
+ //Create a constraint that will describe the default case (ie if not overridden by specific HttpMethodConstraints)
+ Constraint constraint = ConstraintSecurityHandler.createConstraint(name, securityElement);
+
+ //Create a mapping for the pathSpec for the default case
+ ConstraintMapping defaultMapping = new ConstraintMapping();
+ defaultMapping.setPathSpec(pathSpec);
+ defaultMapping.setConstraint(constraint);
+ mappings.add(defaultMapping);
+
+
+ //See Spec 13.4.1.2 p127
+ List<String> methodOmissions = new ArrayList<String>();
+
+ //make constraint mappings for this url for each of the HttpMethodConstraintElements
+ Collection<HttpMethodConstraintElement> methodConstraints = securityElement.getHttpMethodConstraints();
+ if (methodConstraints != null)
+ {
+ for (HttpMethodConstraintElement methodConstraint:methodConstraints)
+ {
+ //Make a Constraint that captures the <auth-constraint> and <user-data-constraint> elements supplied for the HttpMethodConstraintElement
+ Constraint mconstraint = ConstraintSecurityHandler.createConstraint(name, methodConstraint);
+ ConstraintMapping mapping = new ConstraintMapping();
+ mapping.setConstraint(mconstraint);
+ mapping.setPathSpec(pathSpec);
+ if (methodConstraint.getMethodName() != null)
+ {
+ mapping.setMethod(methodConstraint.getMethodName());
+ //See spec 13.4.1.2 p127 - add an omission for every method name to the default constraint
+ methodOmissions.add(methodConstraint.getMethodName());
+ }
+ mappings.add(mapping);
+ }
+ }
+ //See spec 13.4.1.2 p127 - add an omission for every method name to the default constraint
+ if (methodOmissions.size() > 0)
+ defaultMapping.setMethodOmissions(methodOmissions.toArray(new String[methodOmissions.size()]));
+
+ return mappings;
+ }
+
+
+
+ /* ------------------------------------------------------------ */
/** Get the strict mode.
* @return true if the security handler is running in strict mode.
*/
@@ -140,8 +355,6 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
@Override
public void setConstraintMappings(List<ConstraintMapping> constraintMappings, Set<String> roles)
{
- if (isStarted())
- throw new IllegalStateException("Started");
_constraintMappings.clear();
_constraintMappings.addAll(constraintMappings);
@@ -160,6 +373,14 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
}
}
setRoles(roles);
+
+ if (isStarted())
+ {
+ for (ConstraintMapping mapping : _constraintMappings)
+ {
+ processConstraintMapping(mapping);
+ }
+ }
}
/* ------------------------------------------------------------ */
@@ -172,9 +393,6 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
*/
public void setRoles(Set<String> roles)
{
- if (isStarted())
- throw new IllegalStateException("Started");
-
_roles.clear();
_roles.addAll(roles);
}
@@ -238,14 +456,24 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
}
super.doStart();
}
-
+
+
+ /* ------------------------------------------------------------ */
@Override
protected void doStop() throws Exception
{
super.doStop();
_constraintMap.clear();
}
-
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Create and combine the constraint with the existing processed
+ * constraints.
+ *
+ * @param mapping
+ */
protected void processConstraintMapping(ConstraintMapping mapping)
{
Map<String, RoleInfo> mappings = _constraintMap.get(mapping.getPathSpec());
@@ -258,6 +486,12 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
if (allMethodsRoleInfo != null && allMethodsRoleInfo.isForbidden())
return;
+ if (mapping.getMethodOmissions() != null && mapping.getMethodOmissions().length > 0)
+ {
+ processConstraintMappingWithMethodOmissions(mapping, mappings);
+ return;
+ }
+
String httpMethod = mapping.getMethod();
if (httpMethod==null)
httpMethod=ALL_METHODS;
@@ -274,10 +508,10 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
if (roleInfo.isForbidden())
return;
- Constraint constraint = mapping.getConstraint();
- boolean forbidden = constraint.isForbidden();
- roleInfo.setForbidden(forbidden);
- if (forbidden)
+ //add in info from the constraint
+ configureRoleInfo(roleInfo, mapping);
+
+ if (roleInfo.isForbidden())
{
if (httpMethod.equals(ALL_METHODS))
{
@@ -287,41 +521,12 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
}
else
{
- UserDataConstraint userDataConstraint = UserDataConstraint.get(constraint.getDataConstraint());
- roleInfo.setUserDataConstraint(userDataConstraint);
-
- boolean checked = constraint.getAuthenticate();
- roleInfo.setChecked(checked);
- if (roleInfo.isChecked())
- {
- if (constraint.isAnyRole())
- {
- if (_strict)
- {
- // * means "all defined roles"
- for (String role : _roles)
- roleInfo.addRole(role);
- }
- else
- // * means any role
- roleInfo.setAnyRole(true);
- }
- else
- {
- String[] newRoles = constraint.getRoles();
- for (String role : newRoles)
- {
- if (_strict &&!_roles.contains(role))
- throw new IllegalArgumentException("Attempt to use undeclared role: " + role + ", known roles: " + _roles);
- roleInfo.addRole(role);
- }
- }
- }
- if (httpMethod.equals(ALL_METHODS))
+ //combine with any entry that covers all methods
+ if (httpMethod == null)
{
for (Map.Entry<String, RoleInfo> entry : mappings.entrySet())
{
- if (!entry.getKey().equals(ALL_METHODS))
+ if (entry.getKey() != null)
{
RoleInfo specific = entry.getValue();
specific.combine(roleInfo);
@@ -331,17 +536,145 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
}
}
+ /* ------------------------------------------------------------ */
+ /** Constraints that name method omissions are dealt with differently.
+ * We create an entry in the mappings with key "method.omission". This entry
+ * is only ever combined with other omissions for the same method to produce a
+ * consolidated RoleInfo. Then, when we wish to find the relevant constraints for
+ * a given Request (in prepareConstraintInfo()), we consult 3 types of entries in
+ * the mappings: an entry that names the method of the Request specifically, an
+ * entry that names constraints that apply to all methods, entries of the form
+ * method.omission, where the method of the Request is not named in the omission.
+ * @param mapping
+ * @param mappings
+ */
+ protected void processConstraintMappingWithMethodOmissions (ConstraintMapping mapping, Map<String, RoleInfo> mappings)
+ {
+ String[] omissions = mapping.getMethodOmissions();
+
+ for (String omission:omissions)
+ {
+ //for each method omission, see if there is already a RoleInfo for it in mappings
+ RoleInfo ri = mappings.get(omission+OMISSION_SUFFIX);
+ if (ri == null)
+ {
+ //if not, make one
+ ri = new RoleInfo();
+ mappings.put(omission+OMISSION_SUFFIX, ri);
+ }
+
+ //initialize RoleInfo or combine from ConstraintMapping
+ configureRoleInfo(ri, mapping);
+ }
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Initialize or update the RoleInfo from the constraint
+ * @param ri
+ * @param mapping
+ */
+ protected void configureRoleInfo (RoleInfo ri, ConstraintMapping mapping)
+ {
+ Constraint constraint = mapping.getConstraint();
+ boolean forbidden = constraint.isForbidden();
+ ri.setForbidden(forbidden);
+
+ //set up the data constraint (NOTE: must be done after setForbidden, as it nulls out the data constraint
+ //which we need in order to do combining of omissions in prepareConstraintInfo
+ UserDataConstraint userDataConstraint = UserDataConstraint.get(mapping.getConstraint().getDataConstraint());
+ ri.setUserDataConstraint(userDataConstraint);
+
+
+ //if forbidden, no point setting up roles
+ if (!ri.isForbidden())
+ {
+ //add in the roles
+ boolean checked = mapping.getConstraint().getAuthenticate();
+ ri.setChecked(checked);
+ if (ri.isChecked())
+ {
+ if (mapping.getConstraint().isAnyRole())
+ {
+ if (_strict)
+ {
+ // * means "all defined roles"
+ for (String role : _roles)
+ ri.addRole(role);
+ }
+ else
+ // * means any role
+ ri.setAnyRole(true);
+ }
+ else
+ {
+ String[] newRoles = mapping.getConstraint().getRoles();
+ for (String role : newRoles)
+ {
+ if (_strict &&!_roles.contains(role))
+ throw new IllegalArgumentException("Attempt to use undeclared role: " + role + ", known roles: " + _roles);
+ ri.addRole(role);
+ }
+ }
+ }
+ }
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Find constraints that apply to the given path.
+ * In order to do this, we consult 3 different types of information stored in the mappings for each path - each mapping
+ * represents a merged set of user data constraints, roles etc -:
+ * <ol>
+ * <li>A mapping of an exact method name </li>
+ * <li>A mapping will null key that matches every method name</li>
+ * <li>Mappings with keys of the form "method.omission" that indicates it will match every method name EXCEPT that given</li>
+ * </ol>
+ *
+ * @see org.eclipse.jetty.security.SecurityHandler#prepareConstraintInfo(java.lang.String, org.eclipse.jetty.server.Request)
+ */
@Override
protected RoleInfo prepareConstraintInfo(String pathInContext, Request request)
{
- Map<String, RoleInfo> mappings = _constraintMap.match(pathInContext);
+ Map<String, RoleInfo> mappings = (Map<String, RoleInfo>)_constraintMap.match(pathInContext);
if (mappings != null)
{
String httpMethod = request.getMethod();
RoleInfo roleInfo = mappings.get(httpMethod);
if (roleInfo == null)
- roleInfo = mappings.get(ALL_METHODS);
+ {
+ //No specific http-method names matched
+ List<RoleInfo> applicableConstraints = new ArrayList<RoleInfo>();
+
+ //Get info for constraint that matches all methods if it exists
+ RoleInfo all = mappings.get(ALL_METHODS);
+ if (all != null)
+ applicableConstraints.add(all);
+
+
+ //Get info for constraints that name method omissions where target method name is not omitted
+ //(ie matches because target method is not omitted, hence considered covered by the constraint)
+ for (Entry<String, RoleInfo> entry: mappings.entrySet())
+ {
+ if (entry.getKey() != null && entry.getKey().contains(OMISSION_SUFFIX) && !(httpMethod+OMISSION_SUFFIX).equals(entry.getKey()))
+ applicableConstraints.add(entry.getValue());
+ }
+
+ if (applicableConstraints.size() == 1)
+ roleInfo = applicableConstraints.get(0);
+ else
+ {
+ roleInfo = new RoleInfo();
+ roleInfo.setUserDataConstraint(UserDataConstraint.None);
+
+ for (RoleInfo r:applicableConstraints)
+ roleInfo.combine(r);
+ }
+
+ }
return roleInfo;
}
@@ -396,7 +729,12 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
{
return constraintInfo != null && ((RoleInfo)constraintInfo).isChecked();
}
-
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.security.SecurityHandler#checkWebResourcePermissions(java.lang.String, org.eclipse.jetty.server.Request, org.eclipse.jetty.server.Response, java.lang.Object, org.eclipse.jetty.server.UserIdentity)
+ */
@Override
protected boolean checkWebResourcePermissions(String pathInContext, Request request, Response response, Object constraintInfo, UserIdentity userIdentity)
throws IOException
@@ -435,4 +773,5 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
Collections.singleton(_roles),
_constraintMap.entrySet());
}
+
}
diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java
index 7e7ec72d49..9e94282364 100644
--- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java
+++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java
@@ -119,18 +119,25 @@ public class SessionAuthentication implements Authentication.User, Serializable,
@Override
public void sessionWillPassivate(HttpSessionEvent se)
{
+
}
@Override
public void sessionDidActivate(HttpSessionEvent se)
{
if (_session==null)
+ {
_session=se.getSession();
+ }
}
@Override
public void valueBound(HttpSessionBindingEvent event)
{
+ if (_session==null)
+ {
+ _session=event.getSession();
+ }
}
@Override
diff --git a/jetty-security/src/test/java/org/eclipse/jetty/security/ConstraintTest.java b/jetty-security/src/test/java/org/eclipse/jetty/security/ConstraintTest.java
index 1d903c4ad0..75f0e62554 100644
--- a/jetty-security/src/test/java/org/eclipse/jetty/security/ConstraintTest.java
+++ b/jetty-security/src/test/java/org/eclipse/jetty/security/ConstraintTest.java
@@ -79,6 +79,8 @@ public class ConstraintTest
_loginService.putUser("user",new Password("password"));
_loginService.putUser("user2",new Password("password"), new String[] {"user"});
_loginService.putUser("admin",new Password("password"), new String[] {"user","administrator"});
+ _loginService.putUser("user3", new Password("password"), new String[] {"foo"});
+
_context.setContextPath("/ctx");
_server.setHandler(_context);
@@ -190,17 +192,59 @@ public class ConstraintTest
@Test
public void testBasic() throws Exception
{
+
+ List<ConstraintMapping> list = new ArrayList<ConstraintMapping>(_security.getConstraintMappings());
+
+ Constraint constraint6 = new Constraint();
+ constraint6.setAuthenticate(true);
+ constraint6.setName("omit POST and GET");
+ constraint6.setRoles(new String[]{"user"});
+ ConstraintMapping mapping6 = new ConstraintMapping();
+ mapping6.setPathSpec("/omit/*");
+ mapping6.setConstraint(constraint6);
+ mapping6.setMethodOmissions(new String[]{"GET", "HEAD"}); //requests for every method except GET and HEAD must be in role "user"
+ list.add(mapping6);
+
+ Constraint constraint7 = new Constraint();
+ constraint7.setAuthenticate(true);
+ constraint7.setName("non-omitted GET");
+ constraint7.setRoles(new String[]{"administrator"});
+ ConstraintMapping mapping7 = new ConstraintMapping();
+ mapping7.setPathSpec("/omit/*");
+ mapping7.setConstraint(constraint7);
+ mapping7.setMethod("GET"); //requests for GET must be in role "admin"
+ list.add(mapping7);
+
+ Constraint constraint8 = new Constraint();
+ constraint8.setAuthenticate(true);
+ constraint8.setName("non specific");
+ constraint8.setRoles(new String[]{"foo"});
+ ConstraintMapping mapping8 = new ConstraintMapping();
+ mapping8.setPathSpec("/omit/*");
+ mapping8.setConstraint(constraint8);//requests for all methods must be in role "foo"
+ list.add(mapping8);
+
+ Set<String> knownRoles=new HashSet<String>();
+ knownRoles.add("user");
+ knownRoles.add("administrator");
+ knownRoles.add("foo");
+
+ _security.setConstraintMappings(list, knownRoles);
+
+
_security.setAuthenticator(new BasicAuthenticator());
_security.setStrict(false);
_server.start();
String response;
+ /*
response = _connector.getResponses("GET /ctx/noauth/info HTTP/1.0\r\n\r\n");
assertThat(response,startsWith("HTTP/1.1 200 OK"));
-
+*/
+
response = _connector.getResponses("GET /ctx/forbid/info HTTP/1.0\r\n\r\n");
assertThat(response,startsWith("HTTP/1.1 403 Forbidden"));
-
+ /*
response = _connector.getResponses("GET /ctx/auth/info HTTP/1.0\r\n\r\n");
assertThat(response,startsWith("HTTP/1.1 401 Unauthorized"));
assertThat(response,containsString("WWW-Authenticate: basic realm=\"TestRealm\""));
@@ -215,8 +259,8 @@ public class ConstraintTest
"Authorization: Basic " + B64Code.encode("user:password") + "\r\n" +
"\r\n");
assertThat(response,startsWith("HTTP/1.1 200 OK"));
-
-
+*/
+/*
// test admin
response = _connector.getResponses("GET /ctx/admin/info HTTP/1.0\r\n\r\n");
assertThat(response,startsWith("HTTP/1.1 401 Unauthorized"));
@@ -242,7 +286,33 @@ public class ConstraintTest
response = _connector.getResponses("GET /ctx/admin/relax/info HTTP/1.0\r\n\r\n");
assertThat(response,startsWith("HTTP/1.1 200 OK"));
+
+ //check GET is in role administrator
+ response = _connector.getResponses("GET /ctx/omit/x HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("admin:password") + "\r\n" +
+ "\r\n");
+ assertTrue(response.startsWith("HTTP/1.1 200 OK"));
+
+ //check POST is in role user
+ response = _connector.getResponses("POST /ctx/omit/x HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("user2:password") + "\r\n" +
+ "\r\n");
+ assertTrue(response.startsWith("HTTP/1.1 200 OK"));
+
+ //check POST can be in role foo too
+ response = _connector.getResponses("POST /ctx/omit/x HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("user3:password") + "\r\n" +
+ "\r\n");
+ assertTrue(response.startsWith("HTTP/1.1 200 OK"));
+
+ //check HEAD cannot be in role user
+ response = _connector.getResponses("HEAD /ctx/omit/x HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("user2:password") + "\r\n" +
+ "\r\n");
+ assertTrue(response.startsWith("HTTP/1.1 200 OK"));*/
}
+
+
@Test
public void testFormDispatch() throws Exception
@@ -853,7 +923,7 @@ public class ConstraintTest
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response ) throws IOException, ServletException
{
baseRequest.setHandled(true);
- if (request.getAuthType()==null || "user".equals(request.getRemoteUser()) || request.isUserInRole("user"))
+ if (request.getAuthType()==null || "user".equals(request.getRemoteUser()) || request.isUserInRole("user") || request.isUserInRole("foo"))
{
response.setStatus(200);
response.setContentType("text/plain; charset=UTF-8");
diff --git a/jetty-security/src/test/java/org/eclipse/jetty/security/SpecExampleConstraintTest.java b/jetty-security/src/test/java/org/eclipse/jetty/security/SpecExampleConstraintTest.java
new file mode 100644
index 0000000000..ba12f1af57
--- /dev/null
+++ b/jetty-security/src/test/java/org/eclipse/jetty/security/SpecExampleConstraintTest.java
@@ -0,0 +1,315 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.security;
+
+import static org.hamcrest.Matchers.startsWith;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.security.authentication.BasicAuthenticator;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.LocalConnector;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.server.session.SessionHandler;
+import org.eclipse.jetty.util.B64Code;
+import org.eclipse.jetty.util.security.Constraint;
+import org.eclipse.jetty.util.security.Password;
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * @version $Revision: 1441 $ $Date: 2010-04-02 12:28:17 +0200 (Fri, 02 Apr 2010) $
+ */
+public class SpecExampleConstraintTest
+{
+ private static final String TEST_REALM = "TestRealm";
+ private static Server _server;
+ private static LocalConnector _connector;
+ private static SessionHandler _session;
+ private ConstraintSecurityHandler _security;
+
+ @BeforeClass
+ public static void startServer()
+ {
+ _server = new Server();
+ _connector = new LocalConnector(_server);
+ _server.setConnectors(new Connector[]{_connector});
+
+ ContextHandler _context = new ContextHandler();
+ _session = new SessionHandler();
+
+ HashLoginService _loginService = new HashLoginService(TEST_REALM);
+ _loginService.putUser("fred",new Password("password"));
+ _loginService.putUser("harry",new Password("password"), new String[] {"HOMEOWNER"});
+ _loginService.putUser("chris",new Password("password"), new String[] {"CONTRACTOR"});
+ _loginService.putUser("steven", new Password("password"), new String[] {"SALESCLERK"});
+
+
+ _context.setContextPath("/ctx");
+ _server.setHandler(_context);
+ _context.setHandler(_session);
+
+ _server.addBean(_loginService);
+ }
+
+ @Before
+ public void setupSecurity()
+ {
+ _security = new ConstraintSecurityHandler();
+ _session.setHandler(_security);
+ RequestHandler _handler = new RequestHandler();
+ _security.setHandler(_handler);
+
+
+ /*
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>precluded methods</web-resource-name>
+ <url-pattern>/*</url-pattern>
+ <url-pattern>/acme/wholesale/*</url-pattern>
+ <url-pattern>/acme/retail/*</url-pattern>
+ <http-method-exception>GET</http-method-exception>
+ <http-method-exception>POST</http-method-exception>
+ </web-resource-collection>
+ <auth-constraint/>
+ </security-constraint>
+ */
+
+ Constraint constraint0 = new Constraint();
+ constraint0.setAuthenticate(true);
+ constraint0.setName("precluded methods");
+ ConstraintMapping mapping0 = new ConstraintMapping();
+ mapping0.setPathSpec("/*");
+ mapping0.setConstraint(constraint0);
+ mapping0.setMethodOmissions(new String[]{"GET", "POST"});
+
+ ConstraintMapping mapping1 = new ConstraintMapping();
+ mapping1.setPathSpec("/acme/wholesale/*");
+ mapping1.setConstraint(constraint0);
+ mapping1.setMethodOmissions(new String[]{"GET", "POST"});
+
+ ConstraintMapping mapping2 = new ConstraintMapping();
+ mapping2.setPathSpec("/acme/retail/*");
+ mapping2.setConstraint(constraint0);
+ mapping2.setMethodOmissions(new String[]{"GET", "POST"});
+
+ /*
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>wholesale</web-resource-name>
+ <url-pattern>/acme/wholesale/*</url-pattern>
+ <http-method>GET</http-method>
+ <http-method>PUT</http-method>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>SALESCLERK</role-name>
+ </auth-constraint>
+ </security-constraint>
+ */
+ Constraint constraint1 = new Constraint();
+ constraint1.setAuthenticate(true);
+ constraint1.setName("wholesale");
+ constraint1.setRoles(new String[]{"SALESCLERK"});
+ ConstraintMapping mapping3 = new ConstraintMapping();
+ mapping3.setPathSpec("/acme/wholesale/*");
+ mapping3.setConstraint(constraint1);
+ mapping3.setMethod("GET");
+ ConstraintMapping mapping4 = new ConstraintMapping();
+ mapping4.setPathSpec("/acme/wholesale/*");
+ mapping4.setConstraint(constraint1);
+ mapping4.setMethod("PUT");
+
+ /*
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>wholesale 2</web-resource-name>
+ <url-pattern>/acme/wholesale/*</url-pattern>
+ <http-method>GET</http-method>
+ <http-method>POST</http-method>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>CONTRACTOR</role-name>
+ </auth-constraint>
+ <user-data-constraint>
+ <transport-guarantee>CONFIDENTIAL</transport-guarantee>
+ </user-data-constraint>
+ </security-constraint>
+ */
+ Constraint constraint2 = new Constraint();
+ constraint2.setAuthenticate(true);
+ constraint2.setName("wholesale 2");
+ constraint2.setRoles(new String[]{"CONTRACTOR"});
+ constraint2.setDataConstraint(Constraint.DC_CONFIDENTIAL);
+ ConstraintMapping mapping5 = new ConstraintMapping();
+ mapping5.setPathSpec("/acme/wholesale/*");
+ mapping5.setMethod("GET");
+ mapping5.setConstraint(constraint2);
+ ConstraintMapping mapping6 = new ConstraintMapping();
+ mapping6.setPathSpec("/acme/wholesale/*");
+ mapping6.setMethod("POST");
+ mapping6.setConstraint(constraint2);
+
+ /*
+<security-constraint>
+<web-resource-collection>
+<web-resource-name>retail</web-resource-name>
+<url-pattern>/acme/retail/*</url-pattern>
+<http-method>GET</http-method>
+<http-method>POST</http-method>
+</web-resource-collection>
+<auth-constraint>
+<role-name>CONTRACTOR</role-name>
+<role-name>HOMEOWNER</role-name>
+</auth-constraint>
+</security-constraint>
+*/
+ Constraint constraint4 = new Constraint();
+ constraint4.setName("retail");
+ constraint4.setAuthenticate(true);
+ constraint4.setRoles(new String[]{"CONTRACTOR", "HOMEOWNER"});
+ ConstraintMapping mapping7 = new ConstraintMapping();
+ mapping7.setPathSpec("/acme/retail/*");
+ mapping7.setMethod("GET");
+ mapping7.setConstraint(constraint4);
+ ConstraintMapping mapping8 = new ConstraintMapping();
+ mapping8.setPathSpec("/acme/retail/*");
+ mapping8.setMethod("POST");
+ mapping8.setConstraint(constraint4);
+
+
+
+
+ Set<String> knownRoles=new HashSet<String>();
+ knownRoles.add("CONTRACTOR");
+ knownRoles.add("HOMEOWNER");
+ knownRoles.add("SALESCLERK");
+
+ _security.setConstraintMappings(Arrays.asList(new ConstraintMapping[]
+ {
+ mapping0, mapping1, mapping2, mapping3, mapping4, mapping5, mapping6, mapping7, mapping8
+ }), knownRoles);
+ }
+
+ @After
+ public void stopServer() throws Exception
+ {
+ if (_server.isRunning())
+ {
+ _server.stop();
+ _server.join();
+ }
+ }
+
+
+
+ @Test
+ public void testBasic() throws Exception
+ {
+
+ _security.setAuthenticator(new BasicAuthenticator());
+ _security.setStrict(false);
+ _server.start();
+
+ String response;
+ /*
+ /star all methods except GET/POST forbidden
+ /acme/wholesale/star all methods except GET/POST forbidden
+ /acme/retail/star all methods except GET/POST forbidden
+ /acme/wholesale/star GET must be in role CONTRACTOR or SALESCLERK
+ /acme/wholesale/star POST must be in role CONTRACTOR and confidential transport
+ /acme/retail/star GET must be in role CONTRACTOR or HOMEOWNER
+ /acme/retail/star POST must be in role CONTRACTOR or HOMEOWNER
+ */
+
+ //a user in role HOMEOWNER is forbidden HEAD request
+ response = _connector.getResponses("HEAD /ctx/index.html HTTP/1.0\r\n\r\n");
+ assertTrue(response.startsWith("HTTP/1.1 403 Forbidden"));
+
+ response = _connector.getResponses("HEAD /ctx/index.html HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("harry:password") + "\r\n" +
+ "\r\n");
+ assertThat(response,startsWith("HTTP/1.1 403 Forbidden"));
+
+ response = _connector.getResponses("HEAD /ctx/acme/wholesale/index.html HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("harry:password") + "\r\n" +
+ "\r\n");
+ assertThat(response,startsWith("HTTP/1.1 403 Forbidden"));
+
+ response = _connector.getResponses("HEAD /ctx/acme/retail/index.html HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("harry:password") + "\r\n" +
+ "\r\n");
+ assertThat(response,startsWith("HTTP/1.1 403 Forbidden"));
+
+ //a user in role CONTRACTOR can do a GET
+ response = _connector.getResponses("GET /ctx/acme/wholesale/index.html HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("chris:password") + "\r\n" +
+ "\r\n");
+
+ assertThat(response,startsWith("HTTP/1.1 200 OK"));
+
+ //a user in role CONTRACTOR can only do a post if confidential
+ response = _connector.getResponses("POST /ctx/acme/wholesale/index.html HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("chris:password") + "\r\n" +
+ "\r\n");
+ assertThat(response,startsWith("HTTP/1.1 403 !"));
+
+ //a user in role HOMEOWNER can do a GET
+ response = _connector.getResponses("GET /ctx/acme/retail/index.html HTTP/1.0\r\n" +
+ "Authorization: Basic " + B64Code.encode("harry:password") + "\r\n" +
+ "\r\n");
+ assertThat(response,startsWith("HTTP/1.1 200 OK"));
+ }
+
+
+ private class RequestHandler extends AbstractHandler
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response ) throws IOException, ServletException
+ {
+ baseRequest.setHandled(true);
+
+ response.setStatus(200);
+ response.setContentType("text/plain; charset=UTF-8");
+ response.getWriter().println("URI="+request.getRequestURI());
+ String user = request.getRemoteUser();
+ response.getWriter().println("user="+user);
+ if (request.getParameter("test_parameter")!=null)
+ response.getWriter().println(request.getParameter("test_parameter"));
+ }
+ }
+
+}

Back to the top