diff --git a/impl/pom.xml b/impl/pom.xml index 82a0dac..c49eaae 100644 --- a/impl/pom.xml +++ b/impl/pom.xml @@ -41,7 +41,7 @@ EPL 2.0 - http://www.eclipse.org/legal/epl-2.0 + https://www.eclipse.org/legal/epl-2.0 repo @@ -149,7 +149,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.0.0-M3 + 3.0.0 enforce-maven @@ -219,7 +219,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.2.0 + 3.2.2 @@ -269,7 +269,7 @@ exousia-dev@eclipse.org.
Copyright © 2021 Eclipse Foundation. All rights reserved.
-Use is subject to license terms.]]> +Use is subject to license terms.]]>
https://jakarta.ee/specifications/platform/9/apidocs/ diff --git a/impl/src/main/java/org/glassfish/exousia/AuthorizationService.java b/impl/src/main/java/org/glassfish/exousia/AuthorizationService.java index 826e89b..702655a 100644 --- a/impl/src/main/java/org/glassfish/exousia/AuthorizationService.java +++ b/impl/src/main/java/org/glassfish/exousia/AuthorizationService.java @@ -69,7 +69,7 @@ public class AuthorizationService { static final Logger logger = Logger.getLogger(AuthorizationService.class.getName()); - private static boolean isSecMgrOff = System.getSecurityManager() == null; + private static boolean isSecMgrOff = true; //System.getSecurityManager() == null; // removed in java 17+ public static final String HTTP_SERVLET_REQUEST = "jakarta.servlet.http.HttpServletRequest"; public static final String SUBJECT = "javax.security.auth.Subject.container"; @@ -96,9 +96,7 @@ public class AuthorizationService { private String constrainedUriRequestAttribute; - public AuthorizationService( - ServletContext servletContext, - Supplier subjectSupplier) { + public AuthorizationService( ServletContext servletContext, Supplier subjectSupplier) { this( DefaultPolicyConfigurationFactory.class, DefaultPolicy.class, @@ -129,14 +127,9 @@ public AuthorizationService( subjectSupplier, principalMapper); } - public AuthorizationService( - String contextId, - Supplier subjectSupplier, PrincipalMapper principalMapper) { - - this( - getFactory(), getPolicy(), contextId, - subjectSupplier, principalMapper); -} + public AuthorizationService(String contextId, Supplier subjectSupplier, PrincipalMapper principalMapper) { + this(getFactory(), getPolicy(), contextId, subjectSupplier, principalMapper); + } public AuthorizationService( PolicyConfigurationFactory factory, Policy policy, String contextId, @@ -152,15 +145,9 @@ public AuthorizationService( // authorization config PolicyContext.setContextID(contextId); - PolicyContext.registerHandler( - SUBJECT, - new DefaultPolicyContextHandler(SUBJECT, subjectSupplier), - true); + PolicyContext.registerHandler(SUBJECT, new DefaultPolicyContextHandler(SUBJECT, subjectSupplier), true); - PolicyContext.registerHandler( - PRINCIPAL_MAPPER, - new DefaultPolicyContextHandler(PRINCIPAL_MAPPER, () -> principalMapper), - true); + PolicyContext.registerHandler(PRINCIPAL_MAPPER, new DefaultPolicyContextHandler(PRINCIPAL_MAPPER, () -> principalMapper), true); } catch (PolicyContextException | IllegalArgumentException | SecurityException e) { throw new IllegalStateException(e); diff --git a/impl/src/main/java/org/glassfish/exousia/DefaultPolicyContextHandler.java b/impl/src/main/java/org/glassfish/exousia/DefaultPolicyContextHandler.java index 70172c3..11f9c0b 100644 --- a/impl/src/main/java/org/glassfish/exousia/DefaultPolicyContextHandler.java +++ b/impl/src/main/java/org/glassfish/exousia/DefaultPolicyContextHandler.java @@ -38,17 +38,17 @@ public DefaultPolicyContextHandler(String key, Supplier contex } @Override - public Object getContext(String key, Object data) throws PolicyContextException { + public Object getContext(String key, Object data) { return contextObjectSupplier.get(); } @Override - public boolean supports(String key) throws PolicyContextException { + public boolean supports(String key) { return this.key.equals(key); } @Override - public String[] getKeys() throws PolicyContextException { + public String[] getKeys() { return keys; } diff --git a/impl/src/main/java/org/glassfish/exousia/constraints/SecurityConstraint.java b/impl/src/main/java/org/glassfish/exousia/constraints/SecurityConstraint.java index 2ef2e40..287447a 100644 --- a/impl/src/main/java/org/glassfish/exousia/constraints/SecurityConstraint.java +++ b/impl/src/main/java/org/glassfish/exousia/constraints/SecurityConstraint.java @@ -15,18 +15,17 @@ */ package org.glassfish.exousia.constraints; -import static java.util.Arrays.asList; -import static java.util.Collections.unmodifiableList; -import static java.util.Collections.unmodifiableSet; -import static java.util.stream.Collectors.toList; -import static java.util.stream.Stream.concat; -import static jakarta.servlet.annotation.ServletSecurity.TransportGuarantee.NONE; +import jakarta.servlet.annotation.ServletSecurity.TransportGuarantee; -import java.util.HashSet; +import java.util.Collection; import java.util.List; import java.util.Set; -import jakarta.servlet.annotation.ServletSecurity.TransportGuarantee; +import static jakarta.servlet.annotation.ServletSecurity.TransportGuarantee.NONE; +import static java.util.Collections.unmodifiableList; +import static java.util.Collections.unmodifiableSet; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Stream.concat; public class SecurityConstraint { @@ -39,15 +38,15 @@ public SecurityConstraint(String urlPattern, String... rolesAllowed) { } public SecurityConstraint(WebResourceCollection webResourceCollection, String... rolesAllowed) { - this(asList(webResourceCollection), asList(rolesAllowed)); + this(List.of(webResourceCollection), List.of(rolesAllowed)); } public SecurityConstraint(List webResourceCollections, String... rolesAllowed) { - this(webResourceCollections, asList(rolesAllowed)); + this(webResourceCollections, List.of(rolesAllowed)); } - public SecurityConstraint(List webResourceCollections, List rolesAllowed) { - this(webResourceCollections, new HashSet<>(rolesAllowed)); + public SecurityConstraint(List webResourceCollections, Collection rolesAllowed) { + this(webResourceCollections, Set.copyOf(rolesAllowed)); } public SecurityConstraint(List webResourceCollections, Set rolesAllowed) { diff --git a/impl/src/main/java/org/glassfish/exousia/constraints/WebResourceCollection.java b/impl/src/main/java/org/glassfish/exousia/constraints/WebResourceCollection.java index c9cd064..d3800ab 100644 --- a/impl/src/main/java/org/glassfish/exousia/constraints/WebResourceCollection.java +++ b/impl/src/main/java/org/glassfish/exousia/constraints/WebResourceCollection.java @@ -15,14 +15,11 @@ */ package org.glassfish.exousia.constraints; -import static java.util.Arrays.asList; -import static java.util.Collections.emptySet; -import static java.util.Collections.unmodifiableSet; - -import java.util.HashSet; -import java.util.List; +import java.util.Collection; import java.util.Set; +import static java.util.Collections.unmodifiableSet; + public class WebResourceCollection { private final Set urlPatterns; @@ -30,23 +27,23 @@ public class WebResourceCollection { private final Set httpMethodOmissions; public WebResourceCollection(String... urlPatterns) { - this(asList(urlPatterns)); + this(Set.of(urlPatterns)); } - public WebResourceCollection(List urlPatterns) { - this(new HashSet<>(urlPatterns), emptySet(), emptySet()); + public WebResourceCollection(Collection urlPatterns) { + this( Set.copyOf(urlPatterns) , Set.of(), Set.of()); } public WebResourceCollection(Set urlPatterns, Set httpMethods) { - this(urlPatterns, httpMethods, emptySet()); + this(urlPatterns, httpMethods, Set.of()); } public WebResourceCollection(String[] urlPatterns, String[] httpMethods, String[] httpMethodOmissions) { - this(asList(urlPatterns), asList(httpMethods), asList(httpMethodOmissions)); + this( Set.of(urlPatterns) , Set.of(httpMethods) , Set.of(httpMethodOmissions) ); } - public WebResourceCollection(List urlPatterns, List httpMethods, List httpMethodOmissions) { - this(new HashSet<>(urlPatterns), new HashSet<>(httpMethods), new HashSet<>(httpMethodOmissions)); + private WebResourceCollection(Collection urlPatterns, Collection httpMethods, Collection httpMethodOmissions) { + this(Set.copyOf(urlPatterns), Set.copyOf(httpMethods), Set.copyOf(httpMethodOmissions) ); } public WebResourceCollection(Set urlPatterns, Set httpMethods, Set httpMethodOmissions) { @@ -55,17 +52,16 @@ public WebResourceCollection(Set urlPatterns, Set httpMethods, S this.httpMethodOmissions = unmodifiableSet(httpMethodOmissions); } + // --- GETTERS ----------------------------------------------------------------------------- + public Set getUrlPatterns() { return urlPatterns; } - public Set getHttpMethods() { return httpMethods; } - public Set getHttpMethodOmissions() { return httpMethodOmissions; } - } \ No newline at end of file diff --git a/impl/src/main/java/org/glassfish/exousia/modules/def/DefaultPolicyConfigurationFactory.java b/impl/src/main/java/org/glassfish/exousia/modules/def/DefaultPolicyConfigurationFactory.java index f4e00e5..ffc8ebc 100644 --- a/impl/src/main/java/org/glassfish/exousia/modules/def/DefaultPolicyConfigurationFactory.java +++ b/impl/src/main/java/org/glassfish/exousia/modules/def/DefaultPolicyConfigurationFactory.java @@ -28,58 +28,41 @@ * * @author Arjan Tijms */ -public class DefaultPolicyConfigurationFactory - extends PolicyConfigurationFactory { +public class DefaultPolicyConfigurationFactory extends PolicyConfigurationFactory { - private static final - ConcurrentMap - configurators = new ConcurrentHashMap<>(); + private static final ConcurrentMap configurators = new ConcurrentHashMap<>(); @Override - public PolicyConfiguration getPolicyConfiguration( - String contextID, - boolean remove) - throws PolicyContextException { + public PolicyConfiguration getPolicyConfiguration( String contextID , boolean remove ) throws PolicyContextException { - DefaultPolicyConfigurationStateMachine - defaultPolicyConfigurationStateMachine = - configurators.computeIfAbsent(contextID, - contextId -> new DefaultPolicyConfigurationStateMachine( - new DefaultPolicyConfiguration( - contextID))); + // if contextID is null ?? + if ( contextID == null ) return null; - if (remove) { - defaultPolicyConfigurationStateMachine - .delete(); - } + DefaultPolicyConfigurationStateMachine defaultPolicyConfigurationStateMachine = + configurators.computeIfAbsent( + contextID, + contextId -> new DefaultPolicyConfigurationStateMachine(new DefaultPolicyConfiguration(contextID)) + ); - defaultPolicyConfigurationStateMachine - .open(); + // Remove Policy + if (remove) defaultPolicyConfigurationStateMachine.delete(); - return - defaultPolicyConfigurationStateMachine; + // Open and return + defaultPolicyConfigurationStateMachine.open(); + return defaultPolicyConfigurationStateMachine; } @Override - public boolean inService( - String contextID) - throws PolicyContextException { - DefaultPolicyConfigurationStateMachine - defaultPolicyConfigurationStateMachine = - configurators.get(contextID); + public boolean inService(String contextID) throws PolicyContextException { + DefaultPolicyConfigurationStateMachine defaultPolicyConfigurationStateMachine = configurators.get(contextID); - if (defaultPolicyConfigurationStateMachine == null) { - return false; - } + if (defaultPolicyConfigurationStateMachine == null) return false; - return defaultPolicyConfigurationStateMachine - .inService(); + return defaultPolicyConfigurationStateMachine.inService(); } public static DefaultPolicyConfiguration getCurrentPolicyConfiguration() { - return (DefaultPolicyConfiguration) configurators - .get(getContextID()) - .getPolicyConfiguration(); + return (DefaultPolicyConfiguration) configurators.get(getContextID()).getPolicyConfiguration(); } } \ No newline at end of file diff --git a/pom.xml b/pom.xml index e4727dc..c119e51 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,7 @@ EPL 2.0 - http://www.eclipse.org/legal/epl-2.0 + https://www.eclipse.org/legal/epl-2.0 repo @@ -104,7 +104,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.0.0-M3 + 3.0.0 enforce-maven diff --git a/spi/tomcat/pom.xml b/spi/tomcat/pom.xml index f872788..50a5a8c 100644 --- a/spi/tomcat/pom.xml +++ b/spi/tomcat/pom.xml @@ -43,7 +43,7 @@ EPL 2.0 - http://www.eclipse.org/legal/epl-2.0 + https://www.eclipse.org/legal/epl-2.0 repo @@ -120,7 +120,7 @@ org.apache.tomcat tomcat-catalina - 10.0.0 + 10.0.16 provided @@ -129,7 +129,7 @@ junit junit - 4.13.1 + 4.13.2 test @@ -161,7 +161,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.0.0-M3 + 3.0.0 enforce-maven @@ -219,7 +219,7 @@ exousia-dev@eclipse.org.
Copyright © 2021 Eclipse Foundation. All rights reserved.
-Use is subject to license terms.]]> +Use is subject to license terms.]]>
https://jakarta.ee/specifications/platform/9/apidocs/ diff --git a/spi/tomcat/src/main/java/org/glassfish/exousia/spi/tomcat/TomcatAuthorizationFilter.java b/spi/tomcat/src/main/java/org/glassfish/exousia/spi/tomcat/TomcatAuthorizationFilter.java index fbf9dbc..4b292a0 100644 --- a/spi/tomcat/src/main/java/org/glassfish/exousia/spi/tomcat/TomcatAuthorizationFilter.java +++ b/spi/tomcat/src/main/java/org/glassfish/exousia/spi/tomcat/TomcatAuthorizationFilter.java @@ -15,19 +15,9 @@ */ package org.glassfish.exousia.spi.tomcat; -import static jakarta.servlet.annotation.ServletSecurity.TransportGuarantee.CONFIDENTIAL; -import static jakarta.servlet.annotation.ServletSecurity.TransportGuarantee.NONE; -import static java.util.Arrays.asList; -import static java.util.Collections.emptyMap; -import static org.apache.catalina.authenticator.Constants.REQ_JASPIC_SUBJECT_NOTE; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; - -import javax.security.auth.Subject; - +import jakarta.servlet.*; +import jakarta.servlet.http.HttpFilter; +import jakarta.servlet.http.HttpServletRequest; import org.apache.catalina.Context; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.RequestFacade; @@ -37,107 +27,138 @@ import org.glassfish.exousia.AuthorizationService; import org.glassfish.exousia.constraints.WebResourceCollection; -import jakarta.servlet.ServletContext; -import jakarta.servlet.ServletException; -import jakarta.servlet.ServletRequest; -import jakarta.servlet.ServletRequestEvent; -import jakarta.servlet.ServletRequestListener; -import jakarta.servlet.ServletRequestWrapper; -import jakarta.servlet.http.HttpFilter; -import jakarta.servlet.http.HttpServletRequest; +import javax.security.auth.Subject; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import static jakarta.servlet.annotation.ServletSecurity.TransportGuarantee.CONFIDENTIAL; +import static jakarta.servlet.annotation.ServletSecurity.TransportGuarantee.NONE; +import static java.util.Collections.emptyMap; +import static org.apache.catalina.authenticator.Constants.REQ_JASPIC_SUBJECT_NOTE; /** + * Tomcat Authorization Filter and Request Listener + * Runtime bridge from Tomcat request ad exousia auth check + * During Filter initialization all Tomcat's roles are collected and converted to Exousia model * * @author Arjan Tijms - * */ +//@WebFilter( filterName = TomcatAuthorizationFilterName , displayName = TomcatAuthorizationFilterName ) +//@WebListener public class TomcatAuthorizationFilter extends HttpFilter implements ServletRequestListener { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = -1070693477269008527L; + +// public static final String TomcatAuthorizationFilterName = "TomcatAuthorizationFilter"; public static ThreadLocal localServletRequest = new ThreadLocal<>(); @Override - public void init() throws ServletException { + public void init() { ServletContext servletContext = getFilterConfig().getServletContext(); - StandardRoot root = (StandardRoot) servletContext.getAttribute("org.apache.catalina.resources"); - Context context = root.getContext(); - - // Get all the security constraints from Tomcat - SecurityConstraint[] constraints = context.findConstraints(); - List declaredRoles = asList(context.findSecurityRoles()); - boolean isDenyUncoveredHttpMethods = root.getContext().getDenyUncoveredHttpMethods(); - AuthorizationService.setThreadContextId(servletContext); // Initialize the AuthorizationService, which is a front-end for Jakarta Authorization. // It specifically tells Jakarta Authorization how to get the current request, and the current subject AuthorizationService authorizationService = new AuthorizationService( servletContext, - () -> getSubject(localServletRequest.get())); + () -> getSubject(localServletRequest.get()) + ); + + authorizationService.setRequestSupplier( () -> localServletRequest.get() ); - authorizationService.setRequestSupplier(() -> localServletRequest.get()); + + // Get all the security constraints from Tomcat + StandardRoot root = (StandardRoot) servletContext.getAttribute("org.apache.catalina.resources"); + Context context = root.getContext(); + SecurityConstraint[] constraints = context.findConstraints(); + Set declaredRoles = Set.of(context.findSecurityRoles()); + boolean isDenyUncoveredHttpMethods = root.getContext().getDenyUncoveredHttpMethods(); // Copy all the security constraints that Tomcat has collected to the Jakarta Authorization // repository as well. That way Jakarta Authorization can work with the same data as Tomcat // internally does. authorizationService.addConstraintsToPolicy( - convertTomcatConstraintsToExousia(constraints), - new HashSet<>(declaredRoles), isDenyUncoveredHttpMethods, emptyMap()); + convertTomcatConstraintsToExousia(constraints), declaredRoles, isDenyUncoveredHttpMethods, emptyMap() + ); } + + // --- ServletRequestListener ----------------------------------------------------------------------- + @Override - public void requestInitialized(ServletRequestEvent sre) { + public void requestInitialized(ServletRequestEvent event) { // Sets the initial request. // Note that we should actually have the request used before every filter and Servlet that will be executed. - localServletRequest.set((HttpServletRequest) sre.getServletRequest()); + localServletRequest.set((HttpServletRequest) event.getServletRequest()); // Sets the context ID in the current thread. The context ID is a unique name for the current web application and // is used by Jakarta Authorization and Exousia. - AuthorizationService.setThreadContextId(sre.getServletContext()); + AuthorizationService.setThreadContextId(event.getServletContext()); } @Override - public void requestDestroyed(ServletRequestEvent sre) { + public void requestDestroyed(ServletRequestEvent event) { localServletRequest.remove(); } + + + + // --- Utility methods for Tomcat to Exousia constraint conversion --------------------------------------------------------------------------- + /** * Transforms the security constraints (web.xml, annotations, and programmatic) from the Tomcat types to Exousia types. - * - * @param tomcatConstraints - * @return */ - private List convertTomcatConstraintsToExousia(org.apache.tomcat.util.descriptor.web.SecurityConstraint[] tomcatConstraints) { - if (tomcatConstraints == null || tomcatConstraints.length == 0) { - return null; - } + private static List convertTomcatConstraintsToExousia(org.apache.tomcat.util.descriptor.web.SecurityConstraint[] tomcatConstraints) { + if (tomcatConstraints == null || tomcatConstraints.length == 0) return null; List exousiaConstraints = new ArrayList<>(); for (SecurityConstraint tomcatConstraint : tomcatConstraints) { + // Tomcat Security Constraint Collection => List List exousiaWebResourceCollections = new ArrayList<>(); - for (SecurityCollection tomcatSecurityCollection : tomcatConstraint.findCollections()) { - exousiaWebResourceCollections.add(new WebResourceCollection( - tomcatSecurityCollection.findPatterns(), - tomcatSecurityCollection.findMethods(), - tomcatSecurityCollection.findOmittedMethods())); - } - - exousiaConstraints.add(new org.glassfish.exousia.constraints.SecurityConstraint( - exousiaWebResourceCollections, - new HashSet<>(asList(tomcatConstraint.findAuthRoles())), - "confidential".equalsIgnoreCase(tomcatConstraint.getUserConstraint()) - ? CONFIDENTIAL : NONE)); + for (SecurityCollection tomcatSecurityCollection : tomcatConstraint.findCollections()) + exousiaWebResourceCollections.add( toExousiaSecurityCollection(tomcatSecurityCollection) ); + // (TomcatConstraint,List) => Exousia SecurityConstraint + exousiaConstraints.add( toExousiaSecurityConstraint(tomcatConstraint,exousiaWebResourceCollections) ); } return exousiaConstraints; } + + /** + * Create an Exousia {@link org.glassfish.exousia.constraints.SecurityConstraint} + * from a Tomcat Security Constraints and a List of {@link WebResourceCollection} + */ + private static org.glassfish.exousia.constraints.SecurityConstraint toExousiaSecurityConstraint(SecurityConstraint tomcatConstraint, List exousiaWebResourceCollections) { + return new org.glassfish.exousia.constraints.SecurityConstraint( + exousiaWebResourceCollections, + Set.of(tomcatConstraint.findAuthRoles()), + "confidential".equalsIgnoreCase(tomcatConstraint.getUserConstraint()) ? CONFIDENTIAL : NONE + ); + } + + + /** + * Tomcat SecurityCollection -> Exousia WebResourceCollection + */ + public static WebResourceCollection toExousiaSecurityCollection(SecurityCollection tomcatSecurityCollection ) { + return new WebResourceCollection( + tomcatSecurityCollection.findPatterns(), + tomcatSecurityCollection.findMethods(), + tomcatSecurityCollection.findOmittedMethods() + ); + } + + /** * Gets the authenticated Subject (if any) from the Tomcat specific location inside the HttpServletRequest instance. * @@ -159,6 +180,7 @@ private static T unwrapFully(ServletRequest request) } private static Request getRequest(RequestFacade facade) { + if ( facade == null ) return null; // request is null?? try { Field requestField = RequestFacade.class.getDeclaredField("request"); requestField.setAccessible(true); diff --git a/spi/tomcat/src/main/java/org/glassfish/exousia/spi/tomcat/TomcatIntegrationInitializer.java b/spi/tomcat/src/main/java/org/glassfish/exousia/spi/tomcat/TomcatIntegrationInitializer.java index eed9859..e58c2bf 100644 --- a/spi/tomcat/src/main/java/org/glassfish/exousia/spi/tomcat/TomcatIntegrationInitializer.java +++ b/spi/tomcat/src/main/java/org/glassfish/exousia/spi/tomcat/TomcatIntegrationInitializer.java @@ -31,14 +31,14 @@ public class TomcatIntegrationInitializer implements ServletContainerInitializer { @Override - public void onStartup(Set> c, ServletContext ctx) throws ServletException { + public void onStartup(Set> classes, ServletContext context) { if (isTomcat()) { - ctx.addFilter(TomcatAuthorizationFilter.class.getCanonicalName(), TomcatAuthorizationFilter.class); - ctx.addListener(TomcatAuthorizationFilter.class); + context.addFilter(TomcatAuthorizationFilter.class.getCanonicalName(),TomcatAuthorizationFilter.class); + context.addListener(TomcatAuthorizationFilter.class); } } - private boolean isTomcat() { + private static boolean isTomcat() { try { Class.forName("org.apache.tomcat.util.descriptor.web.SecurityConstraint"); return true;