parent
f55794f8bf
commit
409e1c3581
8 changed files with 500 additions and 27 deletions
|
@ -30,35 +30,29 @@
|
||||||
<name>Keycloak Authz: Policy Enforcer</name>
|
<name>Keycloak Authz: Policy Enforcer</name>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<jakarta.servlet.version>6.0.0</jakarta.servlet.version>
|
||||||
|
<wildfly-elytron.version>2.0.0.Final</wildfly-elytron.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
|
||||||
<groupId>org.jboss.logging</groupId>
|
|
||||||
<artifactId>jboss-logging</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-authz-client</artifactId>
|
<artifactId>keycloak-authz-client</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Built-in Elytron/Jakarta Servlet integration -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
<groupId>jakarta.servlet</groupId>
|
||||||
<artifactId>jackson-core</artifactId>
|
<artifactId>jakarta.servlet-api</artifactId>
|
||||||
|
<version>${jakarta.servlet.version}</version>
|
||||||
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
<groupId>org.wildfly.security</groupId>
|
||||||
<artifactId>jackson-databind</artifactId>
|
<artifactId>wildfly-elytron-http-oidc</artifactId>
|
||||||
</dependency>
|
<version>${wildfly-elytron.version}</version>
|
||||||
<dependency>
|
<optional>true</optional>
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
|
||||||
<artifactId>jackson-annotations</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>junit</groupId>
|
|
||||||
<artifactId>junit</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.httpcomponents</groupId>
|
|
||||||
<artifactId>httpclient</artifactId>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,25 @@ public class PolicyEnforcer {
|
||||||
|
|
||||||
protected PolicyEnforcer(Builder builder) {
|
protected PolicyEnforcer(Builder builder) {
|
||||||
enforcerConfig = builder.getEnforcerConfig();
|
enforcerConfig = builder.getEnforcerConfig();
|
||||||
authzClient = AuthzClient.create(builder.authzClientConfig);
|
Configuration authzClientConfig = builder.authzClientConfig;
|
||||||
|
|
||||||
|
if (authzClientConfig.getRealm() == null) {
|
||||||
|
authzClientConfig.setRealm(enforcerConfig.getRealm());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (authzClientConfig.getAuthServerUrl() == null) {
|
||||||
|
authzClientConfig.setAuthServerUrl(enforcerConfig.getAuthServerUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (authzClientConfig.getCredentials() == null || authzClientConfig.getCredentials().isEmpty()) {
|
||||||
|
authzClientConfig.setCredentials(enforcerConfig.getCredentials());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (authzClientConfig.getResource() == null) {
|
||||||
|
authzClientConfig.setResource(enforcerConfig.getResource());
|
||||||
|
}
|
||||||
|
|
||||||
|
authzClient = AuthzClient.create(authzClientConfig);
|
||||||
httpClient = authzClient.getConfiguration().getHttpClient();
|
httpClient = authzClient.getConfiguration().getHttpClient();
|
||||||
pathMatcher = new PathConfigMatcher(builder.getEnforcerConfig(), authzClient);
|
pathMatcher = new PathConfigMatcher(builder.getEnforcerConfig(), authzClient);
|
||||||
paths = pathMatcher.getPathConfig();
|
paths = pathMatcher.getPathConfig();
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
package org.keycloak.adapters.authorization.integration.elytron;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import jakarta.servlet.Filter;
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.FilterConfig;
|
||||||
|
import jakarta.servlet.ServletContextAttributeListener;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.ServletRequest;
|
||||||
|
import jakarta.servlet.ServletResponse;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import jakarta.servlet.http.HttpSession;
|
||||||
|
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.spi.ConfigurationResolver;
|
||||||
|
import org.keycloak.adapters.authorization.spi.HttpRequest;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@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
|
||||||
|
* the request should continue.</p>
|
||||||
|
*
|
||||||
|
* If access is not granted, this filter aborts the request and relies on the {@link PolicyEnforcer} to properly
|
||||||
|
* respond to client.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
*/
|
||||||
|
public class PolicyEnforcerFilter implements Filter, ServletContextAttributeListener {
|
||||||
|
|
||||||
|
private final Logger logger = Logger.getLogger(getClass());
|
||||||
|
private final Map<PolicyEnforcerConfig, PolicyEnforcer> policyEnforcer;
|
||||||
|
private final ConfigurationResolver configResolver;
|
||||||
|
|
||||||
|
public PolicyEnforcerFilter(ConfigurationResolver configResolver) {
|
||||||
|
this.configResolver = configResolver;
|
||||||
|
this.policyEnforcer = Collections.synchronizedMap(new HashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(FilterConfig filterConfig) {
|
||||||
|
// no-init
|
||||||
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
PolicyEnforcer policyEnforcer = getOrCreatePolicyEnforcer(httpRequest, securityContext);
|
||||||
|
AuthorizationContext authzContext = policyEnforcer.enforce(httpRequest, new ServletHttpResponse(response));
|
||||||
|
|
||||||
|
request.setAttribute(AuthorizationContext.class.getName(), authzContext);
|
||||||
|
|
||||||
|
if (authzContext.isGranted()) {
|
||||||
|
logger.debug("Request authorized, continuing the filter chain");
|
||||||
|
filterChain.doFilter(servletRequest, servletResponse);
|
||||||
|
} else {
|
||||||
|
logger.debugf("Unauthorized request to path [%s], aborting the filter chain", request.getRequestURI());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PolicyEnforcer getOrCreatePolicyEnforcer(HttpRequest request, RefreshableOidcSecurityContext securityContext) {
|
||||||
|
return policyEnforcer.computeIfAbsent(configResolver.resolve(request), new Function<PolicyEnforcerConfig, PolicyEnforcer>() {
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
package org.keycloak.adapters.authorization.integration.elytron;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.ServiceLoader;
|
||||||
|
|
||||||
|
import jakarta.servlet.DispatcherType;
|
||||||
|
import jakarta.servlet.ServletContext;
|
||||||
|
import jakarta.servlet.ServletContextEvent;
|
||||||
|
import jakarta.servlet.ServletContextListener;
|
||||||
|
import jakarta.servlet.annotation.WebListener;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.adapters.authorization.spi.ConfigurationResolver;
|
||||||
|
import org.keycloak.adapters.authorization.spi.HttpRequest;
|
||||||
|
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
|
||||||
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link ServletContextListener} to programmatically configure the {@link ServletContext} in order to
|
||||||
|
* enable the policy enforcer.</p>
|
||||||
|
*
|
||||||
|
* By default, the policy enforcer configuration is loaded from a file at {@code WEB-INF/policy-enforcer.json}.</p>
|
||||||
|
*
|
||||||
|
* Applications can also dynamically resolve the configuration by implementing the {@link ConfigurationResolver} SPI. For that,
|
||||||
|
* make sure to create a {@link META-INF/services/org.keycloak.adapters.authorization.spi.ConfigurationResolver} to register
|
||||||
|
* the implementation.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
*/
|
||||||
|
@WebListener
|
||||||
|
public class PolicyEnforcerServletContextListener implements ServletContextListener {
|
||||||
|
|
||||||
|
private final Logger logger = Logger.getLogger(getClass());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void contextInitialized(ServletContextEvent sce) {
|
||||||
|
ServletContext servletContext = sce.getServletContext();
|
||||||
|
Iterator<ConfigurationResolver> configResolvers = ServiceLoader.load(ConfigurationResolver.class).iterator();
|
||||||
|
ConfigurationResolver configResolver;
|
||||||
|
|
||||||
|
if (configResolvers.hasNext()) {
|
||||||
|
configResolver = configResolvers.next();
|
||||||
|
|
||||||
|
if (configResolvers.hasNext()) {
|
||||||
|
throw new IllegalStateException("Multiple " + ConfigurationResolver.class.getName() + " implementations found");
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debugf("Configuration resolver found from classpath: %s", configResolver);
|
||||||
|
} else {
|
||||||
|
String enforcerConfigLocation = "WEB-INF/policy-enforcer.json";
|
||||||
|
InputStream config = servletContext.getResourceAsStream(enforcerConfigLocation);
|
||||||
|
|
||||||
|
if (config == null) {
|
||||||
|
logger.debugf("Could not find the policy enforcer configuration file: %s", enforcerConfigLocation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
configResolver = createDefaultConfigurationResolver(JsonSerialization.readValue(config, PolicyEnforcerConfig.class));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Failed to parse policy enforcer configuration: " + enforcerConfigLocation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("Policy enforcement filter is enabled.");
|
||||||
|
|
||||||
|
servletContext.addFilter("keycloak-policy-enforcer", new PolicyEnforcerFilter(configResolver))
|
||||||
|
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConfigurationResolver createDefaultConfigurationResolver(PolicyEnforcerConfig enforcerConfig) {
|
||||||
|
return new ConfigurationResolver() {
|
||||||
|
@Override
|
||||||
|
public PolicyEnforcerConfig resolve(HttpRequest request) {
|
||||||
|
return enforcerConfig;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.adapters.authorization.integration.elytron;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.Cookie;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.keycloak.adapters.authorization.TokenPrincipal;
|
||||||
|
import org.keycloak.adapters.authorization.spi.HttpRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
*/
|
||||||
|
public class ServletHttpRequest implements HttpRequest {
|
||||||
|
|
||||||
|
private final HttpServletRequest request;
|
||||||
|
private final TokenPrincipal tokenPrincipal;
|
||||||
|
private InputStream inputStream;
|
||||||
|
|
||||||
|
public ServletHttpRequest(HttpServletRequest request, TokenPrincipal tokenPrincipal) {
|
||||||
|
this.request = request;
|
||||||
|
this.tokenPrincipal = tokenPrincipal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRelativePath() {
|
||||||
|
return request.getServletPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMethod() {
|
||||||
|
return request.getMethod();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getURI() {
|
||||||
|
return request.getRequestURI();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getHeaders(String name) {
|
||||||
|
return Collections.list(request.getHeaders(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFirstParam(String name) {
|
||||||
|
Map<String, String[]> parameters = request.getParameterMap();
|
||||||
|
String[] values = parameters.get(name);
|
||||||
|
|
||||||
|
if (values == null || values.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return values[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCookieValue(String name) {
|
||||||
|
Cookie[] cookies = request.getCookies();
|
||||||
|
|
||||||
|
for (Cookie cookie : cookies) {
|
||||||
|
if (cookie.getName().equals(name)) {
|
||||||
|
return cookie.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRemoteAddr() {
|
||||||
|
return request.getRemoteAddr();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSecure() {
|
||||||
|
return request.isSecure();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHeader(String name) {
|
||||||
|
return request.getHeader(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream(boolean buffered) {
|
||||||
|
if (inputStream != null) {
|
||||||
|
return inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffered) {
|
||||||
|
try {
|
||||||
|
return inputStream = new BufferedInputStream(request.getInputStream());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return request.getInputStream();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TokenPrincipal getPrincipal() {
|
||||||
|
return tokenPrincipal;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.adapters.authorization.integration.elytron;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.keycloak.adapters.authorization.spi.HttpResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
*/
|
||||||
|
public class ServletHttpResponse implements HttpResponse {
|
||||||
|
|
||||||
|
private HttpServletResponse response;
|
||||||
|
|
||||||
|
public ServletHttpResponse(HttpServletResponse response) {
|
||||||
|
this.response = response;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendError(int status) {
|
||||||
|
try {
|
||||||
|
response.sendError(status);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendError(int status, String reason) {
|
||||||
|
try {
|
||||||
|
response.sendError(status, reason);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setHeader(String name, String value) {
|
||||||
|
response.setHeader(name, value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.adapters.authorization.spi;
|
||||||
|
|
||||||
|
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a {@link PolicyEnforcerConfig} based on the information from the {@link HttpRequest}.</p>
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
*/
|
||||||
|
public interface ConfigurationResolver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a {@link PolicyEnforcerConfig} based on the information from the {@link HttpRequest}.
|
||||||
|
*
|
||||||
|
* @param request the request
|
||||||
|
* @return the policy enforcer configuration for the given request
|
||||||
|
*/
|
||||||
|
PolicyEnforcerConfig resolve(HttpRequest request);
|
||||||
|
}
|
|
@ -23,12 +23,14 @@ import java.util.Map;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
@ -39,31 +41,42 @@ public class PolicyEnforcerConfig {
|
||||||
private EnforcementMode enforcementMode = EnforcementMode.ENFORCING;
|
private EnforcementMode enforcementMode = EnforcementMode.ENFORCING;
|
||||||
|
|
||||||
@JsonProperty("paths")
|
@JsonProperty("paths")
|
||||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
@JsonInclude(Include.NON_EMPTY)
|
||||||
private List<PathConfig> paths = new ArrayList<>();
|
private List<PathConfig> paths = new ArrayList<>();
|
||||||
|
|
||||||
@JsonProperty("path-cache")
|
@JsonProperty("path-cache")
|
||||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
@JsonInclude(Include.NON_EMPTY)
|
||||||
private PathCacheConfig pathCacheConfig;
|
private PathCacheConfig pathCacheConfig;
|
||||||
|
|
||||||
@JsonProperty("lazy-load-paths")
|
@JsonProperty("lazy-load-paths")
|
||||||
private Boolean lazyLoadPaths = Boolean.FALSE;
|
private Boolean lazyLoadPaths = Boolean.FALSE;
|
||||||
|
|
||||||
@JsonProperty("on-deny-redirect-to")
|
@JsonProperty("on-deny-redirect-to")
|
||||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
@JsonInclude(Include.NON_NULL)
|
||||||
private String onDenyRedirectTo;
|
private String onDenyRedirectTo;
|
||||||
|
|
||||||
@JsonProperty("user-managed-access")
|
@JsonProperty("user-managed-access")
|
||||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
@JsonInclude(Include.NON_NULL)
|
||||||
private UserManagedAccessConfig userManagedAccess;
|
private UserManagedAccessConfig userManagedAccess;
|
||||||
|
|
||||||
@JsonProperty("claim-information-point")
|
@JsonProperty("claim-information-point")
|
||||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
@JsonInclude(Include.NON_NULL)
|
||||||
private Map<String, Map<String, Object>> claimInformationPointConfig;
|
private Map<String, Map<String, Object>> claimInformationPointConfig;
|
||||||
|
|
||||||
@JsonProperty("http-method-as-scope")
|
@JsonProperty("http-method-as-scope")
|
||||||
private Boolean httpMethodAsScope;
|
private Boolean httpMethodAsScope;
|
||||||
|
|
||||||
|
private String realm;
|
||||||
|
|
||||||
|
@JsonProperty("auth-server-url")
|
||||||
|
private String authServerUrl;
|
||||||
|
|
||||||
|
@JsonProperty("credentials")
|
||||||
|
protected Map<String, Object> credentials = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||||
|
|
||||||
|
@JsonProperty("resource")
|
||||||
|
private String resource;
|
||||||
|
|
||||||
public List<PathConfig> getPaths() {
|
public List<PathConfig> getPaths() {
|
||||||
return this.paths;
|
return this.paths;
|
||||||
}
|
}
|
||||||
|
@ -128,6 +141,38 @@ public class PolicyEnforcerConfig {
|
||||||
this.httpMethodAsScope = httpMethodAsScope;
|
this.httpMethodAsScope = httpMethodAsScope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getRealm() {
|
||||||
|
return realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRealm(String realm) {
|
||||||
|
this.realm = realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAuthServerUrl() {
|
||||||
|
return authServerUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAuthServerUrl(String authServerUrl) {
|
||||||
|
this.authServerUrl = authServerUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> getCredentials() {
|
||||||
|
return credentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCredentials(Map<String, Object> credentials) {
|
||||||
|
this.credentials = credentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getResource() {
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResource(String resource) {
|
||||||
|
this.resource = resource;
|
||||||
|
}
|
||||||
|
|
||||||
public static class PathConfig {
|
public static class PathConfig {
|
||||||
|
|
||||||
public static Set<PathConfig> createPathConfigs(ResourceRepresentation resourceDescription) {
|
public static Set<PathConfig> createPathConfigs(ResourceRepresentation resourceDescription) {
|
||||||
|
|
Loading…
Reference in a new issue