Built-in support for Jakarta Servlet

This commit is contained in:
Pedro Igor 2023-04-27 12:41:19 -03:00 committed by Marek Posolda
parent f7988a6ae3
commit 79cd47a280
4 changed files with 107 additions and 31 deletions

View file

@ -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();
}
}

View file

@ -66,7 +66,7 @@ public class PolicyEnforcerServletContextListener implements ServletContextListe
logger.debug("Policy enforcement filter is enabled."); 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, "/*"); .addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
} }

View file

@ -45,7 +45,15 @@ public class ServletHttpRequest implements HttpRequest {
@Override @Override
public String getRelativePath() { 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 @Override

View file

@ -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.IOException;
import java.io.InputStream;
import java.util.Collections; import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
@ -21,6 +21,8 @@ import org.jboss.logging.Logger;
import org.keycloak.AuthorizationContext; import org.keycloak.AuthorizationContext;
import org.keycloak.adapters.authorization.PolicyEnforcer; import org.keycloak.adapters.authorization.PolicyEnforcer;
import org.keycloak.adapters.authorization.TokenPrincipal; 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.ConfigurationResolver;
import org.keycloak.adapters.authorization.spi.HttpRequest; import org.keycloak.adapters.authorization.spi.HttpRequest;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig; 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; import org.wildfly.security.http.oidc.RefreshableOidcSecurityContext;
/** /**
* A {@link Filter} acting as a policy enforcer. This filter does not enforce access for anonymous subjects.</p> * A Jakarta Servlet {@link Filter} acting as a policy enforcer. This filter does not enforce access for anonymous subjects.</p>
* *
* For authenticated subjects, this filter delegates the access decision to the {@link PolicyEnforcer} and decide if * For authenticated subjects, this filter delegates the access decision to the {@link PolicyEnforcer} and decide if
* the request should continue.</p> * the request should continue.</p>
@ -39,13 +41,13 @@ import org.wildfly.security.http.oidc.RefreshableOidcSecurityContext;
* *
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a> * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/ */
public class PolicyEnforcerFilter implements Filter, ServletContextAttributeListener { public class ServletPolicyEnforcerFilter implements Filter, ServletContextAttributeListener {
private final Logger logger = Logger.getLogger(getClass()); private final Logger logger = Logger.getLogger(getClass());
private final Map<PolicyEnforcerConfig, PolicyEnforcer> policyEnforcer; private final Map<PolicyEnforcerConfig, PolicyEnforcer> policyEnforcer;
private final ConfigurationResolver configResolver; private final ConfigurationResolver configResolver;
public PolicyEnforcerFilter(ConfigurationResolver configResolver) { public ServletPolicyEnforcerFilter(ConfigurationResolver configResolver) {
this.configResolver = configResolver; this.configResolver = configResolver;
this.policyEnforcer = Collections.synchronizedMap(new HashMap<>()); this.policyEnforcer = Collections.synchronizedMap(new HashMap<>());
} }
@ -58,25 +60,15 @@ public class PolicyEnforcerFilter implements Filter, ServletContextAttributeList
@Override @Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest; 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; HttpServletResponse response = (HttpServletResponse) servletResponse;
String accessToken = securityContext.getTokenString();
ServletHttpRequest httpRequest = new ServletHttpRequest(request, new TokenPrincipal() { ServletHttpRequest httpRequest = new ServletHttpRequest(request, new TokenPrincipal() {
@Override @Override
public String getRawToken() { 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)); AuthorizationContext authzContext = policyEnforcer.enforce(httpRequest, new ServletHttpResponse(response));
request.setAttribute(AuthorizationContext.class.getName(), authzContext); 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<String> 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<PolicyEnforcerConfig, PolicyEnforcer>() { return policyEnforcer.computeIfAbsent(configResolver.resolve(request), new Function<PolicyEnforcerConfig, PolicyEnforcer>() {
@Override @Override
public PolicyEnforcer apply(PolicyEnforcerConfig enforcerConfig) { public PolicyEnforcer apply(PolicyEnforcerConfig enforcerConfig) {
OidcClientConfiguration configuration = securityContext.getOidcClientConfiguration(); return createPolicyEnforcer(servletRequest, enforcerConfig);
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();
} }
}); });
} }
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();
}
} }