diff --git a/adapters/oidc/spring-boot/pom.xml b/adapters/oidc/spring-boot/pom.xml index 0abc3d847a..48a2809b6e 100755 --- a/adapters/oidc/spring-boot/pom.xml +++ b/adapters/oidc/spring-boot/pom.xml @@ -56,7 +56,7 @@ org.keycloak - keycloak-jetty92-adapter + keycloak-jetty93-adapter provided diff --git a/adapters/oidc/spring-boot/src/main/java/org/keycloak/adapters/springboot/KeycloakSpringBootConfiguration.java b/adapters/oidc/spring-boot/src/main/java/org/keycloak/adapters/springboot/KeycloakSpringBootConfiguration.java index dc2573d462..804436a6fd 100755 --- a/adapters/oidc/spring-boot/src/main/java/org/keycloak/adapters/springboot/KeycloakSpringBootConfiguration.java +++ b/adapters/oidc/spring-boot/src/main/java/org/keycloak/adapters/springboot/KeycloakSpringBootConfiguration.java @@ -141,10 +141,10 @@ public class KeycloakSpringBootConfiguration { List undertowSecurityConstraints = new ArrayList(); for (KeycloakSpringBootProperties.SecurityConstraint constraintDefinition : keycloakProperties.getSecurityConstraints()) { - for (KeycloakSpringBootProperties.SecurityCollection collectionDefinition : constraintDefinition.getSecurityCollections()) { + io.undertow.servlet.api.SecurityConstraint undertowSecurityConstraint = new io.undertow.servlet.api.SecurityConstraint(); + undertowSecurityConstraint.addRolesAllowed(constraintDefinition.getAuthRoles()); - io.undertow.servlet.api.SecurityConstraint undertowSecurityConstraint = new io.undertow.servlet.api.SecurityConstraint(); - undertowSecurityConstraint.addRolesAllowed(collectionDefinition.getAuthRoles()); + for (KeycloakSpringBootProperties.SecurityCollection collectionDefinition : constraintDefinition.getSecurityCollections()) { WebResourceCollection webResourceCollection = new WebResourceCollection(); webResourceCollection.addHttpMethods(collectionDefinition.getMethods()); @@ -153,8 +153,9 @@ public class KeycloakSpringBootConfiguration { undertowSecurityConstraint.addWebResourceCollections(webResourceCollection); - undertowSecurityConstraints.add(undertowSecurityConstraint); } + + undertowSecurityConstraints.add(undertowSecurityConstraint); } return undertowSecurityConstraints; } @@ -174,38 +175,62 @@ public class KeycloakSpringBootConfiguration { KeycloakJettyAuthenticator keycloakJettyAuthenticator = new KeycloakJettyAuthenticator(); keycloakJettyAuthenticator.setConfigResolver(new KeycloakSpringBootConfigResolver()); + /* see org.eclipse.jetty.webapp.StandardDescriptorProcessor#visitSecurityConstraint for an example + on how to map servlet spec to Constraints */ + List jettyConstraintMappings = new ArrayList(); for (KeycloakSpringBootProperties.SecurityConstraint constraintDefinition : keycloakProperties.getSecurityConstraints()) { for (KeycloakSpringBootProperties.SecurityCollection securityCollectionDefinition : constraintDefinition .getSecurityCollections()) { - + // securityCollection matches servlet spec's web-resource-collection Constraint jettyConstraint = new Constraint(); + + if (constraintDefinition.getAuthRoles().size() > 0) { + jettyConstraint.setAuthenticate(true); + jettyConstraint.setRoles(constraintDefinition.getAuthRoles().toArray(new String[0])); + } + jettyConstraint.setName(securityCollectionDefinition.getName()); - jettyConstraint.setAuthenticate(true); - if (securityCollectionDefinition.getName() != null) { - jettyConstraint.setName(securityCollectionDefinition.getName()); + // according to the servlet spec each security-constraint has at least one URL pattern + for(String pattern : securityCollectionDefinition.getPatterns()) { + + /* the following code is asymmetric as Jetty's ConstraintMapping accepts only one allowed HTTP method, + but multiple omitted methods. Therefore we add one ConstraintMapping for each allowed + mapping but only one mapping in the cases of omitted methods or no methods. + */ + + if (securityCollectionDefinition.getMethods().size() > 0) { + // according to the servlet spec we have either methods ... + for(String method : securityCollectionDefinition.getMethods()) { + ConstraintMapping jettyConstraintMapping = new ConstraintMapping(); + jettyConstraintMappings.add(jettyConstraintMapping); + + jettyConstraintMapping.setConstraint(jettyConstraint); + jettyConstraintMapping.setPathSpec(pattern); + jettyConstraintMapping.setMethod(method); + } + } else if (securityCollectionDefinition.getOmittedMethods().size() > 0){ + // ... omitted methods ... + ConstraintMapping jettyConstraintMapping = new ConstraintMapping(); + jettyConstraintMappings.add(jettyConstraintMapping); + + jettyConstraintMapping.setConstraint(jettyConstraint); + jettyConstraintMapping.setPathSpec(pattern); + jettyConstraintMapping.setMethodOmissions( + securityCollectionDefinition.getOmittedMethods().toArray(new String[0])); + } else { + // ... or no methods at all + ConstraintMapping jettyConstraintMapping = new ConstraintMapping(); + jettyConstraintMappings.add(jettyConstraintMapping); + + jettyConstraintMapping.setConstraint(jettyConstraint); + jettyConstraintMapping.setPathSpec(pattern); + } + } - jettyConstraint.setRoles(securityCollectionDefinition.getAuthRoles().toArray(new String[0])); - - ConstraintMapping jettyConstraintMapping = new ConstraintMapping(); - if (securityCollectionDefinition.getPatterns().size() > 0) { - //First pattern wins - jettyConstraintMapping.setPathSpec(securityCollectionDefinition.getPatterns().get(0)); - jettyConstraintMapping.setConstraint(jettyConstraint); - } - - if (securityCollectionDefinition.getMethods().size() > 0) { - //First method wins - jettyConstraintMapping.setMethod(securityCollectionDefinition.getMethods().get(0)); - } - - jettyConstraintMapping.setMethodOmissions( - securityCollectionDefinition.getOmittedMethods().toArray(new String[0])); - - jettyConstraintMappings.add(jettyConstraintMapping); } } @@ -235,12 +260,10 @@ public class KeycloakSpringBootConfiguration { Set authRoles = new HashSet(); for (KeycloakSpringBootProperties.SecurityConstraint constraint : keycloakProperties.getSecurityConstraints()) { - for (KeycloakSpringBootProperties.SecurityCollection collection : constraint.getSecurityCollections()) { - for (String authRole : collection.getAuthRoles()) { - if (!authRoles.contains(authRole)) { - context.addSecurityRole(authRole); - authRoles.add(authRole); - } + for (String authRole : constraint.getAuthRoles()) { + if (!authRoles.contains(authRole)) { + context.addSecurityRole(authRole); + authRoles.add(authRole); } } } @@ -248,6 +271,10 @@ public class KeycloakSpringBootConfiguration { for (KeycloakSpringBootProperties.SecurityConstraint constraint : keycloakProperties.getSecurityConstraints()) { SecurityConstraint tomcatConstraint = new SecurityConstraint(); + for (String authRole : constraint.getAuthRoles()) { + tomcatConstraint.addAuthRole(authRole); + } + for (KeycloakSpringBootProperties.SecurityCollection collection : constraint.getSecurityCollections()) { SecurityCollection tomcatSecCollection = new SecurityCollection(); @@ -258,10 +285,6 @@ public class KeycloakSpringBootConfiguration { tomcatSecCollection.setDescription(collection.getDescription()); } - for (String authRole : collection.getAuthRoles()) { - tomcatConstraint.addAuthRole(authRole); - } - for (String pattern : collection.getPatterns()) { tomcatSecCollection.addPattern(pattern); } diff --git a/adapters/oidc/spring-boot/src/main/java/org/keycloak/adapters/springboot/KeycloakSpringBootProperties.java b/adapters/oidc/spring-boot/src/main/java/org/keycloak/adapters/springboot/KeycloakSpringBootProperties.java index 2c99eba4e4..788412fdb5 100644 --- a/adapters/oidc/spring-boot/src/main/java/org/keycloak/adapters/springboot/KeycloakSpringBootProperties.java +++ b/adapters/oidc/spring-boot/src/main/java/org/keycloak/adapters/springboot/KeycloakSpringBootProperties.java @@ -43,12 +43,20 @@ public class KeycloakSpringBootProperties extends AdapterConfig { */ private List securityConstraints = new ArrayList(); + /** + * This matches security-constraint of the servlet spec + */ @ConfigurationProperties() public static class SecurityConstraint { /** * A list of security collections */ private List securityCollections = new ArrayList(); + private List authRoles = new ArrayList(); + + public List getAuthRoles() { + return authRoles; + } public List getSecurityCollections() { return securityCollections; @@ -57,7 +65,16 @@ public class KeycloakSpringBootProperties extends AdapterConfig { public void setSecurityCollections(List securityCollections) { this.securityCollections = securityCollections; } + + public void setAuthRoles(List authRoles) { + this.authRoles = authRoles; + } + } + + /** + * This matches web-resource-collection of the servlet spec + */ @ConfigurationProperties() public static class SecurityCollection { /** @@ -68,10 +85,6 @@ public class KeycloakSpringBootProperties extends AdapterConfig { * The description of your security collection */ private String description; - /** - * A list of roles that applies for this security collection - */ - private List authRoles = new ArrayList(); /** * A list of URL patterns that should match to apply the security collection */ @@ -85,10 +98,6 @@ public class KeycloakSpringBootProperties extends AdapterConfig { */ private List omittedMethods = new ArrayList(); - public List getAuthRoles() { - return authRoles; - } - public List getPatterns() { return patterns; } @@ -117,10 +126,6 @@ public class KeycloakSpringBootProperties extends AdapterConfig { this.description = description; } - public void setAuthRoles(List authRoles) { - this.authRoles = authRoles; - } - public void setPatterns(List patterns) { this.patterns = patterns; }