keycloak-uncached
Changes
adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java 58(+35 -23)
adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java 47(+12 -35)
adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java 1(+1 -0)
authz/client/src/main/java/org/keycloak/authorization/client/representation/ServerConfiguration.java 7(+7 -0)
authz/client/src/main/java/org/keycloak/authorization/client/resource/PolicyResource.java 187(+187 -0)
authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectionResource.java 4(+4 -0)
authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethodAuthenticator.java 1(+1 -0)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java 2(+1 -1)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProvider.java 8(+4 -4)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java 10(+5 -5)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/group/GroupPolicyProvider.java 8(+4 -4)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/group/GroupPolicyProviderFactory.java 25(+14 -11)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java 2(+1 -1)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/permission/ResourcePolicyProviderFactory.java 2(+1 -1)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/permission/ScopePolicyProviderFactory.java 2(+1 -1)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/permission/UMAPolicyProviderFactory.java 318(+292 -26)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProvider.java 8(+4 -4)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java 6(+3 -3)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java 2(+1 -1)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProvider.java 8(+5 -3)
authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java 7(+3 -4)
authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java 2(+1 -1)
core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java 12(+12 -0)
core/src/main/java/org/keycloak/representations/idm/authorization/AbstractPolicyRepresentation.java 15(+15 -0)
core/src/main/java/org/keycloak/representations/idm/authorization/AuthorizationRequest.java 10(+5 -5)
core/src/main/java/org/keycloak/representations/idm/authorization/UmaPermissionRepresentation.java 134(+134 -0)
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheSession.java 14(+12 -2)
server-spi-private/src/main/java/org/keycloak/authorization/permission/ResourcePermission.java 13(+13 -0)
server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java 10(+10 -0)
server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/PermissionTicketAwareDecisionResultCollector.java 63(+54 -9)
server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java 2(+1 -1)
services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponseBuilder.java 54(+44 -10)
services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java 84(+46 -38)
services/src/main/java/org/keycloak/authorization/protection/permission/PermissionTicketService.java 2(+2 -0)
services/src/main/java/org/keycloak/authorization/protection/policy/UserManagedPermissionService.java 183(+183 -0)
services/src/main/java/org/keycloak/forms/account/freemarker/model/AuthorizationBean.java 67(+66 -1)
testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authorization/TestPolicyProviderFactory.java 2(+1 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/PolicyEnforcerTest.java 100(+89 -11)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AbstractResourceServerTest.java 8(+6 -2)
Details
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java
index c54191f..20f6c90 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/AbstractPolicyEnforcer.java
@@ -28,6 +28,7 @@ import org.jboss.logging.Logger;
 import org.keycloak.AuthorizationContext;
 import org.keycloak.KeycloakSecurityContext;
 import org.keycloak.adapters.OIDCHttpFacade;
+import org.keycloak.adapters.spi.HttpFacade;
 import org.keycloak.adapters.spi.HttpFacade.Request;
 import org.keycloak.authorization.client.AuthzClient;
 import org.keycloak.authorization.client.ClientAuthorizationContext;
@@ -165,7 +166,7 @@ public abstract class AbstractPolicyEnforcer {
                             policyEnforcer.getPathMatcher().removeFromCache(getPath(request));
                         }
 
-                        return hasValidClaims(actualPathConfig, httpFacade, authorization);
+                        return hasValidClaims(actualPathConfig, permission, httpFacade, authorization);
                     }
                 }
             } else {
@@ -187,35 +188,22 @@ public abstract class AbstractPolicyEnforcer {
         return false;
     }
 
-    private boolean hasValidClaims(PathConfig actualPathConfig, OIDCHttpFacade httpFacade, Authorization authorization) {
-        Map<String, Map<String, Object>> claimInformationPointConfig = actualPathConfig.getClaimInformationPointConfig();
+    private boolean hasValidClaims(PathConfig actualPathConfig, Permission permission, OIDCHttpFacade httpFacade, Authorization authorization) {
+        Map<String, Set<String>> grantedClaims = permission.getClaims();
 
-        if (claimInformationPointConfig != null) {
-            Map<String, List<String>> claims = new HashMap<>();
-
-            for (Entry<String, Map<String, Object>> entry : claimInformationPointConfig.entrySet()) {
-                ClaimInformationPointProviderFactory factory = policyEnforcer.getClaimInformationPointProviderFactories().get(entry.getKey());
+        if (grantedClaims != null) {
+            Map<String, List<String>> claims = resolveClaims(actualPathConfig, httpFacade);
 
-                if (factory == null) {
-                    throw new RuntimeException("Could not find claim information provider with name [" + entry.getKey() + "]");
-                }
-
-                claims.putAll(factory.create(entry.getValue()).resolve(httpFacade));
+            if (claims.isEmpty()) {
+                return false;
             }
 
-            Map<String, List<String>> grantedClaims = authorization.getClaims();
+            for (Entry<String, Set<String>> entry : grantedClaims.entrySet()) {
+                List<String> requestClaims = claims.get(entry.getKey());
 
-            if (grantedClaims != null) {
-                if (claims.isEmpty()) {
+                if (requestClaims == null || requestClaims.isEmpty() || !entry.getValue().containsAll(requestClaims)) {
                     return false;
                 }
-                for (Entry<String, List<String>> entry : grantedClaims.entrySet()) {
-                    List<String> requestClaims = claims.get(entry.getKey());
-
-                    if (requestClaims == null || requestClaims.isEmpty() || !entry.getValue().containsAll(requestClaims)) {
-                        return false;
-                    }
-                }
             }
         }
 
@@ -342,4 +330,28 @@ public abstract class AbstractPolicyEnforcer {
     private PathConfig getPathConfig(Request request) {
         return isDefaultAccessDeniedUri(request) ? null : policyEnforcer.getPathMatcher().matches(getPath(request));
     }
+
+    protected Map<String, List<String>> resolveClaims(PathConfig pathConfig, OIDCHttpFacade httpFacade) {
+        Map<String, List<String>> claims = getClaims(getEnforcerConfig().getClaimInformationPointConfig(), httpFacade);
+
+        claims.putAll(getClaims(pathConfig.getClaimInformationPointConfig(), httpFacade));
+
+        return claims;
+    }
+
+    private Map<String, List<String>> getClaims(Map<String, Map<String, Object>>claimInformationPointConfig, HttpFacade httpFacade) {
+        Map<String, List<String>> claims = new HashMap<>();
+
+        if (claimInformationPointConfig != null) {
+            for (Entry<String, Map<String, Object>> claimDef : claimInformationPointConfig.entrySet()) {
+                ClaimInformationPointProviderFactory factory = getPolicyEnforcer().getClaimInformationPointProviderFactories().get(claimDef.getKey());
+
+                if (factory != null) {
+                    claims.putAll(factory.create(claimDef.getValue()).resolve(httpFacade));
+                }
+            }
+        }
+
+        return claims;
+    }
 }
                diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java
index 4e87c90..67149bd 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/KeycloakAdapterPolicyEnforcer.java
@@ -22,7 +22,6 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 
 import org.jboss.logging.Logger;
 import org.keycloak.KeycloakSecurityContext;
@@ -87,19 +86,6 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
                     grantedPermissions.add(newPermission);
                 }
             }
-
-            Map<String, List<String>> newClaims = newAuthorization.getClaims();
-
-            if (newClaims != null) {
-                Map<String, List<String>> claims = authorization.getClaims();
-
-                if (claims == null) {
-                    claims = new HashMap<>();
-                    authorization.setClaims(claims);
-                }
-
-                claims.putAll(newClaims);
-            }
         }
 
         original.setAuthorization(authorization);
@@ -169,11 +155,11 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
                 String ticket = getPermissionTicket(pathConfig, methodConfig, getAuthzClient(), httpFacade);
                 authzRequest.setTicket(ticket);
             } else {
-                if (accessToken.getAuthorization() != null) {
+                if (isBearerAuthorization(httpFacade) || accessToken.getAuthorization() != null) {
                     authzRequest.addPermission(pathConfig.getId(), methodConfig.getScopes());
                 }
 
-                Map<String, List<String>> claims = getClaims(pathConfig, httpFacade);
+                Map<String, List<String>> claims = resolveClaims(pathConfig, httpFacade);
 
                 if (!claims.isEmpty()) {
                     authzRequest.setClaimTokenFormat("urn:ietf:params:oauth:token-type:jwt");
@@ -186,7 +172,14 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
             }
 
             LOGGER.debug("Obtaining authorization for authenticated user.");
-            AuthorizationResponse authzResponse = getAuthzClient().authorization(accessTokenString).authorize(authzRequest);
+            AuthorizationResponse authzResponse;
+
+            if (isBearerAuthorization(httpFacade)) {
+                authzRequest.setSubjectToken(accessTokenString);
+                authzResponse = getAuthzClient().authorization().authorize(authzRequest);
+            } else {
+                authzResponse = getAuthzClient().authorization(accessTokenString).authorize(authzRequest);
+            }
 
             if (authzResponse != null) {
                 return AdapterRSATokenVerifier.verifyToken(authzResponse.getToken(), deployment);
@@ -200,7 +193,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
         return null;
     }
 
-    private String getPermissionTicket(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AuthzClient authzClient, HttpFacade httpFacade) {
+    private String getPermissionTicket(PathConfig pathConfig, PolicyEnforcerConfig.MethodConfig methodConfig, AuthzClient authzClient, OIDCHttpFacade httpFacade) {
         if (getEnforcerConfig().getUserManagedAccess() != null) {
             ProtectionResource protection = authzClient.protection();
             PermissionResource permission = protection.permission();
@@ -209,7 +202,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
             permissionRequest.setResourceId(pathConfig.getId());
             permissionRequest.setScopes(new HashSet<>(methodConfig.getScopes()));
 
-            Map<String, List<String>> claims = getClaims(pathConfig, httpFacade);
+            Map<String, List<String>> claims = resolveClaims(pathConfig, httpFacade);
 
             if (!claims.isEmpty()) {
                 permissionRequest.setClaims(claims);
@@ -221,22 +214,6 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
         return null;
     }
 
-    private Map<String, List<String>> getClaims(PathConfig pathConfig, HttpFacade httpFacade) {
-        Map<String, List<String>> claims = new HashMap<>();
-        Map<String, Map<String, Object>> claimInformationPointConfig = pathConfig.getClaimInformationPointConfig();
-
-        if (claimInformationPointConfig != null) {
-            for (Entry<String, Map<String, Object>> claimDef : claimInformationPointConfig.entrySet()) {
-                ClaimInformationPointProviderFactory factory = getPolicyEnforcer().getClaimInformationPointProviderFactories().get(claimDef.getKey());
-
-                if (factory != null) {
-                    claims.putAll(factory.create(claimDef.getValue()).resolve(httpFacade));
-                }
-            }
-        }
-        return claims;
-    }
-
     private boolean isBearerAuthorization(OIDCHttpFacade httpFacade) {
         List<String> authHeaders = httpFacade.getRequest().getHeaders("Authorization");
         if (authHeaders == null || authHeaders.size() == 0) {
                diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java
index d23de4f..3bd4070 100644
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authorization/PolicyEnforcer.java
@@ -319,6 +319,7 @@ public class PolicyEnforcer {
                     config.setMethods(originalConfig.getMethods());
                     config.setParentConfig(originalConfig);
                     config.setEnforcementMode(originalConfig.getEnforcementMode());
+                    config.setClaimInformationPointConfig(originalConfig.getClaimInformationPointConfig());
 
                     return config;
                 }
                diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/representation/ServerConfiguration.java b/authz/client/src/main/java/org/keycloak/authorization/client/representation/ServerConfiguration.java
index f708e52..eabf085 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/representation/ServerConfiguration.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/representation/ServerConfiguration.java
@@ -102,6 +102,9 @@ public class ServerConfiguration {
 
     @JsonProperty("permission_endpoint")
     private String permissionEndpoint;
+    
+    @JsonProperty("policy_endpoint")
+    private String policyEndpoint;
 
     public String getIssuer() {
         return issuer;
@@ -206,4 +209,8 @@ public class ServerConfiguration {
     public String getPermissionEndpoint() {
         return permissionEndpoint;
     }
+    
+    public String getPolicyEndpoint() {
+        return policyEndpoint;
+    }
 }
                diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/resource/PolicyResource.java b/authz/client/src/main/java/org/keycloak/authorization/client/resource/PolicyResource.java
new file mode 100644
index 0000000..46207eb
--- /dev/null
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/resource/PolicyResource.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2018 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.client.resource;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import org.keycloak.authorization.client.representation.ServerConfiguration;
+import org.keycloak.authorization.client.util.Http;
+import org.keycloak.authorization.client.util.Throwables;
+import org.keycloak.authorization.client.util.TokenCallable;
+import org.keycloak.representations.idm.authorization.UmaPermissionRepresentation;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ * An entry point for managing user-managed permissions for a particular resource
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PolicyResource {
+
+    private String resourceId;
+    private final Http http;
+    private final ServerConfiguration serverConfiguration;
+    private final TokenCallable pat;
+
+    public PolicyResource(String resourceId, Http http, ServerConfiguration serverConfiguration, TokenCallable pat) {
+        this.resourceId = resourceId;
+        this.http = http;
+        this.serverConfiguration = serverConfiguration;
+        this.pat = pat;
+    }
+
+    /**
+     * Creates a new user-managed permission as represented by the given {@code permission}.
+     *
+     * @param permission the permission to create
+     * @return if successful, the permission created
+     */
+    public UmaPermissionRepresentation create(final UmaPermissionRepresentation permission) {
+        if (permission == null) {
+            throw new IllegalArgumentException("Permission must not be null");
+        }
+
+        Callable<UmaPermissionRepresentation> callable = new Callable<UmaPermissionRepresentation>() {
+            @Override
+            public UmaPermissionRepresentation call() throws Exception {
+                return http.<UmaPermissionRepresentation>post(serverConfiguration.getPolicyEndpoint() + "/" + resourceId)
+                        .authorizationBearer(pat.call())
+                        .json(JsonSerialization.writeValueAsBytes(permission))
+                        .response().json(UmaPermissionRepresentation.class).execute();
+            }
+        };
+        try {
+            return callable.call();
+        } catch (Exception cause) {
+            return Throwables.retryAndWrapExceptionIfNecessary(callable, pat, "Error creating policy for resurce [" + resourceId + "]", cause);
+        }
+    }
+
+    /**
+     * Updates an existing user-managed permission
+     *
+     * @param permission the permission to update
+     */
+    public void update(final UmaPermissionRepresentation permission) {
+        if (permission == null) {
+            throw new IllegalArgumentException("Permission must not be null");
+        }
+
+        if (permission.getId() == null) {
+            throw new IllegalArgumentException("Permission id must not be null");
+        }
+
+        Callable<Void> callable = new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                http.<Void>put(serverConfiguration.getPolicyEndpoint() + "/"+ permission.getId())
+                        .authorizationBearer(pat.call())
+                        .json(JsonSerialization.writeValueAsBytes(permission)).execute();
+                return null;
+            }
+        };
+        try {
+            callable.call();
+        } catch (Exception cause) {
+            Throwables.retryAndWrapExceptionIfNecessary(callable, pat, "Error updating policy for resurce [" + resourceId + "]", cause);
+        }
+    }
+
+    /**
+     * Deletes an existing user-managed permission
+     *
+     * @param id the permission id
+     */
+    public void delete(final String id) {
+        Callable<Void> callable = new Callable<Void>() {
+            @Override
+            public Void call() {
+                http.<UmaPermissionRepresentation>delete(serverConfiguration.getPolicyEndpoint() + "/" + id)
+                        .authorizationBearer(pat.call())
+                        .response().execute();
+                return null;
+            }
+        };
+        try {
+            callable.call();
+        } catch (Exception cause) {
+            Throwables.retryAndWrapExceptionIfNecessary(callable, pat, "Error updating policy for resurce [" + resourceId + "]", cause);
+        }
+    }
+
+    /**
+     * Queries the server for permission matching the given parameters.
+     *
+     * @param id the permission id
+     * @param name the name of the permission
+     * @param scope the scope associated with the permission
+     * @param firstResult the position of the first resource to retrieve
+     * @param maxResult the maximum number of resources to retrieve
+     * @return the permissions matching the given parameters
+     */
+    public List<UmaPermissionRepresentation> find(final String name,
+                                                  final String scope,
+                                                  final Integer firstResult,
+                                                  final Integer maxResult) {
+        Callable<List<UmaPermissionRepresentation>> callable = new Callable<List<UmaPermissionRepresentation>>() {
+            @Override
+            public List<UmaPermissionRepresentation> call() {
+                return http.<List<UmaPermissionRepresentation>>get(serverConfiguration.getPolicyEndpoint())
+                        .authorizationBearer(pat.call())
+                        .param("name", name)
+                        .param("resource", resourceId)
+                        .param("scope", scope)
+                        .param("first", firstResult == null ? null : firstResult.toString())
+                        .param("max", maxResult == null ? null : maxResult.toString())
+                        .response().json(new TypeReference<List<UmaPermissionRepresentation>>(){}).execute();
+            }
+        };
+        try {
+            return callable.call();
+        } catch (Exception cause) {
+            return Throwables.retryAndWrapExceptionIfNecessary(callable, pat, "Error querying policies for resource [" + resourceId + "]", cause);
+        }
+    }
+
+    /**
+     * Queries the server for a permission with the given {@code id}.
+     *
+     * @param id the permission id
+     * @return the permission with the given id
+     */
+    public UmaPermissionRepresentation findById(final String id) {
+        if (id == null) {
+            throw new IllegalArgumentException("Permission id must not be null");
+        }
+
+        Callable<UmaPermissionRepresentation> callable = new Callable<UmaPermissionRepresentation>() {
+            @Override
+            public UmaPermissionRepresentation call() {
+                return http.<UmaPermissionRepresentation>get(serverConfiguration.getPolicyEndpoint() + "/" + id)
+                        .authorizationBearer(pat.call())
+                        .response().json(UmaPermissionRepresentation.class).execute();
+            }
+        };
+        try {
+            return callable.call();
+        } catch (Exception cause) {
+            return Throwables.retryAndWrapExceptionIfNecessary(callable, pat, "Error creating policy for resurce [" + resourceId + "]", cause);
+        }
+    }
+}
                diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectionResource.java b/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectionResource.java
index 03fa945..08030e1 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectionResource.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/resource/ProtectionResource.java
@@ -64,6 +64,10 @@ public class ProtectionResource {
         return new PermissionResource(http, serverConfiguration, pat);
     }
 
+    public PolicyResource policy(String resourceId) {
+        return new PolicyResource(resourceId, http, serverConfiguration, pat);
+    }
+
     /**
      * Introspects the given <code>rpt</code> using the token introspection endpoint.
      *
                diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethodAuthenticator.java b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethodAuthenticator.java
index 33674fb..e0a8a99 100644
--- a/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethodAuthenticator.java
+++ b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethodAuthenticator.java
@@ -83,6 +83,7 @@ public class HttpMethodAuthenticator<R> {
         method.param("rpt", request.getRpt());
         method.param("scope", request.getScope());
         method.param("audience", request.getAudience());
+        method.param("subject_token", request.getSubjectToken());
 
         if (permissions != null) {
             for (ResourcePermission permission : permissions.getResources()) {
                diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java
index d6c4f07..476d182 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/aggregated/AggregatePolicyProviderFactory.java
@@ -73,7 +73,7 @@ public class AggregatePolicyProviderFactory implements PolicyProviderFactory<Agg
     }
 
     @Override
-    public AggregatePolicyRepresentation toRepresentation(Policy policy) {
+    public AggregatePolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
         return new AggregatePolicyRepresentation();
     }
 
                diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProvider.java
index ffd92bf..54feda6 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProvider.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProvider.java
@@ -1,6 +1,6 @@
 package org.keycloak.authorization.policy.provider.client;
 
-import java.util.function.Function;
+import java.util.function.BiFunction;
 
 import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.model.Policy;
@@ -13,15 +13,15 @@ import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation
 
 public class ClientPolicyProvider implements PolicyProvider {
 
-    private final Function<Policy, ClientPolicyRepresentation> representationFunction;
+    private final BiFunction<Policy, AuthorizationProvider, ClientPolicyRepresentation> representationFunction;
 
-    public ClientPolicyProvider(Function<Policy, ClientPolicyRepresentation> representationFunction) {
+    public ClientPolicyProvider(BiFunction<Policy, AuthorizationProvider, ClientPolicyRepresentation> representationFunction) {
         this.representationFunction = representationFunction;
     }
 
     @Override
     public void evaluate(Evaluation evaluation) {
-        ClientPolicyRepresentation representation = representationFunction.apply(evaluation.getPolicy());
+        ClientPolicyRepresentation representation = representationFunction.apply(evaluation.getPolicy(), evaluation.getAuthorizationProvider());
         AuthorizationProvider authorizationProvider = evaluation.getAuthorizationProvider();
         RealmModel realm = authorizationProvider.getKeycloakSession().getContext().getRealm();
         EvaluationContext context = evaluation.getContext();
                diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java
index c118c26..c48dacd 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/client/ClientPolicyProviderFactory.java
@@ -30,7 +30,7 @@ import org.keycloak.util.JsonSerialization;
 
 public class ClientPolicyProviderFactory implements PolicyProviderFactory<ClientPolicyRepresentation> {
 
-    private ClientPolicyProvider provider = new ClientPolicyProvider(policy -> toRepresentation(policy));
+    private ClientPolicyProvider provider = new ClientPolicyProvider(this::toRepresentation);
 
     @Override
     public String getName() {
@@ -48,7 +48,7 @@ public class ClientPolicyProviderFactory implements PolicyProviderFactory<Client
     }
 
     @Override
-    public ClientPolicyRepresentation toRepresentation(Policy policy) {
+    public ClientPolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
         ClientPolicyRepresentation representation = new ClientPolicyRepresentation();
         representation.setClients(new HashSet<>(Arrays.asList(getClients(policy))));
         return representation;
@@ -75,12 +75,12 @@ public class ClientPolicyProviderFactory implements PolicyProviderFactory<Client
     }
 
     @Override
-    public void onExport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorizationProvider) {
-        ClientPolicyRepresentation userRep = toRepresentation(policy);
+    public void onExport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
+        ClientPolicyRepresentation userRep = toRepresentation(policy, authorization);
         Map<String, String> config = new HashMap<>();
 
         try {
-            RealmModel realm = authorizationProvider.getRealm();
+            RealmModel realm = authorization.getRealm();
             config.put("clients", JsonSerialization.writeValueAsString(userRep.getClients().stream().map(id -> realm.getClientById(id).getClientId()).collect(Collectors.toList())));
         } catch (IOException cause) {
             throw new RuntimeException("Failed to export user policy [" + policy.getName() + "]", cause);
                diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/group/GroupPolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/group/GroupPolicyProvider.java
index 5f4fcd8..fa76a6d 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/group/GroupPolicyProvider.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/group/GroupPolicyProvider.java
@@ -19,7 +19,7 @@ package org.keycloak.authorization.policy.provider.group;
 import static org.keycloak.models.utils.ModelToRepresentation.buildGroupPath;
 
 import java.util.List;
-import java.util.function.Function;
+import java.util.function.BiFunction;
 
 import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.attribute.Attributes;
@@ -36,16 +36,16 @@ import org.keycloak.representations.idm.authorization.GroupPolicyRepresentation;
  */
 public class GroupPolicyProvider implements PolicyProvider {
 
-    private final Function<Policy, GroupPolicyRepresentation> representationFunction;
+    private final BiFunction<Policy, AuthorizationProvider, GroupPolicyRepresentation> representationFunction;
 
-    public GroupPolicyProvider(Function<Policy, GroupPolicyRepresentation> representationFunction) {
+    public GroupPolicyProvider(BiFunction<Policy, AuthorizationProvider, GroupPolicyRepresentation> representationFunction) {
         this.representationFunction = representationFunction;
     }
 
     @Override
     public void evaluate(Evaluation evaluation) {
-        GroupPolicyRepresentation policy = representationFunction.apply(evaluation.getPolicy());
         AuthorizationProvider authorizationProvider = evaluation.getAuthorizationProvider();
+        GroupPolicyRepresentation policy = representationFunction.apply(evaluation.getPolicy(), authorizationProvider);
         RealmModel realm = authorizationProvider.getRealm();
         Attributes.Entry groupsClaim = evaluation.getContext().getIdentity().getAttributes().getValue(policy.getGroupsClaim());
 
                diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/group/GroupPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/group/GroupPolicyProviderFactory.java
index f18e20d..6f45011 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/group/GroupPolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/group/GroupPolicyProviderFactory.java
@@ -43,7 +43,7 @@ import org.keycloak.util.JsonSerialization;
  */
 public class GroupPolicyProviderFactory implements PolicyProviderFactory<GroupPolicyRepresentation> {
 
-    private GroupPolicyProvider provider = new GroupPolicyProvider(policy -> toRepresentation(policy));
+    private GroupPolicyProvider provider = new GroupPolicyProvider(this::toRepresentation);
 
     @Override
     public String getId() {
@@ -71,7 +71,7 @@ public class GroupPolicyProviderFactory implements PolicyProviderFactory<GroupPo
     }
 
     @Override
-    public GroupPolicyRepresentation toRepresentation(Policy policy) {
+    public GroupPolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
         GroupPolicyRepresentation representation = new GroupPolicyRepresentation();
 
         representation.setGroupsClaim(policy.getConfig().get("groupsClaim"));
@@ -109,19 +109,24 @@ public class GroupPolicyProviderFactory implements PolicyProviderFactory<GroupPo
     }
 
     @Override
-    public void onExport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorizationProvider) {
+    public void onExport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
         Map<String, String> config = new HashMap<>();
-        GroupPolicyRepresentation groupPolicy = toRepresentation(policy);
+        GroupPolicyRepresentation groupPolicy = toRepresentation(policy, authorization);
         Set<GroupPolicyRepresentation.GroupDefinition> groups = groupPolicy.getGroups();
 
         for (GroupPolicyRepresentation.GroupDefinition definition: groups) {
-            GroupModel group = authorizationProvider.getRealm().getGroupById(definition.getId());
+            GroupModel group = authorization.getRealm().getGroupById(definition.getId());
             definition.setId(null);
             definition.setPath(ModelToRepresentation.buildGroupPath(group));
         }
 
         try {
-            config.put("groupsClaim", groupPolicy.getGroupsClaim());
+            String groupsClaim = groupPolicy.getGroupsClaim();
+
+            if (groupsClaim != null) {
+                config.put("groupsClaim", groupsClaim);
+            }
+
             config.put("groups", JsonSerialization.writeValueAsString(groups));
         } catch (IOException cause) {
             throw new RuntimeException("Failed to export group policy [" + policy.getName() + "]", cause);
@@ -147,17 +152,15 @@ public class GroupPolicyProviderFactory implements PolicyProviderFactory<GroupPo
     }
 
     private void updatePolicy(Policy policy, String groupsClaim, Set<GroupPolicyRepresentation.GroupDefinition> groups, AuthorizationProvider authorization) {
-        if (groupsClaim == null) {
-            throw new RuntimeException("Group claims property not provided");
-        }
-
         if (groups == null || groups.isEmpty()) {
             throw new RuntimeException("You must provide at least one group");
         }
 
         Map<String, String> config = new HashMap<>(policy.getConfig());
 
-        config.put("groupsClaim", groupsClaim);
+        if (groupsClaim != null) {
+            config.put("groupsClaim", groupsClaim);
+        }
 
         List<GroupModel> topLevelGroups = authorization.getRealm().getTopLevelGroups();
 
                diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java
index 1b2aa16..5de5db5 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/js/JSPolicyProviderFactory.java
@@ -43,7 +43,7 @@ public class JSPolicyProviderFactory implements PolicyProviderFactory<JSPolicyRe
     }
 
     @Override
-    public JSPolicyRepresentation toRepresentation(Policy policy) {
+    public JSPolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
         JSPolicyRepresentation representation = new JSPolicyRepresentation();
         representation.setCode(policy.getConfig().get("code"));
         return representation;
                diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/permission/ResourcePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/permission/ResourcePolicyProviderFactory.java
index 726f584..b8d982d 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/permission/ResourcePolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/permission/ResourcePolicyProviderFactory.java
@@ -56,7 +56,7 @@ public class ResourcePolicyProviderFactory implements PolicyProviderFactory<Reso
     }
 
     @Override
-    public ResourcePermissionRepresentation toRepresentation(Policy policy) {
+    public ResourcePermissionRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
         ResourcePermissionRepresentation representation = new ResourcePermissionRepresentation();
         representation.setResourceType(policy.getConfig().get("defaultResourceType"));
         return representation;
                diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/permission/ScopePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/permission/ScopePolicyProviderFactory.java
index 7ac63da..52af952 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/permission/ScopePolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/permission/ScopePolicyProviderFactory.java
@@ -58,7 +58,7 @@ public class ScopePolicyProviderFactory implements PolicyProviderFactory<ScopePe
     }
 
     @Override
-    public ScopePermissionRepresentation toRepresentation(Policy policy) {
+    public ScopePermissionRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
         return new ScopePermissionRepresentation();
     }
 
                diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/permission/UMAPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/permission/UMAPolicyProviderFactory.java
index 3d11753..9b0ddec 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/permission/UMAPolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/permission/UMAPolicyProviderFactory.java
@@ -16,22 +16,40 @@
  */
 package org.keycloak.authorization.policy.provider.permission;
 
-import java.util.ArrayList;
-import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 import org.keycloak.Config;
 import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.model.Policy;
+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.models.ClientModel;
+import org.keycloak.models.GroupModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+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.ClientPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.GroupPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.GroupPolicyRepresentation.GroupDefinition;
+import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
 import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
+import org.keycloak.representations.idm.authorization.RolePolicyRepresentation.RoleDefinition;
+import org.keycloak.representations.idm.authorization.UmaPermissionRepresentation;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
  */
-public class UMAPolicyProviderFactory implements PolicyProviderFactory<PolicyRepresentation> {
+public class UMAPolicyProviderFactory implements PolicyProviderFactory<UmaPermissionRepresentation> {
 
     private UMAPolicyProvider provider = new UMAPolicyProvider();
 
@@ -57,53 +75,249 @@ public class UMAPolicyProviderFactory implements PolicyProviderFactory<PolicyRep
 
     @Override
     public PolicyProvider create(KeycloakSession session) {
-        return null;
+        return provider;
     }
 
     @Override
-    public void onCreate(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
-        verifyCircularReference(policy, new ArrayList<>());
+    public void onCreate(Policy policy, UmaPermissionRepresentation representation, AuthorizationProvider authorization) {
+        policy.setOwner(representation.getOwner());
+        PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore();
+        Set<String> roles = representation.getRoles();
+
+        if (roles != null) {
+            for (String role : roles) {
+                createRolePolicy(policy, policyStore, role, representation.getOwner());
+            }
+        }
+
+        Set<String> groups = representation.getGroups();
+
+        if (groups != null) {
+            for (String group : groups) {
+                createGroupPolicy(policy, policyStore, group, representation.getOwner());
+            }
+        }
+
+        Set<String> clients = representation.getClients();
+
+        if (clients != null) {
+            for (String client : clients) {
+                createClientPolicy(policy, policyStore, client, representation.getOwner());
+            }
+        }
+
+        String condition = representation.getCondition();
+
+        if (condition != null) {
+            createJSPolicy(policy, policyStore, condition, representation.getOwner());
+        }
     }
 
     @Override
-    public void onUpdate(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
-        verifyCircularReference(policy, new ArrayList<>());
+    public void onUpdate(Policy policy, UmaPermissionRepresentation representation, AuthorizationProvider authorization) {
+        PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore();
+        Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
+
+        for (Policy associatedPolicy : associatedPolicies) {
+            AbstractPolicyRepresentation associatedRep = ModelToRepresentation.toRepresentation(associatedPolicy, authorization, false, false);
+
+            if ("role".equals(associatedRep.getType())) {
+                RolePolicyRepresentation rep = RolePolicyRepresentation.class.cast(associatedRep);
+
+                rep.setRoles(new HashSet<>());
+
+                Set<String> updatedRoles = representation.getRoles();
+
+                if (updatedRoles != null) {
+                    for (String role : updatedRoles) {
+                        rep.addRole(role);
+                    }
+                }
+
+                if (rep.getRoles().isEmpty()) {
+                    policyStore.delete(associatedPolicy.getId());
+                } else {
+                    RepresentationToModel.toModel(rep, authorization, associatedPolicy);
+                }
+            } else if ("js".equals(associatedRep.getType())) {
+                JSPolicyRepresentation rep = JSPolicyRepresentation.class.cast(associatedRep);
+
+                if (representation.getCondition() != null) {
+                    rep.setCode(representation.getCondition());
+                    RepresentationToModel.toModel(rep, authorization, associatedPolicy);
+                } else {
+                    policyStore.delete(associatedPolicy.getId());
+                }
+            } else if ("group".equals(associatedRep.getType())) {
+                GroupPolicyRepresentation rep = GroupPolicyRepresentation.class.cast(associatedRep);
+
+                rep.setGroups(new HashSet<>());
+
+                Set<String> updatedGroups = representation.getGroups();
+
+                if (updatedGroups != null) {
+                    for (String group : updatedGroups) {
+                        rep.addGroupPath(group);
+                    }
+                }
+
+                if (rep.getGroups().isEmpty()) {
+                    policyStore.delete(associatedPolicy.getId());
+                } else {
+                    RepresentationToModel.toModel(rep, authorization, associatedPolicy);
+                }
+            } else if ("client".equals(associatedRep.getType())) {
+                ClientPolicyRepresentation rep = ClientPolicyRepresentation.class.cast(associatedRep);
+
+                rep.setClients(new HashSet<>());
+
+                Set<String> updatedClients = representation.getClients();
+
+                if (updatedClients != null) {
+                    for (String client : updatedClients) {
+                        rep.addClient(client);
+                    }
+                }
+
+                if (rep.getClients().isEmpty()) {
+                    policyStore.delete(associatedPolicy.getId());
+                } else {
+                    RepresentationToModel.toModel(rep, authorization, associatedPolicy);
+                }
+            }
+        }
+
+        Set<String> updatedRoles = representation.getRoles();
+
+        if (updatedRoles != null) {
+            boolean createPolicy = true;
+
+            for (Policy associatedPolicy : associatedPolicies) {
+                if ("role".equals(associatedPolicy.getType())) {
+                    createPolicy = false;
+                }
+            }
+
+            if (createPolicy) {
+                for (String role : updatedRoles) {
+                    createRolePolicy(policy, policyStore, role, policy.getOwner());
+                }
+            }
+        }
+
+        Set<String> updatedGroups = representation.getGroups();
+
+        if (updatedGroups != null) {
+            boolean createPolicy = true;
+
+            for (Policy associatedPolicy : associatedPolicies) {
+                if ("group".equals(associatedPolicy.getType())) {
+                    createPolicy = false;
+                }
+            }
+
+            if (createPolicy) {
+                for (String group : updatedGroups) {
+                    createGroupPolicy(policy, policyStore, group, policy.getOwner());
+                }
+            }
+        }
+
+        Set<String> updatedClients = representation.getClients();
+
+        if (updatedClients != null) {
+            boolean createPolicy = true;
+
+            for (Policy associatedPolicy : associatedPolicies) {
+                if ("client".equals(associatedPolicy.getType())) {
+                    createPolicy = false;
+                }
+            }
+
+            if (createPolicy) {
+                for (String client : updatedClients) {
+                    createClientPolicy(policy, policyStore, client, policy.getOwner());
+                }
+            }
+        }
+
+        String condition = representation.getCondition();
+
+        if (condition != null) {
+            boolean createPolicy = true;
+
+            for (Policy associatedPolicy : associatedPolicies) {
+                if ("js".equals(associatedPolicy.getType())) {
+                    createPolicy = false;
+                }
+            }
+
+            if (createPolicy) {
+                createJSPolicy(policy, policyStore, condition, policy.getOwner());
+            }
+        }
     }
 
     @Override
     public void onImport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
-        verifyCircularReference(policy, new ArrayList<>());
     }
 
     @Override
-    public PolicyRepresentation toRepresentation(Policy policy) {
-        return new PolicyRepresentation();
-    }
+    public UmaPermissionRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
+        UmaPermissionRepresentation representation = new UmaPermissionRepresentation();
 
-    @Override
-    public Class<PolicyRepresentation> getRepresentationType() {
-        return PolicyRepresentation.class;
-    }
+        representation.setScopes(policy.getScopes().stream().map(Scope::getName).collect(Collectors.toSet()));
+        representation.setOwner(policy.getOwner());
 
-    private void verifyCircularReference(Policy policy, List<String> ids) {
-        if (!policy.getType().equals("uma")) {
-            return;
-        }
+        for (Policy associatedPolicy : policy.getAssociatedPolicies()) {
+            AbstractPolicyRepresentation associatedRep = ModelToRepresentation.toRepresentation(associatedPolicy, authorization, false, false);
+            RealmModel realm = authorization.getRealm();
 
-        if (ids.contains(policy.getId())) {
-            throw new RuntimeException("Circular reference found [" + policy.getName() + "].");
-        }
+            if ("role".equals(associatedRep.getType())) {
+                RolePolicyRepresentation rep = RolePolicyRepresentation.class.cast(associatedRep);
 
-        ids.add(policy.getId());
+                for (RoleDefinition definition : rep.getRoles()) {
+                    RoleModel role = realm.getRoleById(definition.getId());
 
-        for (Policy associated : policy.getAssociatedPolicies()) {
-            verifyCircularReference(associated, ids);
+                    if (role.isClientRole()) {
+                        representation.addClientRole(ClientModel.class.cast(role.getContainer()).getClientId(),role.getName());
+                    } else {
+                        representation.addRole(role.getName());
+                    }
+                }
+            } else if ("js".equals(associatedRep.getType())) {
+                JSPolicyRepresentation rep = JSPolicyRepresentation.class.cast(associatedRep);
+                representation.setCondition(rep.getCode());
+            } else if ("group".equals(associatedRep.getType())) {
+                GroupPolicyRepresentation rep = GroupPolicyRepresentation.class.cast(associatedRep);
+
+                for (GroupDefinition definition : rep.getGroups()) {
+                    representation.addGroup(ModelToRepresentation.buildGroupPath(realm.getGroupById(definition.getId())));
+                }
+            } else if ("client".equals(associatedRep.getType())) {
+                ClientPolicyRepresentation rep = ClientPolicyRepresentation.class.cast(associatedRep);
+
+                for (String client : rep.getClients()) {
+                    representation.addClient(realm.getClientById(client).getClientId());
+                }
+            }
         }
+
+        return representation;
+    }
+
+    @Override
+    public Class<UmaPermissionRepresentation> getRepresentationType() {
+        return UmaPermissionRepresentation.class;
     }
 
     @Override
     public void onRemove(Policy policy, AuthorizationProvider authorization) {
+        PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore();
 
+        for (Policy associatedPolicy : policy.getAssociatedPolicies()) {
+            policyStore.delete(associatedPolicy.getId());
+        }
     }
 
     @Override
@@ -125,4 +339,56 @@ public class UMAPolicyProviderFactory implements PolicyProviderFactory<PolicyRep
     public String getId() {
         return "uma";
     }
+
+    private void createJSPolicy(Policy policy, PolicyStore policyStore, String condition, String owner) {
+        JSPolicyRepresentation rep = new JSPolicyRepresentation();
+
+        rep.setName(KeycloakModelUtils.generateId());
+        rep.setCode(condition);
+
+        Policy associatedPolicy = policyStore.create(rep, policy.getResourceServer());
+
+        associatedPolicy.setOwner(owner);
+
+        policy.addAssociatedPolicy(associatedPolicy);
+    }
+
+    private void createClientPolicy(Policy policy, PolicyStore policyStore, String client, String owner) {
+        ClientPolicyRepresentation rep = new ClientPolicyRepresentation();
+
+        rep.setName(KeycloakModelUtils.generateId());
+        rep.addClient(client);
+
+        Policy associatedPolicy = policyStore.create(rep, policy.getResourceServer());
+
+        associatedPolicy.setOwner(owner);
+
+        policy.addAssociatedPolicy(associatedPolicy);
+    }
+
+    private void createGroupPolicy(Policy policy, PolicyStore policyStore, String group, String owner) {
+        GroupPolicyRepresentation rep = new GroupPolicyRepresentation();
+
+        rep.setName(KeycloakModelUtils.generateId());
+        rep.addGroupPath(group);
+
+        Policy associatedPolicy = policyStore.create(rep, policy.getResourceServer());
+
+        associatedPolicy.setOwner(owner);
+
+        policy.addAssociatedPolicy(associatedPolicy);
+    }
+
+    private void createRolePolicy(Policy policy, PolicyStore policyStore, String role, String owner) {
+        RolePolicyRepresentation rep = new RolePolicyRepresentation();
+
+        rep.setName(KeycloakModelUtils.generateId());
+        rep.addRole(role, false);
+
+        Policy associatedPolicy = policyStore.create(rep, policy.getResourceServer());
+
+        associatedPolicy.setOwner(owner);
+
+        policy.addAssociatedPolicy(associatedPolicy);
+    }
 }
                diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProvider.java
index 4a5718a..2435ba8 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProvider.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProvider.java
@@ -18,7 +18,7 @@
 package org.keycloak.authorization.policy.provider.role;
 
 import java.util.Set;
-import java.util.function.Function;
+import java.util.function.BiFunction;
 
 import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.identity.Identity;
@@ -35,16 +35,16 @@ import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
  */
 public class RolePolicyProvider implements PolicyProvider {
 
-    private final Function<Policy, RolePolicyRepresentation> representationFunction;
+    private final BiFunction<Policy, AuthorizationProvider, RolePolicyRepresentation> representationFunction;
 
-    public RolePolicyProvider(Function<Policy, RolePolicyRepresentation> representationFunction) {
+    public RolePolicyProvider(BiFunction<Policy, AuthorizationProvider, RolePolicyRepresentation> representationFunction) {
         this.representationFunction = representationFunction;
     }
 
     @Override
     public void evaluate(Evaluation evaluation) {
         Policy policy = evaluation.getPolicy();
-        Set<RolePolicyRepresentation.RoleDefinition> roleIds = representationFunction.apply(policy).getRoles();
+        Set<RolePolicyRepresentation.RoleDefinition> roleIds = representationFunction.apply(policy, evaluation.getAuthorizationProvider()).getRoles();
         AuthorizationProvider authorizationProvider = evaluation.getAuthorizationProvider();
         RealmModel realm = authorizationProvider.getKeycloakSession().getContext().getRealm();
         Identity identity = evaluation.getContext().getIdentity();
                diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java
index bfd3e96..7565e24 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java
@@ -52,7 +52,7 @@ import java.util.Set;
  */
 public class RolePolicyProviderFactory implements PolicyProviderFactory<RolePolicyRepresentation> {
 
-    private RolePolicyProvider provider = new RolePolicyProvider(policy -> toRepresentation(policy));
+    private RolePolicyProvider provider = new RolePolicyProvider(this::toRepresentation);
 
     @Override
     public String getName() {
@@ -75,7 +75,7 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory<RolePoli
     }
 
     @Override
-    public RolePolicyRepresentation toRepresentation(Policy policy) {
+    public RolePolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
         RolePolicyRepresentation representation = new RolePolicyRepresentation();
 
         try {
@@ -114,7 +114,7 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory<RolePoli
     @Override
     public void onExport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorizationProvider) {
         Map<String, String> config = new HashMap<>();
-        Set<RolePolicyRepresentation.RoleDefinition> roles = toRepresentation(policy).getRoles();
+        Set<RolePolicyRepresentation.RoleDefinition> roles = toRepresentation(policy, authorizationProvider).getRoles();
 
         for (RolePolicyRepresentation.RoleDefinition roleDefinition : roles) {
             RoleModel role = authorizationProvider.getRealm().getRoleById(roleDefinition.getId());
                diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java
index ffaf6ce..c1e3f41 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/time/TimePolicyProviderFactory.java
@@ -66,7 +66,7 @@ public class TimePolicyProviderFactory implements PolicyProviderFactory<TimePoli
     }
 
     @Override
-    public TimePolicyRepresentation toRepresentation(Policy policy) {
+    public TimePolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
         TimePolicyRepresentation representation = new TimePolicyRepresentation();
         Map<String, String> config = policy.getConfig();
 
                diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProvider.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProvider.java
index f891257..3e1f9b7 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProvider.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProvider.java
@@ -17,8 +17,10 @@
  */
 package org.keycloak.authorization.policy.provider.user;
 
+import java.util.function.BiFunction;
 import java.util.function.Function;
 
+import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.model.Policy;
 import org.keycloak.authorization.policy.evaluation.Evaluation;
 import org.keycloak.authorization.policy.evaluation.EvaluationContext;
@@ -30,16 +32,16 @@ import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
  */
 public class UserPolicyProvider implements PolicyProvider {
 
-    private final Function<Policy, UserPolicyRepresentation> representationFunction;
+    private final BiFunction<Policy, AuthorizationProvider, UserPolicyRepresentation> representationFunction;
 
-    public UserPolicyProvider(Function<Policy, UserPolicyRepresentation> representationFunction) {
+    public UserPolicyProvider(BiFunction<Policy, AuthorizationProvider, UserPolicyRepresentation> representationFunction) {
         this.representationFunction = representationFunction;
     }
 
     @Override
     public void evaluate(Evaluation evaluation) {
         EvaluationContext context = evaluation.getContext();
-        UserPolicyRepresentation representation = representationFunction.apply(evaluation.getPolicy());
+        UserPolicyRepresentation representation = representationFunction.apply(evaluation.getPolicy(), evaluation.getAuthorizationProvider());
 
         for (String userId : representation.getUsers()) {
             if (context.getIdentity().getId().equals(userId)) {
                diff --git a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java
index 9ae349d..6f4c0bf 100644
--- a/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java
+++ b/authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/user/UserPolicyProviderFactory.java
@@ -25,7 +25,6 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.function.Function;
 import java.util.stream.Collectors;
 
 import org.keycloak.Config;
@@ -52,7 +51,7 @@ import org.keycloak.util.JsonSerialization;
  */
 public class UserPolicyProviderFactory implements PolicyProviderFactory<UserPolicyRepresentation> {
 
-    private UserPolicyProvider provider = new UserPolicyProvider((Function<Policy, UserPolicyRepresentation>) policy -> toRepresentation(policy));
+    private UserPolicyProvider provider = new UserPolicyProvider(this::toRepresentation);
 
     @Override
     public String getName() {
@@ -75,7 +74,7 @@ public class UserPolicyProviderFactory implements PolicyProviderFactory<UserPoli
     }
 
     @Override
-    public UserPolicyRepresentation toRepresentation(Policy policy) {
+    public UserPolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
         UserPolicyRepresentation representation = new UserPolicyRepresentation();
 
         try {
@@ -113,7 +112,7 @@ public class UserPolicyProviderFactory implements PolicyProviderFactory<UserPoli
 
     @Override
     public void onExport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorizationProvider) {
-        UserPolicyRepresentation userRep = toRepresentation(policy);
+        UserPolicyRepresentation userRep = toRepresentation(policy, authorizationProvider);
         Map<String, String> config = new HashMap<>();
 
         try {
                diff --git a/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java b/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java
index a879aad..800259a 100644
--- a/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java
+++ b/authz/policy/drools/src/main/java/org/keycloak/authorization/policy/provider/drools/DroolsPolicyProviderFactory.java
@@ -51,7 +51,7 @@ public class DroolsPolicyProviderFactory implements PolicyProviderFactory<RulePo
     }
 
     @Override
-    public RulePolicyRepresentation toRepresentation(Policy policy) {
+    public RulePolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
         RulePolicyRepresentation representation = new RulePolicyRepresentation();
 
         representation.setArtifactGroupId(policy.getConfig().get("mavenArtifactGroupId"));
                diff --git a/core/src/main/java/org/keycloak/representations/AccessToken.java b/core/src/main/java/org/keycloak/representations/AccessToken.java
index 257f1c6..02ff2e0 100755
--- a/core/src/main/java/org/keycloak/representations/AccessToken.java
+++ b/core/src/main/java/org/keycloak/representations/AccessToken.java
@@ -88,9 +88,6 @@ public class AccessToken extends IDToken {
         @JsonProperty("permissions")
         private List<Permission> permissions;
 
-        @JsonProperty("claims")
-        private Map<String, List<String>> claims;
-
         public List<Permission> getPermissions() {
             return permissions;
         }
@@ -98,14 +95,6 @@ public class AccessToken extends IDToken {
         public void setPermissions(List<Permission> permissions) {
             this.permissions = permissions;
         }
-
-        public void setClaims(Map<String, List<String>> claims) {
-            this.claims = claims;
-        }
-
-        public Map<String, List<String>> getClaims() {
-            return claims;
-        }
     }
 
     @JsonProperty("trusted-certs")
                diff --git a/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java b/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java
index 81dc506..ae448d8 100644
--- a/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java
+++ b/core/src/main/java/org/keycloak/representations/adapters/config/PolicyEnforcerConfig.java
@@ -54,6 +54,10 @@ public class PolicyEnforcerConfig {
     @JsonInclude(JsonInclude.Include.NON_NULL)
     private UserManagedAccessConfig userManagedAccess;
 
+    @JsonProperty("claim-information-point")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private Map<String, Map<String, Object>> claimInformationPointConfig;
+
     public List<PathConfig> getPaths() {
         return this.paths;
     }
@@ -102,6 +106,14 @@ public class PolicyEnforcerConfig {
         this.onDenyRedirectTo = onDenyRedirectTo;
     }
 
+    public Map<String, Map<String, Object>> getClaimInformationPointConfig() {
+        return claimInformationPointConfig;
+    }
+
+    public void setClaimInformationPointConfig(Map<String, Map<String, Object>> config) {
+        this.claimInformationPointConfig = config;
+    }
+
     public static class PathConfig {
 
         public static PathConfig createPathConfig(ResourceRepresentation resourceDescription) {
                diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/AbstractPolicyRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/AbstractPolicyRepresentation.java
index ada763c..8d7bbeb 100644
--- a/core/src/main/java/org/keycloak/representations/idm/authorization/AbstractPolicyRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/AbstractPolicyRepresentation.java
@@ -35,6 +35,7 @@ public class AbstractPolicyRepresentation {
     private Set<String> scopes;
     private Logic logic = Logic.POSITIVE;
     private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS;
+    private String owner;
 
     public String getId() {
         return this.id;
@@ -135,6 +136,20 @@ public class AbstractPolicyRepresentation {
         this.scopes.addAll(Arrays.asList(id));
     }
 
+    public void removeScope(String scope) {
+        if (scopes != null) {
+            scopes.remove(scope);
+        }
+    }
+
+    public String getOwner() {
+        return owner;
+    }
+
+    public void setOwner(String owner) {
+        this.owner = owner;
+    }
+
     @Override
     public boolean equals(final Object o) {
         if (this == o) return true;
                diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/AuthorizationRequest.java b/core/src/main/java/org/keycloak/representations/idm/authorization/AuthorizationRequest.java
index 14f1f3d..6682075 100644
--- a/core/src/main/java/org/keycloak/representations/idm/authorization/AuthorizationRequest.java
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/AuthorizationRequest.java
@@ -39,7 +39,7 @@ public class AuthorizationRequest {
     private PermissionTicketToken permissions = new PermissionTicketToken();
     private Metadata metadata;
     private String audience;
-    private String accessToken;
+    private String subjectToken;
     private boolean submitRequest;
     private Map<String, List<String>> claims;
 
@@ -123,12 +123,12 @@ public class AuthorizationRequest {
         return audience;
     }
 
-    public void setAccessToken(String accessToken) {
-        this.accessToken = accessToken;
+    public void setSubjectToken(String subjectToken) {
+        this.subjectToken = subjectToken;
     }
 
-    public String getAccessToken() {
-        return accessToken;
+    public String getSubjectToken() {
+        return subjectToken;
     }
 
     public Map<String, List<String>> getClaims() {
                diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/UmaPermissionRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/UmaPermissionRepresentation.java
new file mode 100644
index 0000000..a7bccea
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/UmaPermissionRepresentation.java
@@ -0,0 +1,134 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.Set;
+
+/**
+ * @author <a href="mailto:federico@martel-innovate.com">Federico M. Facca</a>
+ */
+public class UmaPermissionRepresentation extends AbstractPolicyRepresentation {
+    
+    private String id;
+    private String description;
+    private Set<String> roles;
+    private Set<String> groups;
+    private Set<String> clients;
+    private String condition;
+
+    @Override
+    public String getType() {
+        return "uma";
+    }
+
+    public void setId(String id){
+        this.id = id;
+    }
+    
+    public String getId(){
+        return id;
+    }
+    
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public void setRoles(Set<String> roles) {
+        this.roles = roles;
+    }
+
+    public void addRole(String... role) {
+        if (roles == null) {
+            roles = new HashSet<>();
+        }
+
+        roles.addAll(Arrays.asList(role));
+    }
+
+    public void addClientRole(String clientId, String roleName) {
+        addRole(clientId + "/" + roleName);
+    }
+
+    public void removeRole(String role) {
+        if (roles != null) {
+            roles.remove(role);
+        }
+    }
+
+    public Set<String> getRoles() {
+        return roles;
+    }
+
+    public void setGroups(Set<String> groups) {
+        this.groups = groups;
+    }
+
+    public void addGroup(String... group) {
+        if (groups == null) {
+            groups = new HashSet<>();
+        }
+
+        groups.addAll(Arrays.asList(group));
+    }
+
+    public void removeGroup(String group) {
+        if (groups != null) {
+            groups.remove(group);
+        }
+    }
+
+    public Set<String> getGroups() {
+        return groups;
+    }
+
+    public void setClients(Set<String> clients) {
+        this.clients = clients;
+    }
+
+    public void addClient(String... client) {
+        if (clients == null) {
+            clients = new HashSet<>();
+        }
+
+        clients.addAll(Arrays.asList(client));
+    }
+
+    public void removeClient(String client) {
+        if (clients != null) {
+            clients.remove(client);
+        }
+    }
+
+    public Set<String> getClients() {
+        return clients;
+    }
+
+    public void setCondition(String condition) {
+        this.condition = condition;
+    }
+
+    public String getCondition() {
+        return condition;
+    }
+}
                diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheSession.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheSession.java
index b08629a..06e9f25 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheSession.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/authorization/StoreFactoryCacheSession.java
@@ -454,7 +454,12 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
     protected class ScopeCache implements ScopeStore {
         @Override
         public Scope create(String name, ResourceServer resourceServer) {
-            Scope scope = getScopeStoreDelegate().create(name, resourceServer);
+            return create(null, name, resourceServer);
+        }
+
+        @Override
+        public Scope create(String id, String name, ResourceServer resourceServer) {
+            Scope scope = getScopeStoreDelegate().create(id, name, resourceServer);
             registerScopeInvalidation(scope.getId(), scope.getName(), resourceServer.getId());
             return scope;
         }
@@ -538,7 +543,12 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
     protected class ResourceCache implements ResourceStore {
         @Override
         public Resource create(String name, ResourceServer resourceServer, String owner) {
-            Resource resource = getResourceStoreDelegate().create(name, resourceServer, owner);
+            return create(null, name, resourceServer, owner);
+        }
+
+        @Override
+        public Resource create(String id, String name, ResourceServer resourceServer, String owner) {
+            Resource resource = getResourceStoreDelegate().create(id, name, resourceServer, owner);
             Resource cached = findById(resource.getId(), resourceServer.getId());
             registerResourceInvalidation(resource.getId(), resource.getName(), resource.getType(), resource.getUri(), resource.getScopes().stream().map(scope -> scope.getId()).collect(Collectors.toSet()), resourceServer.getId(), resource.getOwner());
             return cached;
                diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPermissionTicketStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPermissionTicketStore.java
index 3ab4da9..1e7f825 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPermissionTicketStore.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPermissionTicketStore.java
@@ -206,6 +206,8 @@ public class JPAPermissionTicketStore implements PermissionTicketStore {
                 predicates.add(builder.isNull(root.get("requester")));
             } else if (PermissionTicket.POLICY_IS_NOT_NULL.equals(name)) {
                 predicates.add(builder.isNotNull(root.get("policy")));
+            } else if (PermissionTicket.POLICY.equals(name)) {
+                predicates.add(root.join("policy").get("id").in(value));
             } else {
                 throw new RuntimeException("Unsupported filter [" + name + "]");
             }
                diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java
index 021f451..66a31d0 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAPolicyStore.java
@@ -58,7 +58,12 @@ public class JPAPolicyStore implements PolicyStore {
     public Policy create(AbstractPolicyRepresentation representation, ResourceServer resourceServer) {
         PolicyEntity entity = new PolicyEntity();
 
-        entity.setId(KeycloakModelUtils.generateId());
+        if (representation.getId() == null) {
+            entity.setId(KeycloakModelUtils.generateId());
+        } else {
+            entity.setId(representation.getId());
+        }
+
         entity.setType(representation.getType());
         entity.setName(representation.getName());
         entity.setResourceServer(ResourceServerAdapter.toEntity(entityManager, resourceServer));
@@ -136,9 +141,9 @@ public class JPAPolicyStore implements PolicyStore {
         attributes.forEach((name, value) -> {
             if ("permission".equals(name)) {
                 if (Boolean.valueOf(value[0])) {
-                    predicates.add(root.get("type").in("resource", "scope"));
+                    predicates.add(root.get("type").in("resource", "scope", "uma"));
                 } else {
-                    predicates.add(builder.not(root.get("type").in("resource", "scope")));
+                    predicates.add(builder.not(root.get("type").in("resource", "scope", "uma")));
                 }
             } else if ("id".equals(name)) {
                 predicates.add(root.get(name).in(value));
@@ -148,6 +153,8 @@ public class JPAPolicyStore implements PolicyStore {
                 predicates.add(builder.isNotNull(root.get("owner")));
             } else if ("resource".equals(name)) {
                 predicates.add(root.join("resources").get("id").in(value));
+            } else if ("scope".equals(name)) {
+                predicates.add(root.join("scopes").get("id").in(value));
             } else {
                 predicates.add(builder.like(builder.lower(root.get(name)), "%" + value[0].toLowerCase() + "%"));
             }
                diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java
index 7f6338d..38ddb80 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceStore.java
@@ -53,9 +53,19 @@ public class JPAResourceStore implements ResourceStore {
 
     @Override
     public Resource create(String name, ResourceServer resourceServer, String owner) {
+        return create(null, name, resourceServer, owner);
+    }
+
+    @Override
+    public Resource create(String id, String name, ResourceServer resourceServer, String owner) {
         ResourceEntity entity = new ResourceEntity();
 
-        entity.setId(KeycloakModelUtils.generateId());
+        if (id == null) {
+            entity.setId(KeycloakModelUtils.generateId());
+        } else {
+            entity.setId(id);
+        }
+
         entity.setName(name);
         entity.setResourceServer(ResourceServerAdapter.toEntity(entityManager, resourceServer));
         entity.setOwner(owner);
@@ -185,6 +195,8 @@ public class JPAResourceStore implements ResourceStore {
                 predicates.add(builder.equal(builder.lower(root.get(name)), value[0].toLowerCase()));
             } else if ("uri_not_null".equals(name)) {
                 predicates.add(builder.isNotNull(root.get("uri")));
+            } else if ("owner".equals(name)) {
+                predicates.add(root.get(name).in(value));
             } else {
                 predicates.add(builder.like(builder.lower(root.get(name)), "%" + value[0].toLowerCase() + "%"));
             }
                diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAScopeStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAScopeStore.java
index befde65..c7debc3 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAScopeStore.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAScopeStore.java
@@ -54,9 +54,19 @@ public class JPAScopeStore implements ScopeStore {
 
     @Override
     public Scope create(final String name, final ResourceServer resourceServer) {
+        return create(null, name, resourceServer);
+    }
+
+    @Override
+    public Scope create(String id, final String name, final ResourceServer resourceServer) {
         ScopeEntity entity = new ScopeEntity();
 
-        entity.setId(KeycloakModelUtils.generateId());
+        if (id == null) {
+            entity.setId(KeycloakModelUtils.generateId());
+        } else {
+            entity.setId(id);
+        }
+
         entity.setName(name);
         entity.setResourceServer(ResourceServerAdapter.toEntity(entityManager, resourceServer));
 
                diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java b/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java
index 57f4c93..25888b8 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java
@@ -233,6 +233,11 @@ public final class AuthorizationProvider implements Provider {
             }
 
             @Override
+            public Scope create(String id, String name, ResourceServer resourceServer) {
+                return delegate.create(id, name, resourceServer);
+            }
+
+            @Override
             public void delete(String id) {
                 Scope scope = findById(id, null);
                 PermissionTicketStore ticketStore = AuthorizationProvider.this.getStoreFactory().getPermissionTicketStore();
@@ -412,6 +417,11 @@ public final class AuthorizationProvider implements Provider {
             }
 
             @Override
+            public Resource create(String id, String name, ResourceServer resourceServer, String owner) {
+                return delegate.create(id, name, resourceServer, owner);
+            }
+
+            @Override
             public void delete(String id) {
                 Resource resource = findById(id, null);
                 StoreFactory storeFactory = AuthorizationProvider.this.getStoreFactory();
                diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/model/PermissionTicket.java b/server-spi-private/src/main/java/org/keycloak/authorization/model/PermissionTicket.java
index 493bfc2..c011a11 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/model/PermissionTicket.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/model/PermissionTicket.java
@@ -30,6 +30,7 @@ public interface PermissionTicket {
     String REQUESTER = "requester";
     String REQUESTER_IS_NULL = "requester_is_null";
     String POLICY_IS_NOT_NULL = "policy_is_not_null";
+    String POLICY = "policy";
 
     /**
      * Returns the unique identifier for this instance.
                diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/permission/ResourcePermission.java b/server-spi-private/src/main/java/org/keycloak/authorization/permission/ResourcePermission.java
index efd3c27..3789281 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/permission/ResourcePermission.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/permission/ResourcePermission.java
@@ -22,11 +22,14 @@ import org.keycloak.authorization.model.Resource;
 import org.keycloak.authorization.model.ResourceServer;
 import org.keycloak.authorization.model.Scope;
 
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 
 /**
@@ -42,9 +45,19 @@ public class ResourcePermission {
     private Map<String, Set<String>> claims;
 
     public ResourcePermission(Resource resource, List<Scope> scopes, ResourceServer resourceServer) {
+        this(resource, scopes, resourceServer, null);
+    }
+
+    public ResourcePermission(Resource resource, List<Scope> scopes, ResourceServer resourceServer, Map<String, ? extends Collection<String>> claims) {
         this.resource = resource;
         this.scopes = scopes;
         this.resourceServer = resourceServer;
+        if (claims != null) {
+            this.claims = new HashMap<>();
+            for (Entry<String, ? extends Collection<String>> entry : claims.entrySet()) {
+                this.claims.computeIfAbsent(entry.getKey(), key -> new LinkedHashSet<>()).addAll(entry.getValue());
+            }
+        }
     }
 
     /**
                diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java
index 24127d6..dd1a81a 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java
@@ -192,6 +192,16 @@ public class DefaultPolicyEvaluator implements PolicyEvaluator {
             }
         }
 
+        if (policyResources.isEmpty() && scopes.isEmpty()) {
+            String defaultResourceType = policy.getConfig().get("defaultResourceType");
+
+            if (defaultResourceType == null) {
+                return false;
+            }
+
+            return defaultResourceType.equals(permission.getResource().getType());
+        }
+
         return false;
     }
 }
                diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/PermissionTicketAwareDecisionResultCollector.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/PermissionTicketAwareDecisionResultCollector.java
index d86888d..6ab219b 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/PermissionTicketAwareDecisionResultCollector.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/PermissionTicketAwareDecisionResultCollector.java
@@ -16,8 +16,8 @@
  */
 package org.keycloak.authorization.policy.evaluation;
 
-import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -26,6 +26,7 @@ import java.util.stream.Collectors;
 import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.identity.Identity;
 import org.keycloak.authorization.model.PermissionTicket;
+import org.keycloak.authorization.model.Policy;
 import org.keycloak.authorization.model.Resource;
 import org.keycloak.authorization.model.ResourceServer;
 import org.keycloak.authorization.model.Scope;
@@ -58,23 +59,67 @@ public class PermissionTicketAwareDecisionResultCollector extends DecisionResult
     }
 
     @Override
+    public void onDecision(DefaultEvaluation evaluation) {
+        super.onDecision(evaluation);
+        removePermissionsIfGranted(evaluation);
+    }
+
+    /**
+     * Removes permissions (represented by {@code ticket}) granted by any user-managed policy so we don't create unnecessary permission tickets.
+     *
+     * @param evaluation the evaluation
+     */
+    private void removePermissionsIfGranted(DefaultEvaluation evaluation) {
+        if (Effect.PERMIT.equals(evaluation.getEffect())) {
+            Policy policy = evaluation.getParentPolicy();
+
+            if ("uma".equals(policy.getType())) {
+                ResourcePermission grantedPermission = evaluation.getPermission();
+                List<PermissionTicketToken.ResourcePermission> permissions = ticket.getResources();
+
+                Iterator<PermissionTicketToken.ResourcePermission> itPermissions = permissions.iterator();
+
+                while (itPermissions.hasNext()) {
+                    PermissionTicketToken.ResourcePermission permission = itPermissions.next();
+
+                    if (permission.getResourceId().equals(grantedPermission.getResource().getId())) {
+                        Set<String> scopes = permission.getScopes();
+                        Iterator<String> itScopes = scopes.iterator();
+
+                        while (itScopes.hasNext()) {
+                            Scope scope = authorization.getStoreFactory().getScopeStore().findByName(itScopes.next(), resourceServer.getId());
+                            if (policy.getScopes().contains(scope)) {
+                                itScopes.remove();
+                            }
+                        }
+
+                        if (scopes.isEmpty()) {
+                            itPermissions.remove();
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
     public void onComplete() {
         super.onComplete();
 
         if (request.isSubmitRequest()) {
             StoreFactory storeFactory = authorization.getStoreFactory();
             ResourceStore resourceStore = storeFactory.getResourceStore();
-            List<PermissionTicketToken.ResourcePermission> resources = ticket.getResources();
+            List<PermissionTicketToken.ResourcePermission> permissions = ticket.getResources();
 
-            if (resources != null) {
-                for (PermissionTicketToken.ResourcePermission permission : resources) {
+            if (permissions != null) {
+                for (PermissionTicketToken.ResourcePermission permission : permissions) {
                     Resource resource = resourceStore.findById(permission.getResourceId(), resourceServer.getId());
 
                     if (resource == null) {
                         resource = resourceStore.findByName(permission.getResourceId(), identity.getId(), resourceServer.getId());
                     }
 
-                    if (!resource.isOwnerManagedAccess() || resource.getOwner().equals(identity.getId()) || resource.getOwner().equals(resourceServer.getId())) {
+                    if (resource == null || !resource.isOwnerManagedAccess() || resource.getOwner().equals(identity.getId()) || resource.getOwner().equals(resourceServer.getId())) {
                         continue;
                     }
 
@@ -91,9 +136,9 @@ public class PermissionTicketAwareDecisionResultCollector extends DecisionResult
                         filters.put(PermissionTicket.REQUESTER, identity.getId());
                         filters.put(PermissionTicket.SCOPE_IS_NULL, Boolean.TRUE.toString());
 
-                        List<PermissionTicket> permissions = authorization.getStoreFactory().getPermissionTicketStore().find(filters, resource.getResourceServer().getId(), -1, -1);
+                        List<PermissionTicket> tickets = authorization.getStoreFactory().getPermissionTicketStore().find(filters, resource.getResourceServer().getId(), -1, -1);
 
-                        if (permissions.isEmpty()) {
+                        if (tickets.isEmpty()) {
                             authorization.getStoreFactory().getPermissionTicketStore().create(resource.getId(), null, identity.getId(), resource.getResourceServer());
                         }
                     } else {
@@ -112,9 +157,9 @@ public class PermissionTicketAwareDecisionResultCollector extends DecisionResult
                             filters.put(PermissionTicket.REQUESTER, identity.getId());
                             filters.put(PermissionTicket.SCOPE, scope.getId());
 
-                            List<PermissionTicket> permissions = authorization.getStoreFactory().getPermissionTicketStore().find(filters, resource.getResourceServer().getId(), -1, -1);
+                            List<PermissionTicket> tickets = authorization.getStoreFactory().getPermissionTicketStore().find(filters, resource.getResourceServer().getId(), -1, -1);
 
-                            if (permissions.isEmpty()) {
+                            if (tickets.isEmpty()) {
                                 authorization.getStoreFactory().getPermissionTicketStore().create(resource.getId(), scope.getId(), identity.getId(), resource.getResourceServer());
                             }
                         }
                diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java
index 1d353fd..2d3ab76 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java
@@ -40,7 +40,7 @@ public interface PolicyProviderFactory<R extends AbstractPolicyRepresentation> e
 
     PolicyProvider create(AuthorizationProvider authorization);
 
-    R toRepresentation(Policy policy);
+    R toRepresentation(Policy policy, AuthorizationProvider authorization);
 
     Class<R> getRepresentationType();
 
                diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/store/ResourceStore.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/ResourceStore.java
index fd6f85c..78d55db 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/store/ResourceStore.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/store/ResourceStore.java
@@ -41,6 +41,17 @@ public interface ResourceStore {
     Resource create(String name, ResourceServer resourceServer, String owner);
 
     /**
+     * <p>Creates a {@link Resource} instance backed by this persistent storage implementation.
+     *
+     * @param id the id of this resource. It must be unique.
+     * @param name the name of this resource. It must be unique.
+     * @param resourceServer the resource server to where the given resource belongs to
+     * @param owner the owner of this resource or null if the resource server is the owner
+     * @return an instance backed by the underlying storage implementation
+     */
+    Resource create(String id, String name, ResourceServer resourceServer, String owner);
+
+    /**
      * Removes a {@link Resource} instance, with the given {@code id} from the persistent storage.
      *
      * @param id the identifier of an existing resource instance
                diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/store/ScopeStore.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/ScopeStore.java
index fa9e70d..011b6ab 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/store/ScopeStore.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/store/ScopeStore.java
@@ -43,6 +43,18 @@ public interface ScopeStore {
     Scope create(String name, ResourceServer resourceServer);
 
     /**
+     * Creates a new {@link Scope} instance. The new instance is not necessarily persisted though, which may require
+     * a call to the {#save} method to actually make it persistent.
+     *
+     * @param id the id of the scope
+     * @param name the name of the scope
+     * @param resourceServer the resource server to which this scope belongs
+     *
+     * @return a new instance of {@link Scope}
+     */
+    Scope create(String id, String name, ResourceServer resourceServer);
+
+    /**
      * Deletes a scope from the underlying persistence mechanism.
      *
      * @param id the id of the scope to delete
                diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index d35eb8d..9bd2f8e 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -17,6 +17,8 @@
 
 package org.keycloak.models.utils;
 
+import com.fasterxml.jackson.core.type.TypeReference;
+import java.io.IOException;
 import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.model.PermissionTicket;
 import org.keycloak.authorization.model.Policy;
@@ -24,7 +26,6 @@ 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;
 import org.keycloak.component.ComponentModel;
@@ -37,9 +38,11 @@ import org.keycloak.provider.ProviderConfigProperty;
 import org.keycloak.representations.idm.*;
 import org.keycloak.representations.idm.authorization.*;
 import org.keycloak.storage.StorageId;
+import org.keycloak.util.JsonSerialization;
 
 import java.util.*;
 import java.util.stream.Collectors;
+import java.util.Map;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -777,7 +780,7 @@ public class ModelToRepresentation {
             }
         } else {
             try {
-                representation = (R) providerFactory.toRepresentation(policy);
+                representation = (R) providerFactory.toRepresentation(policy, authorization);
             } catch (Exception cause) {
                 throw new RuntimeException("Could not create policy [" + policy.getType() + "] representation", cause);
             }
                diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index fe449cd..99d3405 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -2381,7 +2381,7 @@ public class RepresentationToModel {
             return existing;
         }
 
-        Resource model = resourceStore.create(resource.getName(), resourceServer, ownerId);
+        Resource model = resourceStore.create(resource.getId(), resource.getName(), resourceServer, ownerId);
 
         model.setDisplayName(resource.getDisplayName());
         model.setType(resource.getType());
@@ -2426,7 +2426,7 @@ public class RepresentationToModel {
             return existing;
         }
 
-        Scope model = scopeStore.create(scope.getName(), resourceServer);
+        Scope model = scopeStore.create(scope.getId(), scope.getName(), resourceServer);
 
         model.setDisplayName(scope.getDisplayName());
         model.setIconUri(scope.getIconUri());
                diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java
index 3ba29dd..3e3fa27 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java
@@ -19,11 +19,11 @@ package org.keycloak.authorization.admin;
 
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -34,11 +34,9 @@ import java.util.stream.Stream;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.POST;
 import javax.ws.rs.Produces;
-import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 
-import org.jboss.resteasy.spi.HttpRequest;
 import org.keycloak.OAuthErrorException;
 import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.admin.representation.PolicyEvaluationResponseBuilder;
@@ -49,7 +47,6 @@ import org.keycloak.authorization.model.Resource;
 import org.keycloak.authorization.model.ResourceServer;
 import org.keycloak.authorization.model.Scope;
 import org.keycloak.authorization.permission.ResourcePermission;
-import org.keycloak.authorization.policy.evaluation.DecisionResultCollector;
 import org.keycloak.authorization.policy.evaluation.EvaluationContext;
 import org.keycloak.authorization.policy.evaluation.Result;
 import org.keycloak.authorization.store.ScopeStore;
@@ -62,10 +59,10 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
-import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.protocol.oidc.TokenManager;
 import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.idm.authorization.AuthorizationRequest;
 import org.keycloak.representations.idm.authorization.PolicyEvaluationRequest;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.representations.idm.authorization.ScopeRepresentation;
@@ -90,36 +87,34 @@ public class PolicyEvaluationService {
         this.auth = auth;
     }
 
-    static class Decision extends DecisionResultCollector {
-        Throwable error;
-        List<Result> results;
-
-        @Override
-        protected void onComplete(List<Result> results) {
-            this.results = results;
-        }
-
-        @Override
-        public void onError(Throwable cause) {
-            this.error = cause;
-
-        }
-    }
-
-    public static <T> List<T> asList(T... a) {
-        List<T> list = new LinkedList<T>();
-        for (T t : a) list.add(t);
-        return list;
-    }
-
     @POST
     @Consumes("application/json")
     @Produces("application/json")
-    public Response evaluate(PolicyEvaluationRequest evaluationRequest) throws Throwable {
+    public Response evaluate(PolicyEvaluationRequest evaluationRequest) {
         this.auth.realm().requireViewAuthorization();
         CloseableKeycloakIdentity identity = createIdentity(evaluationRequest);
         try {
-            return Response.ok(PolicyEvaluationResponseBuilder.build(evaluate(evaluationRequest, createEvaluationContext(evaluationRequest, identity)), resourceServer, authorization, identity)).build();
+            AuthorizationRequest request = new AuthorizationRequest();
+            Map<String, List<String>> claims = new HashMap<>();
+            Map<String, String> givenAttributes = evaluationRequest.getContext().get("attributes");
+
+            if (givenAttributes != null) {
+                givenAttributes.forEach((key, entryValue) -> {
+                    if (entryValue != null) {
+                        List<String> values = new ArrayList();
+
+                        for (String value : entryValue.split(",")) {
+                            values.add(value);
+                        }
+
+                        claims.put(key, values);
+                    }
+                });
+            }
+
+            request.setClaims(claims);
+
+            return Response.ok(PolicyEvaluationResponseBuilder.build(evaluate(evaluationRequest, createEvaluationContext(evaluationRequest, identity), request), resourceServer, authorization, identity)).build();
         } catch (Exception e) {
             throw new ErrorResponseException(OAuthErrorException.SERVER_ERROR, "Error while evaluating permissions.", Status.INTERNAL_SERVER_ERROR);
         } finally {
@@ -127,8 +122,8 @@ public class PolicyEvaluationService {
         }
     }
 
-    private List<Result> evaluate(PolicyEvaluationRequest evaluationRequest, EvaluationContext evaluationContext) {
-        return authorization.evaluators().from(createPermissions(evaluationRequest, evaluationContext, authorization), evaluationContext).evaluate();
+    private List<Result> evaluate(PolicyEvaluationRequest evaluationRequest, EvaluationContext evaluationContext, AuthorizationRequest request) {
+        return authorization.evaluators().from(createPermissions(evaluationRequest, evaluationContext, authorization, request), evaluationContext).evaluate();
     }
 
     private EvaluationContext createEvaluationContext(PolicyEvaluationRequest representation, KeycloakIdentity identity) {
@@ -157,7 +152,7 @@ public class PolicyEvaluationService {
         };
     }
 
-    private List<ResourcePermission> createPermissions(PolicyEvaluationRequest representation, EvaluationContext evaluationContext, AuthorizationProvider authorization) {
+    private List<ResourcePermission> createPermissions(PolicyEvaluationRequest representation, EvaluationContext evaluationContext, AuthorizationProvider authorization, AuthorizationRequest request) {
         List<ResourceRepresentation> resources = representation.getResources();
         return resources.stream().flatMap((Function<ResourceRepresentation, Stream<ResourcePermission>>) resource -> {
             StoreFactory storeFactory = authorization.getStoreFactory();
@@ -175,18 +170,18 @@ public class PolicyEvaluationService {
 
             if (resource.getId() != null) {
                 Resource resourceModel = storeFactory.getResourceStore().findById(resource.getId(), resourceServer.getId());
-                return Permissions.createResourcePermissions(resourceModel, scopeNames, authorization).stream();
+                return Arrays.asList(Permissions.createResourcePermissions(resourceModel, scopeNames, authorization, request)).stream();
             } else if (resource.getType() != null) {
-                return storeFactory.getResourceStore().findByType(resource.getType(), resourceServer.getId()).stream().flatMap(resource1 -> Permissions.createResourcePermissions(resource1, scopeNames, authorization).stream());
+                return storeFactory.getResourceStore().findByType(resource.getType(), resourceServer.getId()).stream().map(resource1 -> Permissions.createResourcePermissions(resource1, scopeNames, authorization, request));
             } else {
                 ScopeStore scopeStore = storeFactory.getScopeStore();
                 List<Scope> scopes = scopeNames.stream().map(scopeName -> scopeStore.findByName(scopeName, this.resourceServer.getId())).collect(Collectors.toList());
-                List<ResourcePermission> collect = new ArrayList<ResourcePermission>();
+                List<ResourcePermission> collect = new ArrayList<>();
 
                 if (!scopes.isEmpty()) {
-                    collect.addAll(scopes.stream().map(scope -> new ResourcePermission(null, asList(scope), resourceServer)).collect(Collectors.toList()));
+                    collect.addAll(scopes.stream().map(scope -> new ResourcePermission(null, Arrays.asList(scope), resourceServer)).collect(Collectors.toList()));
                 } else {
-                    collect.addAll(Permissions.all(resourceServer, evaluationContext.getIdentity(), authorization));
+                    collect.addAll(Permissions.all(resourceServer, evaluationContext.getIdentity(), authorization, request));
                 }
 
                 return collect.stream();
@@ -266,13 +261,6 @@ public class PolicyEvaluationService {
         }
 
         AccessToken.Access realmAccess = accessToken.getRealmAccess();
-        Map<String, Object> claims = accessToken.getOtherClaims();
-        Map<String, String> givenAttributes = representation.getContext().get("attributes");
-
-        if (givenAttributes != null) {
-            givenAttributes.forEach((key, value) -> claims.put(key, asList(value)));
-        }
-
 
         if (representation.getRoleIds() != null) {
             representation.getRoleIds().forEach(roleName -> realmAccess.addRole(roleName));
                diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyResourceService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyResourceService.java
index f4df4c4..c89c340 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/PolicyResourceService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyResourceService.java
@@ -73,8 +73,10 @@ public class PolicyResourceService {
     @Consumes("application/json")
     @Produces("application/json")
     @NoCache
-    public Response update(@Context UriInfo uriInfo,String payload) {
-        this.auth.realm().requireManageAuthorization();
+    public Response update(@Context UriInfo uriInfo, String payload) {
+        if (auth != null) {
+            this.auth.realm().requireManageAuthorization();
+        }
 
         AbstractPolicyRepresentation representation = doCreateRepresentation(payload);
 
@@ -94,7 +96,9 @@ public class PolicyResourceService {
 
     @DELETE
     public Response delete(@Context UriInfo uriInfo) {
-        this.auth.realm().requireManageAuthorization();
+        if (auth != null) {
+            this.auth.realm().requireManageAuthorization();
+        }
 
         if (policy == null) {
             return Response.status(Status.NOT_FOUND).build();
@@ -119,7 +123,9 @@ public class PolicyResourceService {
     @Produces("application/json")
     @NoCache
     public Response findById() {
-        this.auth.realm().requireViewAuthorization();
+        if (auth != null) {
+            this.auth.realm().requireViewAuthorization();
+        }
 
         if (policy == null) {
             return Response.status(Status.NOT_FOUND).build();
@@ -137,7 +143,9 @@ public class PolicyResourceService {
     @Produces("application/json")
     @NoCache
     public Response getDependentPolicies() {
-        this.auth.realm().requireViewAuthorization();
+        if (auth != null) {
+            this.auth.realm().requireViewAuthorization();
+        }
 
         if (policy == null) {
             return Response.status(Status.NOT_FOUND).build();
@@ -161,7 +169,9 @@ public class PolicyResourceService {
     @Produces("application/json")
     @NoCache
     public Response getScopes() {
-        this.auth.realm().requireViewAuthorization();
+        if (auth != null) {
+            this.auth.realm().requireViewAuthorization();
+        }
 
         if (policy == null) {
             return Response.status(Status.NOT_FOUND).build();
@@ -182,7 +192,9 @@ public class PolicyResourceService {
     @Produces("application/json")
     @NoCache
     public Response getResources() {
-        this.auth.realm().requireViewAuthorization();
+        if (auth != null) {
+            this.auth.realm().requireViewAuthorization();
+        }
 
         if (policy == null) {
             return Response.status(Status.NOT_FOUND).build();
@@ -203,7 +215,9 @@ public class PolicyResourceService {
     @Produces("application/json")
     @NoCache
     public Response getAssociatedPolicies() {
-        this.auth.realm().requireViewAuthorization();
+        if (auth != null) {
+            this.auth.realm().requireViewAuthorization();
+        }
 
         if (policy == null) {
             return Response.status(Status.NOT_FOUND).build();
                diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java
index cb87631..20a3e69 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java
@@ -24,6 +24,8 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 import javax.ws.rs.Consumes;
@@ -43,10 +45,14 @@ import org.jboss.resteasy.annotations.cache.NoCache;
 import org.jboss.resteasy.spi.ResteasyProviderFactory;
 import org.keycloak.authorization.AuthorizationProvider;
 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.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.events.admin.OperationType;
 import org.keycloak.events.admin.ResourceType;
@@ -103,7 +109,9 @@ public class PolicyService {
     @Produces(MediaType.APPLICATION_JSON)
     @NoCache
     public Response create(@Context UriInfo uriInfo, String payload) {
-        this.auth.realm().requireManageAuthorization();
+        if (auth != null) {
+            this.auth.realm().requireManageAuthorization();
+        }
 
         AbstractPolicyRepresentation representation = doCreateRepresentation(payload);
         Policy policy = create(representation);
@@ -143,7 +151,10 @@ public class PolicyService {
     @Produces(MediaType.APPLICATION_JSON)
     @NoCache
     public Response findByName(@QueryParam("name") String name) {
-        this.auth.realm().requireViewAuthorization();
+        if (auth != null) {
+            this.auth.realm().requireViewAuthorization();
+        }
+
         StoreFactory storeFactory = authorization.getStoreFactory();
 
         if (name == null) {
@@ -168,9 +179,12 @@ public class PolicyService {
                             @QueryParam("resource") String resource,
                             @QueryParam("scope") String scope,
                             @QueryParam("permission") Boolean permission,
+                            @QueryParam("owner") String owner,
                             @QueryParam("first") Integer firstResult,
                             @QueryParam("max") Integer maxResult) {
-        this.auth.realm().requireViewAuthorization();
+        if (auth != null) {
+            this.auth.realm().requireViewAuthorization();
+        }
 
         Map<String, String[]> search = new HashMap<>();
 
@@ -186,42 +200,56 @@ public class PolicyService {
             search.put("type", new String[] {type});
         }
 
+        if (owner != null && !"".equals(owner.trim())) {
+            search.put("owner", new String[] {owner});
+        }
+
         StoreFactory storeFactory = authorization.getStoreFactory();
-        PolicyStore policyStore = storeFactory.getPolicyStore();
 
-        if (resource != null || scope != null) {
-            List<Policy> policies = new ArrayList<>();
+        if (resource != null && !"".equals(resource.trim())) {
+            ResourceStore resourceStore = storeFactory.getResourceStore();
+            Resource resourceModel = resourceStore.findById(resource, resourceServer.getId());
 
-            if (resource != null && !"".equals(resource.trim())) {
-                HashMap<String, String[]> resourceSearch = new HashMap<>();
+            if (resourceModel == null) {
+                Map<String, String[]> resourceFilters = new HashMap<>();
 
-                resourceSearch.put("name", new String[]{resource});
+                resourceFilters.put("name", new String[]{resource});
 
-                storeFactory.getResourceStore().findByResourceServer(resourceSearch, resourceServer.getId(), -1, 1).forEach(resource1 -> {
-                    policies.addAll(policyStore.findByResource(resource1.getId(), resourceServer.getId()));
-                    if (resource1.getType() != null) {
-                        policies.addAll(policyStore.findByResourceType(resource1.getType(), resourceServer.getId()));
-                    }
-                });
-            }
+                if (owner != null) {
+                    resourceFilters.put("owner", new String[]{owner});
+                }
 
-            if (scope != null && !"".equals(scope.trim())) {
-                HashMap<String, String[]> scopeSearch = new HashMap<>();
+                Set<String> resources = resourceStore.findByResourceServer(resourceFilters, resourceServer.getId(), -1, 1).stream().map(Resource::getId).collect(Collectors.toSet());
 
-                scopeSearch.put("name", new String[]{scope});
+                if (resources.isEmpty()) {
+                    return Response.ok().build();
+                }
 
-                storeFactory.getScopeStore().findByResourceServer(scopeSearch, resourceServer.getId(), -1, 1).forEach(scope1 -> {
-                    policies.addAll(policyStore.findByScopeIds(Arrays.asList(scope1.getId()), resourceServer.getId()));
-                });
+                search.put("resource", resources.toArray(new String[resources.size()]));
+            } else {
+                search.put("resource", new String[] {resourceModel.getId()});
             }
+        }
 
-            if (policies.isEmpty()) {
-                return Response.ok(Collections.emptyList()).build();
-            }
+        if (scope != null && !"".equals(scope.trim())) {
+            ScopeStore scopeStore = storeFactory.getScopeStore();
+            Scope scopeModel = scopeStore.findById(scope, resourceServer.getId());
 
-            new ArrayList<>(policies).forEach(policy -> findAssociatedPolicies(policy, policies));
+            if (scopeModel == null) {
+                Map<String, String[]> scopeFilters = new HashMap<>();
 
-            search.put("id", policies.stream().map(Policy::getId).toArray(String[]::new));
+                scopeFilters.put("name", new String[]{scope});
+
+                Set<String> scopes = scopeStore.findByResourceServer(scopeFilters, resourceServer.getId(), -1, 1).stream().map(Scope::getId).collect(Collectors.toSet());
+
+                if (scopes.isEmpty()) {
+                    return Response.ok().build();
+                }
+
+                search.put("scope", scopes.toArray(new String[scopes.size()]));
+            } else {
+                search.put("scope", new String[] {scopeModel.getId()});
+            }
         }
 
         if (permission != null) {
@@ -249,7 +277,10 @@ public class PolicyService {
     @Produces(MediaType.APPLICATION_JSON)
     @NoCache
     public Response findPolicyProviders() {
-        this.auth.realm().requireViewAuthorization();
+        if (auth != null) {
+            this.auth.realm().requireViewAuthorization();
+        }
+
         return Response.ok(
                 authorization.getProviderFactories().stream()
                         .filter(factory -> !factory.isInternal())
@@ -268,7 +299,10 @@ public class PolicyService {
 
     @Path("evaluate")
     public PolicyEvaluationService getPolicyEvaluateResource() {
-        this.auth.realm().requireViewAuthorization();
+        if (auth != null) {
+            this.auth.realm().requireViewAuthorization();
+        }
+
         PolicyEvaluationService resource = new PolicyEvaluationService(this.resourceServer, this.authorization, this.auth);
 
         ResteasyProviderFactory.getInstance().injectProperties(resource);
                diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeService.java
index 2911877..6acc572 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeService.java
@@ -41,7 +41,7 @@ public class PolicyTypeService extends PolicyService {
 
     private final String type;
 
-    PolicyTypeService(String type, ResourceServer resourceServer, AuthorizationProvider authorization, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
+    public PolicyTypeService(String type, ResourceServer resourceServer, AuthorizationProvider authorization, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
         super(resourceServer, authorization, auth, adminEvent);
         this.type = type;
     }
                diff --git a/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponseBuilder.java b/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponseBuilder.java
index b5023b5..3842a94 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponseBuilder.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/representation/PolicyEvaluationResponseBuilder.java
@@ -19,12 +19,15 @@ package org.keycloak.authorization.admin.representation;
 import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.Decision;
 import org.keycloak.authorization.common.KeycloakIdentity;
+import org.keycloak.authorization.model.PermissionTicket;
+import org.keycloak.authorization.model.Policy;
 import org.keycloak.authorization.model.ResourceServer;
 import org.keycloak.authorization.model.Scope;
 import org.keycloak.authorization.policy.evaluation.Result;
 import org.keycloak.authorization.util.Permissions;
 import org.keycloak.models.ClientModel;
-import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.UserModel;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.idm.authorization.DecisionEffect;
 import org.keycloak.representations.idm.authorization.PolicyEvaluationResponse;
@@ -169,33 +172,64 @@ public class PolicyEvaluationResponseBuilder {
         return response;
     }
 
-    private static PolicyEvaluationResponse.PolicyResultRepresentation toRepresentation(Result.PolicyResult policy, AuthorizationProvider authorization) {
+    private static PolicyEvaluationResponse.PolicyResultRepresentation toRepresentation(Result.PolicyResult result, AuthorizationProvider authorization) {
         PolicyEvaluationResponse.PolicyResultRepresentation policyResultRep = new PolicyEvaluationResponse.PolicyResultRepresentation();
 
         PolicyRepresentation representation = new PolicyRepresentation();
+        Policy policy = result.getPolicy();
 
-        representation.setId(policy.getPolicy().getId());
-        representation.setName(policy.getPolicy().getName());
-        representation.setType(policy.getPolicy().getType());
-        representation.setDecisionStrategy(policy.getPolicy().getDecisionStrategy());
+        representation.setId(policy.getId());
+        representation.setName(policy.getName());
+        representation.setType(policy.getType());
+        representation.setDecisionStrategy(policy.getDecisionStrategy());
+        representation.setDescription(policy.getDescription());
 
-        representation.setResources(policy.getPolicy().getResources().stream().map(resource -> resource.getName()).collect(Collectors.toSet()));
+        if ("uma".equals(representation.getType())) {
+            Map<String, String> filters = new HashMap<>();
 
-        Set<String> scopeNames = policy.getPolicy().getScopes().stream().map(scope -> scope.getName()).collect(Collectors.toSet());
+            filters.put(PermissionTicket.POLICY, policy.getId());
+
+            List<PermissionTicket> tickets = authorization.getStoreFactory().getPermissionTicketStore().find(filters, policy.getResourceServer().getId(), -1, 1);
+
+            if (!tickets.isEmpty()) {
+                KeycloakSession keycloakSession = authorization.getKeycloakSession();
+                PermissionTicket ticket = tickets.get(0);
+                UserModel owner = keycloakSession.users().getUserById(ticket.getOwner(), authorization.getRealm());
+                UserModel requester = keycloakSession.users().getUserById(ticket.getRequester(), authorization.getRealm());
+
+                representation.setDescription("Resource owner (" + getUserEmailOrUserName(owner) + ") grants access to " + getUserEmailOrUserName(requester));
+            } else {
+                String description = representation.getDescription();
+
+                if (description != null) {
+                    representation.setDescription(description + " (User-Managed Policy)");
+                } else {
+                    representation.setDescription("User-Managed Policy");
+                }
+            }
+        }
+
+        representation.setResources(policy.getResources().stream().map(resource -> resource.getName()).collect(Collectors.toSet()));
+
+        Set<String> scopeNames = policy.getScopes().stream().map(scope -> scope.getName()).collect(Collectors.toSet());
 
         representation.setScopes(scopeNames);
 
         policyResultRep.setPolicy(representation);
 
-        if (policy.getStatus() == Decision.Effect.DENY) {
+        if (result.getStatus() == Decision.Effect.DENY) {
             policyResultRep.setStatus(DecisionEffect.DENY);
             policyResultRep.setScopes(representation.getScopes());
         } else {
             policyResultRep.setStatus(DecisionEffect.PERMIT);
         }
 
-        policyResultRep.setAssociatedPolicies(policy.getAssociatedPolicies().stream().map(result -> toRepresentation(result, authorization)).collect(Collectors.toList()));
+        policyResultRep.setAssociatedPolicies(result.getAssociatedPolicies().stream().map(policy1 -> toRepresentation(policy1, authorization)).collect(Collectors.toList()));
 
         return policyResultRep;
     }
+
+    private static String getUserEmailOrUserName(UserModel user) {
+        return (user.getEmail() != null ? user.getEmail() : user.getUsername());
+    }
 }
                diff --git a/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java b/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java
index b791f7f..a0351e1 100644
--- a/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java
+++ b/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java
@@ -100,7 +100,7 @@ public class AuthorizationTokenService {
                 try {
                     Map claims = JsonSerialization.readValue(Base64Url.decode(authorizationRequest.getClaimToken()), Map.class);
                     authorizationRequest.setClaims(claims);
-                    return new KeycloakEvaluationContext(new KeycloakIdentity(authorization.getKeycloakSession(), Tokens.getAccessToken(authorizationRequest.getAccessToken(), authorization.getKeycloakSession())), claims, authorization.getKeycloakSession());
+                    return new KeycloakEvaluationContext(new KeycloakIdentity(authorization.getKeycloakSession(), Tokens.getAccessToken(authorizationRequest.getSubjectToken(), authorization.getKeycloakSession())), claims, authorization.getKeycloakSession());
                 } catch (IOException cause) {
                     throw new RuntimeException("Failed to map claims from claim token [" + claimToken + "]", cause);
                 }
@@ -112,7 +112,7 @@ public class AuthorizationTokenService {
             try {
                 KeycloakSession keycloakSession = authorization.getKeycloakSession();
                 RealmModel realm = authorization.getRealm();
-                String accessToken = authorizationRequest.getAccessToken();
+                String accessToken = authorizationRequest.getSubjectToken();
 
                 if (accessToken == null) {
                     throw new RuntimeException("Claim token can not be null and must be a valid IDToken");
@@ -161,7 +161,7 @@ public class AuthorizationTokenService {
             List<Result> evaluation;
 
             if (ticket.getResources().isEmpty() && request.getRpt() == null) {
-                evaluation = evaluateAllPermissions(resourceServer, evaluationContext, identity);
+                evaluation = evaluateAllPermissions(request, resourceServer, evaluationContext, identity);
             } else if(!request.getPermissions().getResources().isEmpty()) {
                 evaluation = evaluatePermissions(request, ticket, resourceServer, evaluationContext, identity);
             } else {
@@ -212,9 +212,9 @@ public class AuthorizationTokenService {
                 .evaluate(new PermissionTicketAwareDecisionResultCollector(request, ticket, identity, resourceServer, authorization)).results();
     }
 
-    private List<Result> evaluateAllPermissions(ResourceServer resourceServer, KeycloakEvaluationContext evaluationContext, KeycloakIdentity identity) {
+    private List<Result> evaluateAllPermissions(AuthorizationRequest request, ResourceServer resourceServer, KeycloakEvaluationContext evaluationContext, KeycloakIdentity identity) {
         return authorization.evaluators()
-                .from(Permissions.all(resourceServer, identity, authorization), evaluationContext)
+                .from(Permissions.all(resourceServer, identity, authorization, request), evaluationContext)
                 .evaluate();
     }
 
@@ -235,7 +235,6 @@ public class AuthorizationTokenService {
         Authorization authorization = new Authorization();
 
         authorization.setPermissions(entitlements);
-        authorization.setClaims(request.getClaims());
 
         rpt.setAuthorization(authorization);
 
@@ -309,8 +308,9 @@ public class AuthorizationTokenService {
 
     private List<ResourcePermission> createPermissions(PermissionTicketToken ticket, AuthorizationRequest request, ResourceServer resourceServer, KeycloakIdentity identity, AuthorizationProvider authorization) {
         StoreFactory storeFactory = authorization.getStoreFactory();
-        Map<String, Set<String>> permissionsToEvaluate = new LinkedHashMap<>();
+        Map<String, ResourcePermission> permissionsToEvaluate = new LinkedHashMap<>();
         ResourceStore resourceStore = storeFactory.getResourceStore();
+        ScopeStore scopeStore = storeFactory.getScopeStore();
         Metadata metadata = request.getMetadata();
         Integer limit = metadata != null ? metadata.getLimit() : null;
 
@@ -359,31 +359,37 @@ public class AuthorizationTokenService {
                 requestedScopes.addAll(Arrays.asList(clientAdditionalScopes.split(" ")));
             }
 
+            List<Scope> requestedScopesModel = requestedScopes.stream().map(s -> scopeStore.findByName(s, resourceServer.getId())).collect(Collectors.toList());
+
             if (!existingResources.isEmpty()) {
                 for (Resource resource : existingResources) {
-                    Set<String> scopes = permissionsToEvaluate.get(resource.getId());
+                    ResourcePermission permission = permissionsToEvaluate.get(resource.getId());
 
-                    if (scopes == null) {
-                        scopes = new HashSet<>();
-                        permissionsToEvaluate.put(resource.getId(), scopes);
+                    if (permission == null) {
+                        permission = Permissions.createResourcePermissions(resource, requestedScopes, authorization, request);
+                        permissionsToEvaluate.put(resource.getId(), permission);
                         if (limit != null) {
                             limit--;
                         }
+                    } else {
+                        for (Scope scope : requestedScopesModel) {
+                            if (!permission.getScopes().contains(scope)) {
+                                permission.getScopes().add(scope);
+                            }
+                        }
                     }
-
-                    scopes.addAll(requestedScopes);
                 }
             } else {
-                List<Resource> resources = resourceStore.findByScope(new ArrayList<>(requestedScopes), ticket.getAudience()[0]);
+                List<Resource> resources = resourceStore.findByScope(new ArrayList<>(requestedScopes), resourceServer.getId());
 
                 for (Resource resource : resources) {
-                    permissionsToEvaluate.put(resource.getId(), requestedScopes);
+                    permissionsToEvaluate.put(resource.getId(), Permissions.createResourcePermissions(resource, requestedScopes, authorization, request));
                     if (limit != null) {
                         limit--;
                     }
                 }
 
-                permissionsToEvaluate.put("$KC_SCOPE_PERMISSION", requestedScopes);
+                permissionsToEvaluate.put("$KC_SCOPE_PERMISSION", new ResourcePermission(null, requestedScopesModel, resourceServer, request.getClaims()));
             }
         }
 
@@ -409,28 +415,42 @@ public class AuthorizationTokenService {
                     List<Permission> permissions = authorizationData.getPermissions();
 
                     if (permissions != null) {
-                        for (Permission permission : permissions) {
+                        for (Permission grantedPermission : permissions) {
                             if (limit != null && limit <= 0) {
                                 break;
                             }
 
-                            Resource resourcePermission = resourceStore.findById(permission.getResourceId(), ticket.getAudience()[0]);
+                            Resource resourcePermission = resourceStore.findById(grantedPermission.getResourceId(), ticket.getAudience()[0]);
 
                             if (resourcePermission != null) {
-                                Set<String> scopes = permissionsToEvaluate.get(resourcePermission.getId());
+                                ResourcePermission permission = permissionsToEvaluate.get(resourcePermission.getId());
 
-                                if (scopes == null) {
-                                    scopes = new HashSet<>();
-                                    permissionsToEvaluate.put(resourcePermission.getId(), scopes);
+                                if (permission == null) {
+                                    permission = new ResourcePermission(resourcePermission, new ArrayList<>(), resourceServer, grantedPermission.getClaims());
+                                    permissionsToEvaluate.put(resourcePermission.getId(), permission);
                                     if (limit != null) {
                                         limit--;
                                     }
+                                } else {
+                                    if (grantedPermission.getClaims() != null) {
+                                        for (Entry<String, Set<String>> entry : grantedPermission.getClaims().entrySet()) {
+                                            Set<String> claims = permission.getClaims().get(entry.getKey());
+
+                                            if (claims != null) {
+                                                claims.addAll(entry.getValue());
+                                            }
+                                        }
+                                    }
                                 }
 
-                                Set<String> scopePermission = permission.getScopes();
+                                for (String scopeName : grantedPermission.getScopes()) {
+                                    Scope scope = scopeStore.findByName(scopeName, resourceServer.getId());
 
-                                if (scopePermission != null) {
-                                    scopes.addAll(scopePermission);
+                                    if (scope != null) {
+                                        if (!permission.getScopes().contains(scope)) {
+                                            permission.getScopes().add(scope);
+                                        }
+                                    }
                                 }
                             }
                         }
@@ -439,19 +459,7 @@ public class AuthorizationTokenService {
             }
         }
 
-        ScopeStore scopeStore = storeFactory.getScopeStore();
-
-        return permissionsToEvaluate.entrySet().stream()
-                .flatMap((Function<Entry<String, Set<String>>, Stream<ResourcePermission>>) entry -> {
-                    String key = entry.getKey();
-                    if ("$KC_SCOPE_PERMISSION".equals(key)) {
-                        List<Scope> scopes = entry.getValue().stream().map(scopeName -> scopeStore.findByName(scopeName, resourceServer.getId())).filter(scope -> Objects.nonNull(scope)).collect(Collectors.toList());
-                        return Arrays.asList(new ResourcePermission(null, scopes, resourceServer)).stream();
-                    } else {
-                        Resource entryResource = resourceStore.findById(key, resourceServer.getId());
-                        return Permissions.createResourcePermissions(entryResource, entry.getValue(), authorization).stream();
-                    }
-                }).collect(Collectors.toList());
+        return new ArrayList<>(permissionsToEvaluate.values());
     }
 
     private PermissionTicketToken verifyPermissionTicket(AuthorizationRequest request) {
                diff --git a/services/src/main/java/org/keycloak/authorization/config/UmaConfiguration.java b/services/src/main/java/org/keycloak/authorization/config/UmaConfiguration.java
index 67fb296..f622dc4 100644
--- a/services/src/main/java/org/keycloak/authorization/config/UmaConfiguration.java
+++ b/services/src/main/java/org/keycloak/authorization/config/UmaConfiguration.java
@@ -61,6 +61,7 @@ public class UmaConfiguration extends OIDCConfigurationRepresentation {
 
         configuration.setPermissionEndpoint(uriBuilder.clone().path(RealmsResource.class).path(RealmsResource.class, "getAuthorizationService").path(AuthorizationService.class, "getProtectionService").path(ProtectionService.class, "permission").build(realm.getName()).toString());
         configuration.setResourceRegistrationEndpoint(uriBuilder.clone().path(RealmsResource.class).path(RealmsResource.class, "getAuthorizationService").path(AuthorizationService.class, "getProtectionService").path(ProtectionService.class, "resource").build(realm.getName()).toString());
+        configuration.setPolicyEndpoint(uriBuilder.clone().path(RealmsResource.class).path(RealmsResource.class, "getAuthorizationService").path(AuthorizationService.class, "getProtectionService").path(ProtectionService.class, "policy").build(realm.getName()).toString());
 
         return configuration;
     }
@@ -70,6 +71,9 @@ public class UmaConfiguration extends OIDCConfigurationRepresentation {
 
     @JsonProperty("permission_endpoint")
     private String permissionEndpoint;
+    
+    @JsonProperty("policy_endpoint")
+    private String policyEndpoint;
 
     public String getResourceRegistrationEndpoint() {
         return this.resourceRegistrationEndpoint;
@@ -86,4 +90,12 @@ public class UmaConfiguration extends OIDCConfigurationRepresentation {
     void setPermissionEndpoint(String permissionEndpoint) {
         this.permissionEndpoint = permissionEndpoint;
     }
+    
+    public String getPolicyEndpoint() {
+        return this.policyEndpoint;
+    }
+
+    void setPolicyEndpoint(String policyEndpoint) {
+        this.policyEndpoint = policyEndpoint;
+    }
 }
                diff --git a/services/src/main/java/org/keycloak/authorization/protection/permission/PermissionTicketService.java b/services/src/main/java/org/keycloak/authorization/protection/permission/PermissionTicketService.java
index 59ff1fd..fafbd71 100644
--- a/services/src/main/java/org/keycloak/authorization/protection/permission/PermissionTicketService.java
+++ b/services/src/main/java/org/keycloak/authorization/protection/permission/PermissionTicketService.java
@@ -125,6 +125,8 @@ public class PermissionTicketService {
             throw new ErrorResponseException("invalid_permission", "Permission already exists", Response.Status.BAD_REQUEST);
         
         PermissionTicket ticket = ticketStore.create(resource.getId(), scope.getId(), user.getId(), resourceServer);
+        if(representation.isGranted())
+                ticket.setGrantedTimestamp(java.lang.System.currentTimeMillis());
         representation = ModelToRepresentation.toRepresentation(ticket, authorization);
         return Response.ok(representation).build();
     }
                diff --git a/services/src/main/java/org/keycloak/authorization/protection/policy/UserManagedPermissionService.java b/services/src/main/java/org/keycloak/authorization/protection/policy/UserManagedPermissionService.java
new file mode 100644
index 0000000..d9663a4
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/protection/policy/UserManagedPermissionService.java
@@ -0,0 +1,183 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual 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.protection.policy;
+
+import java.io.IOException;
+import java.util.Set;
+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.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriInfo;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.keycloak.OAuthErrorException;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.admin.PermissionService;
+import org.keycloak.authorization.admin.PolicyTypeResourceService;
+import org.keycloak.authorization.common.KeycloakIdentity;
+import org.keycloak.authorization.identity.Identity;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.store.ResourceStore;
+import org.keycloak.representations.idm.authorization.UmaPermissionRepresentation;
+import org.keycloak.services.ErrorResponseException;
+import org.keycloak.services.resources.admin.AdminEventBuilder;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ * @author <a href="mailto:federico@martel-innovate.com">Federico M. Facca</a>
+ */
+public class UserManagedPermissionService {
+
+    private final ResourceServer resourceServer;
+    private final Identity identity;
+    private final AuthorizationProvider authorization;
+    private final PermissionService delegate;
+
+    public UserManagedPermissionService(KeycloakIdentity identity, ResourceServer resourceServer, AuthorizationProvider authorization, AdminEventBuilder eventBuilder) {
+        this.identity = identity;
+        this.resourceServer = resourceServer;
+        this.authorization = authorization;
+        delegate = new PermissionService(resourceServer, authorization, null, eventBuilder);
+        ResteasyProviderFactory.getInstance().injectProperties(delegate);
+    }
+
+    @POST
+    @Path("{resourceId}")
+    @Consumes("application/json")
+    @Produces("application/json")
+    public Response create(@Context UriInfo uriInfo, @PathParam("resourceId") String resourceId, UmaPermissionRepresentation representation) {
+        if (representation.getId() != null) {
+            throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Newly created uma policies should not have an id", Response.Status.BAD_REQUEST);
+        }
+
+        checkRequest(resourceId, representation);
+
+        representation.addResource(resourceId);
+        representation.setOwner(identity.getId());
+
+        return findById(delegate.create(representation).getId());
+    }
+
+    @Path("{policyId}")
+    @PUT
+    @Consumes("application/json")
+    @Produces("application/json")
+    public Response update(@Context UriInfo uriInfo, @PathParam("policyId") String policyId, String payload) {
+        UmaPermissionRepresentation representation;
+
+        try {
+            representation = JsonSerialization.readValue(payload, UmaPermissionRepresentation.class);
+        } catch (IOException e) {
+            throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Failed to parse representation", Status.BAD_REQUEST);
+        }
+
+        checkRequest(getAssociatedResourceId(policyId), representation);
+
+        return PolicyTypeResourceService.class.cast(delegate.getResource(policyId)).update(uriInfo, payload);
+    }
+
+    @Path("{policyId}")
+    @DELETE
+    public Response delete(@Context UriInfo uriInfo, @PathParam("policyId") String policyId) {
+        checkRequest(getAssociatedResourceId(policyId), null);
+        PolicyTypeResourceService.class.cast(delegate.getResource(policyId)).delete(uriInfo);
+        return Response.noContent().build();
+    }
+
+    @Path("{policyId}")
+    @GET
+    @Produces("application/json")
+    public Response findById(@PathParam("policyId") String policyId) {
+        checkRequest(getAssociatedResourceId(policyId), null);
+        return PolicyTypeResourceService.class.cast(delegate.getResource(policyId)).findById();
+    }
+
+    @GET
+    @NoCache
+    @Produces("application/json")
+    public Response find(@QueryParam("name") String name,
+                         @QueryParam("resource") String resource,
+                         @QueryParam("scope") String scope,
+                         @QueryParam("first") Integer firstResult,
+                         @QueryParam("max") Integer maxResult) {
+        return  delegate.findAll(null, name, "uma", resource, scope, true, identity.getId(), firstResult, maxResult);
+    }
+
+    private Policy getPolicy(@PathParam("policyId") String policyId) {
+        Policy existing = authorization.getStoreFactory().getPolicyStore().findById(policyId, resourceServer.getId());
+
+        if (existing == null) {
+            throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Policy with [" + policyId + "] does not exist", Status.NOT_FOUND);
+        }
+
+        return existing;
+    }
+
+    private void checkRequest(String resourceId, UmaPermissionRepresentation representation) {
+        ResourceStore resourceStore = this.authorization.getStoreFactory().getResourceStore();
+        Resource resource = resourceStore.findById(resourceId, resourceServer.getId());
+
+        if (resource == null) {
+            throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Resource [" + resourceId + "] cannot be found", Response.Status.BAD_REQUEST);
+        }
+
+        if (!resource.getOwner().equals(identity.getId())) {
+            throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Only resource onwer can access policies for resource [" + resourceId + "]", Status.BAD_REQUEST);
+        }
+
+        if (!resource.isOwnerManagedAccess()) {
+            throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Only resources with owner managed accessed can have policies", Status.BAD_REQUEST);
+        }
+
+        if (!resourceServer.isAllowRemoteResourceManagement()) {
+            throw new ErrorResponseException(OAuthErrorException.REQUEST_NOT_SUPPORTED, "Remote Resource Management not enabled on resource server [" + resourceServer.getId() + "]", Status.FORBIDDEN);
+        }
+
+        if (representation != null) {
+            Set<String> resourceScopes = resource.getScopes().stream().map(scope -> scope.getName()).collect(Collectors.toSet());
+            Set<String> scopes = representation.getScopes();
+
+            if (scopes == null || scopes.isEmpty()) {
+                scopes = resourceScopes;
+                representation.setScopes(scopes);
+            }
+
+            if (!resourceScopes.containsAll(scopes)) {
+                throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "Some of the scopes [" + scopes + "] are not valid for resource [" + resourceId + "]", Response.Status.BAD_REQUEST);
+            }
+        }
+    }
+
+    private String getAssociatedResourceId(String policyId) {
+        return getPolicy(policyId).getResources().iterator().next().getId();
+    }
+}
                diff --git a/services/src/main/java/org/keycloak/authorization/protection/ProtectionService.java b/services/src/main/java/org/keycloak/authorization/protection/ProtectionService.java
index ce4dff6..f4bbba6 100644
--- a/services/src/main/java/org/keycloak/authorization/protection/ProtectionService.java
+++ b/services/src/main/java/org/keycloak/authorization/protection/ProtectionService.java
@@ -38,6 +38,7 @@ import javax.ws.rs.Path;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Response.Status;
 import org.keycloak.authorization.protection.permission.PermissionTicketService;
+import org.keycloak.authorization.protection.policy.UserManagedPermissionService;
 
 /**
  * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -57,12 +58,7 @@ public class ProtectionService {
     public Object resource() {
         KeycloakIdentity identity = createIdentity(true);
         ResourceServer resourceServer = getResourceServer(identity);
-        RealmModel realm = authorization.getRealm();
-        ClientModel client = realm.getClientById(resourceServer.getId());
-        KeycloakSession keycloakSession = authorization.getKeycloakSession();
-        UserModel serviceAccount = keycloakSession.users().getServiceAccount(client);
-        AdminEventBuilder adminEvent = new AdminEventBuilder(realm, new AdminAuth(realm, identity.getAccessToken(), serviceAccount, client), keycloakSession, clientConnection);
-        ResourceSetService resourceManager = new ResourceSetService(resourceServer, this.authorization, null, adminEvent.realm(realm).authClient(client).authUser(serviceAccount));
+        ResourceSetService resourceManager = new ResourceSetService(resourceServer, this.authorization, null, createAdminEventBuilder(identity, resourceServer));
 
         ResteasyProviderFactory.getInstance().injectProperties(resourceManager);
 
@@ -73,6 +69,15 @@ public class ProtectionService {
         return resource;
     }
 
+    private AdminEventBuilder createAdminEventBuilder(KeycloakIdentity identity, ResourceServer resourceServer) {
+        RealmModel realm = authorization.getRealm();
+        ClientModel client = realm.getClientById(resourceServer.getId());
+        KeycloakSession keycloakSession = authorization.getKeycloakSession();
+        UserModel serviceAccount = keycloakSession.users().getServiceAccount(client);
+        AdminEventBuilder adminEvent = new AdminEventBuilder(realm, new AdminAuth(realm, identity.getAccessToken(), serviceAccount, client), keycloakSession, clientConnection);
+        return adminEvent.realm(realm).authClient(client).authUser(serviceAccount);
+    }
+
     @Path("/permission")
     public Object permission() {
         KeycloakIdentity identity = createIdentity(false);
@@ -94,6 +99,17 @@ public class ProtectionService {
 
         return resource;
     }
+    
+    @Path("/uma-policy")
+    public Object policy() {
+        KeycloakIdentity identity = createIdentity(false);
+
+        UserManagedPermissionService resource = new UserManagedPermissionService(identity, getResourceServer(identity), this.authorization, createAdminEventBuilder(identity, getResourceServer(identity)));
+
+        ResteasyProviderFactory.getInstance().injectProperties(resource);
+
+        return resource;
+    }
 
     private KeycloakIdentity createIdentity(boolean checkProtectionScope) {
         KeycloakIdentity identity = new KeycloakIdentity(this.authorization.getKeycloakSession());
                diff --git a/services/src/main/java/org/keycloak/authorization/util/Permissions.java b/services/src/main/java/org/keycloak/authorization/util/Permissions.java
index 180ec33..32219dd 100644
--- a/services/src/main/java/org/keycloak/authorization/util/Permissions.java
+++ b/services/src/main/java/org/keycloak/authorization/util/Permissions.java
@@ -44,6 +44,7 @@ import org.keycloak.authorization.policy.evaluation.Result;
 import org.keycloak.authorization.store.ResourceStore;
 import org.keycloak.authorization.store.ScopeStore;
 import org.keycloak.authorization.store.StoreFactory;
+import org.keycloak.representations.idm.authorization.AuthorizationRequest;
 import org.keycloak.representations.idm.authorization.AuthorizationRequest.Metadata;
 import org.keycloak.representations.idm.authorization.Permission;
 import org.keycloak.services.ErrorResponseException;
@@ -68,23 +69,23 @@ public final class Permissions {
      * @param authorization
      * @return
      */
-    public static List<ResourcePermission> all(ResourceServer resourceServer, Identity identity, AuthorizationProvider authorization) {
+    public static List<ResourcePermission> all(ResourceServer resourceServer, Identity identity, AuthorizationProvider authorization, AuthorizationRequest request) {
         List<ResourcePermission> permissions = new ArrayList<>();
         StoreFactory storeFactory = authorization.getStoreFactory();
         ResourceStore resourceStore = storeFactory.getResourceStore();
 
         // obtain all resources where owner is the resource server
-        resourceStore.findByOwner(resourceServer.getId(), resourceServer.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissionsWithScopes(resource, new LinkedList(resource.getScopes()), authorization)));
+        resourceStore.findByOwner(resourceServer.getId(), resourceServer.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissionsWithScopes(resource, new LinkedList(resource.getScopes()), authorization, request)));
 
         // obtain all resources where owner is the current user
-        resourceStore.findByOwner(identity.getId(), resourceServer.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissionsWithScopes(resource, new LinkedList(resource.getScopes()), authorization)));
+        resourceStore.findByOwner(identity.getId(), resourceServer.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissionsWithScopes(resource, new LinkedList(resource.getScopes()), authorization, request)));
 
         // obtain all resources granted to the user via permission tickets (uma)
         List<PermissionTicket> tickets = storeFactory.getPermissionTicketStore().findGranted(identity.getId(), resourceServer.getId());
         Map<String, ResourcePermission> userManagedPermissions = new HashMap<>();
 
         for (PermissionTicket ticket : tickets) {
-            userManagedPermissions.computeIfAbsent(ticket.getResource().getId(), id -> new ResourcePermission(ticket.getResource(), new ArrayList<>(), resourceServer));
+            userManagedPermissions.computeIfAbsent(ticket.getResource().getId(), id -> new ResourcePermission(ticket.getResource(), new ArrayList<>(), resourceServer, request.getClaims()));
         }
 
         permissions.addAll(userManagedPermissions.values());
@@ -92,8 +93,7 @@ public final class Permissions {
         return permissions;
     }
 
-    public static List<ResourcePermission> createResourcePermissions(Resource resource, Set<String> requestedScopes, AuthorizationProvider authorization) {
-        List<ResourcePermission> permissions = new ArrayList<>();
+    public static ResourcePermission createResourcePermissions(Resource resource, Set<String> requestedScopes, AuthorizationProvider authorization, AuthorizationRequest request) {
         String type = resource.getType();
         ResourceServer resourceServer = resource.getResourceServer();
         List<Scope> scopes;
@@ -127,12 +127,11 @@ public final class Permissions {
                 return byName;
             }).collect(Collectors.toList());
         }
-        permissions.add(new ResourcePermission(resource, scopes, resource.getResourceServer()));
 
-        return permissions;
+        return new ResourcePermission(resource, scopes, resource.getResourceServer(), request.getClaims());
     }
 
-    public static List<ResourcePermission> createResourcePermissionsWithScopes(Resource resource, List<Scope> scopes, AuthorizationProvider authorization) {
+    public static List<ResourcePermission> createResourcePermissionsWithScopes(Resource resource, List<Scope> scopes, AuthorizationProvider authorization, AuthorizationRequest request) {
         List<ResourcePermission> permissions = new ArrayList<>();
         String type = resource.getType();
         ResourceServer resourceServer = resource.getResourceServer();
@@ -153,7 +152,7 @@ public final class Permissions {
             });
         }
 
-        permissions.add(new ResourcePermission(resource, scopes, resource.getResourceServer()));
+        permissions.add(new ResourcePermission(resource, scopes, resource.getResourceServer(), request.getClaims()));
 
         return permissions;
     }
                diff --git a/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java b/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
index a84765b..169b5bf 100755
--- a/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
+++ b/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
@@ -320,7 +320,6 @@ public class ExportUtils {
                     } else {
                         rep.getOwner().setId(null);
                     }
-                    rep.setId(null);
                     rep.getScopes().forEach(scopeRepresentation -> {
                         scopeRepresentation.setId(null);
                         scopeRepresentation.setIconUri(null);
@@ -335,10 +334,10 @@ public class ExportUtils {
         PolicyStore policyStore = storeFactory.getPolicyStore();
 
         policies.addAll(policyStore.findByResourceServer(settingsModel.getId())
-                .stream().filter(policy -> !policy.getType().equals("resource") && !policy.getType().equals("scope"))
+                .stream().filter(policy -> !policy.getType().equals("resource") && !policy.getType().equals("scope") && policy.getOwner() == null)
                 .map(policy -> createPolicyRepresentation(authorization, policy)).collect(Collectors.toList()));
         policies.addAll(policyStore.findByResourceServer(settingsModel.getId())
-                .stream().filter(policy -> policy.getType().equals("resource") || policy.getType().equals("scope"))
+                .stream().filter(policy -> (policy.getType().equals("resource") || policy.getType().equals("scope") && policy.getOwner() == null))
                 .map(policy -> createPolicyRepresentation(authorization, policy)).collect(Collectors.toList()));
 
         representation.setPolicies(policies);
@@ -346,7 +345,6 @@ public class ExportUtils {
         List<ScopeRepresentation> scopes = storeFactory.getScopeStore().findByResourceServer(settingsModel.getId()).stream().map(scope -> {
             ScopeRepresentation rep = toRepresentation(scope);
 
-            rep.setId(null);
             rep.setPolicies(null);
             rep.setResources(null);
 
@@ -362,8 +360,6 @@ public class ExportUtils {
         try {
             PolicyRepresentation rep = toRepresentation(policy, authorizationProvider, true, true);
 
-            rep.setId(null);
-
             Map<String, String> config = new HashMap<>(rep.getConfig());
 
             rep.setConfig(config);
                diff --git a/services/src/main/java/org/keycloak/forms/account/freemarker/model/AuthorizationBean.java b/services/src/main/java/org/keycloak/forms/account/freemarker/model/AuthorizationBean.java
index 837a843..4ffe503 100755
--- a/services/src/main/java/org/keycloak/forms/account/freemarker/model/AuthorizationBean.java
+++ b/services/src/main/java/org/keycloak/forms/account/freemarker/model/AuthorizationBean.java
@@ -19,6 +19,7 @@ package org.keycloak.forms.account.freemarker.model;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
@@ -30,6 +31,7 @@ import javax.ws.rs.core.UriInfo;
 
 import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.model.PermissionTicket;
+import org.keycloak.authorization.model.Policy;
 import org.keycloak.authorization.model.Resource;
 import org.keycloak.authorization.model.Scope;
 import org.keycloak.authorization.store.PermissionTicketStore;
@@ -260,7 +262,7 @@ public class AuthorizationBean {
             if (shares == null) {
                 Map<String, String> filters = new HashMap<>();
 
-                filters.put(PermissionTicket.RESOURCE, resource.getId());
+                filters.put(PermissionTicket.RESOURCE, this.resource.getId());
                 filters.put(PermissionTicket.GRANTED, Boolean.TRUE.toString());
 
                 shares = toPermissionRepresentation(findPermissions(filters));
@@ -269,6 +271,31 @@ public class AuthorizationBean {
             return shares;
         }
 
+        public Collection<ManagedPermissionBean> getPolicies() {
+            Map<String, String[]> filters = new HashMap<>();
+
+            filters.put("type", new String[] {"uma"});
+            filters.put("resource", new String[] {this.resource.getId()});
+            filters.put("owner", new String[] {getOwner().getId()});
+
+            List<Policy> policies = authorization.getStoreFactory().getPolicyStore().findByResourceServer(filters, getResourceServer().getId(), -1, -1);
+
+            if (policies.isEmpty()) {
+                return Collections.emptyList();
+            }
+
+            return policies.stream()
+                    .filter(policy -> {
+                        Map<String, String> filters1 = new HashMap<>();
+
+                        filters1.put(PermissionTicket.POLICY, policy.getId());
+
+                        return authorization.getStoreFactory().getPermissionTicketStore().find(filters1, resourceServer.getId(), -1, 1)
+                                .isEmpty();
+                    })
+                    .map(ManagedPermissionBean::new).collect(Collectors.toList());
+        }
+
         public ResourceServerBean getResourceServer() {
             return resourceServer;
         }
@@ -326,6 +353,10 @@ public class AuthorizationBean {
             this.clientModel = clientModel;
         }
 
+        public String getId() {
+            return clientModel.getId();
+        }
+
         public String getName() {
             String name = clientModel.getName();
 
@@ -336,6 +367,10 @@ public class AuthorizationBean {
             return clientModel.getClientId();
         }
 
+        public String getClientId() {
+            return clientModel.getClientId();
+        }
+
         public String getRedirectUri() {
             Set<String> redirectUris = clientModel.getRedirectUris();
 
@@ -346,4 +381,34 @@ public class AuthorizationBean {
             return redirectUris.iterator().next();
         }
     }
+
+    public class ManagedPermissionBean {
+
+        private final Policy policy;
+        private List<ManagedPermissionBean> policies;
+
+        public ManagedPermissionBean(Policy policy) {
+            this.policy = policy;
+        }
+
+        public String getId() {
+            return policy.getId();
+        }
+
+        public Collection<ScopeRepresentation> getScopes() {
+            return policy.getScopes().stream().map(ModelToRepresentation::toRepresentation).collect(Collectors.toList());
+        }
+
+        public String getDescription() {
+            return this.policy.getDescription();
+        }
+
+        public Collection<ManagedPermissionBean> getPolicies() {
+            if (this.policies == null) {
+                this.policies = policy.getAssociatedPolicies().stream().map(ManagedPermissionBean::new).collect(Collectors.toList());
+            }
+
+            return this.policies;
+        }
+    }
 }
                diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
index ceb29d4..981f316 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
@@ -1069,7 +1069,7 @@ public class TokenEndpoint {
         authorizationRequest.setRpt(formParams.getFirst("rpt"));
         authorizationRequest.setScope(formParams.getFirst("scope"));
         authorizationRequest.setAudience(formParams.getFirst("audience"));
-        authorizationRequest.setAccessToken(accessTokenString);
+        authorizationRequest.setSubjectToken(formParams.getFirst("subject_token") != null ? formParams.getFirst("subject_token") : accessTokenString);
 
         String submitRequest = formParams.getFirst("submit_request");
 
                diff --git a/services/src/main/java/org/keycloak/services/resources/account/AccountFormService.java b/services/src/main/java/org/keycloak/services/resources/account/AccountFormService.java
index d316e34..1355614 100755
--- a/services/src/main/java/org/keycloak/services/resources/account/AccountFormService.java
+++ b/services/src/main/java/org/keycloak/services/resources/account/AccountFormService.java
@@ -20,9 +20,11 @@ import org.jboss.logging.Logger;
 import org.keycloak.authentication.RequiredActionContext;
 import org.keycloak.authorization.AuthorizationProvider;
 import org.keycloak.authorization.model.PermissionTicket;
+import org.keycloak.authorization.model.Policy;
 import org.keycloak.authorization.model.Resource;
 import org.keycloak.authorization.model.Scope;
 import org.keycloak.authorization.store.PermissionTicketStore;
+import org.keycloak.authorization.store.PolicyStore;
 import org.keycloak.common.Profile;
 import org.keycloak.common.Profile.Feature;
 import org.keycloak.common.util.Base64Url;
@@ -726,45 +728,90 @@ public class AccountFormService extends AbstractSecuredLocalService {
         boolean isGrant = "grant".equals(action);
         boolean isDeny = "deny".equals(action);
         boolean isRevoke = "revoke".equals(action);
+        boolean isRevokePolicy = "revokePolicy".equals(action);
+        boolean isRevokePolicyAll = "revokePolicyAll".equals(action);
 
-        Map<String, String> filters = new HashMap<>();
+        if (isRevokePolicy || isRevokePolicyAll) {
+            List<String> ids = new ArrayList(Arrays.asList(permissionId));
+            Iterator<String> iterator = ids.iterator();
+            PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore();
+            Policy policy = null;
 
-        filters.put(PermissionTicket.RESOURCE, resource.getId());
-        filters.put(PermissionTicket.REQUESTER, session.users().getUserByUsername(requester, realm).getId());
+            while (iterator.hasNext()) {
+                String id = iterator.next();
 
-        if (isRevoke) {
-            filters.put(PermissionTicket.GRANTED, Boolean.TRUE.toString());
-        } else {
-            filters.put(PermissionTicket.GRANTED, Boolean.FALSE.toString());
-        }
+                if (!id.contains(":")) {
+                    policy = policyStore.findById(id, client.getId());
+                    iterator.remove();
+                    break;
+                }
+            }
 
-        List<PermissionTicket> tickets = ticketStore.find(filters, resource.getResourceServer().getId(), -1, -1);
-        Iterator<PermissionTicket> iterator = tickets.iterator();
+            Set<Scope> scopesToKeep = new HashSet<>();
 
-        while (iterator.hasNext()) {
-            PermissionTicket ticket = iterator.next();
+            if (isRevokePolicyAll) {
+                for (Scope scope : policy.getScopes()) {
+                    policy.removeScope(scope);
+                }
+            } else {
+                for (String id : ids) {
+                    scopesToKeep.add(authorization.getStoreFactory().getScopeStore().findById(id.split(":")[1], client.getId()));
+                }
 
-            if (isGrant) {
-                if (permissionId != null && permissionId.length > 0 && !Arrays.asList(permissionId).contains(ticket.getId())) {
-                    continue;
+                for (Scope scope : policy.getScopes()) {
+                    if (!scopesToKeep.contains(scope)) {
+                        policy.removeScope(scope);
+                    }
                 }
             }
 
-            if (isGrant && !ticket.isGranted()) {
-                ticket.setGrantedTimestamp(System.currentTimeMillis());
-                iterator.remove();
-            } else if (isDeny || isRevoke) {
-                if (permissionId != null && permissionId.length > 0 && Arrays.asList(permissionId).contains(ticket.getId())) {
+            if (policy.getScopes().isEmpty()) {
+                for (Policy associated : policy.getAssociatedPolicies()) {
+                    policyStore.delete(associated.getId());
+                }
+
+                policyStore.delete(policy.getId());
+            }
+        } else {
+            Map<String, String> filters = new HashMap<>();
+
+            filters.put(PermissionTicket.RESOURCE, resource.getId());
+            filters.put(PermissionTicket.REQUESTER, session.users().getUserByUsername(requester, realm).getId());
+
+            if (isRevoke) {
+                filters.put(PermissionTicket.GRANTED, Boolean.TRUE.toString());
+            } else {
+                filters.put(PermissionTicket.GRANTED, Boolean.FALSE.toString());
+            }
+
+            List<PermissionTicket> tickets = ticketStore.find(filters, resource.getResourceServer().getId(), -1, -1);
+            Iterator<PermissionTicket> iterator = tickets.iterator();
+
+            while (iterator.hasNext()) {
+                PermissionTicket ticket = iterator.next();
+
+                if (isGrant) {
+                    if (permissionId != null && permissionId.length > 0 && !Arrays.asList(permissionId).contains(ticket.getId())) {
+                        continue;
+                    }
+                }
+
+                if (isGrant && !ticket.isGranted()) {
+                    ticket.setGrantedTimestamp(System.currentTimeMillis());
                     iterator.remove();
+                } else if (isDeny || isRevoke) {
+                    if (permissionId != null && permissionId.length > 0 && Arrays.asList(permissionId).contains(ticket.getId())) {
+                        iterator.remove();
+                    }
                 }
             }
-        }
 
-        for (PermissionTicket ticket : tickets) {
-            ticketStore.delete(ticket.getId());
+            for (PermissionTicket ticket : tickets) {
+                ticketStore.delete(ticket.getId());
+            }
         }
 
-        if (isRevoke) {
+        if (isRevoke || isRevokePolicy || isRevokePolicyAll) {
             return forwardToPage("resource-detail", AccountPages.RESOURCE_DETAIL);
         }
 
                diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authorization/TestPolicyProviderFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authorization/TestPolicyProviderFactory.java
index 4da1695..b450b76 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authorization/TestPolicyProviderFactory.java
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/authorization/TestPolicyProviderFactory.java
@@ -50,7 +50,7 @@ public class TestPolicyProviderFactory implements PolicyProviderFactory {
     }
 
     @Override
-    public AbstractPolicyRepresentation toRepresentation(Policy policy) {
+    public AbstractPolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
         return new PolicyRepresentation();
     }
 
                diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/PolicyEnforcerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/PolicyEnforcerTest.java
index 81f2ffd..4713239 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/PolicyEnforcerTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/PolicyEnforcerTest.java
@@ -16,6 +16,7 @@
  */
 package org.keycloak.testsuite.admin.client.authorization;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
@@ -38,6 +39,7 @@ import org.junit.BeforeClass;
 import org.junit.Test;
 import org.keycloak.AuthorizationContext;
 import org.keycloak.KeycloakSecurityContext;
+import org.keycloak.OAuth2Constants;
 import org.keycloak.adapters.KeycloakDeployment;
 import org.keycloak.adapters.KeycloakDeploymentBuilder;
 import org.keycloak.adapters.OIDCHttpFacade;
@@ -59,12 +61,14 @@ import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.representations.idm.authorization.AuthorizationRequest;
 import org.keycloak.representations.idm.authorization.AuthorizationResponse;
 import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
+import org.keycloak.representations.idm.authorization.Permission;
 import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
 import org.keycloak.representations.idm.authorization.ScopeRepresentation;
 import org.keycloak.testsuite.AbstractKeycloakTest;
 import org.keycloak.testsuite.ProfileAssume;
 import org.keycloak.testsuite.util.ClientBuilder;
+import org.keycloak.testsuite.util.OAuthClient;
 import org.keycloak.testsuite.util.RealmBuilder;
 import org.keycloak.testsuite.util.RoleBuilder;
 import org.keycloak.testsuite.util.RolesBuilder;
@@ -106,6 +110,10 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
                         .redirectUris("http://localhost/resource-server-test")
                         .defaultRoles("uma_protection")
                         .directAccessGrants())
+                .client(ClientBuilder.create().clientId("public-client-test")
+                        .publicClient()
+                        .redirectUris("http://localhost:8180/auth/realms/master/app/auth/*")
+                        .directAccessGrants())
                 .build());
     }
 
@@ -125,7 +133,7 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
 
         headers.put("Authorization", Arrays.asList("Bearer " + token));
 
-        AuthorizationContext context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", token, headers, parameters));
+        AuthorizationContext context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", "POST", token, headers, parameters));
         assertFalse(context.isGranted());
 
         AuthorizationRequest request = new AuthorizationRequest();
@@ -137,22 +145,22 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
 
         assertNotNull(token);
 
-        context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", token, headers, parameters));
+        context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", "POST", token, headers, parameters));
         assertTrue(context.isGranted());
 
         parameters.put("withdrawal.amount", Arrays.asList("200"));
 
-        context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", token, headers, parameters));
+        context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", "POST", token, headers, parameters));
         assertFalse(context.isGranted());
 
         parameters.put("withdrawal.amount", Arrays.asList("50"));
 
-        context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", token, headers, parameters));
+        context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", "POST", token, headers, parameters));
         assertTrue(context.isGranted());
 
         parameters.put("withdrawal.amount", Arrays.asList("10"));
 
-        context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", token, headers, parameters));
+        context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", "POST", token, headers, parameters));
 
         request = new AuthorizationRequest();
 
@@ -161,8 +169,23 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
         response = authzClient.authorization("marta", "password").authorize(request);
         token = response.getToken();
 
-        context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", token, headers, parameters));
+        context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", "POST", token, headers, parameters));
         assertTrue(context.isGranted());
+
+        request = new AuthorizationRequest();
+
+        request.setTicket(extractTicket(headers));
+
+        response = authzClient.authorization("marta", "password").authorize(request);
+        token = response.getToken();
+
+        context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", "GET", token, headers, parameters));
+        assertTrue(context.isGranted());
+
+        assertEquals(1, context.getPermissions().size());
+        Permission permission = context.getPermissions().get(0);
+
+        assertEquals(parameters.get("withdrawal.amount").get(0), permission.getClaims().get("withdrawal.amount").iterator().next());
     }
 
     @Test
@@ -184,6 +207,9 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
 
         context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", token, headers, parameters));
         assertTrue(context.isGranted());
+        assertEquals(1, context.getPermissions().size());
+        Permission permission = context.getPermissions().get(0);
+        assertEquals(parameters.get("withdrawal.amount").get(0), permission.getClaims().get("withdrawal.amount").iterator().next());
 
         parameters.put("withdrawal.amount", Arrays.asList("200"));
 
@@ -200,6 +226,10 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
         context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", token, headers, parameters));
 
         assertTrue(context.isGranted());
+
+        assertEquals(1, context.getPermissions().size());
+        permission = context.getPermissions().get(0);
+        assertEquals(parameters.get("withdrawal.amount").get(0), permission.getClaims().get("withdrawal.amount").iterator().next());
     }
 
     @Test
@@ -241,6 +271,50 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
         assertTrue(context.isGranted());
     }
 
+    @Test
+    public void testEnforceEntitlementAccessWithClaimsWithBearerTokenFromPublicClient() {
+        initAuthorizationSettings(getClientResource("resource-server-test"));
+
+        KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(getAdapterConfiguration("enforcer-entitlement-claims-test.json"));
+        PolicyEnforcer policyEnforcer = deployment.getPolicyEnforcer();
+        HashMap<String, List<String>> headers = new HashMap<>();
+        HashMap<String, List<String>> parameters = new HashMap<>();
+
+        oauth.realm(REALM_NAME);
+        oauth.clientId("public-client-test");
+        oauth.doLogin("marta", "password");
+
+        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+        OAuthClient.AccessTokenResponse response = oauth.doAccessTokenRequest(code, null);
+        String token = response.getAccessToken();
+
+        headers.put("Authorization", Arrays.asList("Bearer " + token));
+
+        AuthorizationContext context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", token, headers, parameters));
+        assertFalse(context.isGranted());
+
+        parameters.put("withdrawal.amount", Arrays.asList("50"));
+
+        context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", token, headers, parameters));
+        assertTrue(context.isGranted());
+
+        parameters.put("withdrawal.amount", Arrays.asList("200"));
+
+        context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", token, headers, parameters));
+        assertFalse(context.isGranted());
+
+        parameters.put("withdrawal.amount", Arrays.asList("50"));
+
+        context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", token, headers, parameters));
+        assertTrue(context.isGranted());
+
+        parameters.put("withdrawal.amount", Arrays.asList("10"));
+
+        context = policyEnforcer.enforce(createHttpFacade("/api/bank/account/1/withdrawal", token, headers, parameters));
+
+        assertTrue(context.isGranted());
+    }
+
     private String extractTicket(HashMap<String, List<String>> headers) {
         List<String> wwwAuthenticateHeader = headers.get("WWW-Authenticate");
 
@@ -306,7 +380,7 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
         return clients.get(representation.getId());
     }
 
-    private OIDCHttpFacade createHttpFacade(String path, String token, Map<String, List<String>> headers, Map<String, List<String>> parameters, InputStream requestBody) {
+    private OIDCHttpFacade createHttpFacade(String path, String method, String token, Map<String, List<String>> headers, Map<String, List<String>> parameters, InputStream requestBody) {
         return new OIDCHttpFacade() {
             Request request;
             Response response;
@@ -325,7 +399,7 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
             @Override
             public Request getRequest() {
                 if (request == null) {
-                    request = createHttpRequest(path, headers, parameters, requestBody);
+                    request = createHttpRequest(path, method, headers, parameters, requestBody);
                 }
                 return request;
             }
@@ -346,7 +420,11 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
     }
 
     private OIDCHttpFacade createHttpFacade(String path, String token, Map<String, List<String>> headers, Map<String, List<String>> parameters) {
-        return createHttpFacade(path, token, headers, parameters, null);
+        return createHttpFacade(path, null, token, headers, parameters, null);
+    }
+
+    private OIDCHttpFacade createHttpFacade(String path, String method, String token, Map<String, List<String>> headers, Map<String, List<String>> parameters) {
+        return createHttpFacade(path, method, token, headers, parameters, null);
     }
 
     private Response createHttpResponse(Map<String, List<String>> headers) {
@@ -401,14 +479,14 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
         };
     }
 
-    private Request createHttpRequest(String path, Map<String, List<String>> headers, Map<String, List<String>> parameters, InputStream requestBody) {
+    private Request createHttpRequest(String path, String method, Map<String, List<String>> headers, Map<String, List<String>> parameters, InputStream requestBody) {
         return new Request() {
 
             private InputStream inputStream;
 
             @Override
             public String getMethod() {
-                return "GET";
+                return method == null ? "GET" : method;
             }
 
             @Override
                diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AbstractResourceServerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AbstractResourceServerTest.java
index 237d5e3..c5edbcc 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AbstractResourceServerTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AbstractResourceServerTest.java
@@ -67,13 +67,17 @@ public abstract class AbstractResourceServerTest extends AbstractKeycloakTest {
                 .user(UserBuilder.create().username("marta").password("password")
                         .addRoles("uma_authorization", "uma_protection")
                         .role("resource-server-test", "uma_protection"))
+                .user(UserBuilder.create().username("alice").password("password")
+                        .addRoles("uma_authorization", "uma_protection")
+                        .role("resource-server-test", "uma_protection"))
                 .user(UserBuilder.create().username("kolo").password("password"))
                 .client(ClientBuilder.create().clientId("resource-server-test")
                         .secret("secret")
                         .authorizationServicesEnabled(true)
                         .redirectUris("http://localhost/resource-server-test")
                         .defaultRoles("uma_protection")
-                        .directAccessGrants())
+                        .directAccessGrants()
+                        .serviceAccountsEnabled(true))
                 .client(ClientBuilder.create().clientId("test-app")
                         .redirectUris("http://localhost:8180/auth/realms/master/app/auth")
                         .publicClient())
@@ -155,7 +159,7 @@ public abstract class AbstractResourceServerTest extends AbstractKeycloakTest {
         return authorization.authorize(authorizationRequest);
     }
 
-    protected RealmResource getRealm() throws Exception {
+    protected RealmResource getRealm() {
         return adminClient.realm("authz-test");
     }
 
                diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UserManagedPermissionServiceTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UserManagedPermissionServiceTest.java
new file mode 100644
index 0000000..4abe759
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/UserManagedPermissionServiceTest.java
@@ -0,0 +1,561 @@
+/*
+ * Copyright 2018 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.testsuite.authz;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+
+import javax.ws.rs.NotFoundException;
+
+import org.junit.Test;
+import org.keycloak.authorization.client.AuthorizationDeniedException;
+import org.keycloak.authorization.client.resource.AuthorizationResource;
+import org.keycloak.authorization.client.resource.PolicyResource;
+import org.keycloak.authorization.client.resource.ProtectionResource;
+import org.keycloak.authorization.client.util.HttpResponseException;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.authorization.AuthorizationRequest;
+import org.keycloak.representations.idm.authorization.AuthorizationResponse;
+import org.keycloak.representations.idm.authorization.PermissionRequest;
+import org.keycloak.representations.idm.authorization.PermissionResponse;
+import org.keycloak.representations.idm.authorization.PermissionTicketRepresentation;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.UmaPermissionRepresentation;
+import org.keycloak.testsuite.util.ClientBuilder;
+import org.keycloak.testsuite.util.GroupBuilder;
+import org.keycloak.testsuite.util.RealmBuilder;
+import org.keycloak.testsuite.util.RoleBuilder;
+import org.keycloak.testsuite.util.RolesBuilder;
+import org.keycloak.testsuite.util.UserBuilder;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class UserManagedPermissionServiceTest extends AbstractResourceServerTest {
+
+    @Override
+    public void addTestRealms(List<RealmRepresentation> testRealms) {
+        testRealms.add(RealmBuilder.create().name(REALM_NAME)
+                .roles(RolesBuilder.create()
+                        .realmRole(RoleBuilder.create().name("uma_authorization").build())
+                        .realmRole(RoleBuilder.create().name("uma_protection").build())
+                        .realmRole(RoleBuilder.create().name("role_a").build())
+                        .realmRole(RoleBuilder.create().name("role_b").build())
+                        .realmRole(RoleBuilder.create().name("role_c").build())
+                        .realmRole(RoleBuilder.create().name("role_d").build())
+                )
+                .group(GroupBuilder.create().name("group_a")
+                        .subGroups(Arrays.asList(GroupBuilder.create().name("group_b").build()))
+                        .build())
+                .group(GroupBuilder.create().name("group_c").build())
+                .user(UserBuilder.create().username("marta").password("password")
+                        .addRoles("uma_authorization", "uma_protection")
+                        .role("resource-server-test", "uma_protection"))
+                .user(UserBuilder.create().username("alice").password("password")
+                        .addRoles("uma_authorization", "uma_protection")
+                        .role("resource-server-test", "uma_protection"))
+                .user(UserBuilder.create().username("kolo").password("password")
+                        .addRoles("role_a")
+                        .addGroups("group_a"))
+                .client(ClientBuilder.create().clientId("resource-server-test")
+                        .secret("secret")
+                        .authorizationServicesEnabled(true)
+                        .redirectUris("http://localhost/resource-server-test")
+                        .defaultRoles("uma_protection")
+                        .directAccessGrants()
+                        .serviceAccountsEnabled(true))
+                .client(ClientBuilder.create().clientId("client-a")
+                        .redirectUris("http://localhost/resource-server-test")
+                        .publicClient())
+                .build());
+    }
+
+    @Test
+    public void testCreate() {
+        ResourceRepresentation resource = new ResourceRepresentation();
+
+        resource.setName("Resource A");
+        resource.setOwnerManagedAccess(true);
+        resource.setOwner("marta");
+        resource.addScope("Scope A", "Scope B", "Scope C");
+
+        resource = getAuthzClient().protection().resource().create(resource);
+
+        UmaPermissionRepresentation newPermission = new UmaPermissionRepresentation();
+
+        newPermission.setName("Custom User-Managed Permission");
+        newPermission.setDescription("Users from specific roles are allowed to access");
+        newPermission.addScope("Scope A", "Scope B", "Scope C");
+        newPermission.addRole("role_a", "role_b", "role_c", "role_d");
+        newPermission.addGroup("/group_a", "/group_a/group_b", "/group_c");
+        newPermission.addClient("client-a", "resource-server-test");
+        newPermission.setCondition("$evaluation.grant()");
+
+        ProtectionResource protection = getAuthzClient().protection("marta", "password");
+
+        UmaPermissionRepresentation permission = protection.policy(resource.getId()).create(newPermission);
+
+        assertEquals(newPermission.getName(), permission.getName());
+        assertEquals(newPermission.getDescription(), permission.getDescription());
+        assertTrue(permission.getScopes().containsAll(newPermission.getScopes()));
+        assertTrue(permission.getRoles().containsAll(newPermission.getRoles()));
+        assertTrue(permission.getGroups().containsAll(newPermission.getGroups()));
+        assertTrue(permission.getClients().containsAll(newPermission.getClients()));
+        assertEquals(newPermission.getCondition(), permission.getCondition());
+    }
+
+    @Test
+    public void testUpdate() {
+        ResourceRepresentation resource = new ResourceRepresentation();
+
+        resource.setName("Resource A");
+        resource.setOwnerManagedAccess(true);
+        resource.setOwner("marta");
+        resource.addScope("Scope A", "Scope B", "Scope C");
+
+        resource = getAuthzClient().protection().resource().create(resource);
+
+        UmaPermissionRepresentation permission = new UmaPermissionRepresentation();
+
+        permission.setName("Custom User-Managed Permission");
+        permission.setDescription("Users from specific roles are allowed to access");
+        permission.addScope("Scope A");
+        permission.addRole("role_a");
+
+        ProtectionResource protection = getAuthzClient().protection("marta", "password");
+
+        permission = protection.policy(resource.getId()).create(permission);
+
+        assertEquals(1, getAssociatedPolicies(permission).size());
+
+        permission.setName("Changed");
+        permission.setDescription("Changed");
+
+        protection.policy(resource.getId()).update(permission);
+
+        UmaPermissionRepresentation updated = protection.policy(resource.getId()).findById(permission.getId());
+
+        assertEquals(permission.getName(), updated.getName());
+        assertEquals(permission.getDescription(), updated.getDescription());
+
+        permission.removeRole("role_a");
+        permission.addRole("role_b", "role_c");
+
+        protection.policy(resource.getId()).update(permission);
+        assertEquals(1, getAssociatedPolicies(permission).size());
+        updated = protection.policy(resource.getId()).findById(permission.getId());
+
+        assertTrue(permission.getRoles().containsAll(updated.getRoles()));
+
+        permission.addRole("role_d");
+
+        protection.policy(resource.getId()).update(permission);
+        assertEquals(1, getAssociatedPolicies(permission).size());
+        updated = protection.policy(resource.getId()).findById(permission.getId());
+
+        assertTrue(permission.getRoles().containsAll(updated.getRoles()));
+
+        permission.addGroup("/group_a/group_b");
+
+        protection.policy(resource.getId()).update(permission);
+        assertEquals(2, getAssociatedPolicies(permission).size());
+        updated = protection.policy(resource.getId()).findById(permission.getId());
+
+        assertTrue(permission.getGroups().containsAll(updated.getGroups()));
+
+        permission.addGroup("/group_a");
+
+        protection.policy(resource.getId()).update(permission);
+        assertEquals(2, getAssociatedPolicies(permission).size());
+        updated = protection.policy(resource.getId()).findById(permission.getId());
+
+        assertTrue(permission.getGroups().containsAll(updated.getGroups()));
+
+        permission.removeGroup("/group_a/group_b");
+        permission.addGroup("/group_c");
+
+        protection.policy(resource.getId()).update(permission);
+        assertEquals(2, getAssociatedPolicies(permission).size());
+        updated = protection.policy(resource.getId()).findById(permission.getId());
+
+        assertTrue(permission.getGroups().containsAll(updated.getGroups()));
+
+        permission.addClient("client-a");
+
+        protection.policy(resource.getId()).update(permission);
+        assertEquals(3, getAssociatedPolicies(permission).size());
+        updated = protection.policy(resource.getId()).findById(permission.getId());
+
+        assertTrue(permission.getClients().containsAll(updated.getClients()));
+
+        permission.addClient("resource-server-test");
+
+        protection.policy(resource.getId()).update(permission);
+        assertEquals(3, getAssociatedPolicies(permission).size());
+        updated = protection.policy(resource.getId()).findById(permission.getId());
+
+        assertTrue(permission.getClients().containsAll(updated.getClients()));
+
+        permission.removeClient("client-a");
+
+        protection.policy(resource.getId()).update(permission);
+        assertEquals(3, getAssociatedPolicies(permission).size());
+        updated = protection.policy(resource.getId()).findById(permission.getId());
+
+        assertTrue(permission.getClients().containsAll(updated.getClients()));
+
+        permission.setCondition("$evaluation.grant()");
+
+        protection.policy(resource.getId()).update(permission);
+        assertEquals(4, getAssociatedPolicies(permission).size());
+        updated = protection.policy(resource.getId()).findById(permission.getId());
+
+        assertEquals(permission.getCondition(), updated.getCondition());
+
+        permission.setCondition(null);
+
+        protection.policy(resource.getId()).update(permission);
+        assertEquals(3, getAssociatedPolicies(permission).size());
+        updated = protection.policy(resource.getId()).findById(permission.getId());
+
+        assertEquals(permission.getCondition(), updated.getCondition());
+
+        permission.setRoles(null);
+
+        protection.policy(resource.getId()).update(permission);
+        assertEquals(2, getAssociatedPolicies(permission).size());
+        updated = protection.policy(resource.getId()).findById(permission.getId());
+
+        assertEquals(permission.getRoles(), updated.getRoles());
+
+        permission.setClients(null);
+
+        protection.policy(resource.getId()).update(permission);
+        assertEquals(1, getAssociatedPolicies(permission).size());
+        updated = protection.policy(resource.getId()).findById(permission.getId());
+
+        assertEquals(permission.getClients(), updated.getClients());
+
+        permission.setGroups(null);
+
+        try {
+            protection.policy(resource.getId()).update(permission);
+            assertEquals(1, getAssociatedPolicies(permission).size());
+            fail("Permission must be removed because the last associated policy was removed");
+        } catch (NotFoundException ignore) {
+
+        } catch (Exception e) {
+            fail("Expected not found");
+        }
+    }
+
+    @Test
+    public void testUserManagedPermission() {
+        ResourceRepresentation resource = new ResourceRepresentation();
+
+        resource.setName("Resource A");
+        resource.setOwnerManagedAccess(true);
+        resource.setOwner("marta");
+        resource.addScope("Scope A", "Scope B", "Scope C");
+
+        resource = getAuthzClient().protection().resource().create(resource);
+
+        UmaPermissionRepresentation permission = new UmaPermissionRepresentation();
+
+        permission.setName("Custom User-Managed Permission");
+        permission.setDescription("Users from specific roles are allowed to access");
+        permission.addScope("Scope A");
+        permission.addRole("role_a");
+
+        ProtectionResource protection = getAuthzClient().protection("marta", "password");
+
+        permission = protection.policy(resource.getId()).create(permission);
+
+        AuthorizationResource authorization = getAuthzClient().authorization("kolo", "password");
+
+        AuthorizationRequest request = new AuthorizationRequest();
+
+        request.addPermission(resource.getId(), "Scope A");
+
+        AuthorizationResponse authzResponse = authorization.authorize(request);
+
+        assertNotNull(authzResponse);
+
+        permission.removeRole("role_a");
+        permission.addRole("role_b");
+
+        protection.policy(resource.getId()).update(permission);
+
+        try {
+            authzResponse = authorization.authorize(request);
+            fail("User should not have permission");
+        } catch (Exception e) {
+            assertTrue(AuthorizationDeniedException.class.isInstance(e));
+        }
+
+        try {
+            authzResponse = getAuthzClient().authorization("alice", "password").authorize(request);
+            fail("User should not have permission");
+        } catch (Exception e) {
+            assertTrue(AuthorizationDeniedException.class.isInstance(e));
+        }
+
+        permission.addRole("role_a");
+
+        protection.policy(resource.getId()).update(permission);
+
+        authzResponse = authorization.authorize(request);
+
+        assertNotNull(authzResponse);
+
+        protection.policy(resource.getId()).delete(permission.getId());
+
+        try {
+            authzResponse = authorization.authorize(request);
+            fail("User should not have permission");
+        } catch (Exception e) {
+            assertTrue(AuthorizationDeniedException.class.isInstance(e));
+        }
+
+        try {
+            getAuthzClient().protection("marta", "password").policy(resource.getId()).findById(permission.getId());
+            fail("Permission must not exist");
+        } catch (Exception e) {
+            assertEquals(404, HttpResponseException.class.cast(e.getCause()).getStatusCode());
+        }
+    }
+
+    @Test
+    public void testPermissionInAdditionToUserGrantedPermission() {
+        ResourceRepresentation resource = new ResourceRepresentation();
+
+        resource.setName("Resource A");
+        resource.setOwnerManagedAccess(true);
+        resource.setOwner("marta");
+        resource.addScope("Scope A", "Scope B", "Scope C");
+
+        resource = getAuthzClient().protection().resource().create(resource);
+
+        PermissionResponse ticketResponse = getAuthzClient().protection().permission().create(new PermissionRequest(resource.getId(), "Scope A"));
+
+        AuthorizationRequest request = new AuthorizationRequest();
+
+        request.setTicket(ticketResponse.getTicket());
+
+        try {
+            getAuthzClient().authorization("kolo", "password").authorize(request);
+            fail("User should not have permission");
+        } catch (Exception e) {
+            assertTrue(AuthorizationDeniedException.class.isInstance(e));
+            assertTrue(e.getMessage().contains("request_submitted"));
+        }
+
+        List<PermissionTicketRepresentation> tickets = getAuthzClient().protection().permission().findByResource(resource.getId());
+
+        assertEquals(1, tickets.size());
+
+        PermissionTicketRepresentation ticket = tickets.get(0);
+
+        ticket.setGranted(true);
+
+        getAuthzClient().protection().permission().update(ticket);
+
+        AuthorizationResponse authzResponse = getAuthzClient().authorization("kolo", "password").authorize(request);
+
+        assertNotNull(authzResponse);
+
+        UmaPermissionRepresentation permission = new UmaPermissionRepresentation();
+
+        permission.setName("Custom User-Managed Permission");
+        permission.addScope("Scope A");
+        permission.addRole("role_a");
+
+        ProtectionResource protection = getAuthzClient().protection("marta", "password");
+
+        permission = protection.policy(resource.getId()).create(permission);
+
+        authzResponse = getAuthzClient().authorization("kolo", "password").authorize(request);
+
+        ticket.setGranted(false);
+
+        getAuthzClient().protection().permission().update(ticket);
+
+        authzResponse = getAuthzClient().authorization("kolo", "password").authorize(request);
+
+        permission = getAuthzClient().protection("marta", "password").policy(resource.getId()).findById(permission.getId());
+
+        assertNotNull(permission);
+
+        permission.removeRole("role_a");
+        permission.addRole("role_b");
+
+        getAuthzClient().protection("marta", "password").policy(resource.getId()).update(permission);
+
+        try {
+            getAuthzClient().authorization("kolo", "password").authorize(request);
+            fail("User should not have permission");
+        } catch (Exception e) {
+            assertTrue(AuthorizationDeniedException.class.isInstance(e));
+        }
+
+        request = new AuthorizationRequest();
+
+        request.addPermission(resource.getId());
+
+        try {
+            getAuthzClient().authorization("kolo", "password").authorize(request);
+            fail("User should not have permission");
+        } catch (Exception e) {
+            assertTrue(AuthorizationDeniedException.class.isInstance(e));
+        }
+
+        getAuthzClient().protection("marta", "password").policy(resource.getId()).delete(permission.getId());
+
+        try {
+            getAuthzClient().authorization("kolo", "password").authorize(request);
+            fail("User should not have permission");
+        } catch (Exception e) {
+            assertTrue(AuthorizationDeniedException.class.isInstance(e));
+        }
+    }
+
+    @Test
+    public void testPermissionWithoutScopes() {
+        ResourceRepresentation resource = new ResourceRepresentation();
+
+        resource.setName(UUID.randomUUID().toString());
+        resource.setOwner("marta");
+        resource.setOwnerManagedAccess(true);
+        resource.addScope("Scope A", "Scope B", "Scope C");
+
+        ProtectionResource protection = getAuthzClient().protection();
+
+        resource = protection.resource().create(resource);
+
+        UmaPermissionRepresentation permission = new UmaPermissionRepresentation();
+
+        permission.setName("Custom User-Managed Policy");
+        permission.addRole("role_a");
+
+        PolicyResource policy = getAuthzClient().protection("marta", "password").policy(resource.getId());
+
+        permission = policy.create(permission);
+
+        assertEquals(3, permission.getScopes().size());
+        assertTrue(Arrays.asList("Scope A", "Scope B", "Scope C").containsAll(permission.getScopes()));
+
+        permission = policy.findById(permission.getId());
+
+        assertTrue(Arrays.asList("Scope A", "Scope B", "Scope C").containsAll(permission.getScopes()));
+        assertEquals(3, permission.getScopes().size());
+
+        permission.removeScope("Scope B");
+
+        policy.update(permission);
+        permission = policy.findById(permission.getId());
+
+        assertEquals(2, permission.getScopes().size());
+        assertTrue(Arrays.asList("Scope A", "Scope C").containsAll(permission.getScopes()));
+    }
+
+    @Test
+    public void testOnlyResourceOwnerCanManagePolicies() {
+        ResourceRepresentation resource = new ResourceRepresentation();
+
+        resource.setName(UUID.randomUUID().toString());
+        resource.setOwner("marta");
+        resource.addScope("Scope A", "Scope B", "Scope C");
+
+        ProtectionResource protection = getAuthzClient().protection();
+
+        resource = protection.resource().create(resource);
+
+        try {
+            getAuthzClient().protection("alice", "password").policy(resource.getId()).create(new UmaPermissionRepresentation());
+            fail("Error expected");
+        } catch (Exception e) {
+            assertTrue(HttpResponseException.class.cast(e.getCause()).toString().contains("Only resource onwer can access policies for resource"));
+        }
+    }
+
+    @Test
+    public void testOnlyResourcesWithOwnerManagedAccess() {
+        ResourceRepresentation resource = new ResourceRepresentation();
+
+        resource.setName(UUID.randomUUID().toString());
+        resource.setOwner("marta");
+        resource.addScope("Scope A", "Scope B", "Scope C");
+
+        ProtectionResource protection = getAuthzClient().protection();
+
+        resource = protection.resource().create(resource);
+
+        try {
+            getAuthzClient().protection("marta", "password").policy(resource.getId()).create(new UmaPermissionRepresentation());
+            fail("Error expected");
+        } catch (Exception e) {
+            assertTrue(HttpResponseException.class.cast(e.getCause()).toString().contains("Only resources with owner managed accessed can have policies"));
+        }
+    }
+
+    @Test
+    public void testFindPermission() {
+        ResourceRepresentation resource = new ResourceRepresentation();
+
+        resource.setName(UUID.randomUUID().toString());
+        resource.setOwner("marta");
+        resource.setOwnerManagedAccess(true);
+        resource.addScope("Scope A", "Scope B", "Scope C");
+
+        ProtectionResource protection = getAuthzClient().protection();
+
+        resource = protection.resource().create(resource);
+
+        PolicyResource policy = getAuthzClient().protection("marta", "password").policy(resource.getId());
+
+        for (int i = 0; i < 10; i++) {
+            UmaPermissionRepresentation permission = new UmaPermissionRepresentation();
+
+            permission.setName("Custom User-Managed Policy " + i);
+            permission.addRole("role_a");
+
+            policy.create(permission);
+        }
+
+        assertEquals(10, policy.find(null, null, null, null).size());
+
+        List<UmaPermissionRepresentation> byId = policy.find("Custom User-Managed Policy 8", null, null, null);
+
+        assertEquals(1, byId.size());
+        assertEquals(byId.get(0).getId(), policy.findById(byId.get(0).getId()).getId());
+        assertEquals(10, policy.find(null, "Scope A", null, null).size());
+        assertEquals(5, policy.find(null, null, -1, 5).size());
+        assertEquals(2, policy.find(null, null, -1, 2).size());
+    }
+
+    private List<PolicyRepresentation> getAssociatedPolicies(UmaPermissionRepresentation permission) {
+        return getClient(getRealm()).authorization().policies().policy(permission.getId()).associatedPolicies();
+    }
+
+}
                diff --git a/themes/src/main/resources/theme/base/account/messages/messages_en.properties b/themes/src/main/resources/theme/base/account/messages/messages_en.properties
index 1398730..06f6094 100755
--- a/themes/src/main/resources/theme/base/account/messages/messages_en.properties
+++ b/themes/src/main/resources/theme/base/account/messages/messages_en.properties
@@ -200,6 +200,10 @@ doApprove=Approve
 doRemoveSharing=Remove Sharing
 doRemoveRequest=Remove Request
 peopleAccessResource=People with access to this resource
+resourceManagedPolicies=Permissions granting access to this resource
+resourceNoPermissionsGrantingAccess=No permissions granting access to this resource
+anyAction=Any action
+description=Description
 name=Name
 scopes=Scopes
 resource=Resource
                diff --git a/themes/src/main/resources/theme/base/account/resource-detail.ftl b/themes/src/main/resources/theme/base/account/resource-detail.ftl
index fd4e5ec..e30d947 100755
--- a/themes/src/main/resources/theme/base/account/resource-detail.ftl
+++ b/themes/src/main/resources/theme/base/account/resource-detail.ftl
@@ -183,6 +183,70 @@
     <div class="row">
         <div class="col-md-10">
             <h3>
+                ${msg("resourceManagedPolicies")}
+            </h3>
+        </div>
+    </div>
+    <div class="row">
+        <div class="col-md-12">
+                <table class="table table-striped table-bordered">
+                    <thead>
+                        <tr>
+                            <th>${msg("description")}</th>
+                            <th>${msg("permission")}</th>
+                            <th>${msg("action")}</th>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        <#if authorization.resource.policies?size != 0>
+                            <#list authorization.resource.policies as permission>
+                                <form action="${url.getResourceGrant(authorization.resource.id)}" name="revokePolicyForm-${authorization.resource.id}-${permission.id}" method="post">
+                                    <input type="hidden" name="action" value="revokePolicy">
+                                    <input type="hidden" name="permission_id" value="${permission.id}"/>
+                                    <tr>
+                                        <td>
+                                            <#if permission.description??>
+                                                ${permission.description}
+                                            </#if>
+                                        </td>
+                                        <td>
+                                            <#if permission.scopes?size != 0>
+                                                <#list permission.scopes as scope>
+                                                    <div class="search-box">
+                                                        <#if scope.displayName??>
+                                                            ${scope.displayName}
+                                                        <#else>
+                                                            ${scope.name}
+                                                        </#if>
+                                                        <button class="close-icon" type="button" name="removePolicyScope-${authorization.resource.id}-${permission.id}-${scope.id}" onclick="removeScopeElm(this.parentNode);document.forms['revokePolicyForm-${authorization.resource.id}-${permission.id}'].submit();"><i class="fa fa-times" aria-hidden="true"></i></button>
+                                                        <input type="hidden" name="permission_id" value="${permission.id}:${scope.id}"/>
+                                                    </div>
+                                                </#list>
+                                            <#else>
+                                                ${msg("anyAction")}
+                                            </#if>
+                                        </td>
+                                        <td width="20%" align="middle" style="vertical-align: middle">
+                                            <a href="#" id="revokePolicy-${authorization.resource.name}-${permission.id}" onclick="document.forms['revokePolicyForm-${authorization.resource.id}-${permission.id}']['action'].value = 'revokePolicyAll';document.forms['revokePolicyForm-${authorization.resource.id}-${permission.id}'].submit();" type="submit" class="btn btn-primary">${msg("doRevoke")}</a>
+                                        </td>
+                                    </tr>
+                                </form>
+                            </#list>
+                        <#else>
+                            <tr>
+                                <td colspan="3">
+                                    ${msg("resourceNoPermissionsGrantingAccess")}
+                                </td>
+                            </tr>
+                        </#if>
+                    </tbody>
+                </table>
+            </form>
+        </div>
+    </div>
+    <div class="row">
+        <div class="col-md-10">
+            <h3>
                 ${msg("shareWithOthers")}
             </h3>
         </div>
                diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-evaluate-result.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-evaluate-result.html
index cd8007f..19ff720 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-evaluate-result.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/policy/resource-server-policy-evaluate-result.html
@@ -41,12 +41,18 @@
                     <div>
                         <div>
                             <li data-ng-repeat="policyResult in result.policies">
-                                <strong><a
-                                        href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/{{policyResult.policy.type}}/{{policyResult.policy.id}}">{{policyResult.policy.name}}</a></strong>
+                                <strong>
+                                    <a data-ng-show="policyResult.policy.type != 'uma'"
+                                        href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/{{policyResult.policy.type}}/{{policyResult.policy.id}}">{{policyResult.policy.name}}</a>
+                                    <a data-ng-show="policyResult.policy.type == 'uma'"
+                                       href="">
+                                        {{policyResult.policy.description}}
+                                    </a>
+                                </strong>
                                 decision was <span style="color: green" data-ng-show="policyResult.status == 'PERMIT'"><strong>{{policyResult.status}}</strong></span>
                                 <span style="color: red" data-ng-hide="policyResult.status == 'PERMIT'"><strong>{{policyResult.status}}</strong></span>
                                 by <strong>{{policyResult.policy.decisionStrategy}}</strong> decision. {{policyResult.policy.scopes.length > 0 ? (policyResult.status == 'DENY' ? 'Denied Scopes:' : 'Granted Scopes:') : ''}} <span data-ng-repeat="scope in policyResult.policy.scopes"><strong style="color: {{(policyResult.status == 'DENY' ? 'red' : 'green')}}">{{scope}}{{$last ? '' : ', '}}</strong></span>{{policyResult.policy.scopes.length > 0 ? '.' : ''}}
-                                <ul>
+                                <ul data-ng-show="policyResult.policy.type != 'uma'">
                                     <li data-ng-repeat="subPolicy in policyResult.associatedPolicies">
                                         <strong><a
                                                 href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy/{{subPolicy.policy.type}}/{{subPolicy.policy.id}}">{{subPolicy.policy.name}}</a></strong>