Built-in support for Jakarta Servlet
This commit is contained in:
parent
f7988a6ae3
commit
79cd47a280
4 changed files with 107 additions and 31 deletions
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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, "/*");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue