diff --git a/authz/policy-enforcer/src/main/java/org/keycloak/adapters/authorization/integration/elytron/ElytronPolicyEnforcerFilter.java b/authz/policy-enforcer/src/main/java/org/keycloak/adapters/authorization/integration/elytron/ElytronPolicyEnforcerFilter.java new file mode 100644 index 0000000000..99c42912e4 --- /dev/null +++ b/authz/policy-enforcer/src/main/java/org/keycloak/adapters/authorization/integration/elytron/ElytronPolicyEnforcerFilter.java @@ -0,0 +1,53 @@ +package org.keycloak.adapters.authorization.integration.elytron; + +import java.security.Principal; + +import jakarta.servlet.http.HttpServletRequest; +import org.keycloak.adapters.authorization.PolicyEnforcer; +import org.keycloak.adapters.authorization.integration.jakarta.ServletPolicyEnforcerFilter; +import org.keycloak.adapters.authorization.spi.ConfigurationResolver; +import org.keycloak.representations.adapters.config.PolicyEnforcerConfig; +import org.wildfly.security.http.oidc.OidcClientConfiguration; +import org.wildfly.security.http.oidc.OidcPrincipal; +import org.wildfly.security.http.oidc.RefreshableOidcSecurityContext; + +public class ElytronPolicyEnforcerFilter extends ServletPolicyEnforcerFilter { + + public ElytronPolicyEnforcerFilter(ConfigurationResolver configResolver) { + super(configResolver); + } + + @Override + protected String extractBearerToken(HttpServletRequest request) { + Principal principal = request.getUserPrincipal(); + + if (principal == null) { + return null; + } + + OidcPrincipal oidcPrincipal = (OidcPrincipal) principal; + RefreshableOidcSecurityContext securityContext = (RefreshableOidcSecurityContext) oidcPrincipal.getOidcSecurityContext(); + + if (securityContext == null) { + return null; + } + + return securityContext.getTokenString(); + } + + @Override + protected PolicyEnforcer createPolicyEnforcer(HttpServletRequest servletRequest, PolicyEnforcerConfig enforcerConfig) { + RefreshableOidcSecurityContext securityContext = (RefreshableOidcSecurityContext) ((OidcPrincipal) servletRequest.getUserPrincipal()).getOidcSecurityContext(); + OidcClientConfiguration configuration = securityContext.getOidcClientConfiguration(); + String authServerUrl = configuration.getAuthServerBaseUrl(); + + return PolicyEnforcer.builder() + .authServerUrl(authServerUrl) + .realm(configuration.getRealm()) + .clientId(configuration.getClientId()) + .credentials(configuration.getResourceCredentials()) + .bearerOnly(false) + .enforcerConfig(enforcerConfig) + .httpClient(configuration.getClient()).build(); + } +} diff --git a/authz/policy-enforcer/src/main/java/org/keycloak/adapters/authorization/integration/elytron/PolicyEnforcerServletContextListener.java b/authz/policy-enforcer/src/main/java/org/keycloak/adapters/authorization/integration/elytron/PolicyEnforcerServletContextListener.java index 496937966d..d5bec3b955 100644 --- a/authz/policy-enforcer/src/main/java/org/keycloak/adapters/authorization/integration/elytron/PolicyEnforcerServletContextListener.java +++ b/authz/policy-enforcer/src/main/java/org/keycloak/adapters/authorization/integration/elytron/PolicyEnforcerServletContextListener.java @@ -66,7 +66,7 @@ public class PolicyEnforcerServletContextListener implements ServletContextListe logger.debug("Policy enforcement filter is enabled."); - servletContext.addFilter("keycloak-policy-enforcer", new PolicyEnforcerFilter(configResolver)) + servletContext.addFilter("keycloak-policy-enforcer", new ElytronPolicyEnforcerFilter(configResolver)) .addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*"); } diff --git a/authz/policy-enforcer/src/main/java/org/keycloak/adapters/authorization/integration/elytron/ServletHttpRequest.java b/authz/policy-enforcer/src/main/java/org/keycloak/adapters/authorization/integration/elytron/ServletHttpRequest.java index ed4892980c..6b645fce5e 100644 --- a/authz/policy-enforcer/src/main/java/org/keycloak/adapters/authorization/integration/elytron/ServletHttpRequest.java +++ b/authz/policy-enforcer/src/main/java/org/keycloak/adapters/authorization/integration/elytron/ServletHttpRequest.java @@ -45,7 +45,15 @@ public class ServletHttpRequest implements HttpRequest { @Override public String getRelativePath() { - return request.getServletPath(); + String uri = request.getRequestURI(); + String contextPath = request.getContextPath(); + String servletPath = uri.substring(uri.indexOf(contextPath) + contextPath.length()); + + if ("".equals(servletPath)) { + servletPath = "/"; + } + + return servletPath; } @Override diff --git a/authz/policy-enforcer/src/main/java/org/keycloak/adapters/authorization/integration/elytron/PolicyEnforcerFilter.java b/authz/policy-enforcer/src/main/java/org/keycloak/adapters/authorization/integration/jakarta/ServletPolicyEnforcerFilter.java similarity index 62% rename from authz/policy-enforcer/src/main/java/org/keycloak/adapters/authorization/integration/elytron/PolicyEnforcerFilter.java rename to authz/policy-enforcer/src/main/java/org/keycloak/adapters/authorization/integration/jakarta/ServletPolicyEnforcerFilter.java index 9ca6164490..ee0931044b 100644 --- a/authz/policy-enforcer/src/main/java/org/keycloak/adapters/authorization/integration/elytron/PolicyEnforcerFilter.java +++ b/authz/policy-enforcer/src/main/java/org/keycloak/adapters/authorization/integration/jakarta/ServletPolicyEnforcerFilter.java @@ -1,8 +1,8 @@ -package org.keycloak.adapters.authorization.integration.elytron; +package org.keycloak.adapters.authorization.integration.jakarta; import java.io.IOException; -import java.io.InputStream; import java.util.Collections; +import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.function.Function; @@ -21,6 +21,8 @@ import org.jboss.logging.Logger; import org.keycloak.AuthorizationContext; import org.keycloak.adapters.authorization.PolicyEnforcer; import org.keycloak.adapters.authorization.TokenPrincipal; +import org.keycloak.adapters.authorization.integration.elytron.ServletHttpRequest; +import org.keycloak.adapters.authorization.integration.elytron.ServletHttpResponse; import org.keycloak.adapters.authorization.spi.ConfigurationResolver; import org.keycloak.adapters.authorization.spi.HttpRequest; import org.keycloak.representations.adapters.config.PolicyEnforcerConfig; @@ -29,7 +31,7 @@ import org.wildfly.security.http.oidc.OidcPrincipal; import org.wildfly.security.http.oidc.RefreshableOidcSecurityContext; /** - * A {@link Filter} acting as a policy enforcer. This filter does not enforce access for anonymous subjects.

