// // ======================================================================== // Copyright (c) 1995-2017 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.annotations; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.Arrays; import java.util.List; import javax.servlet.annotation.HttpConstraint; import javax.servlet.annotation.HttpMethodConstraint; import javax.servlet.annotation.ServletSecurity; import javax.servlet.annotation.ServletSecurity.EmptyRoleSemantic; import javax.servlet.annotation.ServletSecurity.TransportGuarantee; import javax.servlet.http.HttpServlet; import org.eclipse.jetty.security.ConstraintAware; import org.eclipse.jetty.security.ConstraintMapping; import org.eclipse.jetty.security.ConstraintSecurityHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletMapping; import org.eclipse.jetty.util.security.Constraint; import org.eclipse.jetty.webapp.WebAppContext; import org.junit.Test; public class TestSecurityAnnotationConversions { @ServletSecurity(value=@HttpConstraint(value=EmptyRoleSemantic.DENY)) public static class DenyServlet extends HttpServlet {} @ServletSecurity public static class PermitServlet extends HttpServlet {} @ServletSecurity(value=@HttpConstraint(value=EmptyRoleSemantic.PERMIT, transportGuarantee=TransportGuarantee.CONFIDENTIAL, rolesAllowed={"tom", "dick", "harry"})) public static class RolesServlet extends HttpServlet {} @ServletSecurity(value=@HttpConstraint(value=EmptyRoleSemantic.PERMIT, transportGuarantee=TransportGuarantee.CONFIDENTIAL, rolesAllowed={"tom", "dick", "harry"}), httpMethodConstraints={@HttpMethodConstraint(value="GET")}) public static class Method1Servlet extends HttpServlet {} @ServletSecurity(value=@HttpConstraint(value=EmptyRoleSemantic.PERMIT, transportGuarantee=TransportGuarantee.CONFIDENTIAL, rolesAllowed={"tom", "dick", "harry"}), httpMethodConstraints={@HttpMethodConstraint(value="GET", transportGuarantee=TransportGuarantee.CONFIDENTIAL)}) public static class Method2Servlet extends HttpServlet {} public void setUp() { } @Test public void testDenyAllOnClass() throws Exception { WebAppContext wac = makeWebAppContext(DenyServlet.class.getCanonicalName(), "denyServlet", new String[]{"/foo/*", "*.foo"}); //Assume we found 1 servlet with a @HttpConstraint with value=EmptyRoleSemantic.DENY security annotation ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac); AnnotationIntrospector introspector = new AnnotationIntrospector(); introspector.registerHandler(annotationHandler); //set up the expected outcomes: //1 ConstraintMapping per ServletMapping pathSpec Constraint expectedConstraint = new Constraint(); expectedConstraint.setAuthenticate(true); expectedConstraint.setDataConstraint(Constraint.DC_NONE); ConstraintMapping[] expectedMappings = new ConstraintMapping[2]; expectedMappings[0] = new ConstraintMapping(); expectedMappings[0].setConstraint(expectedConstraint); expectedMappings[0].setPathSpec("/foo/*"); expectedMappings[1] = new ConstraintMapping(); expectedMappings[1].setConstraint(expectedConstraint); expectedMappings[1].setPathSpec("*.foo"); introspector.introspect(DenyServlet.class); compareResults(expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings()); } @Test public void testPermitAll() throws Exception { //Assume we found 1 servlet with a @ServletSecurity security annotation WebAppContext wac = makeWebAppContext(PermitServlet.class.getCanonicalName(), "permitServlet", new String[]{"/foo/*", "*.foo"}); ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac); AnnotationIntrospector introspector = new AnnotationIntrospector(); introspector.registerHandler(annotationHandler); //set up the expected outcomes - no constraints at all as per Servlet Spec 3.1 pg 129 //1 ConstraintMapping per ServletMapping pathSpec ConstraintMapping[] expectedMappings = new ConstraintMapping[]{}; introspector.introspect(PermitServlet.class); compareResults (expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings()); } @Test public void testRolesAllowedWithTransportGuarantee() throws Exception { //Assume we found 1 servlet with annotation with roles defined and //and a TransportGuarantee WebAppContext wac = makeWebAppContext(RolesServlet.class.getCanonicalName(), "rolesServlet", new String[]{"/foo/*", "*.foo"}); ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac); AnnotationIntrospector introspector = new AnnotationIntrospector(); introspector.registerHandler(annotationHandler); //set up the expected outcomes:compareResults //1 ConstraintMapping per ServletMapping Constraint expectedConstraint = new Constraint(); expectedConstraint.setAuthenticate(true); expectedConstraint.setRoles(new String[]{"tom", "dick", "harry"}); expectedConstraint.setDataConstraint(Constraint.DC_CONFIDENTIAL); ConstraintMapping[] expectedMappings = new ConstraintMapping[2]; expectedMappings[0] = new ConstraintMapping(); expectedMappings[0].setConstraint(expectedConstraint); expectedMappings[0].setPathSpec("/foo/*"); expectedMappings[1] = new ConstraintMapping(); expectedMappings[1].setConstraint(expectedConstraint); expectedMappings[1].setPathSpec("*.foo"); introspector.introspect(RolesServlet.class); compareResults (expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings()); } @Test public void testMethodAnnotation() throws Exception { //ServletSecurity annotation with HttpConstraint of TransportGuarantee.CONFIDENTIAL, and a list of rolesAllowed, and //a HttpMethodConstraint for GET method that permits all and has TransportGuarantee.NONE (ie is default) WebAppContext wac = makeWebAppContext(Method1Servlet.class.getCanonicalName(), "method1Servlet", new String[]{"/foo/*", "*.foo"}); //set up the expected outcomes: - a Constraint for the RolesAllowed on the class //with userdata constraint of DC_CONFIDENTIAL //and mappings for each of the pathSpecs Constraint expectedConstraint1 = new Constraint(); expectedConstraint1.setAuthenticate(true); expectedConstraint1.setRoles(new String[]{"tom", "dick", "harry"}); expectedConstraint1.setDataConstraint(Constraint.DC_CONFIDENTIAL); //a Constraint for the PermitAll on the doGet method with a userdata //constraint of DC_CONFIDENTIAL inherited from the class Constraint expectedConstraint2 = new Constraint(); expectedConstraint2.setDataConstraint(Constraint.DC_NONE); ConstraintMapping[] expectedMappings = new ConstraintMapping[4]; expectedMappings[0] = new ConstraintMapping(); expectedMappings[0].setConstraint(expectedConstraint1); expectedMappings[0].setPathSpec("/foo/*"); expectedMappings[0].setMethodOmissions(new String[]{"GET"}); expectedMappings[1] = new ConstraintMapping(); expectedMappings[1].setConstraint(expectedConstraint1); expectedMappings[1].setPathSpec("*.foo"); expectedMappings[1].setMethodOmissions(new String[]{"GET"}); expectedMappings[2] = new ConstraintMapping(); expectedMappings[2].setConstraint(expectedConstraint2); expectedMappings[2].setPathSpec("/foo/*"); expectedMappings[2].setMethod("GET"); expectedMappings[3] = new ConstraintMapping(); expectedMappings[3].setConstraint(expectedConstraint2); expectedMappings[3].setPathSpec("*.foo"); expectedMappings[3].setMethod("GET"); AnnotationIntrospector introspector = new AnnotationIntrospector(); ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac); introspector.registerHandler(annotationHandler); introspector.introspect(Method1Servlet.class); compareResults (expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings()); } @Test public void testMethodAnnotation2() throws Exception { //A ServletSecurity annotation that has HttpConstraint of CONFIDENTIAL with defined roles, but a //HttpMethodConstraint for GET that permits all, but also requires CONFIDENTIAL WebAppContext wac = makeWebAppContext(Method2Servlet.class.getCanonicalName(), "method2Servlet", new String[]{"/foo/*", "*.foo"}); AnnotationIntrospector introspector = new AnnotationIntrospector(); ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac); introspector.registerHandler(annotationHandler); //set up the expected outcomes: - a Constraint for the RolesAllowed on the class //with userdata constraint of DC_CONFIDENTIAL //and mappings for each of the pathSpecs Constraint expectedConstraint1 = new Constraint(); expectedConstraint1.setAuthenticate(true); expectedConstraint1.setRoles(new String[]{"tom", "dick", "harry"}); expectedConstraint1.setDataConstraint(Constraint.DC_CONFIDENTIAL); //a Constraint for the Permit on the GET method with a userdata //constraint of DC_CONFIDENTIAL Constraint expectedConstraint2 = new Constraint(); expectedConstraint2.setDataConstraint(Constraint.DC_CONFIDENTIAL); ConstraintMapping[] expectedMappings = new ConstraintMapping[4]; expectedMappings[0] = new ConstraintMapping(); expectedMappings[0].setConstraint(expectedConstraint1); expectedMappings[0].setPathSpec("/foo/*"); expectedMappings[0].setMethodOmissions(new String[]{"GET"}); expectedMappings[1] = new ConstraintMapping(); expectedMappings[1].setConstraint(expectedConstraint1); expectedMappings[1].setPathSpec("*.foo"); expectedMappings[1].setMethodOmissions(new String[]{"GET"}); expectedMappings[2] = new ConstraintMapping(); expectedMappings[2].setConstraint(expectedConstraint2); expectedMappings[2].setPathSpec("/foo/*"); expectedMappings[2].setMethod("GET"); expectedMappings[3] = new ConstraintMapping(); expectedMappings[3].setConstraint(expectedConstraint2); expectedMappings[3].setPathSpec("*.foo"); expectedMappings[3].setMethod("GET"); introspector.introspect(Method2Servlet.class); compareResults (expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings()); } private void compareResults (ConstraintMapping[] expectedMappings, List<ConstraintMapping> actualMappings) { assertNotNull(actualMappings); assertEquals(expectedMappings.length, actualMappings.size()); for (int k=0; k < actualMappings.size(); k++) { ConstraintMapping am = actualMappings.get(k); boolean matched = false; for (int i=0; i< expectedMappings.length && !matched; i++) { ConstraintMapping em = expectedMappings[i]; if (em.getPathSpec().equals(am.getPathSpec())) { if ((em.getMethod()==null && am.getMethod() == null) || em.getMethod() != null && em.getMethod().equals(am.getMethod())) { matched = true; assertEquals(em.getConstraint().getAuthenticate(), am.getConstraint().getAuthenticate()); assertEquals(em.getConstraint().getDataConstraint(), am.getConstraint().getDataConstraint()); if (em.getMethodOmissions() == null) { assertNull(am.getMethodOmissions()); } else { assertTrue(Arrays.equals(am.getMethodOmissions(), em.getMethodOmissions())); } if (em.getConstraint().getRoles() == null) { assertNull(am.getConstraint().getRoles()); } else { assertTrue(Arrays.equals(em.getConstraint().getRoles(), am.getConstraint().getRoles())); } } } } if (!matched) fail("No expected ConstraintMapping matching method:"+am.getMethod()+" pathSpec: "+am.getPathSpec()); } } private WebAppContext makeWebAppContext (String className, String servletName, String[] paths) { WebAppContext wac = new WebAppContext(); ServletHolder[] holders = new ServletHolder[1]; holders[0] = new ServletHolder(); holders[0].setClassName(className); holders[0].setName(servletName); holders[0].setServletHandler(wac.getServletHandler()); wac.getServletHandler().setServlets(holders); wac.setSecurityHandler(new ConstraintSecurityHandler()); ServletMapping[] servletMappings = new ServletMapping[1]; servletMappings[0] = new ServletMapping(); servletMappings[0].setPathSpecs(paths); servletMappings[0].setServletName(servletName); wac.getServletHandler().setServletMappings(servletMappings); return wac; } }