Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Bill Burke 2017-04-24 10:05:46 -04:00
commit 12cb295a35
227 changed files with 10674 additions and 3378 deletions

View file

@ -28,7 +28,6 @@ import org.keycloak.AuthorizationContext;
import org.keycloak.KeycloakSecurityContext;
import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.spi.HttpFacade.Request;
import org.keycloak.adapters.spi.HttpFacade.Response;
import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
@ -71,7 +70,6 @@ public abstract class AbstractPolicyEnforcer {
if (accessToken != null) {
Request request = httpFacade.getRequest();
Response response = httpFacade.getResponse();
String path = getPath(request);
PathConfig pathConfig = this.pathMatcher.matches(path, this.paths);
@ -83,7 +81,12 @@ public abstract class AbstractPolicyEnforcer {
}
LOGGER.debugf("Could not find a configuration for path [%s]", path);
response.sendError(403, "Could not find a configuration for path [" + path + "].");
if (isDefaultAccessDeniedUri(request, enforcerConfig)) {
return createAuthorizationContext(accessToken);
}
handleAccessDenied(httpFacade);
return createEmptyAuthorizationContext(false);
}
@ -102,9 +105,11 @@ public abstract class AbstractPolicyEnforcer {
}
}
LOGGER.debugf("Sending challenge to the client. Path [%s]", pathConfig);
if (!challenge(pathConfig, requiredScopes, httpFacade)) {
LOGGER.debugf("Sending challenge to the client. Path [%s]", pathConfig);
response.sendError(403, "Authorization failed.");
LOGGER.debugf("Challenge not sent, sending default forbidden response. Path [%s]", pathConfig);
handleAccessDenied(httpFacade);
}
}
}
@ -165,6 +170,10 @@ public abstract class AbstractPolicyEnforcer {
return false;
}
protected void handleAccessDenied(OIDCHttpFacade httpFacade) {
httpFacade.getResponse().sendError(403);
}
private boolean isDefaultAccessDeniedUri(Request request, PolicyEnforcerConfig enforcerConfig) {
String accessDeniedPath = enforcerConfig.getOnDenyRedirectTo();

View file

@ -56,6 +56,9 @@ public class BearerTokenPolicyEnforcer extends AbstractPolicyEnforcer {
String authorizationServerUri = authzClient.getServerConfiguration().getIssuer().toString() + "/authz/entitlement";
response.setStatus(401);
response.setHeader("WWW-Authenticate", "KC_ETT realm=\"" + clientId + "\",as_uri=\"" + authorizationServerUri + "\"");
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Sending Entitlement challenge");
}
}
private void challengeUmaAuthentication(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade) {
@ -66,6 +69,9 @@ public class BearerTokenPolicyEnforcer extends AbstractPolicyEnforcer {
String authorizationServerUri = authzClient.getServerConfiguration().getIssuer().toString() + "/authz/authorize";
response.setStatus(401);
response.setHeader("WWW-Authenticate", "UMA realm=\"" + clientId + "\",as_uri=\"" + authorizationServerUri + "\",ticket=\"" + ticket + "\"");
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Sending UMA challenge");
}
}
private String getPermissionTicket(PathConfig pathConfig, Set<String> requiredScopes, AuthzClient authzClient) {

View file

@ -83,6 +83,12 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
@Override
protected boolean challenge(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade facade) {
handleAccessDenied(facade);
return true;
}
@Override
protected void handleAccessDenied(OIDCHttpFacade facade) {
String accessDeniedPath = getEnforcerConfig().getOnDenyRedirectTo();
HttpFacade.Response response = facade.getResponse();
@ -92,8 +98,6 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
} else {
response.sendError(403);
}
return true;
}
private AccessToken requestAuthorizationToken(PathConfig pathConfig, Set<String> requiredScopes, OIDCHttpFacade httpFacade) {

View file

@ -49,7 +49,6 @@ class PathMatcher {
PathConfig matchingAnyPath = null;
PathConfig matchingAnySuffixPath = null;
PathConfig matchingPath = null;
for (PathConfig entry : paths.values()) {
String expectedUri = entry.getPath();
@ -132,58 +131,79 @@ class PathMatcher {
return targetUri.startsWith(expectedUri.substring(0, expectedUri.length() - 2));
}
String suffix = "/*.";
int suffixIndex = expectedUri.indexOf(suffix);
if (suffixIndex != -1) {
return targetUri.endsWith(expectedUri.substring(suffixIndex + suffix.length() - 1));
}
return false;
}
public String buildUriFromTemplate(String expectedUri, String targetUri) {
int patternStartIndex = expectedUri.indexOf("{");
if (patternStartIndex >= targetUri.length()) {
if (patternStartIndex == -1 || patternStartIndex >= targetUri.length()) {
return null;
}
if (expectedUri.split("/").length > targetUri.split("/").length) {
return null;
}
char[] expectedUriChars = expectedUri.toCharArray();
char[] matchingUri = Arrays.copyOfRange(expectedUriChars, 0, patternStartIndex);
int matchingUriLastIndex = matchingUri.length;
String targetUriParams = targetUri.substring(patternStartIndex);
if (Arrays.equals(matchingUri, Arrays.copyOf(targetUri.toCharArray(), matchingUri.length))) {
int matchingLastIndex = matchingUri.length;
matchingUri = Arrays.copyOf(matchingUri, targetUri.length()); // +1 so we can add a slash at the end
int targetPatternStartIndex = patternStartIndex;
matchingUri = Arrays.copyOf(matchingUri, targetUri.length());
int paramIndex = 0;
while (patternStartIndex != -1) {
int parameterStartIndex = -1;
for (int i = targetPatternStartIndex; i < targetUri.length(); i++) {
char c = targetUri.charAt(i);
if (c != '/') {
if (parameterStartIndex == -1) {
parameterStartIndex = matchingLastIndex;
}
matchingUri[matchingLastIndex] = c;
matchingLastIndex++;
}
if (c == '/' || ((i + 1 == targetUri.length()))) {
if (matchingUri[matchingLastIndex - 1] != '/' && matchingLastIndex < matchingUri.length) {
matchingUri[matchingLastIndex] = '/';
matchingLastIndex++;
}
targetPatternStartIndex = targetUri.indexOf('/', i) + 1;
break;
}
}
if ((patternStartIndex = expectedUri.indexOf('{', patternStartIndex + 1)) == -1) {
for (int i = patternStartIndex; i < expectedUriChars.length; i++) {
if (matchingUriLastIndex >= matchingUri.length) {
break;
}
if ((targetPatternStartIndex == 0 || targetPatternStartIndex == targetUri.length()) && parameterStartIndex != -1) {
return null;
char c = expectedUriChars[i];
if (c == '{' || c == '*') {
String[] params = targetUriParams.split("/");
for (int k = paramIndex; k <= (c == '*' ? params.length : paramIndex); k++) {
if (k == params.length) {
break;
}
int paramLength = params[k].length();
if (matchingUriLastIndex + paramLength > matchingUri.length) {
return null;
}
for (int j = 0; j < paramLength; j++) {
matchingUri[matchingUriLastIndex++] = params[k].charAt(j);
}
if (c == '*' && matchingUriLastIndex < matchingUri.length) {
matchingUri[matchingUriLastIndex++] = '/';
}
}
i = expectedUri.indexOf('}', i);
} else {
if (c == '/') {
paramIndex++;
}
matchingUri[matchingUriLastIndex++] = c;
}
}
if (matchingUri[matchingUri.length - 1] == '\u0000') {
return null;
}
return String.valueOf(matchingUri);
}

View file

@ -220,7 +220,14 @@ public class PolicyEnforcer {
pathConfig.setId(resourceDescription.getId());
pathConfig.setName(resourceDescription.getName());
pathConfig.setPath(resourceDescription.getUri());
String uri = resourceDescription.getUri();
if (uri == null || "".equals(uri.trim())) {
throw new RuntimeException("Failed to configure paths. Resource [" + resourceDescription.getName() + "] has an invalid or empty URI [" + uri + "].");
}
pathConfig.setPath(uri);
List<String> scopeNames = new ArrayList<>();

View file

@ -157,7 +157,7 @@
processCallback(callback, initPromise);
return;
} else if (initOptions) {
if (initOptions.refreshToken) {
if (initOptions.token && initOptions.refreshToken) {
setToken(initOptions.token, initOptions.refreshToken, initOptions.idToken);
if (loginIframe.enable) {
@ -832,7 +832,7 @@
document.body.appendChild(iframe);
var messageCallback = function(event) {
if ((event.origin !== loginIframe.iframeOrigin) || (loginIframe.iframe.contentWindow !== event.source)) {
if ((event.origin !== loginIframe.iframeOrigin) || (loginIframe.iframe.contentWindow !== event.source)) {
return;
}
@ -1228,7 +1228,7 @@
break;
default:
if (responseMode != 'query' || !handleQueryParam(param, queryParams[param], oauth)) {
oauth.newUrl += (oauth.newUrl.indexOf('?') == -1 ? '?' : '&') + param + '=' + queryParams[param];
oauth.newUrl += (oauth.newUrl.indexOf('?') == -1 ? '?' : '&') + param + '=' + encodeURIComponent(queryParams[param]);
}
break;
}

View file

@ -1,49 +0,0 @@
<!--
~ Copyright 2016 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.
-->
<script>
function getCookie(cname)
{
var name = cname + "=";
var ca = document.cookie.split(';');
for(var i=0; i<ca.length; i++)
{
var c = ca[i].trim();
if (c.indexOf(name)==0) return c.substring(name.length,c.length);
}
return null;
}
function receiveMessage(event)
{
if (event.origin !== "ORIGIN") {
console.log(event.origin + " does not match built origin");
return;
}
var data = JSON.parse(event.data);
data.loggedIn = false;
var cookie = getCookie('KEYCLOAK_SESSION');
if (cookie) {
data.loggedIn = true;
data.session = cookie;
}
event.source.postMessage(JSON.stringify(data),
event.origin);
}
window.addEventListener("message", receiveMessage, false);
</script>

View file

@ -56,7 +56,7 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-jetty92-adapter</artifactId>
<artifactId>keycloak-jetty93-adapter</artifactId>
<scope>provided</scope>
</dependency>

View file

@ -141,10 +141,10 @@ public class KeycloakSpringBootConfiguration {
List<io.undertow.servlet.api.SecurityConstraint> undertowSecurityConstraints = new ArrayList<io.undertow.servlet.api.SecurityConstraint>();
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,42 +175,70 @@ 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<ConstraintMapping> jettyConstraintMappings = new ArrayList<ConstraintMapping>();
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);
}
}
WebAppContext webAppContext = server.getBean(WebAppContext.class);
//if not found as registered bean let's try the handler
if(webAppContext==null){
webAppContext = (WebAppContext) server.getHandler();
}
ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
securityHandler.setConstraintMappings(jettyConstraintMappings);
@ -235,12 +264,10 @@ public class KeycloakSpringBootConfiguration {
Set<String> authRoles = new HashSet<String>();
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 +275,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 +289,6 @@ public class KeycloakSpringBootConfiguration {
tomcatSecCollection.setDescription(collection.getDescription());
}
for (String authRole : collection.getAuthRoles()) {
tomcatConstraint.addAuthRole(authRole);
}
for (String pattern : collection.getPatterns()) {
tomcatSecCollection.addPattern(pattern);
}

View file

@ -43,12 +43,20 @@ public class KeycloakSpringBootProperties extends AdapterConfig {
*/
private List<SecurityConstraint> securityConstraints = new ArrayList<SecurityConstraint>();
/**
* This matches security-constraint of the servlet spec
*/
@ConfigurationProperties()
public static class SecurityConstraint {
/**
* A list of security collections
*/
private List<SecurityCollection> securityCollections = new ArrayList<SecurityCollection>();
private List<String> authRoles = new ArrayList<String>();
public List<String> getAuthRoles() {
return authRoles;
}
public List<SecurityCollection> getSecurityCollections() {
return securityCollections;
@ -57,7 +65,16 @@ public class KeycloakSpringBootProperties extends AdapterConfig {
public void setSecurityCollections(List<SecurityCollection> securityCollections) {
this.securityCollections = securityCollections;
}
public void setAuthRoles(List<String> 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<String> authRoles = new ArrayList<String>();
/**
* A list of URL patterns that should match to apply the security collection
*/
@ -85,10 +98,6 @@ public class KeycloakSpringBootProperties extends AdapterConfig {
*/
private List<String> omittedMethods = new ArrayList<String>();
public List<String> getAuthRoles() {
return authRoles;
}
public List<String> getPatterns() {
return patterns;
}
@ -117,10 +126,6 @@ public class KeycloakSpringBootProperties extends AdapterConfig {
this.description = description;
}
public void setAuthRoles(List<String> authRoles) {
this.authRoles = authRoles;
}
public void setPatterns(List<String> patterns) {
this.patterns = patterns;
}

View file

@ -167,7 +167,12 @@ public class SharedAttributeDefinitons {
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
protected static final SimpleAttributeDefinition AUTODETECT_BEARER_ONLY =
new SimpleAttributeDefinitionBuilder("autodetect-bearer-only", ModelType.BOOLEAN, true)
.setXmlName("autodetect-bearer-only")
.setAllowExpression(true)
.setDefaultValue(new ModelNode(false))
.build();
protected static final List<SimpleAttributeDefinition> ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
static {
@ -193,6 +198,7 @@ public class SharedAttributeDefinitons {
ATTRIBUTES.add(REGISTER_NODE_PERIOD);
ATTRIBUTES.add(TOKEN_STORE);
ATTRIBUTES.add(PRINCIPAL_ATTRIBUTE);
ATTRIBUTES.add(AUTODETECT_BEARER_ONLY);
}
/**

View file

@ -46,7 +46,7 @@ keycloak.realm.register-node-at-startup=Cluster setting
keycloak.realm.register-node-period=how often to re-register node
keycloak.realm.token-store=cookie or session storage for auth session data
keycloak.realm.principal-attribute=token attribute to use to set Principal name
keycloak.realm.autodetect-bearer-only=autodetect bearer-only requests
keycloak.secure-deployment=A deployment secured by Keycloak
keycloak.secure-deployment.add=Add a deployment to be secured by Keycloak
@ -83,6 +83,7 @@ keycloak.secure-deployment.principal-attribute=token attribute to use to set Pri
keycloak.secure-deployment.turn-off-change-session-id-on-login=The session id is changed by default on a successful login. Change this to true if you want to turn this off
keycloak.secure-deployment.token-minimum-time-to-live=The adapter will refresh the token if the current token is expired OR will expire in 'token-minimum-time-to-live' seconds or less
keycloak.secure-deployment.min-time-between-jwks-requests=If adapter recognize token signed by unknown public key, it will try to download new public key from keycloak server. However it won't try to download if already tried it in less than 'min-time-between-jwks-requests' seconds
keycloak.secure-deployment.autodetect-bearer-only=autodetect bearer-only requests
keycloak.secure-deployment.credential=Credential value

View file

@ -65,6 +65,7 @@
<xs:element name="register-node-period" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:element name="token-store" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="principal-attribute" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="autodetect-bearer-only" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
</xs:all>
<xs:attribute name="name" type="xs:string" use="required">
<xs:annotation>
@ -107,6 +108,7 @@
<xs:element name="turn-off-change-session-id-on-login" type="xs:boolean" minOccurs="0" maxOccurs="1" />
<xs:element name="token-minimum-time-to-live" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:element name="min-time-between-jwks-requests" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:element name="autodetect-bearer-only" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
</xs:all>
<xs:attribute name="name" type="xs:string" use="required">
<xs:annotation>

View file

@ -166,6 +166,12 @@ public class SharedAttributeDefinitons {
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
protected static final SimpleAttributeDefinition AUTODETECT_BEARER_ONLY =
new SimpleAttributeDefinitionBuilder("autodetect-bearer-only", ModelType.BOOLEAN, true)
.setXmlName("autodetect-bearer-only")
.setAllowExpression(true)
.setDefaultValue(new ModelNode(false))
.build();
@ -193,6 +199,7 @@ public class SharedAttributeDefinitons {
ATTRIBUTES.add(REGISTER_NODE_PERIOD);
ATTRIBUTES.add(TOKEN_STORE);
ATTRIBUTES.add(PRINCIPAL_ATTRIBUTE);
ATTRIBUTES.add(AUTODETECT_BEARER_ONLY);
}
private static boolean isSet(ModelNode attributes, SimpleAttributeDefinition def) {

View file

@ -46,7 +46,7 @@ keycloak.realm.register-node-at-startup=Cluster setting
keycloak.realm.register-node-period=how often to re-register node
keycloak.realm.token-store=cookie or session storage for auth session data
keycloak.realm.principal-attribute=token attribute to use to set Principal name
keycloak.realm.autodetect-bearer-only=autodetect bearer-only requests
keycloak.secure-deployment=A deployment secured by Keycloak
keycloak.secure-deployment.add=Add a deployment to be secured by Keycloak
@ -67,6 +67,7 @@ keycloak.secure-deployment.bearer-only=Bearer Token Auth only
keycloak.secure-deployment.enable-basic-auth=Enable Basic Authentication
keycloak.secure-deployment.public-client=Public client
keycloak.secure-deployment.enable-cors=Enable Keycloak CORS support
keycloak.secure-deployment.autodetect-bearer-only=autodetect bearer-only requests
keycloak.secure-deployment.client-keystore=n/a
keycloak.secure-deployment.client-keystore-password=n/a
keycloak.secure-deployment.client-key-password=n/a

View file

@ -65,6 +65,7 @@
<xs:element name="register-node-period" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:element name="token-store" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="principal-attribute" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="autodetect-bearer-only" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
</xs:all>
<xs:attribute name="name" type="xs:string" use="required">
<xs:annotation>
@ -107,6 +108,7 @@
<xs:element name="turn-off-change-session-id-on-login" type="xs:boolean" minOccurs="0" maxOccurs="1" />
<xs:element name="token-minimum-time-to-live" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:element name="min-time-between-jwks-requests" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:element name="autodetect-bearer-only" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
</xs:all>
<xs:attribute name="name" type="xs:string" use="required">
<xs:annotation>

View file

@ -1,69 +0,0 @@
/*
* Copyright 2016 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.authorization.policy.provider.aggregated;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import java.util.ArrayList;
import java.util.List;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class AggregatePolicyAdminResource implements PolicyProviderAdminService {
private final ResourceServer resourceServer;
public AggregatePolicyAdminResource(ResourceServer resourceServer) {
this.resourceServer = resourceServer;
}
@Override
public void onCreate(Policy policy) {
verifyCircularReference(policy, new ArrayList<>());
}
@Override
public void onUpdate(Policy policy) {
verifyCircularReference(policy, new ArrayList<>());
}
private void verifyCircularReference(Policy policy, List<String> ids) {
if (!policy.getType().equals("aggregate")) {
return;
}
if (ids.contains(policy.getId())) {
throw new RuntimeException("Circular reference found [" + policy.getName() + "].");
}
ids.add(policy.getId());
for (Policy associated : policy.getAssociatedPolicies()) {
verifyCircularReference(associated, ids);
}
}
@Override
public void onRemove(Policy policy) {
}
}

View file

@ -17,19 +17,24 @@
*/
package org.keycloak.authorization.policy.provider.aggregated;
import java.util.ArrayList;
import java.util.List;
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class AggregatePolicyProviderFactory implements PolicyProviderFactory {
public class AggregatePolicyProviderFactory implements PolicyProviderFactory<PolicyRepresentation> {
private AggregatePolicyProvider provider = new AggregatePolicyProvider();
@ -49,8 +54,8 @@ public class AggregatePolicyProviderFactory implements PolicyProviderFactory {
}
@Override
public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
return new AggregatePolicyAdminResource(resourceServer);
public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) {
return null;
}
@Override
@ -58,6 +63,37 @@ public class AggregatePolicyProviderFactory implements PolicyProviderFactory {
return null;
}
@Override
public void onCreate(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
verifyCircularReference(policy, new ArrayList<>());
}
@Override
public void onUpdate(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
verifyCircularReference(policy, new ArrayList<>());
}
private void verifyCircularReference(Policy policy, List<String> ids) {
if (!policy.getType().equals("aggregate")) {
return;
}
if (ids.contains(policy.getId())) {
throw new RuntimeException("Circular reference found [" + policy.getName() + "].");
}
ids.add(policy.getId());
for (Policy associated : policy.getAssociatedPolicies()) {
verifyCircularReference(associated, ids);
}
}
@Override
public void onRemove(Policy policy, AuthorizationProvider authorization) {
}
@Override
public void init(Config.Scope config) {

View file

@ -40,7 +40,7 @@ public class ClientPolicyProviderFactory implements PolicyProviderFactory {
}
@Override
public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) {
return null;
}
@ -78,9 +78,6 @@ public class ClientPolicyProviderFactory implements PolicyProviderFactory {
try {
if (clients.isEmpty()) {
policyStore.findDependentPolicies(policy.getId(), resourceServer.getId()).forEach(dependentPolicy -> {
dependentPolicy.removeAssociatedPolicy(policy);
});
policyStore.delete(policy.getId());
} else {
policy.getConfig().put("clients", JsonSerialization.writeValueAsString(clients));

View file

@ -42,7 +42,7 @@ public class JSPolicyProviderFactory implements PolicyProviderFactory {
}
@Override
public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) {
return null;
}

View file

@ -1,18 +1,20 @@
package org.keycloak.authorization.policy.provider.resource;
import java.util.Map;
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class ResourcePolicyProviderFactory implements PolicyProviderFactory {
public class ResourcePolicyProviderFactory implements PolicyProviderFactory<ResourcePermissionRepresentation> {
private ResourcePolicyProvider provider = new ResourcePolicyProvider();
@ -32,8 +34,14 @@ public class ResourcePolicyProviderFactory implements PolicyProviderFactory {
}
@Override
public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
return null;
public Class<ResourcePermissionRepresentation> getRepresentationType() {
return ResourcePermissionRepresentation.class;
}
@Override
public ResourcePermissionRepresentation toRepresentation(Policy policy, ResourcePermissionRepresentation representation) {
representation.setResourceType(policy.getConfig().get("defaultResourceType"));
return representation;
}
@Override
@ -41,6 +49,39 @@ public class ResourcePolicyProviderFactory implements PolicyProviderFactory {
return null;
}
@Override
public void onCreate(Policy policy, ResourcePermissionRepresentation representation, AuthorizationProvider authorization) {
updateResourceType(policy, representation);
}
@Override
public void onUpdate(Policy policy, ResourcePermissionRepresentation representation, AuthorizationProvider authorization) {
updateResourceType(policy, representation);
}
private void updateResourceType(Policy policy, ResourcePermissionRepresentation representation) {
if (representation != null) {
//TODO: remove this check once we migrate to new API
if (ResourcePermissionRepresentation.class.equals(representation.getClass())) {
ResourcePermissionRepresentation resourcePermission = ResourcePermissionRepresentation.class.cast(representation);
Map<String, String> config = policy.getConfig();
config.compute("defaultResourceType", (key, value) -> {
String resourceType = resourcePermission.getResourceType();
return resourceType != null ? resourcePermission.getResourceType() : null;
});
policy.setConfig(config);
}
}
}
@Override
public void onRemove(Policy policy, AuthorizationProvider authorization) {
}
@Override
public void init(Config.Scope config) {

View file

@ -35,19 +35,23 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleContainerModel.RoleRemovedEvent;
import org.keycloak.models.RoleModel;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
import org.keycloak.util.JsonSerialization;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.Set;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class RolePolicyProviderFactory implements PolicyProviderFactory {
public class RolePolicyProviderFactory implements PolicyProviderFactory<RolePolicyRepresentation> {
private RolePolicyProvider provider = new RolePolicyProvider();
@ -67,7 +71,7 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory {
}
@Override
public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) {
return null;
}
@ -76,6 +80,107 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory {
return new RolePolicyProvider();
}
@Override
public RolePolicyRepresentation toRepresentation(Policy policy, RolePolicyRepresentation representation) {
try {
representation.setRoles(JsonSerialization.readValue(policy.getConfig().get("roles"), Set.class));
} catch (IOException cause) {
throw new RuntimeException("Failed to deserialize roles", cause);
}
return representation;
}
@Override
public Class<RolePolicyRepresentation> getRepresentationType() {
return RolePolicyRepresentation.class;
}
@Override
public void onCreate(Policy policy, RolePolicyRepresentation representation, AuthorizationProvider authorization) {
updateRoles(policy, representation, authorization);
}
@Override
public void onUpdate(Policy policy, RolePolicyRepresentation representation, AuthorizationProvider authorization) {
updateRoles(policy, representation, authorization);
}
@Override
public void onImport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
try {
updateRoles(policy, authorization, new HashSet<>(Arrays.asList(JsonSerialization.readValue(representation.getConfig().get("roles"), RolePolicyRepresentation.RoleDefinition[].class))));
} catch (IOException cause) {
throw new RuntimeException("Failed to deserialize roles during import", cause);
}
}
private void updateRoles(Policy policy, RolePolicyRepresentation representation, AuthorizationProvider authorization) {
updateRoles(policy, authorization, representation.getRoles());
}
private void updateRoles(Policy policy, AuthorizationProvider authorization, Set<RolePolicyRepresentation.RoleDefinition> roles) {
try {
RealmModel realm = authorization.getRealm();
Set<RolePolicyRepresentation.RoleDefinition> updatedRoles = new HashSet<>();
if (roles != null) {
for (RolePolicyRepresentation.RoleDefinition definition : roles) {
String roleName = definition.getId();
String clientId = null;
int clientIdSeparator = roleName.indexOf("/");
if (clientIdSeparator != -1) {
clientId = roleName.substring(0, clientIdSeparator);
roleName = roleName.substring(clientIdSeparator + 1);
}
RoleModel role;
if (clientId == null) {
role = realm.getRole(roleName);
if (role == null) {
role = realm.getRoleById(roleName);
}
} else {
ClientModel client = realm.getClientByClientId(clientId);
if (client == null) {
throw new RuntimeException("Client with id [" + clientId + "] not found.");
}
role = client.getRole(roleName);
}
// fallback to find any client role with the given name
if (role == null) {
String finalRoleName = roleName;
role = realm.getClients().stream().map(clientModel -> clientModel.getRole(finalRoleName)).filter(roleModel -> roleModel != null)
.findFirst().orElse(null);
}
if (role == null) {
throw new RuntimeException("Error while importing configuration. Role [" + roleName + "] could not be found.");
}
definition.setId(role.getId());
updatedRoles.add(definition);
}
try {
} catch (Exception e) {
throw new RuntimeException("Error while updating policy [" + policy.getName() + "].", e);
}
}
Map<String, String> config = policy.getConfig();
config.put("roles", JsonSerialization.writeValueAsString(updatedRoles));
policy.setConfig(config);
} catch (IOException cause) {
throw new RuntimeException("Failed to deserialize roles", cause);
}
}
@Override
public void init(Config.Scope config) {
@ -125,12 +230,6 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory {
try {
if (roles.isEmpty()) {
policyStore.findDependentPolicies(policy.getId(), resourceServer.getId()).forEach(dependentPolicy -> {
dependentPolicy.removeAssociatedPolicy(policy);
if (dependentPolicy.getAssociatedPolicies().isEmpty()) {
policyStore.delete(dependentPolicy.getId());
}
});
policyStore.delete(policy.getId());
} else {
Map<String, String> config = policy.getConfig();

View file

@ -2,17 +2,17 @@ package org.keycloak.authorization.policy.provider.scope;
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class ScopePolicyProviderFactory implements PolicyProviderFactory {
public class ScopePolicyProviderFactory implements PolicyProviderFactory<ScopePermissionRepresentation> {
private ScopePolicyProvider provider = new ScopePolicyProvider();
@ -32,13 +32,18 @@ public class ScopePolicyProviderFactory implements PolicyProviderFactory {
}
@Override
public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
public PolicyProvider create(KeycloakSession session) {
return null;
}
@Override
public PolicyProvider create(KeycloakSession session) {
return null;
public Class<ScopePermissionRepresentation> getRepresentationType() {
return ScopePermissionRepresentation.class;
}
@Override
public ScopePermissionRepresentation toRepresentation(Policy policy, ScopePermissionRepresentation representation) {
return representation;
}
@Override

View file

@ -18,45 +18,11 @@
package org.keycloak.authorization.policy.provider.time;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import java.text.SimpleDateFormat;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class TimePolicyAdminResource implements PolicyProviderAdminService {
@Override
public void onCreate(Policy policy) {
validateConfig(policy);
}
private void validateConfig(Policy policy) {
String nbf = policy.getConfig().get("nbf");
String noa = policy.getConfig().get("noa");
if (nbf != null && noa != null) {
validateFormat(nbf);
validateFormat(noa);
}
}
@Override
public void onUpdate(Policy policy) {
validateConfig(policy);
}
@Override
public void onRemove(Policy policy) {
}
private void validateFormat(String date) {
try {
new SimpleDateFormat(TimePolicyProvider.DEFAULT_DATE_PATTERN).parse(TimePolicyProvider.format(date));
} catch (Exception e) {
throw new RuntimeException("Could not parse a date using format [" + date + "]");
}
}
}

View file

@ -1,18 +1,22 @@
package org.keycloak.authorization.policy.provider.time;
import java.text.SimpleDateFormat;
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class TimePolicyProviderFactory implements PolicyProviderFactory {
public class TimePolicyProviderFactory implements PolicyProviderFactory<PolicyRepresentation> {
private TimePolicyProvider provider = new TimePolicyProvider();
@ -32,7 +36,7 @@ public class TimePolicyProviderFactory implements PolicyProviderFactory {
}
@Override
public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) {
return new TimePolicyAdminResource();
}
@ -41,6 +45,38 @@ public class TimePolicyProviderFactory implements PolicyProviderFactory {
return null;
}
@Override
public void onCreate(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
validateConfig(policy);
}
private void validateConfig(Policy policy) {
String nbf = policy.getConfig().get("nbf");
String noa = policy.getConfig().get("noa");
if (nbf != null && noa != null) {
validateFormat(nbf);
validateFormat(noa);
}
}
@Override
public void onUpdate(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
validateConfig(policy);
}
@Override
public void onRemove(Policy policy, AuthorizationProvider authorization) {
}
private void validateFormat(String date) {
try {
new SimpleDateFormat(TimePolicyProvider.DEFAULT_DATE_PATTERN).parse(TimePolicyProvider.format(date));
} catch (Exception e) {
throw new RuntimeException("Could not parse a date using format [" + date + "]");
}
}
@Override
public void init(Config.Scope config) {

View file

@ -20,14 +20,16 @@ package org.keycloak.authorization.policy.provider.user;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.ResourceServerStore;
@ -37,12 +39,15 @@ import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserModel.UserRemovedEvent;
import org.keycloak.models.UserProvider;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
import org.keycloak.util.JsonSerialization;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class UserPolicyProviderFactory implements PolicyProviderFactory {
public class UserPolicyProviderFactory implements PolicyProviderFactory<UserPolicyRepresentation> {
private UserPolicyProvider provider = new UserPolicyProvider();
@ -62,13 +67,86 @@ public class UserPolicyProviderFactory implements PolicyProviderFactory {
}
@Override
public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
public PolicyProvider create(KeycloakSession session) {
return null;
}
@Override
public PolicyProvider create(KeycloakSession session) {
return null;
public UserPolicyRepresentation toRepresentation(Policy policy, UserPolicyRepresentation representation) {
try {
representation.setUsers(JsonSerialization.readValue(policy.getConfig().get("users"), Set.class));
} catch (IOException cause) {
throw new RuntimeException("Failed to deserialize roles", cause);
}
return representation;
}
@Override
public Class<UserPolicyRepresentation> getRepresentationType() {
return UserPolicyRepresentation.class;
}
@Override
public void onCreate(Policy policy, UserPolicyRepresentation representation, AuthorizationProvider authorization) {
updateUsers(policy, representation, authorization);
}
@Override
public void onUpdate(Policy policy, UserPolicyRepresentation representation, AuthorizationProvider authorization) {
updateUsers(policy, representation, authorization);
}
@Override
public void onImport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
try {
updateUsers(policy, authorization, JsonSerialization.readValue(representation.getConfig().get("users"), Set.class));
} catch (IOException cause) {
throw new RuntimeException("Failed to deserialize users during import", cause);
}
}
private void updateUsers(Policy policy, UserPolicyRepresentation representation, AuthorizationProvider authorization) {
updateUsers(policy, authorization, representation.getUsers());
}
private void updateUsers(Policy policy, AuthorizationProvider authorization, Set<String> users) {
try {
KeycloakSession session = authorization.getKeycloakSession();
RealmModel realm = authorization.getRealm();
UserProvider userProvider = session.users();
Set<String> updatedUsers = new HashSet<>();
if (users != null) {
try {
for (String userId : users) {
UserModel user = null;
try {
user = userProvider.getUserByUsername(userId, realm);
} catch (Exception ignore) {
}
if (user == null) {
user = userProvider.getUserById(userId, realm);
}
if (user == null) {
throw new RuntimeException("Error while importing configuration. User [" + userId + "] could not be found.");
}
updatedUsers.add(user.getId());
}
} catch (Exception e) {
throw new RuntimeException("Error while updating policy [" + policy.getName() + "].", e);
}
}
Map<String, String> config = policy.getConfig();
config.put("users", JsonSerialization.writeValueAsString(updatedUsers));
policy.setConfig(config);
} catch (IOException cause) {
throw new RuntimeException("Failed to deserialize roles", cause);
}
}
@Override
@ -102,12 +180,6 @@ public class UserPolicyProviderFactory implements PolicyProviderFactory {
try {
if (users.isEmpty()) {
policyStore.findDependentPolicies(policy.getId(), resourceServer.getId()).forEach(dependentPolicy -> {
dependentPolicy.removeAssociatedPolicy(policy);
if (dependentPolicy.getAssociatedPolicies().isEmpty()) {
policyStore.delete(dependentPolicy.getId());
}
});
policyStore.delete(policy.getId());
} else {
policy.getConfig().put("users", JsonSerialization.writeValueAsString(users));
@ -133,16 +205,16 @@ public class UserPolicyProviderFactory implements PolicyProviderFactory {
}
static String[] getUsers(Policy policy) {
String roles = policy.getConfig().get("users");
String users = policy.getConfig().get("users");
if (roles != null) {
if (users != null) {
try {
return JsonSerialization.readValue(roles.getBytes(), String[].class);
return JsonSerialization.readValue(users.getBytes(), String[].class);
} catch (IOException e) {
throw new RuntimeException("Could not parse roles [" + roles + "] from policy config [" + policy.getName() + ".", e);
throw new RuntimeException("Could not parse users [" + users + "] from policy config [" + policy.getName() + ".", e);
}
}
return new String[]{};
return new String[0];
}
}

View file

@ -16,8 +16,6 @@
*/
package org.keycloak.authorization.policy.provider.drools;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.kie.api.runtime.KieContainer;
@ -33,29 +31,12 @@ import javax.ws.rs.core.Response;
*/
public class DroolsPolicyAdminResource implements PolicyProviderAdminService {
private final ResourceServer resourceServer;
private final DroolsPolicyProviderFactory factory;
public DroolsPolicyAdminResource(ResourceServer resourceServer, DroolsPolicyProviderFactory factory) {
this.resourceServer = resourceServer;
public DroolsPolicyAdminResource(DroolsPolicyProviderFactory factory) {
this.factory = factory;
}
@Override
public void onCreate(Policy policy) {
this.factory.update(policy);
}
@Override
public void onUpdate(Policy policy) {
this.factory.update(policy);
}
@Override
public void onRemove(Policy policy) {
this.factory.remove(policy);
}
@Path("/resolveModules")
@POST
@Consumes("application/json")

View file

@ -13,6 +13,8 @@ import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.kie.api.KieServices;
import org.kie.api.KieServices.Factory;
import org.kie.api.runtime.KieContainer;
@ -49,8 +51,8 @@ public class DroolsPolicyProviderFactory implements PolicyProviderFactory {
}
@Override
public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer) {
return new DroolsPolicyAdminResource(resourceServer, this);
public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) {
return new DroolsPolicyAdminResource(this);
}
@Override
@ -58,6 +60,26 @@ public class DroolsPolicyProviderFactory implements PolicyProviderFactory {
return null;
}
@Override
public void onCreate(Policy policy, AbstractPolicyRepresentation representation, AuthorizationProvider authorization) {
update(policy);
}
@Override
public void onUpdate(Policy policy, AbstractPolicyRepresentation representation, AuthorizationProvider authorization) {
update(policy);
}
@Override
public void onImport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
update(policy);
}
@Override
public void onRemove(Policy policy, AuthorizationProvider authorization) {
remove(policy);
}
@Override
public void init(Config.Scope config) {
this.ks = Factory.get();

View file

@ -53,7 +53,6 @@ public final class StreamUtil {
* @param charset Charset used to decode the input stream
* @return String representation of the input stream contents decoded using given charset
* @throws IOException
* @deprecated Use {@link #readString(java.io.InputStream, java.nio.charset.Charset)} variant.
*/
public static String readString(InputStream in, Charset charset) throws IOException
{

View file

@ -22,6 +22,7 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
/**
* Common Adapter configuration
@ -58,7 +59,7 @@ public class BaseAdapterConfig extends BaseRealmConfig {
@JsonProperty("public-client")
protected boolean publicClient;
@JsonProperty("credentials")
protected Map<String, Object> credentials = new HashMap<>();
protected Map<String, Object> credentials = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
public boolean isUseResourceRoleMappings() {

View file

@ -196,10 +196,12 @@ public class PolicyEnforcerConfig {
'}';
}
@JsonIgnore
public boolean hasPattern() {
return getPath().indexOf("{") != -1;
}
@JsonIgnore
public boolean isInstance() {
return this.parentConfig != null;
}

View file

@ -0,0 +1,132 @@
/*
* Copyright 2016 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.representations.idm.authorization;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class AbstractPolicyRepresentation {
private String id;
private String name;
private String description;
private String type;
private Set<String> policies;
private Set<String> resources;
private Set<String> scopes;
private Logic logic = Logic.POSITIVE;
private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS;
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
public String getType() {
return this.type;
}
public void setType(String type) {
this.type = type;
}
public DecisionStrategy getDecisionStrategy() {
return this.decisionStrategy;
}
public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
this.decisionStrategy = decisionStrategy;
}
public Logic getLogic() {
return logic;
}
public void setLogic(Logic logic) {
this.logic = logic;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
public Set<String> getPolicies() {
return policies;
}
public void addPolicy(String... id) {
if (this.policies == null) {
this.policies = new HashSet<>();
}
this.policies.addAll(Arrays.asList(id));
}
public Set<String> getResources() {
return resources;
}
public void addResource(String id) {
if (this.resources == null) {
this.resources = new HashSet<>();
}
this.resources.add(id);
}
public Set<String> getScopes() {
return scopes;
}
public void addScope(String... id) {
if (this.scopes == null) {
this.scopes = new HashSet<>();
}
this.scopes.addAll(Arrays.asList(id));
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final AbstractPolicyRepresentation policy = (AbstractPolicyRepresentation) o;
return Objects.equals(getId(), policy.getId());
}
@Override
public int hashCode() {
return Objects.hash(getId());
}
}

View file

@ -18,53 +18,14 @@ package org.keycloak.representations.idm.authorization;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class PolicyRepresentation {
public class PolicyRepresentation extends AbstractPolicyRepresentation {
private String id;
private String name;
private String description;
private String type;
private Logic logic = Logic.POSITIVE;
private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS;
private Map<String, String> config = new HashMap();
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
public String getType() {
return this.type;
}
public void setType(String type) {
this.type = type;
}
public DecisionStrategy getDecisionStrategy() {
return this.decisionStrategy;
}
public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
this.decisionStrategy = decisionStrategy;
}
public Logic getLogic() {
return logic;
}
public void setLogic(Logic logic) {
this.logic = logic;
}
public Map<String, String> getConfig() {
return this.config;
}
@ -72,33 +33,4 @@ public class PolicyRepresentation {
public void setConfig(Map<String, String> config) {
this.config = config;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final PolicyRepresentation policy = (PolicyRepresentation) o;
return Objects.equals(getId(), policy.getId());
}
@Override
public int hashCode() {
return Objects.hash(getId());
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright 2016 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.representations.idm.authorization;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class ResourcePermissionRepresentation extends AbstractPolicyRepresentation {
private String resourceType;
@Override
public String getType() {
return "resource";
}
public void setResourceType(String resourceType) {
this.resourceType = resourceType;
}
public String getResourceType() {
return resourceType;
}
}

View file

@ -0,0 +1,86 @@
/*
* Copyright 2016 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.representations.idm.authorization;
import java.util.HashSet;
import java.util.Set;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class RolePolicyRepresentation extends AbstractPolicyRepresentation {
private Set<RoleDefinition> roles;
public Set<RoleDefinition> getRoles() {
return roles;
}
public void setRoles(Set<RoleDefinition> roles) {
this.roles = roles;
}
public void addRole(String name, boolean required) {
if (roles == null) {
roles = new HashSet<>();
}
roles.add(new RoleDefinition(name, required));
}
public void addRole(String name) {
addRole(name, false);
}
public void addClientRole(String clientId, String name) {
addRole(clientId + "/" +name, false);
}
public void addClientRole(String clientId, String name, boolean required) {
addRole(clientId + "/" + name, required);
}
public static class RoleDefinition {
private String id;
private boolean required;
public RoleDefinition() {
this(null, false);
}
public RoleDefinition(String id, boolean required) {
this.id = id;
this.required = required;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public boolean isRequired() {
return required;
}
public void setRequired(boolean required) {
this.required = required;
}
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright 2016 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.representations.idm.authorization;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class ScopePermissionRepresentation extends AbstractPolicyRepresentation {
@Override
public String getType() {
return "scope";
}
}

View file

@ -0,0 +1,43 @@
/*
* Copyright 2016 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.representations.idm.authorization;
import java.util.HashSet;
import java.util.Set;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class UserPolicyRepresentation extends AbstractPolicyRepresentation {
private Set<String> users;
public Set<String> getUsers() {
return users;
}
public void setUsers(Set<String> users) {
this.users= users;
}
public void addUser(String name) {
if (users == null) {
users = new HashSet<>();
}
users.add(name);
}
}

View file

@ -206,7 +206,6 @@
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
@ -223,12 +222,10 @@
<outputDirectory>target</outputDirectory>
<workDirectory>target/assembly/work</workDirectory>
<appendAssemblyId>false</appendAssemblyId>
<tarLongFileMode>gnu</tarLongFileMode>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>

View file

@ -79,6 +79,7 @@
<plugin>
<groupId>org.wildfly.build</groupId>
<artifactId>wildfly-feature-pack-build-maven-plugin</artifactId>
<version>${wildfly.build-tools.version}</version>
<executions>
<execution>
<id>feature-pack-build</id>
@ -111,7 +112,6 @@
<appendAssemblyId>false</appendAssemblyId>
<outputDirectory>target/</outputDirectory>
<workDirectory>target/assembly/work</workDirectory>
<tarLongFileMode>gnu</tarLongFileMode>
</configuration>
</execution>
</executions>

View file

@ -28,6 +28,43 @@
<fileSet>
<directory>target/${project.build.finalName}</directory>
<outputDirectory/>
<excludes>
<exclude>configuration/**</exclude>
</excludes>
</fileSet>
<fileSet>
<directory>target/unpacked-themes/theme</directory>
<outputDirectory>content/themes</outputDirectory>
</fileSet>
<fileSet>
<directory>src/main/resources/identity/module</directory>
<includes>
<include>**/**</include>
</includes>
<outputDirectory>modules/system/layers/keycloak/org/jboss/as/product/${product.slot}</outputDirectory>
<filtered>true</filtered>
</fileSet>
<fileSet>
<directory>src/main/resources/identity</directory>
<includes>
<include>product.conf</include>
</includes>
<outputDirectory>content/bin</outputDirectory>
<filtered>true</filtered>
</fileSet>
<fileSet>
<directory>${configDir}</directory>
<includes>
<include>**/**</include>
</includes>
<outputDirectory>configuration</outputDirectory>
</fileSet>
<fileSet>
<directory>../../../</directory>
<includes>
<include>License.html</include>
</includes>
<outputDirectory>content</outputDirectory>
</fileSet>
</fileSets>
</assembly>
</assembly>

View file

@ -74,126 +74,6 @@
</executions>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-configuration</id>
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>target/resources/configuration</outputDirectory>
<resources>
<resource>
<directory>src/main/resources/configuration</directory>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
<execution>
<id>copy-modules</id>
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>target/resources/modules</outputDirectory>
<resources>
<resource>
<directory>src/main/resources/modules</directory>
</resource>
</resources>
</configuration>
</execution>
<execution>
<id>copy-content</id>
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>target/resources/content</outputDirectory>
<resources>
<resource>
<directory>src/main/resources/content</directory>
</resource>
</resources>
</configuration>
</execution>
<execution>
<id>copy-identity</id>
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>target/resources/content/bin</outputDirectory>
<resources>
<resource>
<directory>src/main/resources/identity</directory>
<includes>
<include>**/product.conf</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
<execution>
<id>copy-identity-module</id>
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>target/resources/modules/system/layers/keycloak/org/jboss/as/product/${product.slot}</outputDirectory>
<resources>
<resource>
<directory>src/main/resources/identity/module</directory>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
<execution>
<id>copy-themes</id>
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>target/resources/content/themes</outputDirectory>
<resources>
<resource>
<directory>target/unpacked-themes/theme</directory>
</resource>
</resources>
</configuration>
</execution>
<execution>
<id>copy-license</id>
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>target/resources/content</outputDirectory>
<resources>
<resource>
<directory>../../../</directory>
<includes>
<include>License.html</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.wildfly.build</groupId>
<artifactId>wildfly-feature-pack-build-maven-plugin</artifactId>
@ -206,11 +86,11 @@
<phase>compile</phase>
<configuration>
<config-file>feature-pack-build.xml</config-file>
<resources-dir>target/resources</resources-dir>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
@ -226,11 +106,9 @@
<descriptor>assembly.xml</descriptor>
</descriptors>
<recompressZippedFiles>true</recompressZippedFiles>
<finalName>${project.build.finalName}</finalName>
<appendAssemblyId>false</appendAssemblyId>
<outputDirectory>target/</outputDirectory>
<workDirectory>target/assembly/work</workDirectory>
<tarLongFileMode>gnu</tarLongFileMode>
</configuration>
</execution>
</executions>
@ -248,8 +126,9 @@
</activation>
<properties>
<build-tools.version>${wildfly.build-tools.version}</build-tools.version>
<feature.parent>org.wildfly:wildfly-feature-pack</feature.parent>
<xmlns.domain>urn:jboss:domain:4.0</xmlns.domain>
<configDir>src/main/resources/configuration</configDir>
</properties>
<dependencies>
@ -266,7 +145,9 @@
<id>wf11</id>
<properties>
<xmlns.domain>urn:jboss:domain:5.0</xmlns.domain>
<build-tools.version>${wildfly11.build-tools.version}</build-tools.version>
<feature.parent>org.wildfly:wildfly-feature-pack</feature.parent>
<configDir>src/main/resources-wf11/configuration</configDir>
</properties>
<dependencies>
@ -304,6 +185,7 @@
</plugins>
</build>
</profile>
<profile>
<id>product</id>
<activation>
@ -313,8 +195,9 @@
</activation>
<properties>
<build-tools.version>${eap.build-tools.version}</build-tools.version>
<feature.parent>org.jboss.eap:wildfly-feature-pack</feature.parent>
<xmlns.domain>urn:jboss:domain:5.0</xmlns.domain>
<configDir>src/main/resources-wf11/configuration</configDir>
</properties>
<dependencies>

View file

@ -0,0 +1,85 @@
<?xml version='1.0' encoding='UTF-8'?>
<!--
~ Copyright 2016 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.
-->
<!-- See src/resources/configuration/ReadMe.txt for how the configuration assembly works -->
<config>
<subsystems name="load-balancer">
<!-- Each subsystem to be included relative to the src/main/resources directory -->
<subsystem>logging.xml</subsystem>
<subsystem>io.xml</subsystem>
<subsystem supplement="domain">jmx.xml</subsystem>
<subsystem>naming.xml</subsystem>
<subsystem>remoting.xml</subsystem>
<subsystem>request-controller.xml</subsystem>
<subsystem>security.xml</subsystem>
<subsystem>security-manager.xml</subsystem>
</subsystems>
<subsystems name="auth-server-standalone">
<!-- Each subsystem to be included relative to the src/main/resources directory -->
<subsystem>logging.xml</subsystem>
<subsystem>bean-validation.xml</subsystem>
<subsystem supplement="default">keycloak-datasources.xml</subsystem>
<subsystem>ee.xml</subsystem>
<subsystem>ejb3.xml</subsystem>
<subsystem>io.xml</subsystem>
<subsystem>keycloak-infinispan.xml</subsystem>
<subsystem>jaxrs.xml</subsystem>
<subsystem>jca.xml</subsystem>
<subsystem>jdr.xml</subsystem>
<subsystem supplement="domain">jmx.xml</subsystem>
<subsystem>jpa.xml</subsystem>
<subsystem>jsf.xml</subsystem>
<subsystem>mail.xml</subsystem>
<subsystem>naming.xml</subsystem>
<subsystem>remoting.xml</subsystem>
<subsystem>request-controller.xml</subsystem>
<subsystem>security.xml</subsystem>
<subsystem>security-manager.xml</subsystem>
<subsystem>transactions.xml</subsystem>
<subsystem>undertow.xml</subsystem>
<subsystem>keycloak-server.xml</subsystem>
</subsystems>
<subsystems name="auth-server-clustered">
<!-- Each subsystem to be included relative to the src/main/resources directory -->
<subsystem>logging.xml</subsystem>
<subsystem>bean-validation.xml</subsystem>
<subsystem supplement="domain">keycloak-datasources.xml</subsystem>
<subsystem>ee.xml</subsystem>
<subsystem supplement="ha">ejb3.xml</subsystem>
<subsystem>io.xml</subsystem>
<subsystem supplement="ha">keycloak-infinispan.xml</subsystem>
<subsystem>jaxrs.xml</subsystem>
<subsystem>jca.xml</subsystem>
<subsystem>jdr.xml</subsystem>
<subsystem>jgroups.xml</subsystem>
<subsystem supplement="domain">jmx.xml</subsystem>
<subsystem>jpa.xml</subsystem>
<subsystem>jsf.xml</subsystem>
<subsystem>mail.xml</subsystem>
<subsystem>mod_cluster.xml</subsystem>
<subsystem>naming.xml</subsystem>
<subsystem>remoting.xml</subsystem>
<subsystem>request-controller.xml</subsystem>
<subsystem>security.xml</subsystem>
<subsystem>security-manager.xml</subsystem>
<subsystem>transactions.xml</subsystem>
<subsystem supplement="ha">undertow.xml</subsystem>
<subsystem>keycloak-server.xml</subsystem>
</subsystems>
</config>

View file

@ -0,0 +1,146 @@
<?xml version='1.0' encoding='UTF-8'?>
<!--
~ Copyright 2016 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.
-->
<domain xmlns="urn:jboss:domain:4.0">
<extensions>
<?EXTENSIONS?>
</extensions>
<system-properties>
<!-- IPv4 is not required, but setting this helps avoid unintended use of IPv6 -->
<property name="java.net.preferIPv4Stack" value="true"/>
</system-properties>
<management>
<access-control provider="simple">
<role-mapping>
<role name="SuperUser">
<include>
<user name="$local"/>
</include>
</role>
</role-mapping>
</access-control>
</management>
<profiles>
<!-- Non clustered authentication server profile -->
<profile name="auth-server-standalone">
<?SUBSYSTEMS socket-binding-group="standard-sockets"?>
</profile>
<!--
Clustering authentication server setup.
You must configure a remote shared external database like PostgreSQL or MySql if you want this to be
able to work on multiple machines.
-->
<profile name="auth-server-clustered">
<?SUBSYSTEMS socket-binding-group="ha-sockets"?>
</profile>
<!--
This is a profile for the built-in Underto Loadbalancer
It should be removed in production systems and replaced with a better software or hardware based one
-->
<profile name="load-balancer">
<?SUBSYSTEMS socket-binding-group="load-balancer-sockets"?>
<subsystem xmlns="urn:jboss:domain:undertow:3.0">
<buffer-cache name="default"/>
<server name="default-server">
<http-listener name="default" socket-binding="http" redirect-socket="https"/>
<host name="default-host" alias="localhost">
<location name="/" handler="lb-handler"/>
<filter-ref name="server-header"/>
<filter-ref name="x-powered-by-header"/>
</host>
</server>
<servlet-container name="default">
<jsp-config/>
<websockets/>
</servlet-container>
<handlers>
<reverse-proxy name="lb-handler">
<host name="host1" outbound-socket-binding="remote-host1" scheme="ajp" path="/" instance-id="myroute1"/>
<host name="host2" outbound-socket-binding="remote-host2" scheme="ajp" path="/" instance-id="myroute2"/>
</reverse-proxy>
</handlers>
<filters>
<response-header name="server-header" header-name="Server" header-value="WildFly/10"/>
<response-header name="x-powered-by-header" header-name="X-Powered-By" header-value="Undertow/1"/>
</filters>
</subsystem>
</profile>
</profiles>
<!--
Named interfaces that can be referenced elsewhere in the configuration. The configuration
for how to associate these logical names with an actual network interface can either
be specified here or can be declared on a per-host basis in the equivalent element in host.xml.
These default configurations require the binding specification to be done in host.xml.
-->
<interfaces>
<interface name="management">
<inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
</interface>
<interface name="public">
<inet-address value="${jboss.bind.address:127.0.0.1}"/>
</interface>
<?INTERFACES?>
</interfaces>
<socket-binding-groups>
<socket-binding-group name="standard-sockets" default-interface="public">
<?SOCKET-BINDINGS?>
</socket-binding-group>
<socket-binding-group name="ha-sockets" default-interface="public">
<?SOCKET-BINDINGS?>
</socket-binding-group>
<!-- load-balancer-sockets should be removed in production systems and replaced with a better softare or hardare based one -->
<socket-binding-group name="load-balancer-sockets" default-interface="public">
<socket-binding name="ajp" port="${jboss.ajp.port:8009}"/>
<socket-binding name="http" port="${jboss.http.port:8080}"/>
<socket-binding name="https" port="${jboss.https.port:8443}"/>
<outbound-socket-binding name="remote-host1">
<remote-destination host="localhost" port="8159"/>
</outbound-socket-binding>
<outbound-socket-binding name="remote-host2">
<remote-destination host="localhost" port="8259"/>
</outbound-socket-binding>
<?SOCKET-BINDINGS?>
</socket-binding-group>
</socket-binding-groups>
<server-groups>
<!-- load-balancer-group should be removed in production systems and replaced with a better softare or hardare based one -->
<server-group name="load-balancer-group" profile="load-balancer">
<jvm name="default">
<heap size="64m" max-size="512m"/>
</jvm>
<socket-binding-group ref="load-balancer-sockets"/>
</server-group>
<server-group name="auth-server-group" profile="auth-server-clustered">
<jvm name="default">
<heap size="64m" max-size="512m"/>
</jvm>
<socket-binding-group ref="ha-sockets"/>
</server-group>
</server-groups>
</domain>

View file

@ -0,0 +1,127 @@
<?xml version='1.0' encoding='UTF-8'?>
<!--
~ Copyright 2016 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.
-->
<!--
Runs an HTTP Loadbalancer that balances to two separate auth server instances. The first auth server instance
is also started by this host controller file. The other instance must be started
via host-slave.xml
-->
<host name="master" xmlns="urn:jboss:domain:4.0">
<extensions>
<?EXTENSIONS?>
</extensions>
<management>
<security-realms>
<security-realm name="ManagementRealm">
<authentication>
<local default-user="$local" skip-group-loading="true"/>
<properties path="mgmt-users.properties" relative-to="jboss.domain.config.dir"/>
</authentication>
<authorization map-groups-to-roles="false">
<properties path="mgmt-groups.properties" relative-to="jboss.domain.config.dir"/>
</authorization>
</security-realm>
<security-realm name="ApplicationRealm">
<authentication>
<local default-user="$local" allowed-users="*" skip-group-loading="true"/>
<properties path="application-users.properties" relative-to="jboss.domain.config.dir"/>
</authentication>
<authorization>
<properties path="application-roles.properties" relative-to="jboss.domain.config.dir"/>
</authorization>
</security-realm>
</security-realms>
<audit-log>
<formatters>
<json-formatter name="json-formatter"/>
</formatters>
<handlers>
<file-handler name="host-file" formatter="json-formatter" relative-to="jboss.domain.data.dir" path="audit-log.log"/>
<file-handler name="server-file" formatter="json-formatter" relative-to="jboss.server.data.dir" path="audit-log.log"/>
</handlers>
<logger log-boot="true" log-read-only="false" enabled="false">
<handlers>
<handler name="host-file"/>
</handlers>
</logger>
<server-logger log-boot="true" log-read-only="false" enabled="false">
<handlers>
<handler name="server-file"/>
</handlers>
</server-logger>
</audit-log>
<management-interfaces>
<native-interface security-realm="ManagementRealm">
<socket interface="management" port="${jboss.management.native.port:9999}"/>
</native-interface>
<http-interface security-realm="ManagementRealm" http-upgrade-enabled="true">
<socket interface="management" port="${jboss.management.http.port:9990}"/>
</http-interface>
</management-interfaces>
</management>
<domain-controller>
<local/>
</domain-controller>
<interfaces>
<interface name="management">
<inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
</interface>
<interface name="public">
<inet-address value="${jboss.bind.address:127.0.0.1}"/>
</interface>
<?INTERFACES?>
</interfaces>
<jvms>
<jvm name="default">
<heap size="64m" max-size="256m"/>
<jvm-options>
<option value="-server"/>
</jvm-options>
</jvm>
</jvms>
<servers>
<!-- load-balancer should be removed in production systems and replaced with a better softare or hardare based one -->
<server name="load-balancer" group="load-balancer-group">
</server>
<server name="server-one" group="auth-server-group" auto-start="true">
<!-- Remote JPDA debugging for a specific server
<jvm name="default">
<jvm-options>
<option value="-agentlib:jdwp=transport=dt_socket,address=8787,server=y,suspend=n"/>
</jvm-options>
</jvm>
-->
<!-- server-two avoids port conflicts by incrementing the ports in
the default socket-group declared in the server-group -->
<socket-bindings port-offset="150"/>
</server>
</servers>
<profile>
<?SUBSYSTEMS socket-binding-group="standard-sockets"?>
</profile>
</host>

View file

@ -0,0 +1,117 @@
<?xml version='1.0' encoding='UTF-8'?>
<!--
~ Copyright 2016 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.
-->
<host xmlns="urn:jboss:domain:4.0">
<extensions>
<?EXTENSIONS?>
</extensions>
<management>
<security-realms>
<security-realm name="ManagementRealm">
<server-identities>
<!-- Replace this with either a base64 password of your own, or use a vault with a vault expression -->
<secret value="c2xhdmVfdXNlcl9wYXNzd29yZA=="/>
</server-identities>
<authentication>
<local default-user="$local" skip-group-loading="true"/>
<properties path="mgmt-users.properties" relative-to="jboss.domain.config.dir"/>
</authentication>
<authorization map-groups-to-roles="false">
<properties path="mgmt-groups.properties" relative-to="jboss.domain.config.dir"/>
</authorization>
</security-realm>
<security-realm name="ApplicationRealm">
<authentication>
<local default-user="$local" allowed-users="*" skip-group-loading="true"/>
<properties path="application-users.properties" relative-to="jboss.domain.config.dir"/>
</authentication>
<authorization>
<properties path="application-roles.properties" relative-to="jboss.domain.config.dir"/>
</authorization>
</security-realm>
</security-realms>
<audit-log>
<formatters>
<json-formatter name="json-formatter"/>
</formatters>
<handlers>
<file-handler name="host-file" formatter="json-formatter" relative-to="jboss.domain.data.dir" path="audit-log.log"/>
<file-handler name="server-file" formatter="json-formatter" relative-to="jboss.server.data.dir" path="audit-log.log"/>
</handlers>
<logger log-boot="true" log-read-only="false" enabled="false">
<handlers>
<handler name="host-file"/>
</handlers>
</logger>
<server-logger log-boot="true" log-read-only="false" enabled="false">
<handlers>
<handler name="server-file"/>
</handlers>
</server-logger>
</audit-log>
<management-interfaces>
<native-interface security-realm="ManagementRealm">
<socket interface="management" port="${jboss.management.native.port:3456}"/>
</native-interface>
</management-interfaces>
</management>
<domain-controller>
<remote security-realm="ManagementRealm">
<discovery-options>
<static-discovery name="primary" protocol="${jboss.domain.master.protocol:remote}" host="${jboss.domain.master.address:127.0.0.1}" port="${jboss.domain.master.port:9999}"/>
</discovery-options>
</remote>
</domain-controller>
<interfaces>
<interface name="management">
<inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
</interface>
<interface name="public">
<inet-address value="${jboss.bind.address:127.0.0.1}"/>
</interface>
<?INTERFACES?>
</interfaces>
<jvms>
<jvm name="default">
<heap size="64m" max-size="256m"/>
<jvm-options>
<option value="-server"/>
</jvm-options>
</jvm>
</jvms>
<servers>
<server name="server-two" group="auth-server-group" auto-start="true">
<!-- server-two avoids port conflicts by incrementing the ports in
the default socket-group declared in the server-group -->
<socket-bindings port-offset="250"/>
</server>
</servers>
<profile>
<?SUBSYSTEMS socket-binding-group="standard-sockets"?>
</profile>
</host>

View file

@ -0,0 +1,127 @@
<?xml version='1.0' encoding='UTF-8'?>
<!--
~ Copyright 2016 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.
-->
<!--
Runs an HTTP Loadbalancer that balances to two separate auth server instances. The first auth server instance
is also started by this host controller file. The other instance must be started
via host-slave.xml
-->
<host name="master" xmlns="urn:jboss:domain:4.0">
<extensions>
<?EXTENSIONS?>
</extensions>
<management>
<security-realms>
<security-realm name="ManagementRealm">
<authentication>
<local default-user="$local" skip-group-loading="true"/>
<properties path="mgmt-users.properties" relative-to="jboss.domain.config.dir"/>
</authentication>
<authorization map-groups-to-roles="false">
<properties path="mgmt-groups.properties" relative-to="jboss.domain.config.dir"/>
</authorization>
</security-realm>
<security-realm name="ApplicationRealm">
<authentication>
<local default-user="$local" allowed-users="*" skip-group-loading="true"/>
<properties path="application-users.properties" relative-to="jboss.domain.config.dir"/>
</authentication>
<authorization>
<properties path="application-roles.properties" relative-to="jboss.domain.config.dir"/>
</authorization>
</security-realm>
</security-realms>
<audit-log>
<formatters>
<json-formatter name="json-formatter"/>
</formatters>
<handlers>
<file-handler name="host-file" formatter="json-formatter" relative-to="jboss.domain.data.dir" path="audit-log.log"/>
<file-handler name="server-file" formatter="json-formatter" relative-to="jboss.server.data.dir" path="audit-log.log"/>
</handlers>
<logger log-boot="true" log-read-only="false" enabled="false">
<handlers>
<handler name="host-file"/>
</handlers>
</logger>
<server-logger log-boot="true" log-read-only="false" enabled="false">
<handlers>
<handler name="server-file"/>
</handlers>
</server-logger>
</audit-log>
<management-interfaces>
<native-interface security-realm="ManagementRealm">
<socket interface="management" port="${jboss.management.native.port:9999}"/>
</native-interface>
<http-interface security-realm="ManagementRealm" http-upgrade-enabled="true">
<socket interface="management" port="${jboss.management.http.port:9990}"/>
</http-interface>
</management-interfaces>
</management>
<domain-controller>
<local/>
</domain-controller>
<interfaces>
<interface name="management">
<inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
</interface>
<interface name="public">
<inet-address value="${jboss.bind.address:127.0.0.1}"/>
</interface>
<?INTERFACES?>
</interfaces>
<jvms>
<jvm name="default">
<heap size="64m" max-size="256m"/>
<jvm-options>
<option value="-server"/>
</jvm-options>
</jvm>
</jvms>
<servers>
<!-- load-balancer should be removed in production systems and replaced with a better softare or hardare based one -->
<server name="load-balancer" group="load-balancer-group">
</server>
<server name="server-one" group="auth-server-group" auto-start="true">
<!-- Remote JPDA debugging for a specific server
<jvm name="default">
<jvm-options>
<option value="-agentlib:jdwp=transport=dt_socket,address=8787,server=y,suspend=n"/>
</jvm-options>
</jvm>
-->
<!-- server-two avoids port conflicts by incrementing the ports in
the default socket-group declared in the server-group -->
<socket-bindings port-offset="150"/>
</server>
</servers>
<profile>
<?SUBSYSTEMS socket-binding-group="standard-sockets"?>
</profile>
</host>

View file

@ -0,0 +1,24 @@
<?xml version='1.0' encoding='UTF-8'?>
<!--
~ Copyright 2016 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.
-->
<!-- See src/resources/configuration/ReadMe.txt for how the configuration assembly works -->
<config>
<subsystems>
<subsystem>jmx.xml</subsystem>
</subsystems>
</config>

View file

@ -1,6 +1,6 @@
<?xml version='1.0' encoding='UTF-8'?>
<server xmlns="${xmlns.domain}">
<server xmlns="urn:jboss:domain:5.0">
<extensions>
<?EXTENSIONS?>

View file

@ -17,7 +17,7 @@
~ limitations under the License.
-->
<domain xmlns="${xmlns.domain}">
<domain xmlns="urn:jboss:domain:4.0">
<extensions>
<?EXTENSIONS?>

View file

@ -22,7 +22,7 @@
is also started by this host controller file. The other instance must be started
via host-slave.xml
-->
<host name="master" xmlns="${xmlns.domain}">
<host name="master" xmlns="urn:jboss:domain:4.0">
<extensions>
<?EXTENSIONS?>
</extensions>

View file

@ -17,7 +17,7 @@
~ limitations under the License.
-->
<host xmlns="${xmlns.domain}">
<host xmlns="urn:jboss:domain:4.0">
<extensions>
<?EXTENSIONS?>
</extensions>

View file

@ -23,7 +23,7 @@
via host-slave.xml
-->
<host name="master" xmlns="${xmlns.domain}">
<host name="master" xmlns="urn:jboss:domain:4.0">
<extensions>
<?EXTENSIONS?>
</extensions>

View file

@ -17,7 +17,7 @@
~ limitations under the License.
-->
<server xmlns="${xmlns.domain}">
<server xmlns="urn:jboss:domain:4.0">
<extensions>
<?EXTENSIONS?>

View file

@ -42,6 +42,7 @@
<plugin>
<groupId>org.wildfly.build</groupId>
<artifactId>wildfly-server-provisioning-maven-plugin</artifactId>
<version>${build-tools.version}</version>
<executions>
<execution>
<id>server-provisioning</id>
@ -74,7 +75,6 @@
<appendAssemblyId>false</appendAssemblyId>
<outputDirectory>${project.build.directory}</outputDirectory>
<workDirectory>${project.build.directory}/assembly/work</workDirectory>
<tarLongFileMode>gnu</tarLongFileMode>
</configuration>
</execution>
</executions>
@ -114,6 +114,7 @@
</property>
</activation>
<properties>
<build-tools.version>${wildfly.build-tools.version}</build-tools.version>
<assemblyFile>assembly.xml</assemblyFile>
</properties>
<build>
@ -121,6 +122,13 @@
</build>
</profile>
<profile>
<id>wf11</id>
<properties>
<build-tools.version>${wildfly11.build-tools.version}</build-tools.version>
</properties>
</profile>
<profile>
<id>product</id>
<activation>
@ -129,6 +137,7 @@
</property>
</activation>
<properties>
<build-tools.version>${eap.build-tools.version}</build-tools.version>
<assemblyFile>assembly.xml</assemblyFile>
<profileExcludes>%regex[(providers.*)|(docs/contrib.*)|(docs/examples.*)|(docs/schema.*)]</profileExcludes>
</properties>

View file

@ -138,7 +138,6 @@
<appendAssemblyId>false</appendAssemblyId>
<outputDirectory>${project.build.directory}</outputDirectory>
<workDirectory>${project.build.directory}/assembly/work</workDirectory>
<tarLongFileMode>gnu</tarLongFileMode>
</configuration>
</execution>
</executions>

View file

@ -20,6 +20,7 @@ package org.keycloak.admin.client;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
import org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider;
import org.keycloak.admin.client.resource.BearerAuthFilter;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.RealmsResource;
@ -31,7 +32,6 @@ import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import java.net.URI;
import java.security.KeyStore;
import static org.keycloak.OAuth2Constants.PASSWORD;
@ -66,12 +66,20 @@ public class Keycloak {
}
public static Keycloak getInstance(String serverUrl, String realm, String username, String password, String clientId, String clientSecret, SSLContext sslContext) {
ResteasyClient client = new ResteasyClientBuilder()
return getInstance(serverUrl, realm, username, password, clientId, clientSecret, sslContext, null);
}
public static Keycloak getInstance(String serverUrl, String realm, String username, String password, String clientId, String clientSecret, SSLContext sslContext, ResteasyJackson2Provider customJacksonProvider) {
ResteasyClientBuilder clientBuilder = new ResteasyClientBuilder()
.sslContext(sslContext)
.hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.WILDCARD)
.connectionPoolSize(10).build();
.connectionPoolSize(10);
return new Keycloak(serverUrl, realm, username, password, clientId, clientSecret, PASSWORD, client, null);
if (customJacksonProvider != null) {
clientBuilder.register(customJacksonProvider);
}
return new Keycloak(serverUrl, realm, username, password, clientId, clientSecret, PASSWORD, clientBuilder.build(), null);
}
private static ResteasyClientBuilder newResteasyClientBuilder() {

View file

@ -23,6 +23,7 @@ import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@ -58,4 +59,7 @@ public interface AuthorizationResource {
@Path("/policy")
PoliciesResource policies();
@Path("/permission")
PermissionsResource permissions();
}

View file

@ -0,0 +1,31 @@
/*
* Copyright 2016 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.admin.client.resource;
import javax.ws.rs.Path;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public interface PermissionsResource {
@Path("resource")
ResourcePermissionsResource resource();
@Path("scope")
ScopePermissionsResource scope();
}

View file

@ -28,6 +28,7 @@ import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.List;
@ -45,6 +46,12 @@ public interface PoliciesResource {
@Path("{id}")
PolicyResource policy(@PathParam("id") String id);
@Path("/search")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
PolicyRepresentation findByName(@QueryParam("name") String name);
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
@ -62,4 +69,9 @@ public interface PoliciesResource {
@Path("evaluate")
PolicyEvaluationResponse evaluate(PolicyEvaluationRequest evaluationRequest);
@Path("role")
RolePoliciesResource roles();
@Path("user")
UserPoliciesResource users();
}

View file

@ -0,0 +1,69 @@
/*
* Copyright 2016 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.admin.client.resource;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public interface ResourcePermissionResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
ResourcePermissionRepresentation toRepresentation();
@PUT
@Consumes(MediaType.APPLICATION_JSON)
void update(ResourcePermissionRepresentation representation);
@DELETE
void remove();
@Path("/associatedPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> associatedPolicies();
@Path("/dependentPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> dependentPolicies();
@Path("/resources")
@GET
@Produces("application/json")
@NoCache
List<ResourceRepresentation> resources();
}

View file

@ -0,0 +1,50 @@
/*
* Copyright 2016 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.admin.client.resource;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public interface ResourcePermissionsResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
Response create(ResourcePermissionRepresentation representation);
@Path("{id}")
ResourcePermissionResource findById(@PathParam("id") String id);
@Path("/search")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
ResourcePermissionRepresentation findByName(@QueryParam("name") String name);
}

View file

@ -25,6 +25,7 @@ import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.List;
@ -46,4 +47,10 @@ public interface ResourceScopesResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
List<ScopeRepresentation> scopes();
@Path("/search")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
ScopeRepresentation findByName(@QueryParam("name") String name);
}

View file

@ -54,6 +54,11 @@ public interface ResourcesResource {
@QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResult);
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
List<ResourceRepresentation> findByName(@QueryParam("name") String name);
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)

View file

@ -0,0 +1,50 @@
/*
* Copyright 2016 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.admin.client.resource;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public interface RolePoliciesResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
Response create(RolePolicyRepresentation representation);
@Path("{id}")
RolePolicyResource findById(@PathParam("id") String id);
@Path("/search")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
RolePolicyRepresentation findByName(@QueryParam("name") String name);
}

View file

@ -0,0 +1,69 @@
/*
* Copyright 2016 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.admin.client.resource;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public interface RolePolicyResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
RolePolicyRepresentation toRepresentation();
@PUT
@Consumes(MediaType.APPLICATION_JSON)
void update(RolePolicyRepresentation representation);
@DELETE
void remove();
@Path("/associatedPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> associatedPolicies();
@Path("/dependentPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> dependentPolicies();
@Path("/resources")
@GET
@Produces("application/json")
@NoCache
List<ResourceRepresentation> resources();
}

View file

@ -0,0 +1,75 @@
/*
* Copyright 2016 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.admin.client.resource;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public interface ScopePermissionResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
ScopePermissionRepresentation toRepresentation();
@PUT
@Consumes(MediaType.APPLICATION_JSON)
void update(ScopePermissionRepresentation representation);
@DELETE
void remove();
@Path("/associatedPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> associatedPolicies();
@Path("/dependentPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> dependentPolicies();
@Path("/resources")
@GET
@Produces("application/json")
@NoCache
List<ResourceRepresentation> resources();
@Path("/scopes")
@GET
@Produces("application/json")
@NoCache
List<ScopeRepresentation> scopes();
}

View file

@ -0,0 +1,50 @@
/*
* Copyright 2016 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.admin.client.resource;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public interface ScopePermissionsResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
Response create(ScopePermissionRepresentation representation);
@Path("{id}")
ScopePermissionResource findById(@PathParam("id") String id);
@Path("/search")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
ScopePermissionRepresentation findByName(@QueryParam("name") String name);
}

View file

@ -0,0 +1,50 @@
/*
* Copyright 2016 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.admin.client.resource;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public interface UserPoliciesResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
Response create(UserPolicyRepresentation representation);
@Path("{id}")
UserPolicyResource findById(@PathParam("id") String id);
@Path("/search")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
UserPolicyRepresentation findByName(@QueryParam("name") String name);
}

View file

@ -0,0 +1,69 @@
/*
* Copyright 2016 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.admin.client.resource;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public interface UserPolicyResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
UserPolicyRepresentation toRepresentation();
@PUT
@Consumes(MediaType.APPLICATION_JSON)
void update(UserPolicyRepresentation representation);
@DELETE
void remove();
@Path("/associatedPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> associatedPolicies();
@Path("/dependentPolicies")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
List<PolicyRepresentation> dependentPolicies();
@Path("/resources")
@GET
@Produces("application/json")
@NoCache
List<ResourceRepresentation> resources();
}

View file

@ -43,6 +43,8 @@ import java.io.InputStream;
import java.net.URI;
import java.util.Arrays;
import static org.keycloak.test.builders.ClientBuilder.AccessType.PUBLIC;
public class TestsHelper {
public static String baseUrl;
@ -83,7 +85,7 @@ public class TestsHelper {
}
public static String createDirectGrantClient() {
return createClient(ClientBuilder.create("test-dga").publicClient(true));
return createClient(ClientBuilder.create("test-dga").accessType(PUBLIC));
}
public static void deleteClient(String clientId) {

View file

@ -28,6 +28,8 @@ public class ClientBuilder {
private ClientRepresentation rep;
public enum AccessType { BEARER_ONLY, PUBLIC, CONFIDENTIAL };
public static ClientBuilder create(String clientId) {
ClientRepresentation rep = new ClientRepresentation();
rep.setEnabled(Boolean.TRUE);
@ -39,9 +41,19 @@ public class ClientBuilder {
this.rep = rep;
}
public ClientRepresentation bearerOnly(boolean bearerOnly) {
rep.setBearerOnly(bearerOnly);
return rep;
public ClientRepresentation accessType(AccessType accessType) {
switch (accessType) {
case BEARER_ONLY:
rep.setBearerOnly(true);
break;
case PUBLIC:
rep.setPublicClient(true);
break;
case CONFIDENTIAL:
rep.setPublicClient(false);
break;
}
return defaultSettings();
}
public ClientBuilder rootUrl(String rootUrl) {
@ -64,9 +76,13 @@ public class ClientBuilder {
return this;
}
public ClientRepresentation publicClient(boolean publicClient) {
public ClientBuilder secret(String secret) {
rep.setSecret(secret);
return this;
}
private ClientRepresentation defaultSettings() {
rep.setFullScopeAllowed(true);
rep.setPublicClient(publicClient);
rep.setDirectAccessGrantsEnabled(true);
rep.setAdminUrl(rep.getRootUrl());

View file

@ -42,6 +42,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.authorization.infinispan.InfinispanStoreFactoryProvider.CacheTransaction;
import org.keycloak.models.authorization.infinispan.entities.CachedPolicy;
import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
import org.keycloak.representations.idm.authorization.DecisionStrategy;
import org.keycloak.representations.idm.authorization.Logic;
@ -53,15 +54,14 @@ public class CachedPolicyStore implements PolicyStore {
private static final String POLICY_ID_CACHE_PREFIX = "policy-id-";
private final Cache<String, Map<String, List<CachedPolicy>>> cache;
private final KeycloakSession session;
private final CachedStoreFactoryProvider cacheStoreFactory;
private final CacheTransaction transaction;
private final List<String> cacheKeys;
private StoreFactory storeFactory;
private final StoreFactory storeFactory;
private PolicyStore delegate;
private CachedStoreFactoryProvider cachedStoreFactory;
public CachedPolicyStore(KeycloakSession session, CacheTransaction transaction, StoreFactory storeFactory) {
this.session = session;
public CachedPolicyStore(KeycloakSession session, CachedStoreFactoryProvider cacheStoreFactory, CacheTransaction transaction, StoreFactory delegate) {
this.cacheStoreFactory = cacheStoreFactory;
this.transaction = transaction;
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
this.cache = provider.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME);
@ -70,12 +70,12 @@ public class CachedPolicyStore implements PolicyStore {
cacheKeys.add("findByResourceType");
cacheKeys.add("findByScopeIds");
cacheKeys.add("findByType");
this.storeFactory = storeFactory;
this.storeFactory = delegate;
}
@Override
public Policy create(String name, String type, ResourceServer resourceServer) {
Policy policy = getDelegate().create(name, type, getStoreFactory().getResourceServerStore().findById(resourceServer.getId()));
public Policy create(AbstractPolicyRepresentation representation, ResourceServer resourceServer) {
Policy policy = getDelegate().create(representation, getStoreFactory().getResourceServerStore().findById(resourceServer.getId()));
String id = policy.getId();
this.transaction.whenRollback(() -> {
@ -102,6 +102,10 @@ public class CachedPolicyStore implements PolicyStore {
@Override
public Policy findById(String id, String resourceServerId) {
if (resourceServerId == null) {
return getDelegate().findById(id, null);
}
String cacheKeyForPolicy = getCacheKeyForPolicy(id);
List<CachedPolicy> cached = resolveResourceServerCache(resourceServerId).get(cacheKeyForPolicy);
@ -285,6 +289,7 @@ public class CachedPolicyStore implements PolicyStore {
public void removeScope(Scope scope) {
getDelegateForUpdate().removeScope(getStoreFactory().getScopeStore().findById(scope.getId(), cached.getResourceServerId()));
cached.removeScope(scope);
scopes.remove(scope);
}
@Override
@ -297,6 +302,7 @@ public class CachedPolicyStore implements PolicyStore {
public void removeAssociatedPolicy(Policy associatedPolicy) {
getDelegateForUpdate().removeAssociatedPolicy(getStoreFactory().getPolicyStore().findById(associatedPolicy.getId(), cached.getResourceServerId()));
cached.removeAssociatedPolicy(associatedPolicy);
associatedPolicies.remove(associatedPolicy);
}
@Override
@ -309,6 +315,7 @@ public class CachedPolicyStore implements PolicyStore {
public void removeResource(Resource resource) {
getDelegateForUpdate().removeResource(getStoreFactory().getResourceStore().findById(resource.getId(), cached.getResourceServerId()));
cached.removeResource(resource);
resources.remove(resource);
}
@Override
@ -401,10 +408,7 @@ public class CachedPolicyStore implements PolicyStore {
}
private CachedStoreFactoryProvider getCachedStoreFactory() {
if (cachedStoreFactory == null) {
cachedStoreFactory = session.getProvider(CachedStoreFactoryProvider.class);
}
return cachedStoreFactory;
return cacheStoreFactory;
}
private void invalidateCache(String resourceServerId) {

View file

@ -41,18 +41,16 @@ public class CachedResourceServerStore implements ResourceServerStore {
private static final String RS_ID_CACHE_PREFIX = "rs-id-";
private static final String RS_CLIENT_ID_CACHE_PREFIX = "rs-client-id-";
private final KeycloakSession session;
private final CacheTransaction transaction;
private StoreFactory storeFactory;
private ResourceServerStore delegate;
private final Cache<String, Map<String, List<CachedResourceServer>>> cache;
public CachedResourceServerStore(KeycloakSession session, CacheTransaction transaction, StoreFactory storeFactory) {
this.session = session;
public CachedResourceServerStore(KeycloakSession session, CacheTransaction transaction, StoreFactory delegate) {
this.transaction = transaction;
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
this.cache = provider.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME);
this.storeFactory = storeFactory;
this.storeFactory = delegate;
}
@Override

View file

@ -49,15 +49,15 @@ public class CachedResourceStore implements ResourceStore {
private static final String RESOURCE_ID_CACHE_PREFIX = "rsc-id-";
private static final String RESOURCE_NAME_CACHE_PREFIX = "rsc-name-";
private final KeycloakSession session;
private final CachedStoreFactoryProvider cacheStoreFactory;
private final CacheTransaction transaction;
private final List<String> cacheKeys;
private StoreFactory storeFactory;
private StoreFactory delegateStoreFactory;
private ResourceStore delegate;
private final Cache<String, Map<String, List<CachedResource>>> cache;
public CachedResourceStore(KeycloakSession session, CacheTransaction transaction, StoreFactory storeFactory) {
this.session = session;
public CachedResourceStore(KeycloakSession session, CachedStoreFactoryProvider cacheStoreFactory, CacheTransaction transaction, StoreFactory delegate) {
this.cacheStoreFactory = cacheStoreFactory;
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
this.cache = provider.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME);
this.transaction = transaction;
@ -65,12 +65,12 @@ public class CachedResourceStore implements ResourceStore {
cacheKeys.add("findByOwner");
cacheKeys.add("findByUri");
cacheKeys.add("findByName");
this.storeFactory = storeFactory;
this.delegateStoreFactory = delegate;
}
@Override
public Resource create(String name, ResourceServer resourceServer, String owner) {
Resource resource = getDelegate().create(name, getStoreFactory().getResourceServerStore().findById(resourceServer.getId()), owner);
Resource resource = getDelegate().create(name, getDelegateStoreFactory().getResourceServerStore().findById(resourceServer.getId()), owner);
this.transaction.whenRollback(() -> {
resolveResourceServerCache(resourceServer.getId()).remove(getCacheKeyForResource(resource.getId()));
@ -176,14 +176,14 @@ public class CachedResourceStore implements ResourceStore {
private ResourceStore getDelegate() {
if (this.delegate == null) {
this.delegate = getStoreFactory().getResourceStore();
this.delegate = getDelegateStoreFactory().getResourceStore();
}
return this.delegate;
}
private StoreFactory getStoreFactory() {
return this.storeFactory;
private StoreFactory getDelegateStoreFactory() {
return this.delegateStoreFactory;
}
private Resource createAdapter(CachedResource cached) {
@ -270,7 +270,7 @@ public class CachedResourceStore implements ResourceStore {
@Override
public void updateScopes(Set<Scope> scopes) {
getDelegateForUpdate().updateScopes(scopes.stream().map(scope -> getStoreFactory().getScopeStore().findById(scope.getId(), cached.getResourceServerId())).collect(Collectors.toSet()));
getDelegateForUpdate().updateScopes(scopes.stream().map(scope -> getDelegateStoreFactory().getScopeStore().findById(scope.getId(), cached.getResourceServerId())).collect(Collectors.toSet()));
cached.updateScopes(scopes);
}
@ -293,7 +293,7 @@ public class CachedResourceStore implements ResourceStore {
}
private CachedStoreFactoryProvider getCachedStoreFactory() {
return session.getProvider(CachedStoreFactoryProvider.class);
return cacheStoreFactory;
}
private List<Resource> cacheResult(String resourceServerId, String key, Supplier<List<Resource>> provider) {

View file

@ -44,17 +44,17 @@ public class CachedScopeStore implements ScopeStore {
private static final String SCOPE_NAME_CACHE_PREFIX = "scp-name-";
private final Cache<String, Map<String, List<CachedScope>>> cache;
private final KeycloakSession session;
private final CachedStoreFactoryProvider cacheStoreFactory;
private final CacheTransaction transaction;
private ScopeStore delegate;
private StoreFactory storeFactory;
public CachedScopeStore(KeycloakSession session, CacheTransaction transaction, StoreFactory storeFactory) {
this.session = session;
public CachedScopeStore(KeycloakSession session, CachedStoreFactoryProvider cacheStoreFactory, CacheTransaction transaction, StoreFactory delegate) {
this.cacheStoreFactory = cacheStoreFactory;
this.transaction = transaction;
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
this.cache = provider.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME);
this.storeFactory = storeFactory;
this.storeFactory = delegate;
}
@Override
@ -220,7 +220,7 @@ public class CachedScopeStore implements ScopeStore {
}
private CachedStoreFactoryProvider getCachedStoreFactory() {
return session.getProvider(CachedStoreFactoryProvider.class);
return cacheStoreFactory;
}
private void invalidateCache(String resourceServerId) {

View file

@ -18,6 +18,9 @@
package org.keycloak.models.authorization.infinispan;
import java.util.ArrayList;
import java.util.List;
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.ResourceServerStore;
import org.keycloak.authorization.store.ResourceStore;
@ -27,31 +30,25 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakTransaction;
import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
import java.util.ArrayList;
import java.util.List;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class InfinispanStoreFactoryProvider implements CachedStoreFactoryProvider {
private final KeycloakSession session;
private final CacheTransaction transaction;
private final StoreFactory storeFactory;
private final CachedResourceStore resourceStore;
private final CachedScopeStore scopeStore;
private final CachedPolicyStore policyStore;
private ResourceServerStore resourceServerStore;
InfinispanStoreFactoryProvider(KeycloakSession delegate) {
this.session = delegate;
public InfinispanStoreFactoryProvider(KeycloakSession session) {
this.transaction = new CacheTransaction();
this.session.getTransactionManager().enlistAfterCompletion(transaction);
storeFactory = this.session.getProvider(StoreFactory.class);
resourceStore = new CachedResourceStore(this.session, this.transaction, storeFactory);
resourceServerStore = new CachedResourceServerStore(this.session, this.transaction, storeFactory);
scopeStore = new CachedScopeStore(this.session, this.transaction, storeFactory);
policyStore = new CachedPolicyStore(this.session, this.transaction, storeFactory);
session.getTransactionManager().enlistAfterCompletion(transaction);
StoreFactory delegate = session.getProvider(StoreFactory.class);
resourceStore = new CachedResourceStore(session, this, this.transaction, delegate);
resourceServerStore = new CachedResourceServerStore(session, this.transaction, delegate);
scopeStore = new CachedScopeStore(session, this, this.transaction, delegate);
policyStore = new CachedPolicyStore(session, this, this.transaction, delegate);
}
@Override

View file

@ -19,6 +19,8 @@
package org.keycloak.models.authorization.infinispan;
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;

View file

@ -160,7 +160,7 @@ public class CachedPolicy implements Policy, Serializable {
@Override
public void removeResource(Resource resource) {
this.resourcesIds.add(resource.getId());
this.resourcesIds.remove(resource.getId());
}
@Override

View file

@ -637,6 +637,11 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
while(itr.hasNext()) {
UserSessionEntity entity = (UserSessionEntity) itr.next().getValue();
Set<String> currClientSessions = entity.getClientSessions();
if (currClientSessions == null) {
continue;
}
for (String clientSessionId : currClientSessions) {
ClientSessionEntity cls = (ClientSessionEntity) offlineSessionCache.get(clientSessionId);
if (cls != null) {

View file

@ -18,14 +18,15 @@
package org.keycloak.authorization.jpa.store;
import javax.persistence.EntityManager;
import org.keycloak.Config;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.store.AuthorizationStoreFactory;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.connections.jpa.JpaConnectionProvider;
import org.keycloak.models.KeycloakSession;
import javax.persistence.EntityManager;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/

View file

@ -17,14 +17,10 @@
*/
package org.keycloak.authorization.jpa.store;
import org.keycloak.authorization.jpa.entities.PolicyEntity;
import org.keycloak.authorization.jpa.entities.ResourceServerEntity;
import org.keycloak.authorization.jpa.entities.ScopeEntity;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.models.utils.KeycloakModelUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
@ -33,11 +29,14 @@ import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.keycloak.authorization.jpa.entities.PolicyEntity;
import org.keycloak.authorization.jpa.entities.ResourceServerEntity;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@ -51,23 +50,19 @@ public class JPAPolicyStore implements PolicyStore {
}
@Override
public Policy create(String name, String type, ResourceServer resourceServer) {
public Policy create(AbstractPolicyRepresentation representation, ResourceServer resourceServer) {
PolicyEntity entity = new PolicyEntity();
entity.setId(KeycloakModelUtils.generateId());
entity.setName(name);
entity.setType(type);
entity.setType(representation.getType());
entity.setName(representation.getName());
entity.setResourceServer((ResourceServerEntity) resourceServer);
this.entityManager.persist(entity);
this.entityManager.flush();
return entity;
}
public EntityManager getEntityManager() {
return this.entityManager;
}
@Override
public void delete(String id) {
Policy policy = entityManager.find(PolicyEntity.class, id);
@ -99,7 +94,7 @@ public class JPAPolicyStore implements PolicyStore {
@Override
public Policy findByName(String name, String resourceServerId) {
try {
Query query = getEntityManager().createQuery("from PolicyEntity where name = :name and resourceServer.id = :serverId");
Query query = entityManager.createQuery("from PolicyEntity where name = :name and resourceServer.id = :serverId");
query.setParameter("name", name);
query.setParameter("serverId", resourceServerId);
@ -112,7 +107,7 @@ public class JPAPolicyStore implements PolicyStore {
@Override
public List<Policy> findByResourceServer(final String resourceServerId) {
Query query = getEntityManager().createQuery("from PolicyEntity where resourceServer.id = :serverId");
Query query = entityManager.createQuery("from PolicyEntity where resourceServer.id = :serverId");
query.setParameter("serverId", resourceServerId);
@ -158,7 +153,7 @@ public class JPAPolicyStore implements PolicyStore {
@Override
public List<Policy> findByResource(final String resourceId, String resourceServerId) {
Query query = getEntityManager().createQuery("select p from PolicyEntity p inner join p.resources r where p.resourceServer.id = :serverId and (r.resourceServer.id = :serverId and r.id = :resourceId)");
Query query = entityManager.createQuery("select p from PolicyEntity p inner join p.resources r where p.resourceServer.id = :serverId and (r.resourceServer.id = :serverId and r.id = :resourceId)");
query.setParameter("resourceId", resourceId);
query.setParameter("serverId", resourceServerId);
@ -168,7 +163,7 @@ public class JPAPolicyStore implements PolicyStore {
@Override
public List<Policy> findByResourceType(final String resourceType, String resourceServerId) {
Query query = getEntityManager().createQuery("select p from PolicyEntity p inner join p.config c where p.resourceServer.id = :serverId and KEY(c) = 'defaultResourceType' and c like :type");
Query query = entityManager.createQuery("select p from PolicyEntity p inner join p.config c where p.resourceServer.id = :serverId and KEY(c) = 'defaultResourceType' and c like :type");
query.setParameter("serverId", resourceServerId);
query.setParameter("type", resourceType);
@ -183,7 +178,7 @@ public class JPAPolicyStore implements PolicyStore {
}
// Use separate subquery to handle DB2 and MSSSQL
Query query = getEntityManager().createQuery("select pe from PolicyEntity pe where pe.resourceServer.id = :serverId and pe.id IN (select p.id from ScopeEntity s inner join s.policies p where s.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.type = 'scope' and s.id in (:scopeIds)))");
Query query = entityManager.createQuery("select pe from PolicyEntity pe where pe.resourceServer.id = :serverId and pe.id IN (select p.id from ScopeEntity s inner join s.policies p where s.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.type = 'scope' and s.id in (:scopeIds)))");
query.setParameter("serverId", resourceServerId);
query.setParameter("scopeIds", scopeIds);
@ -193,7 +188,7 @@ public class JPAPolicyStore implements PolicyStore {
@Override
public List<Policy> findByType(String type, String resourceServerId) {
Query query = getEntityManager().createQuery("select p from PolicyEntity p where p.resourceServer.id = :serverId and p.type = :type");
Query query = entityManager.createQuery("select p from PolicyEntity p where p.resourceServer.id = :serverId and p.type = :type");
query.setParameter("serverId", resourceServerId);
query.setParameter("type", type);
@ -203,7 +198,7 @@ public class JPAPolicyStore implements PolicyStore {
@Override
public List<Policy> findDependentPolicies(String policyId, String resourceServerId) {
Query query = getEntityManager().createQuery("select p from PolicyEntity p inner join p.associatedPolicies ap where p.resourceServer.id = :serverId and (ap.resourceServer.id = :serverId and ap.id = :policyId)");
Query query = entityManager.createQuery("select p from PolicyEntity p inner join p.associatedPolicies ap where p.resourceServer.id = :serverId and (ap.resourceServer.id = :serverId and ap.id = :policyId)");
query.setParameter("serverId", resourceServerId);
query.setParameter("policyId", policyId);

View file

@ -18,43 +18,49 @@
package org.keycloak.authorization.jpa.store;
import javax.persistence.EntityManager;
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.ResourceServerStore;
import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.authorization.store.ScopeStore;
import org.keycloak.authorization.store.StoreFactory;
import javax.persistence.EntityManager;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class JPAStoreFactory implements StoreFactory {
private final EntityManager entityManager;
private final PolicyStore policyStore;
private final ResourceServerStore resourceServerStore;
private final ResourceStore resourceStore;
private final ScopeStore scopeStore;
public JPAStoreFactory(EntityManager entityManager) {
this.entityManager = entityManager;
policyStore = new JPAPolicyStore(entityManager);
resourceServerStore = new JPAResourceServerStore(entityManager);
resourceStore = new JPAResourceStore(entityManager);
scopeStore = new JPAScopeStore(entityManager);
}
@Override
public PolicyStore getPolicyStore() {
return new JPAPolicyStore(this.entityManager);
return policyStore;
}
@Override
public ResourceServerStore getResourceServerStore() {
return new JPAResourceServerStore(this.entityManager);
return resourceServerStore;
}
@Override
public ResourceStore getResourceStore() {
return new JPAResourceStore(this.entityManager);
return resourceStore;
}
@Override
public ScopeStore getScopeStore() {
return new JPAScopeStore(this.entityManager);
return scopeStore;
}
@Override

16
pom.xml
View file

@ -42,10 +42,12 @@
<!-- WildFly -->
<jboss.as.version>7.2.0.Final</jboss.as.version>
<wildfly.version>10.0.0.Final</wildfly.version>
<wildfly.build-tools.version>1.1.3.Final</wildfly.build-tools.version>
<wildfly11.version>11.0.0.Alpha1</wildfly11.version> <!-- for testing with wf11 pre-releases -->
<wildfly11.build-tools.version>1.1.8.Final</wildfly11.build-tools.version>
<eap.version>7.1.0.Alpha1-redhat-16</eap.version>
<eap.build-tools.version>1.1.8.Final</eap.build-tools.version>
<wildfly.core.version>2.0.10.Final</wildfly.core.version>
<wildfly.build-tools.version>1.1.8.Final</wildfly.build-tools.version>
<version.org.wildfly.security.wildfly-elytron>1.1.0.Beta32</version.org.wildfly.security.wildfly-elytron>
<version.org.wildfly.security.elytron-web.undertow-server>1.0.0.Beta14</version.org.wildfly.security.elytron-web.undertow-server>
@ -1391,6 +1393,13 @@
<serverId>jboss-releases-repository</serverId>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<tarLongFileMode>posix</tarLongFileMode>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
@ -1461,11 +1470,6 @@
<artifactId>liquibase-maven-plugin</artifactId>
<version>${liquibase.version}</version>
</plugin>
<plugin>
<groupId>org.wildfly.build</groupId>
<artifactId>wildfly-feature-pack-build-maven-plugin</artifactId>
<version>${wildfly.build-tools.version}</version>
</plugin>
<plugin>
<groupId>org.wildfly.build</groupId>
<artifactId>wildfly-server-provisioning-maven-plugin</artifactId>

View file

@ -21,17 +21,23 @@ package org.keycloak.authorization;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.permission.evaluator.Evaluators;
import org.keycloak.authorization.policy.evaluation.DefaultPolicyEvaluator;
import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.ResourceServerStore;
import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.authorization.store.ScopeStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.provider.Provider;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
/**
* <p>The main contract here is the creation of {@link org.keycloak.authorization.permission.evaluator.PermissionEvaluator} instances. Usually
@ -62,25 +68,19 @@ import org.keycloak.provider.Provider;
public final class AuthorizationProvider implements Provider {
private final DefaultPolicyEvaluator policyEvaluator;
private final Executor scheduller;
private final Supplier<StoreFactory> storeFactory;
private StoreFactory storeFactory;
private final Map<String, PolicyProviderFactory> policyProviderFactories;
private final KeycloakSession keycloakSession;
private final RealmModel realm;
public AuthorizationProvider(KeycloakSession session, RealmModel realm, Supplier<StoreFactory> storeFactory, Map<String, PolicyProviderFactory> policyProviderFactories, Executor scheduller) {
public AuthorizationProvider(KeycloakSession session, RealmModel realm, StoreFactory storeFactory, Map<String, PolicyProviderFactory> policyProviderFactories) {
this.keycloakSession = session;
this.realm = realm;
this.storeFactory = storeFactory;
this.scheduller = scheduller;
this.policyProviderFactories = policyProviderFactories;
this.policyEvaluator = new DefaultPolicyEvaluator(this);
}
public AuthorizationProvider(KeycloakSession session, RealmModel realm, StoreFactory storeFactory, Map<String, PolicyProviderFactory> policyProviderFactories) {
this(session, realm, () -> storeFactory, policyProviderFactories, Runnable::run);
}
/**
* Returns a {@link Evaluators} instance from where {@link org.keycloak.authorization.policy.evaluation.PolicyEvaluator} instances
* can be obtained.
@ -88,7 +88,7 @@ public final class AuthorizationProvider implements Provider {
* @return a {@link Evaluators} instance
*/
public Evaluators evaluators() {
return new Evaluators(this.policyEvaluator, this.scheduller);
return new Evaluators(policyEvaluator);
}
/**
@ -97,7 +97,105 @@ public final class AuthorizationProvider implements Provider {
* @return the {@link StoreFactory}
*/
public StoreFactory getStoreFactory() {
return this.storeFactory.get();
return createStoreFactory();
}
private StoreFactory createStoreFactory() {
return new StoreFactory() {
@Override
public ResourceStore getResourceStore() {
return storeFactory.getResourceStore();
}
@Override
public ResourceServerStore getResourceServerStore() {
return storeFactory.getResourceServerStore();
}
@Override
public ScopeStore getScopeStore() {
return storeFactory.getScopeStore();
}
@Override
public PolicyStore getPolicyStore() {
PolicyStore policyStore = storeFactory.getPolicyStore();
return new PolicyStore() {
@Override
public Policy create(AbstractPolicyRepresentation representation, ResourceServer resourceServer) {
return RepresentationToModel.toModel(representation, AuthorizationProvider.this, policyStore.create(representation, resourceServer));
}
@Override
public void delete(String id) {
Policy policy = findById(id, null);
if (policy != null) {
ResourceServer resourceServer = policy.getResourceServer();
findDependentPolicies(policy.getId(), resourceServer.getId()).forEach(dependentPolicy -> {
dependentPolicy.removeAssociatedPolicy(policy);
if (dependentPolicy.getAssociatedPolicies().isEmpty()) {
delete(dependentPolicy.getId());
}
});
policyStore.delete(id);
}
}
@Override
public Policy findById(String id, String resourceServerId) {
return policyStore.findById(id, resourceServerId);
}
@Override
public Policy findByName(String name, String resourceServerId) {
return policyStore.findByName(name, resourceServerId);
}
@Override
public List<Policy> findByResourceServer(String resourceServerId) {
return policyStore.findByResourceServer(resourceServerId);
}
@Override
public List<Policy> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
return policyStore.findByResourceServer(attributes, resourceServerId, firstResult, maxResult);
}
@Override
public List<Policy> findByResource(String resourceId, String resourceServerId) {
return policyStore.findByResource(resourceId, resourceServerId);
}
@Override
public List<Policy> findByResourceType(String resourceType, String resourceServerId) {
return policyStore.findByResourceType(resourceType, resourceServerId);
}
@Override
public List<Policy> findByScopeIds(List<String> scopeIds, String resourceServerId) {
return policyStore.findByScopeIds(scopeIds, resourceServerId);
}
@Override
public List<Policy> findByType(String type, String resourceServerId) {
return policyStore.findByType(type, resourceServerId);
}
@Override
public List<Policy> findDependentPolicies(String id, String resourceServerId) {
return policyStore.findDependentPolicies(id, resourceServerId);
}
};
}
@Override
public void close() {
storeFactory.close();
}
};
}
/**

View file

@ -17,22 +17,18 @@
*/
package org.keycloak.authorization.permission.evaluator;
import java.util.concurrent.Executor;
import org.keycloak.authorization.Decision;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
* @see PermissionEvaluator
*/
class ScheduledPermissionEvaluator implements PermissionEvaluator {
class DefaultPermissionEvaluator implements PermissionEvaluator {
private final PermissionEvaluator publisher;
private final Executor scheduler;
ScheduledPermissionEvaluator(PermissionEvaluator publisher, Executor scheduler) {
DefaultPermissionEvaluator(PermissionEvaluator publisher) {
this.publisher = publisher;
this.scheduler = scheduler;
}
@Override

View file

@ -19,7 +19,6 @@
package org.keycloak.authorization.permission.evaluator;
import java.util.List;
import java.util.concurrent.Executor;
import org.keycloak.authorization.permission.ResourcePermission;
import org.keycloak.authorization.policy.evaluation.DefaultPolicyEvaluator;
@ -33,11 +32,9 @@ import org.keycloak.authorization.policy.evaluation.EvaluationContext;
public final class Evaluators {
private final DefaultPolicyEvaluator policyEvaluator;
private final Executor scheduler;
public Evaluators(DefaultPolicyEvaluator policyEvaluator, Executor scheduler) {
public Evaluators(DefaultPolicyEvaluator policyEvaluator) {
this.policyEvaluator = policyEvaluator;
this.scheduler = scheduler;
}
public PermissionEvaluator from(List<ResourcePermission> permissions, EvaluationContext evaluationContext) {
@ -45,6 +42,6 @@ public final class Evaluators {
}
public PermissionEvaluator schedule(List<ResourcePermission> permissions, EvaluationContext evaluationContext) {
return new ScheduledPermissionEvaluator(new IterablePermissionEvaluator(permissions.iterator(), evaluationContext, this.policyEvaluator), this.scheduler);
return new DefaultPermissionEvaluator(new IterablePermissionEvaluator(permissions.iterator(), evaluationContext, this.policyEvaluator));
}
}

View file

@ -35,6 +35,7 @@ import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.permission.ResourcePermission;
import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
@ -46,11 +47,13 @@ public class DefaultPolicyEvaluator implements PolicyEvaluator {
private final AuthorizationProvider authorization;
private final StoreFactory storeFactory;
private final PolicyStore policyStore;
private final ResourceStore resourceStore;
public DefaultPolicyEvaluator(AuthorizationProvider authorization) {
this.authorization = authorization;
storeFactory = this.authorization.getStoreFactory();
policyStore = storeFactory.getPolicyStore();
resourceStore = storeFactory.getResourceStore();
}
@Override
@ -159,7 +162,7 @@ public class DefaultPolicyEvaluator implements PolicyEvaluator {
String type = resource.getType();
if (type != null) {
List<Resource> resourcesByType = authorization.getStoreFactory().getResourceStore().findByType(type, resource.getResourceServer().getId());
List<Resource> resourcesByType = resourceStore.findByType(type, resource.getResourceServer().getId());
for (Resource resourceType : resourcesByType) {
if (resourceType.getOwner().equals(resource.getResourceServer().getClientId())) {

View file

@ -18,16 +18,11 @@
package org.keycloak.authorization.policy.provider;
import org.keycloak.authorization.model.Policy;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public interface PolicyProviderAdminService {
public interface PolicyProviderAdminService<R extends AbstractPolicyRepresentation> {
void onCreate(Policy policy);
void onUpdate(Policy policy);
void onRemove(Policy policy);
}

View file

@ -22,11 +22,13 @@ import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public interface PolicyProviderFactory extends ProviderFactory<PolicyProvider> {
public interface PolicyProviderFactory<R extends AbstractPolicyRepresentation> extends ProviderFactory<PolicyProvider> {
String getName();
@ -34,5 +36,31 @@ public interface PolicyProviderFactory extends ProviderFactory<PolicyProvider> {
PolicyProvider create(AuthorizationProvider authorization);
PolicyProviderAdminService getAdminResource(ResourceServer resourceServer);
default R toRepresentation(Policy policy, R representation) {
return representation;
}
default Class<R> getRepresentationType() {
return (Class<R>) PolicyRepresentation.class;
}
default void onCreate(Policy policy, R representation, AuthorizationProvider authorization) {
}
default void onUpdate(Policy policy, R representation, AuthorizationProvider authorization) {
}
default void onRemove(Policy policy, AuthorizationProvider authorization) {
}
default void onImport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
}
default PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) {
return null;
}
}

View file

@ -18,6 +18,9 @@
package org.keycloak.authorization.store;
import java.util.HashMap;
import java.util.Map;
import org.keycloak.authorization.store.syncronization.ClientApplicationSynchronizer;
import org.keycloak.authorization.store.syncronization.RealmSynchronizer;
import org.keycloak.authorization.store.syncronization.Synchronizer;
@ -29,9 +32,6 @@ import org.keycloak.models.UserModel.UserRemovedEvent;
import org.keycloak.provider.ProviderEvent;
import org.keycloak.provider.ProviderFactory;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/

View file

@ -18,12 +18,13 @@
package org.keycloak.authorization.store;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import java.util.List;
import java.util.Map;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
/**
* A {@link PolicyStore} is responsible to manage the persistence of {@link Policy} instances.
*
@ -35,12 +36,11 @@ public interface PolicyStore {
* Creates a new {@link Policy} instance. The new instance is not necessarily persisted though, which may require
* a call to the {#save} method to actually make it persistent.
*
* @param name the name of the policy
* @param type the type of the policy
* @param representation the policy representation
* @param resourceServer the resource server to which this policy belongs
* @return a new instance of {@link Policy}
*/
Policy create(String name, String type, ResourceServer resourceServer);
Policy create(AbstractPolicyRepresentation representation, ResourceServer resourceServer);
/**
* Deletes a policy from the underlying persistence mechanism.

View file

@ -33,6 +33,7 @@ import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.common.util.Time;
@ -87,6 +88,7 @@ import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserConsentRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.UserSessionRepresentation;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
@ -793,16 +795,29 @@ public class ModelToRepresentation {
return server;
}
public static PolicyRepresentation toRepresentation(Policy model) {
PolicyRepresentation representation = new PolicyRepresentation();
public static <R extends AbstractPolicyRepresentation> R toRepresentation(Policy policy, Class<R> representationType, AuthorizationProvider authorization) {
R representation;
representation.setId(model.getId());
representation.setName(model.getName());
representation.setDescription(model.getDescription());
representation.setType(model.getType());
representation.setDecisionStrategy(model.getDecisionStrategy());
representation.setLogic(model.getLogic());
representation.setConfig(new HashMap<>(model.getConfig()));
try {
representation = representationType.newInstance();
} catch (Exception cause) {
throw new RuntimeException("Could not create policy [" + policy.getType() + "] representation", cause);
}
PolicyProviderFactory providerFactory = authorization.getProviderFactory(policy.getType());
representation.setId(policy.getId());
representation.setName(policy.getName());
representation.setDescription(policy.getDescription());
representation.setType(policy.getType());
representation.setDecisionStrategy(policy.getDecisionStrategy());
representation.setLogic(policy.getLogic());
if (representation instanceof PolicyRepresentation) {
PolicyRepresentation.class.cast(representation).setConfig(policy.getConfig());
} else {
representation = (R) providerFactory.toRepresentation(policy, representation);
}
return representation;
}

View file

@ -17,6 +17,18 @@
package org.keycloak.models.utils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.jboss.logging.Logger;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.AuthorizationProviderFactory;
@ -24,7 +36,7 @@ import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.policy.provider.PolicyProvider;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.ResourceServerStore;
import org.keycloak.authorization.store.ResourceStore;
@ -92,6 +104,7 @@ import org.keycloak.representations.idm.UserConsentRepresentation;
import org.keycloak.representations.idm.UserFederationMapperRepresentation;
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
@ -103,18 +116,6 @@ import org.keycloak.storage.UserStorageProviderModel;
import org.keycloak.storage.federated.UserFederatedStorageProvider;
import org.keycloak.util.JsonSerialization;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
public class RepresentationToModel {
private static Logger logger = Logger.getLogger(RepresentationToModel.class);
@ -1896,9 +1897,6 @@ public class RepresentationToModel {
resourceServer.setPolicyEnforcementMode(rep.getPolicyEnforcementMode());
resourceServer.setAllowRemoteResourceManagement(rep.isAllowRemoteResourceManagement());
StoreFactory storeFactory = authorization.getStoreFactory();
ScopeStore scopeStore = storeFactory.getScopeStore();
rep.getScopes().forEach(scope -> {
toModel(scope, resourceServer, authorization);
});
@ -1932,138 +1930,12 @@ public class RepresentationToModel {
private static Policy importPolicies(AuthorizationProvider authorization, ResourceServer resourceServer, List<PolicyRepresentation> policiesToImport, String parentPolicyName) {
StoreFactory storeFactory = authorization.getStoreFactory();
KeycloakSession session = authorization.getKeycloakSession();
RealmModel realm = authorization.getRealm();
for (PolicyRepresentation policyRepresentation : policiesToImport) {
if (parentPolicyName != null && !parentPolicyName.equals(policyRepresentation.getName())) {
continue;
}
Map<String, String> config = policyRepresentation.getConfig();
String roles = config.get("roles");
if (roles != null && !roles.isEmpty()) {
try {
List<Map> rolesMap = (List<Map>) JsonSerialization.readValue(roles, List.class);
config.put("roles", JsonSerialization.writeValueAsString(rolesMap.stream().map(roleConfig -> {
String roleName = roleConfig.get("id").toString();
String clientId = null;
int clientIdSeparator = roleName.indexOf("/");
if (clientIdSeparator != -1) {
clientId = roleName.substring(0, clientIdSeparator);
roleName = roleName.substring(clientIdSeparator + 1);
}
RoleModel role;
if (clientId == null) {
role = realm.getRole(roleName);
} else {
role = realm.getClientByClientId(clientId).getRole(roleName);
}
// fallback to find any client role with the given name
if (role == null) {
String finalRoleName = roleName;
role = realm.getClients().stream().map(clientModel -> clientModel.getRole(finalRoleName)).filter(roleModel -> roleModel != null)
.findFirst().orElse(null);
}
if (role == null) {
role = realm.getRoleById(roleName);
if (role == null) {
String finalRoleName1 = roleName;
role = realm.getClients().stream().map(clientModel -> clientModel.getRole(finalRoleName1)).filter(roleModel -> roleModel != null)
.findFirst().orElse(null);
}
}
if (role == null) {
throw new RuntimeException("Error while importing configuration. Role [" + roleName + "] could not be found.");
}
roleConfig.put("id", role.getId());
return roleConfig;
}).collect(Collectors.toList())));
} catch (Exception e) {
throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
}
}
String users = config.get("users");
if (users != null && !users.isEmpty()) {
try {
List<String> usersMap = (List<String>) JsonSerialization.readValue(users, List.class);
config.put("users", JsonSerialization.writeValueAsString(usersMap.stream().map(userId -> {
UserModel user = session.users().getUserByUsername(userId, realm);
if (user == null) {
user = session.users().getUserById(userId, realm);
}
if (user == null) {
throw new RuntimeException("Error while importing configuration. User [" + userId + "] could not be found.");
}
return user.getId();
}).collect(Collectors.toList())));
} catch (Exception e) {
throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
}
}
String scopes = config.get("scopes");
if (scopes != null && !scopes.isEmpty()) {
try {
ScopeStore scopeStore = storeFactory.getScopeStore();
List<String> scopesMap = (List<String>) JsonSerialization.readValue(scopes, List.class);
config.put("scopes", JsonSerialization.writeValueAsString(scopesMap.stream().map(scopeName -> {
Scope newScope = scopeStore.findByName(scopeName, resourceServer.getId());
if (newScope == null) {
newScope = scopeStore.findById(scopeName, resourceServer.getId());
}
if (newScope == null) {
throw new RuntimeException("Scope with name [" + scopeName + "] not defined.");
}
return newScope.getId();
}).collect(Collectors.toList())));
} catch (Exception e) {
throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
}
}
String policyResources = config.get("resources");
if (policyResources != null && !policyResources.isEmpty()) {
ResourceStore resourceStore = storeFactory.getResourceStore();
try {
List<String> resources = JsonSerialization.readValue(policyResources, List.class);
config.put("resources", JsonSerialization.writeValueAsString(resources.stream().map(resourceName -> {
Resource resource = resourceStore.findByName(resourceName, resourceServer.getId());
if (resource == null) {
resource = resourceStore.findById(resourceName, resourceServer.getId());
}
if (resource == null) {
throw new RuntimeException("Resource with name [" + resourceName + "] not defined.");
}
return resource.getId();
}).collect(Collectors.toList())));
} catch (Exception e) {
throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
}
}
String applyPolicies = config.get("applyPolicies");
if (applyPolicies != null && !applyPolicies.isEmpty()) {
@ -2087,91 +1959,111 @@ public class RepresentationToModel {
return policy.getId();
}).collect(Collectors.toList())));
} catch (Exception e) {
throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
throw new RuntimeException("Error while importing policy [" + policyRepresentation.getName() + "].", e);
}
}
if (parentPolicyName == null) {
toModel(policyRepresentation, resourceServer, authorization);
} else if (parentPolicyName.equals(policyRepresentation.getName())) {
return toModel(policyRepresentation, resourceServer, authorization);
PolicyStore policyStore = storeFactory.getPolicyStore();
Policy policy = policyStore.findById(policyRepresentation.getId(), resourceServer.getId());
if (policy == null) {
policy = policyStore.findByName(policyRepresentation.getName(), resourceServer.getId());
}
if (policy == null) {
policy = policyStore.create(policyRepresentation, resourceServer);
} else {
policy = toModel(policyRepresentation, authorization, policy);
}
if (parentPolicyName != null && parentPolicyName.equals(policyRepresentation.getName())) {
return policy;
}
}
return null;
}
public static Policy toModel(PolicyRepresentation policy, ResourceServer resourceServer, AuthorizationProvider authorization) {
String type = policy.getType();
PolicyProvider provider = authorization.getProvider(type);
public static Policy toModel(AbstractPolicyRepresentation representation, AuthorizationProvider authorization, Policy model) {
model.setName(representation.getName());
model.setDescription(representation.getDescription());
model.setDecisionStrategy(representation.getDecisionStrategy());
model.setLogic(representation.getLogic());
if (provider == null) {
//TODO: temporary, remove this check on future versions as drools type is now deprecated
if ("drools".equalsIgnoreCase(type)) {
type = "rules";
Set resources = representation.getResources();
Set scopes = representation.getScopes();
Set policies = representation.getPolicies();
if (representation instanceof PolicyRepresentation) {
PolicyRepresentation policy = PolicyRepresentation.class.cast(representation);
String resourcesConfig = policy.getConfig().get("resources");
if (resourcesConfig != null) {
try {
resources = JsonSerialization.readValue(resourcesConfig, Set.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (authorization.getProvider(type) == null) {
throw new RuntimeException("Unknown polucy type [" + type + "]. Could not find a provider for this type.");
String scopesConfig = policy.getConfig().get("scopes");
if (scopesConfig != null) {
try {
scopes = JsonSerialization.readValue(scopesConfig, Set.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
String policiesConfig = policy.getConfig().get("applyPolicies");
if (policiesConfig != null) {
try {
policies = JsonSerialization.readValue(policiesConfig, Set.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
model.setConfig(policy.getConfig());
}
PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore();
Policy existing;
StoreFactory storeFactory = authorization.getStoreFactory();
if (policy.getId() != null) {
existing = policyStore.findById(policy.getId(), resourceServer.getId());
updateResources(resources, model, storeFactory);
updateScopes(scopes, model, storeFactory);
updateAssociatedPolicies(policies, model, storeFactory);
PolicyProviderFactory provider = authorization.getProviderFactory(model.getType());
if (representation instanceof PolicyRepresentation) {
provider.onImport(model, PolicyRepresentation.class.cast(representation), authorization);
} else if (representation.getId() == null) {
provider.onCreate(model, representation, authorization);
} else {
existing = policyStore.findByName(policy.getName(), resourceServer.getId());
provider.onUpdate(model, representation, authorization);
}
if (existing != null) {
existing.setName(policy.getName());
existing.setDescription(policy.getDescription());
existing.setConfig(policy.getConfig());
existing.setDecisionStrategy(policy.getDecisionStrategy());
existing.setLogic(policy.getLogic());
updateResources(existing, authorization);
updateAssociatedPolicies(existing, resourceServer, authorization);
updateScopes(existing, authorization);
return existing;
}
Policy model = policyStore.create(policy.getName(), type, resourceServer);
model.setDescription(policy.getDescription());
model.setDecisionStrategy(policy.getDecisionStrategy());
model.setLogic(policy.getLogic());
model.setConfig(policy.getConfig());
updateResources(model, authorization);
updateAssociatedPolicies(model, resourceServer, authorization);
updateScopes(model, authorization);
policy.setId(model.getId());
representation.setId(model.getId());
return model;
}
private static void updateScopes(Policy policy, AuthorizationProvider authorization) {
String scopes = policy.getConfig().get("scopes");
if (scopes != null) {
String[] scopeIds;
try {
scopeIds = JsonSerialization.readValue(scopes, String[].class);
} catch (IOException e) {
throw new RuntimeException(e);
private static void updateScopes(Set<String> scopeIds, Policy policy, StoreFactory storeFactory) {
if (scopeIds != null) {
if (scopeIds.isEmpty()) {
for (Scope scope : new HashSet<Scope>(policy.getScopes())) {
policy.removeScope(scope);
}
return;
}
StoreFactory storeFactory = authorization.getStoreFactory();
for (String scopeId : scopeIds) {
boolean hasScope = false;
for (Scope scopeModel : new HashSet<Scope>(policy.getScopes())) {
if (scopeModel.getId().equals(scopeId)) {
if (scopeModel.getId().equals(scopeId) || scopeModel.getName().equals(scopeId)) {
hasScope = true;
}
}
@ -2180,7 +2072,10 @@ public class RepresentationToModel {
Scope scope = storeFactory.getScopeStore().findById(scopeId, resourceServer.getId());
if (scope == null) {
storeFactory.getScopeStore().findByName(scopeId, resourceServer.getId());
scope = storeFactory.getScopeStore().findByName(scopeId, resourceServer.getId());
if (scope == null) {
throw new RuntimeException("Scope with id or name [" + scopeId + "] does not exist");
}
}
policy.addScope(scope);
@ -2191,7 +2086,7 @@ public class RepresentationToModel {
boolean hasScope = false;
for (String scopeId : scopeIds) {
if (scopeModel.getId().equals(scopeId)) {
if (scopeModel.getId().equals(scopeId) || scopeModel.getName().equals(scopeId)) {
hasScope = true;
}
}
@ -2199,24 +2094,22 @@ public class RepresentationToModel {
policy.removeScope(scopeModel);
}
}
policy.getConfig().remove("scopes");
}
policy.getConfig().remove("scopes");
}
private static void updateAssociatedPolicies(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization) {
String policies = policy.getConfig().get("applyPolicies");
private static void updateAssociatedPolicies(Set<String> policyIds, Policy policy, StoreFactory storeFactory) {
ResourceServer resourceServer = policy.getResourceServer();
if (policies != null) {
String[] policyIds;
try {
policyIds = JsonSerialization.readValue(policies, String[].class);
} catch (IOException e) {
throw new RuntimeException(e);
if (policyIds != null) {
if (policyIds.isEmpty()) {
for (Policy associated: new HashSet<Policy>(policy.getAssociatedPolicies())) {
policy.removeAssociatedPolicy(associated);
}
return;
}
StoreFactory storeFactory = authorization.getStoreFactory();
PolicyStore policyStore = storeFactory.getPolicyStore();
for (String policyId : policyIds) {
@ -2228,12 +2121,14 @@ public class RepresentationToModel {
}
}
if (!hasPolicy) {
Policy associatedPolicy = policyStore.findById(policyId, resourceServer.getId());
if (associatedPolicy == null) {
associatedPolicy = policyStore.findByName(policyId, resourceServer.getId());
if (associatedPolicy == null) {
throw new RuntimeException("Policy with id or name [" + policyId + "] does not exist");
}
}
policy.addAssociatedPolicy(associatedPolicy);
@ -2250,31 +2145,24 @@ public class RepresentationToModel {
}
if (!hasPolicy) {
policy.removeAssociatedPolicy(policyModel);
;
}
}
policy.getConfig().remove("applyPolicies");
}
policy.getConfig().remove("applyPolicies");
}
private static void updateResources(Policy policy, AuthorizationProvider authorization) {
String resources = policy.getConfig().get("resources");
if (resources != null) {
String[] resourceIds;
try {
resourceIds = JsonSerialization.readValue(resources, String[].class);
} catch (IOException e) {
throw new RuntimeException(e);
private static void updateResources(Set<String> resourceIds, Policy policy, StoreFactory storeFactory) {
if (resourceIds != null) {
if (resourceIds.isEmpty()) {
for (Scope scope : new HashSet<Scope>(policy.getScopes())) {
policy.removeScope(scope);
}
}
StoreFactory storeFactory = authorization.getStoreFactory();
for (String resourceId : resourceIds) {
boolean hasResource = false;
for (Resource resourceModel : new HashSet<Resource>(policy.getResources())) {
if (resourceModel.getId().equals(resourceId)) {
if (resourceModel.getId().equals(resourceId) || resourceModel.getName().equals(resourceId)) {
hasResource = true;
}
}
@ -2282,7 +2170,10 @@ public class RepresentationToModel {
Resource resource = storeFactory.getResourceStore().findById(resourceId, policy.getResourceServer().getId());
if (resource == null) {
throw new RuntimeException("Resource [" + resourceId + "] not found.");
resource = storeFactory.getResourceStore().findByName(resourceId, policy.getResourceServer().getId());
if (resource == null) {
throw new RuntimeException("Resource with id or name [" + resourceId + "] does not exist");
}
}
policy.addResource(resource);
@ -2293,7 +2184,7 @@ public class RepresentationToModel {
boolean hasResource = false;
for (String resourceId : resourceIds) {
if (resourceModel.getId().equals(resourceId)) {
if (resourceModel.getId().equals(resourceId) || resourceModel.getName().equals(resourceId)) {
hasResource = true;
}
}
@ -2302,9 +2193,9 @@ public class RepresentationToModel {
policy.removeResource(resourceModel);
}
}
policy.getConfig().remove("resources");
}
policy.getConfig().remove("resources");
}
public static Resource toModel(ResourceRepresentation resource, ResourceServer resourceServer, AuthorizationProvider authorization) {

View file

@ -21,7 +21,6 @@ package org.keycloak.authorization;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import org.keycloak.Config;
import org.keycloak.authorization.policy.provider.PolicyProvider;
@ -38,7 +37,6 @@ import org.keycloak.provider.ProviderFactory;
*/
public class DefaultAuthorizationProviderFactory implements AuthorizationProviderFactory {
private Executor scheduler;
private Map<String, PolicyProviderFactory> policyProviderFactories;
@Override
@ -48,15 +46,6 @@ public class DefaultAuthorizationProviderFactory implements AuthorizationProvide
@Override
public void init(Config.Scope config) {
//TODO: user-defined configuration
// Executor executor = Executors.newWorkStealingPool();
// this.scheduler = command -> {
// Map<Class<?>, Object> contextDataMap = ResteasyProviderFactory.getContextDataMap();
// executor.execute(() -> {
// ResteasyProviderFactory.pushContextDataMap(contextDataMap);
// command.run();
// });
// };
}
@Override
@ -77,11 +66,9 @@ public class DefaultAuthorizationProviderFactory implements AuthorizationProvide
@Override
public AuthorizationProvider create(KeycloakSession session, RealmModel realm) {
StoreFactory storeFactory = session.getProvider(CachedStoreFactoryProvider.class);
if (storeFactory == null) {
storeFactory = session.getProvider(StoreFactory.class);
}
return new AuthorizationProvider(session, realm, storeFactory, policyProviderFactories);
}

View file

@ -0,0 +1,46 @@
/*
* Copyright 2016 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.authorization.admin;
import java.util.List;
import java.util.Map;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.services.resources.admin.RealmAuth;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class PermissionService extends PolicyService {
public PermissionService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
super(resourceServer, authorization, auth);
}
@Override
protected PolicyResourceService doCreatePolicyResource(Policy policy) {
return new PolicyTypeResourceService(policy, resourceServer, authorization, auth);
}
@Override
protected List<Object> doSearch(Integer firstResult, Integer maxResult, Map<String, String[]> filters) {
filters.put("permission", new String[] {Boolean.TRUE.toString()});
return super.doSearch(firstResult, maxResult, filters);
}
}

View file

@ -0,0 +1,228 @@
/*
* Copyright 2016 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.authorization.admin;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.keycloak.services.resources.admin.RealmAuth;
import org.keycloak.util.JsonSerialization;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class PolicyResourceService {
private final Policy policy;
protected final ResourceServer resourceServer;
protected final AuthorizationProvider authorization;
protected final RealmAuth auth;
public PolicyResourceService(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
this.policy = policy;
this.resourceServer = resourceServer;
this.authorization = authorization;
this.auth = auth;
}
@PUT
@Consumes("application/json")
@Produces("application/json")
@NoCache
public Response update(String payload) {
this.auth.requireManage();
AbstractPolicyRepresentation representation = doCreateRepresentation(payload);
if (policy == null) {
return Response.status(Status.NOT_FOUND).build();
}
representation.setId(policy.getId());
RepresentationToModel.toModel(representation, authorization, policy);
return Response.status(Status.CREATED).build();
}
@DELETE
public Response delete() {
this.auth.requireManage();
if (policy == null) {
return Response.status(Status.NOT_FOUND).build();
}
StoreFactory storeFactory = authorization.getStoreFactory();
PolicyStore policyStore = storeFactory.getPolicyStore();
PolicyProviderFactory resource = getProviderFactory(policy.getType());
resource.onRemove(policy, authorization);
policyStore.delete(policy.getId());
return Response.noContent().build();
}
@GET
@Produces("application/json")
@NoCache
public Response findById() {
this.auth.requireView();
if (policy == null) {
return Response.status(Status.NOT_FOUND).build();
}
return Response.ok(toRepresentation(policy, authorization)).build();
}
protected AbstractPolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
return ModelToRepresentation.toRepresentation(policy, PolicyRepresentation.class, authorization);
}
@Path("/dependentPolicies")
@GET
@Produces("application/json")
@NoCache
public Response getDependentPolicies() {
this.auth.requireView();
if (policy == null) {
return Response.status(Status.NOT_FOUND).build();
}
List<Policy> policies = authorization.getStoreFactory().getPolicyStore().findDependentPolicies(policy.getId(), resourceServer.getId());
return Response.ok(policies.stream().map(policy -> {
PolicyRepresentation representation1 = new PolicyRepresentation();
representation1.setId(policy.getId());
representation1.setName(policy.getName());
representation1.setType(policy.getType());
return representation1;
}).collect(Collectors.toList())).build();
}
@Path("/scopes")
@GET
@Produces("application/json")
@NoCache
public Response getScopes() {
this.auth.requireView();
if (policy == null) {
return Response.status(Status.NOT_FOUND).build();
}
return Response.ok(policy.getScopes().stream().map(scope -> {
ScopeRepresentation representation = new ScopeRepresentation();
representation.setId(scope.getId());
representation.setName(scope.getName());
return representation;
}).collect(Collectors.toList())).build();
}
@Path("/resources")
@GET
@Produces("application/json")
@NoCache
public Response getResources() {
this.auth.requireView();
if (policy == null) {
return Response.status(Status.NOT_FOUND).build();
}
return Response.ok(policy.getResources().stream().map(resource -> {
ResourceRepresentation representation = new ResourceRepresentation();
representation.setId(resource.getId());
representation.setName(resource.getName());
return representation;
}).collect(Collectors.toList())).build();
}
@Path("/associatedPolicies")
@GET
@Produces("application/json")
@NoCache
public Response getAssociatedPolicies() {
this.auth.requireView();
if (policy == null) {
return Response.status(Status.NOT_FOUND).build();
}
return Response.ok(policy.getAssociatedPolicies().stream().map(policy -> {
PolicyRepresentation representation1 = new PolicyRepresentation();
representation1.setId(policy.getId());
representation1.setName(policy.getName());
representation1.setType(policy.getType());
return representation1;
}).collect(Collectors.toList())).build();
}
protected AbstractPolicyRepresentation doCreateRepresentation(String payload) {
PolicyRepresentation representation;
try {
representation = JsonSerialization.readValue(payload, PolicyRepresentation.class);
} catch (IOException cause) {
throw new RuntimeException("Failed to deserialize representation", cause);
}
return representation;
}
private PolicyProviderFactory getProviderFactory(String policyType) {
return authorization.getProviderFactory(policyType);
}
protected Policy getPolicy() {
return policy;
}
}

View file

@ -17,9 +17,7 @@
*/
package org.keycloak.authorization.admin;
import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
import static org.keycloak.models.utils.RepresentationToModel.toModel;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -29,14 +27,13 @@ import java.util.Map;
import java.util.stream.Collectors;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
@ -48,24 +45,24 @@ import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.authorization.store.ScopeStore;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.Constants;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
import org.keycloak.representations.idm.authorization.PolicyProviderRepresentation;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.resources.admin.RealmAuth;
import org.keycloak.util.JsonSerialization;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class PolicyService {
private final ResourceServer resourceServer;
private final AuthorizationProvider authorization;
private final RealmAuth auth;
protected final ResourceServer resourceServer;
protected final AuthorizationProvider authorization;
protected final RealmAuth auth;
public PolicyService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
this.resourceServer = resourceServer;
@ -73,210 +70,66 @@ public class PolicyService {
this.auth = auth;
}
@POST
@Consumes("application/json")
@Produces("application/json")
@NoCache
public Response create(PolicyRepresentation representation) {
this.auth.requireManage();
Policy policy = toModel(representation, this.resourceServer, authorization);
PolicyProviderAdminService resource = getPolicyProviderAdminResource(policy.getType(), authorization);
@Path("{type}")
public Object getResource(@PathParam("type") String type) {
PolicyProviderFactory providerFactory = getPolicyProviderFactory(type);
if (resource != null) {
try {
resource.onCreate(policy);
} catch (Exception e) {
throw new RuntimeException(e);
}
if (providerFactory != null) {
return new PolicyTypeService(type, resourceServer, authorization, auth);
}
Policy policy = authorization.getStoreFactory().getPolicyStore().findById(type, resourceServer.getId());
return doCreatePolicyResource(policy);
}
protected Object doCreatePolicyResource(Policy policy) {
return new PolicyResourceService(policy, resourceServer, authorization, auth);
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public Response create(String payload) {
this.auth.requireManage();
AbstractPolicyRepresentation representation = doCreateRepresentation(payload);
Policy policy = create(representation);
representation.setId(policy.getId());
return Response.status(Status.CREATED).entity(representation).build();
}
@Path("{id}")
@PUT
@Consumes("application/json")
@Produces("application/json")
@NoCache
public Response update(@PathParam("id") String id, PolicyRepresentation representation) {
this.auth.requireManage();
representation.setId(id);
StoreFactory storeFactory = authorization.getStoreFactory();
Policy policy = storeFactory.getPolicyStore().findById(representation.getId(), resourceServer.getId());
protected AbstractPolicyRepresentation doCreateRepresentation(String payload) {
PolicyRepresentation representation;
if (policy == null) {
return Response.status(Status.NOT_FOUND).build();
try {
representation = JsonSerialization.readValue(payload, PolicyRepresentation.class);
} catch (IOException cause) {
throw new RuntimeException("Failed to deserialize representation", cause);
}
policy = toModel(representation, resourceServer, authorization);
PolicyProviderAdminService resource = getPolicyProviderAdminResource(policy.getType(), authorization);
if (resource != null) {
try {
resource.onUpdate(policy);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return Response.status(Status.CREATED).build();
return representation;
}
@Path("{id}")
@DELETE
public Response delete(@PathParam("id") String id) {
this.auth.requireManage();
StoreFactory storeFactory = authorization.getStoreFactory();
PolicyStore policyStore = storeFactory.getPolicyStore();
Policy policy = policyStore.findById(id, resourceServer.getId());
public Policy create(AbstractPolicyRepresentation representation) {
PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore();
Policy existing = policyStore.findByName(representation.getName(), resourceServer.getId());
if (policy == null) {
return Response.status(Status.NOT_FOUND).build();
if (existing != null) {
throw new ErrorResponseException("Policy with name [" + representation.getName() + "] already exists", "Conflicting policy", Status.CONFLICT);
}
PolicyProviderAdminService resource = getPolicyProviderAdminResource(policy.getType(), authorization);
if (resource != null) {
try {
resource.onRemove(policy);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
policyStore.findDependentPolicies(id, resourceServer.getId()).forEach(dependentPolicy -> {
if (dependentPolicy.getAssociatedPolicies().size() == 1) {
policyStore.delete(dependentPolicy.getId());
} else {
dependentPolicy.removeAssociatedPolicy(policy);
}
});
policyStore.delete(policy.getId());
return Response.noContent().build();
}
@Path("{id}")
@GET
@Produces("application/json")
@NoCache
public Response findById(@PathParam("id") String id) {
this.auth.requireView();
StoreFactory storeFactory = authorization.getStoreFactory();
Policy model = storeFactory.getPolicyStore().findById(id, resourceServer.getId());
if (model == null) {
return Response.status(Status.NOT_FOUND).build();
}
return Response.ok(toRepresentation(model)).build();
}
@Path("{id}/dependentPolicies")
@GET
@Produces("application/json")
@NoCache
public Response getDependentPolicies(@PathParam("id") String id) {
this.auth.requireView();
StoreFactory storeFactory = authorization.getStoreFactory();
Policy model = storeFactory.getPolicyStore().findById(id, resourceServer.getId());
if (model == null) {
return Response.status(Status.NOT_FOUND).build();
}
List<Policy> policies = authorization.getStoreFactory().getPolicyStore().findDependentPolicies(model.getId(), resourceServer.getId());
return Response.ok(policies.stream().map(policy -> {
PolicyRepresentation representation1 = new PolicyRepresentation();
representation1.setId(policy.getId());
representation1.setName(policy.getName());
representation1.setType(policy.getType());
return representation1;
}).collect(Collectors.toList())).build();
}
@Path("{id}/scopes")
@GET
@Produces("application/json")
@NoCache
public Response getScopes(@PathParam("id") String id) {
this.auth.requireView();
StoreFactory storeFactory = authorization.getStoreFactory();
Policy model = storeFactory.getPolicyStore().findById(id, resourceServer.getId());
if (model == null) {
return Response.status(Status.NOT_FOUND).build();
}
return Response.ok(model.getScopes().stream().map(scope -> {
ScopeRepresentation representation = new ScopeRepresentation();
representation.setId(scope.getId());
representation.setName(scope.getName());
return representation;
}).collect(Collectors.toList())).build();
}
@Path("{id}/resources")
@GET
@Produces("application/json")
@NoCache
public Response getResources(@PathParam("id") String id) {
this.auth.requireView();
StoreFactory storeFactory = authorization.getStoreFactory();
Policy model = storeFactory.getPolicyStore().findById(id, resourceServer.getId());
if (model == null) {
return Response.status(Status.NOT_FOUND).build();
}
return Response.ok(model.getResources().stream().map(resource -> {
ResourceRepresentation representation = new ResourceRepresentation();
representation.setId(resource.getId());
representation.setName(resource.getName());
return representation;
}).collect(Collectors.toList())).build();
}
@Path("{id}/associatedPolicies")
@GET
@Produces("application/json")
@NoCache
public Response getAssociatedPolicies(@PathParam("id") String id) {
this.auth.requireView();
StoreFactory storeFactory = authorization.getStoreFactory();
Policy model = storeFactory.getPolicyStore().findById(id, resourceServer.getId());
if (model == null) {
return Response.status(Status.NOT_FOUND).build();
}
return Response.ok(model.getAssociatedPolicies().stream().map(policy -> {
PolicyRepresentation representation1 = new PolicyRepresentation();
representation1.setId(policy.getId());
representation1.setName(policy.getName());
representation1.setType(policy.getType());
return representation1;
}).collect(Collectors.toList())).build();
return policyStore.create(representation, resourceServer);
}
@Path("/search")
@GET
@Produces("application/json")
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public Response find(@QueryParam("name") String name) {
public Response findByName(@QueryParam("name") String name) {
this.auth.requireView();
StoreFactory storeFactory = authorization.getStoreFactory();
@ -290,11 +143,11 @@ public class PolicyService {
return Response.status(Status.OK).build();
}
return Response.ok(toRepresentation(model)).build();
return Response.ok(toRepresentation(model, authorization)).build();
}
@GET
@Produces("application/json")
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public Response findAll(@QueryParam("policyId") String id,
@QueryParam("name") String name,
@ -363,15 +216,24 @@ public class PolicyService {
}
return Response.ok(
policyStore.findByResourceServer(search, resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS).stream()
.map(policy -> toRepresentation(policy))
.collect(Collectors.toList()))
doSearch(firstResult, maxResult, search))
.build();
}
protected AbstractPolicyRepresentation toRepresentation(Policy model, AuthorizationProvider authorization) {
return ModelToRepresentation.toRepresentation(model, PolicyRepresentation.class, authorization);
}
protected List<Object> doSearch(Integer firstResult, Integer maxResult, Map<String, String[]> filters) {
PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore();
return policyStore.findByResourceServer(filters, resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS).stream()
.map(policy -> toRepresentation(policy, authorization))
.collect(Collectors.toList());
}
@Path("providers")
@GET
@Produces("application/json")
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public Response findPolicyProviders() {
this.auth.requireView();
@ -400,20 +262,12 @@ public class PolicyService {
return resource;
}
@Path("{policyType}")
public Object getPolicyTypeResource(@PathParam("policyType") String policyType) {
this.auth.requireView();
return getPolicyProviderAdminResource(policyType, this.authorization);
protected PolicyProviderAdminService getPolicyProviderAdminResource(String policyType) {
return getPolicyProviderFactory(policyType).getAdminResource(resourceServer, authorization);
}
private PolicyProviderAdminService getPolicyProviderAdminResource(String policyType, AuthorizationProvider authorization) {
PolicyProviderFactory providerFactory = authorization.getProviderFactory(policyType);
if (providerFactory != null) {
return providerFactory.getAdminResource(this.resourceServer);
}
return null;
protected PolicyProviderFactory getPolicyProviderFactory(String policyType) {
return authorization.getProviderFactory(policyType);
}
private void findAssociatedPolicies(Policy policy, List<Policy> policies) {

View file

@ -0,0 +1,66 @@
/*
* Copyright 2016 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.authorization.admin;
import java.io.IOException;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
import org.keycloak.services.resources.admin.RealmAuth;
import org.keycloak.util.JsonSerialization;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class PolicyTypeResourceService extends PolicyResourceService {
public PolicyTypeResourceService(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
super(policy, resourceServer, authorization, auth);
}
@Override
protected AbstractPolicyRepresentation doCreateRepresentation(String payload) {
String type = getPolicy().getType();
Class<? extends AbstractPolicyRepresentation> representationType = authorization.getProviderFactory(type).getRepresentationType();
if (representationType == null) {
throw new RuntimeException("Policy provider for type [" + type + "] returned a null representation type.");
}
AbstractPolicyRepresentation representation;
try {
representation = JsonSerialization.readValue(payload, representationType);
} catch (IOException e) {
throw new RuntimeException("Failed to deserialize JSON using policy provider for type [" + type + "].", e);
}
representation.setType(type);
return representation;
}
@Override
protected AbstractPolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
PolicyProviderFactory providerFactory = authorization.getProviderFactory(policy.getType());
return ModelToRepresentation.toRepresentation(policy, providerFactory.getRepresentationType(), authorization);
}
}

View file

@ -0,0 +1,91 @@
/*
* Copyright 2016 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.authorization.admin;
import java.io.IOException;
import javax.ws.rs.Path;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
import org.keycloak.services.resources.admin.RealmAuth;
import org.keycloak.util.JsonSerialization;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class PolicyTypeService extends PolicyService {
private final String type;
PolicyTypeService(String type, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
super(resourceServer, authorization, auth);
this.type = type;
}
@Path("/provider")
public Object getPolicyAdminResourceProvider() {
PolicyProviderAdminService resource = getPolicyProviderAdminResource(type);
if (resource == null) {
return null;
}
ResteasyProviderFactory.getInstance().injectProperties(resource);
return resource;
}
@Override
protected Object doCreatePolicyResource(Policy policy) {
return new PolicyTypeResourceService(policy, resourceServer,authorization, auth);
}
@Override
protected AbstractPolicyRepresentation doCreateRepresentation(String payload) {
PolicyProviderFactory provider = getPolicyProviderFactory(type);
Class<? extends AbstractPolicyRepresentation> representationType = provider.getRepresentationType();
if (representationType == null) {
throw new RuntimeException("Policy provider for type [" + type + "] returned a null representation type.");
}
AbstractPolicyRepresentation representation;
try {
representation = JsonSerialization.readValue(payload, representationType);
} catch (IOException e) {
throw new RuntimeException("Failed to deserialize JSON using policy provider for type [" + type + "].", e);
}
representation.setType(type);
return representation;
}
@Override
protected AbstractPolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
PolicyProviderFactory providerFactory = authorization.getProviderFactory(policy.getType());
return ModelToRepresentation.toRepresentation(policy, providerFactory.getRepresentationType(), authorization);
}
}

View file

@ -17,6 +17,22 @@
*/
package org.keycloak.authorization.admin;
import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
import java.io.IOException;
import java.util.HashMap;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.ResourceServer;
@ -34,25 +50,11 @@ import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.idm.authorization.DecisionStrategy;
import org.keycloak.representations.idm.authorization.Logic;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
import org.keycloak.services.resources.admin.RealmAuth;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.io.IOException;
import java.util.HashMap;
import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
@ -171,22 +173,26 @@ public class ResourceServerService {
return resource;
}
@Path("/permission")
public Object getPermissionTypeResource() {
this.auth.requireView();
PermissionService resource = new PermissionService(this.resourceServer, this.authorization, this.auth);
ResteasyProviderFactory.getInstance().injectProperties(resource);
return resource;
}
private void createDefaultPermission(ResourceRepresentation resource, PolicyRepresentation policy) {
PolicyRepresentation defaultPermission = new PolicyRepresentation();
ResourcePermissionRepresentation defaultPermission = new ResourcePermissionRepresentation();
defaultPermission.setName("Default Permission");
defaultPermission.setType("resource");
defaultPermission.setDescription("A permission that applies to the default resource type");
defaultPermission.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
defaultPermission.setLogic(Logic.POSITIVE);
HashMap<String, String> defaultPermissionConfig = new HashMap<>();
defaultPermissionConfig.put("default", "true");
defaultPermissionConfig.put("defaultResourceType", resource.getType());
defaultPermissionConfig.put("applyPolicies", "[\"" + policy.getName() + "\"]");
defaultPermission.setConfig(defaultPermissionConfig);
defaultPermission.setResourceType(resource.getType());
defaultPermission.addPolicy(policy.getName());
getPolicyResource().create(defaultPermission);
}

Some files were not shown because too many files have changed in this diff Show more