+ * A Jakarta Servlet {@link Filter} acting as a policy enforcer. This filter does not enforce access for anonymous subjects.

* * For authenticated subjects, this filter delegates the access decision to the {@link PolicyEnforcer} and decide if * the request should continue.

@@ -39,13 +41,13 @@ import org.wildfly.security.http.oidc.RefreshableOidcSecurityContext; * * @author Pedro Igor */ -public class PolicyEnforcerFilter implements Filter, ServletContextAttributeListener { +public class ServletPolicyEnforcerFilter implements Filter, ServletContextAttributeListener { private final Logger logger = Logger.getLogger(getClass()); private final Map policyEnforcer; private final ConfigurationResolver configResolver; - public PolicyEnforcerFilter(ConfigurationResolver configResolver) { + public ServletPolicyEnforcerFilter(ConfigurationResolver configResolver) { this.configResolver = configResolver; this.policyEnforcer = Collections.synchronizedMap(new HashMap<>()); } @@ -58,25 +60,15 @@ public class PolicyEnforcerFilter implements Filter, ServletContextAttributeList @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; - HttpSession session = request.getSession(false); - - if (session == null) { - logger.debug("Anonymous request, continuing the filter chain"); - filterChain.doFilter(servletRequest, servletResponse); - return; - } - - RefreshableOidcSecurityContext securityContext = (RefreshableOidcSecurityContext) ((OidcPrincipal) request.getUserPrincipal()).getOidcSecurityContext(); HttpServletResponse response = (HttpServletResponse) servletResponse; - String accessToken = securityContext.getTokenString(); ServletHttpRequest httpRequest = new ServletHttpRequest(request, new TokenPrincipal() { @Override public String getRawToken() { - return accessToken; + return extractBearerToken(request); } }); - PolicyEnforcer policyEnforcer = getOrCreatePolicyEnforcer(httpRequest, securityContext); + PolicyEnforcer policyEnforcer = getOrCreatePolicyEnforcer(request, httpRequest); AuthorizationContext authzContext = policyEnforcer.enforce(httpRequest, new ServletHttpResponse(response)); request.setAttribute(AuthorizationContext.class.getName(), authzContext); @@ -89,22 +81,45 @@ public class PolicyEnforcerFilter implements Filter, ServletContextAttributeList } } - private PolicyEnforcer getOrCreatePolicyEnforcer(HttpRequest request, RefreshableOidcSecurityContext securityContext) { + protected String extractBearerToken(HttpServletRequest request) { + Enumeration authorizationHeaderValues = request.getHeaders("Authorization"); + + while (authorizationHeaderValues.hasMoreElements()) { + String value = authorizationHeaderValues.nextElement(); + String[] parts = value.trim().split("\\s+"); + + if (parts.length != 2) { + continue; + } + + String bearer = parts[0]; + + if (bearer.equalsIgnoreCase("Bearer")) { + return parts[1]; + } + } + + return null; + } + + private PolicyEnforcer getOrCreatePolicyEnforcer(HttpServletRequest servletRequest, HttpRequest request) { return policyEnforcer.computeIfAbsent(configResolver.resolve(request), new Function() { @Override public PolicyEnforcer apply(PolicyEnforcerConfig enforcerConfig) { - OidcClientConfiguration configuration = securityContext.getOidcClientConfiguration(); - String authServerUrl = configuration.getAuthServerBaseUrl(); - - return PolicyEnforcer.builder() - .authServerUrl(authServerUrl) - .realm(configuration.getRealm()) - .clientId(configuration.getClientId()) - .credentials(configuration.getResourceCredentials()) - .bearerOnly(false) - .enforcerConfig(enforcerConfig) - .httpClient(configuration.getClient()).build(); + return createPolicyEnforcer(servletRequest, enforcerConfig); } }); } + + protected PolicyEnforcer createPolicyEnforcer(HttpServletRequest servletRequest, PolicyEnforcerConfig enforcerConfig) { + String authServerUrl = enforcerConfig.getAuthServerUrl(); + + return PolicyEnforcer.builder() + .authServerUrl(authServerUrl) + .realm(enforcerConfig.getRealm()) + .clientId(enforcerConfig.getResource()) + .credentials(enforcerConfig.getCredentials()) + .bearerOnly(false) + .enforcerConfig(enforcerConfig).build(); + } }