keycloak-aplcache

KEYCLOAK-4727 KEYCLOAK-4652

4/6/2017 11:44:33 AM

Details

diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/DecisionEffect.java b/core/src/main/java/org/keycloak/representations/idm/authorization/DecisionEffect.java
new file mode 100644
index 0000000..cc13a08
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/DecisionEffect.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.representations.idm.authorization;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public enum DecisionEffect {
+    PERMIT,
+    DENY
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/PolicyEvaluationResponse.java b/core/src/main/java/org/keycloak/representations/idm/authorization/PolicyEvaluationResponse.java
new file mode 100644
index 0000000..4565771
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/PolicyEvaluationResponse.java
@@ -0,0 +1,173 @@
+/*
+ * 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 org.keycloak.representations.AccessToken;
+import org.keycloak.representations.idm.authorization.DecisionEffect;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
+import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class PolicyEvaluationResponse {
+
+    private List<EvaluationResultRepresentation> results;
+    private boolean entitlements;
+    private DecisionEffect status;
+    private AccessToken rpt;
+
+    public List<EvaluationResultRepresentation> getResults() {
+        return results;
+    }
+
+    public DecisionEffect getStatus() {
+        return status;
+    }
+
+    public boolean isEntitlements() {
+        return entitlements;
+    }
+
+    public AccessToken getRpt() {
+        return rpt;
+    }
+
+    public void setResults(List<EvaluationResultRepresentation> results) {
+        this.results = results;
+    }
+
+    public void setEntitlements(boolean entitlements) {
+        this.entitlements = entitlements;
+    }
+
+    public void setStatus(DecisionEffect status) {
+        this.status = status;
+    }
+
+    public void setRpt(AccessToken rpt) {
+        this.rpt = rpt;
+    }
+
+    public static class EvaluationResultRepresentation {
+
+        private ResourceRepresentation resource;
+        private List<ScopeRepresentation> scopes;
+        private List<PolicyResultRepresentation> policies;
+        private DecisionEffect status;
+        private List<ScopeRepresentation> allowedScopes = new ArrayList<>();
+
+        public void setResource(final ResourceRepresentation resource) {
+            this.resource = resource;
+        }
+
+        public ResourceRepresentation getResource() {
+            return resource;
+        }
+
+        public void setScopes(List<ScopeRepresentation> scopes) {
+            this.scopes = scopes;
+        }
+
+        public List<ScopeRepresentation> getScopes() {
+            return scopes;
+        }
+
+        public void setPolicies(final List<PolicyResultRepresentation> policies) {
+            this.policies = policies;
+        }
+
+        public List<PolicyResultRepresentation> getPolicies() {
+            return policies;
+        }
+
+        public void setStatus(final DecisionEffect status) {
+            this.status = status;
+        }
+
+        public DecisionEffect getStatus() {
+            return status;
+        }
+
+        public void setAllowedScopes(List<ScopeRepresentation> allowedScopes) {
+            this.allowedScopes = allowedScopes;
+        }
+
+        public List<ScopeRepresentation> getAllowedScopes() {
+            return allowedScopes;
+        }
+    }
+
+    public static class PolicyResultRepresentation {
+
+        private PolicyRepresentation policy;
+        private DecisionEffect status;
+        private List<PolicyResultRepresentation> associatedPolicies;
+        private List<ScopeRepresentation> scopes = new ArrayList<>();
+
+        public PolicyRepresentation getPolicy() {
+            return policy;
+        }
+
+        public void setPolicy(final PolicyRepresentation policy) {
+            this.policy = policy;
+        }
+
+        public DecisionEffect getStatus() {
+            return status;
+        }
+
+        public void setStatus(final DecisionEffect status) {
+            this.status = status;
+        }
+
+        public List<PolicyResultRepresentation> getAssociatedPolicies() {
+            return associatedPolicies;
+        }
+
+        public void setAssociatedPolicies(final List<PolicyResultRepresentation> associatedPolicies) {
+            this.associatedPolicies = associatedPolicies;
+        }
+
+        @Override
+        public int hashCode() {
+            return this.policy.hashCode();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            final PolicyResultRepresentation policy = (PolicyResultRepresentation) o;
+            return this.policy.equals(policy.getPolicy());
+        }
+
+        public void setScopes(List<ScopeRepresentation> scopes) {
+            this.scopes = scopes;
+        }
+
+        public List<ScopeRepresentation> getScopes() {
+            return scopes;
+        }
+    }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceRepresentation.java
index 86f6f98..d3c2e46 100644
--- a/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/authorization/ResourceRepresentation.java
@@ -18,6 +18,8 @@ package org.keycloak.representations.idm.authorization;
 
 import java.net.URI;
 import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -91,6 +93,15 @@ public class ResourceRepresentation {
         this(name, scopes, null, null, null);
     }
 
+    public ResourceRepresentation(String name, String... scopes) {
+        this.name = name;
+        this.scopes = new HashSet<>();
+        for (String s : scopes) {
+            ScopeRepresentation rep = new ScopeRepresentation(s);
+            this.scopes.add(rep);
+        }
+    }
+
     /**
      * Creates a new instance.
      *
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PoliciesResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PoliciesResource.java
index fd5d43a..e5120c6 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PoliciesResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/PoliciesResource.java
@@ -17,6 +17,8 @@
 package org.keycloak.admin.client.resource;
 
 import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.representations.idm.authorization.PolicyEvaluationRequest;
+import org.keycloak.representations.idm.authorization.PolicyEvaluationResponse;
 import org.keycloak.representations.idm.authorization.PolicyProviderRepresentation;
 import org.keycloak.representations.idm.authorization.PolicyRepresentation;
 
@@ -53,4 +55,11 @@ public interface PoliciesResource {
     @Produces(MediaType.APPLICATION_JSON)
     @NoCache
     List<PolicyProviderRepresentation> policyProviders();
+
+    @POST
+    @Consumes("application/json")
+    @Produces("application/json")
+    @Path("evaluate")
+    PolicyEvaluationResponse evaluate(PolicyEvaluationRequest evaluationRequest);
+
 }
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UsersResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UsersResource.java
index 3d57c07..b5fa96d 100755
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UsersResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UsersResource.java
@@ -36,11 +36,15 @@ public interface UsersResource {
     @GET
     @Produces(MediaType.APPLICATION_JSON)
     List<UserRepresentation> search(@QueryParam("username") String username,
-                                           @QueryParam("firstName") String firstName,
-                                           @QueryParam("lastName") String lastName,
-                                             @QueryParam("email") String email,
-                                             @QueryParam("first") Integer firstResult,
-                                             @QueryParam("max") Integer maxResults);
+                                    @QueryParam("firstName") String firstName,
+                                    @QueryParam("lastName") String lastName,
+                                    @QueryParam("email") String email,
+                                    @QueryParam("first") Integer firstResult,
+                                    @QueryParam("max") Integer maxResults);
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    List<UserRepresentation> search(@QueryParam("username") String username);
 
     @GET
     @Produces(MediaType.APPLICATION_JSON)
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 b096d24..6a2f522 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java
@@ -17,12 +17,12 @@
  */
 package org.keycloak.authorization.admin;
 
-import static java.util.Arrays.asList;
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -33,15 +33,13 @@ import java.util.stream.Stream;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.POST;
 import javax.ws.rs.Produces;
-import javax.ws.rs.container.AsyncResponse;
-import javax.ws.rs.container.Suspended;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Response;
 
 import org.jboss.resteasy.spi.HttpRequest;
 import org.keycloak.authorization.AuthorizationProvider;
-import org.keycloak.authorization.admin.representation.PolicyEvaluationRequest;
-import org.keycloak.authorization.admin.representation.PolicyEvaluationResponse;
+import org.keycloak.representations.idm.authorization.PolicyEvaluationRequest;
+import org.keycloak.authorization.admin.representation.PolicyEvaluationResponseBuilder;
 import org.keycloak.authorization.attribute.Attributes;
 import org.keycloak.authorization.common.KeycloakEvaluationContext;
 import org.keycloak.authorization.common.KeycloakIdentity;
@@ -58,15 +56,13 @@ import org.keycloak.authorization.util.Permissions;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.KeycloakSessionFactory;
-import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
-import org.keycloak.protocol.ProtocolMapper;
-import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper;
+import org.keycloak.protocol.oidc.TokenManager;
 import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.idm.authorization.ResourceRepresentation;
 import org.keycloak.representations.idm.authorization.ScopeRepresentation;
 import org.keycloak.services.Urls;
 import org.keycloak.services.resources.admin.RealmAuth;
@@ -89,32 +85,41 @@ 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 void evaluate(PolicyEvaluationRequest evaluationRequest, @Suspended AsyncResponse asyncResponse) {
+    public Response evaluate(PolicyEvaluationRequest evaluationRequest) throws Throwable {
         this.auth.requireView();
         KeycloakIdentity identity = createIdentity(evaluationRequest);
         EvaluationContext evaluationContext = createEvaluationContext(evaluationRequest, identity);
-        authorization.evaluators().from(createPermissions(evaluationRequest, evaluationContext, authorization), evaluationContext).evaluate(createDecisionCollector(authorization, identity, asyncResponse));
-    }
-
-    private DecisionResultCollector createDecisionCollector(AuthorizationProvider authorization, KeycloakIdentity identity, AsyncResponse asyncResponse) {
-        return new DecisionResultCollector() {
-            @Override
-            protected void onComplete(List<Result> results) {
-                try {
-                    asyncResponse.resume(Response.ok(PolicyEvaluationResponse.build(results, resourceServer,  authorization, identity)).build());
-                } catch (Throwable cause) {
-                    asyncResponse.resume(cause);
-                }
-            }
-
-            @Override
-            public void onError(Throwable cause) {
-                asyncResponse.resume(cause);
-            }
-        };
+        Decision decisionCollector = new Decision();
+        authorization.evaluators().from(createPermissions(evaluationRequest, evaluationContext, authorization), evaluationContext).evaluate(decisionCollector);
+        if (decisionCollector.error != null) {
+            throw decisionCollector.error;
+        }
+        return Response.ok(PolicyEvaluationResponseBuilder.build(decisionCollector.results, resourceServer,  authorization, identity)).build();
     }
 
     private EvaluationContext createEvaluationContext(PolicyEvaluationRequest representation, KeycloakIdentity identity) {
@@ -144,11 +149,11 @@ public class PolicyEvaluationService {
     }
 
     private List<ResourcePermission> createPermissions(PolicyEvaluationRequest representation, EvaluationContext evaluationContext, AuthorizationProvider authorization) {
-        List<PolicyEvaluationRequest.Resource> resources = representation.getResources();
-        return resources.stream().flatMap((Function<PolicyEvaluationRequest.Resource, Stream<ResourcePermission>>) resource -> {
+        List<ResourceRepresentation> resources = representation.getResources();
+        return resources.stream().flatMap((Function<ResourceRepresentation, Stream<ResourcePermission>>) resource -> {
             StoreFactory storeFactory = authorization.getStoreFactory();
             if (resource == null) {
-                resource = new PolicyEvaluationRequest.Resource();
+                resource = new ResourceRepresentation();
             }
 
             Set<ScopeRepresentation> givenScopes = resource.getScopes();
@@ -183,32 +188,15 @@ public class PolicyEvaluationService {
     private KeycloakIdentity createIdentity(PolicyEvaluationRequest representation) {
         KeycloakSession keycloakSession = this.authorization.getKeycloakSession();
         RealmModel realm = keycloakSession.getContext().getRealm();
-        AccessToken accessToken = new AccessToken();
+        AccessToken accessToken = null;
 
-        accessToken.subject(representation.getUserId());
-        accessToken.issuedFor(representation.getClientId());
-        accessToken.audience(representation.getClientId());
-        accessToken.issuer(Urls.realmIssuer(keycloakSession.getContext().getUri().getBaseUri(), realm.getName()));
-        accessToken.setRealmAccess(new AccessToken.Access());
 
-        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)));
-        }
-
-        String subject = accessToken.getSubject();
+        String subject = representation.getUserId();
 
         if (subject != null) {
             UserModel userModel = keycloakSession.users().getUserById(subject, realm);
 
             if (userModel != null) {
-                userModel.getAttributes().forEach(claims::put);
-
-                userModel.getRoleMappings().stream().map(RoleModel::getName).forEach(roleName -> realmAccess.addRole(roleName));
-
                 String clientId = representation.getClientId();
 
                 if (clientId == null) {
@@ -223,17 +211,18 @@ public class PolicyEvaluationService {
                         clientSession = keycloakSession.sessions().createClientSession(realm, clientModel);
                         userSession = keycloakSession.sessions().createUserSession(realm, userModel, userModel.getUsername(), "127.0.0.1", "passwd", false, null, null);
 
-                        UserSessionModel finalUserSession = userSession;
-                        ClientSessionModel finalClientSession = clientSession;
+                        new TokenManager().attachClientSession(userSession, clientSession);
 
-                        for (ProtocolMapperModel mapping : clientModel.getProtocolMappers()) {
-                            KeycloakSessionFactory sessionFactory = keycloakSession.getKeycloakSessionFactory();
-                            ProtocolMapper mapper = (ProtocolMapper)sessionFactory.getProviderFactory(ProtocolMapper.class, mapping.getProtocolMapper());
-
-                            if (mapper != null && (mapper instanceof OIDCAccessTokenMapper)) {
-                                accessToken = ((OIDCAccessTokenMapper)mapper).transformAccessToken(accessToken, mapping, keycloakSession, finalUserSession, finalClientSession);
+                        Set<RoleModel> requestedRoles = new HashSet<>();
+                        for (String roleId : clientSession.getRoles()) {
+                            RoleModel role = realm.getRoleById(roleId);
+                            if (role != null) {
+                                requestedRoles.add(role);
                             }
                         }
+
+
+                        accessToken = new TokenManager().createClientAccessToken(keycloakSession, requestedRoles, realm, clientModel, userModel, userSession, clientSession);
                     } finally {
                         if (clientSession != null) {
                             keycloakSession.sessions().removeClientSession(realm, clientSession);
@@ -243,21 +232,30 @@ public class PolicyEvaluationService {
                             keycloakSession.sessions().removeUserSession(realm, userSession);
                         }
                     }
+                }
+            }
+        }
 
-                    AccessToken.Access clientAccess = accessToken.addAccess(clientModel.getClientId());
-                    clientAccess.roles(new HashSet<>());
+        if (accessToken == null) {
+            accessToken = new AccessToken();
 
-                    userModel.getClientRoleMappings(clientModel).stream().map(RoleModel::getName).forEach(roleName -> clientAccess.addRole(roleName));
+            accessToken.subject(representation.getUserId());
+            accessToken.issuedFor(representation.getClientId());
+            accessToken.audience(representation.getClientId());
+            accessToken.issuer(Urls.realmIssuer(keycloakSession.getContext().getUri().getBaseUri(), realm.getName()));
+            accessToken.setRealmAccess(new AccessToken.Access());
 
-                    ClientModel resourceServerClient = realm.getClientById(resourceServer.getClientId());
-                    AccessToken.Access resourceServerAccess = accessToken.addAccess(resourceServerClient.getClientId());
-                    resourceServerAccess.roles(new HashSet<>());
+        }
 
-                    userModel.getClientRoleMappings(resourceServerClient).stream().map(RoleModel::getName).forEach(roleName -> resourceServerAccess.addRole(roleName));
-                }
-            }
+        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/common/AbstractEvaluationContext.java b/services/src/main/java/org/keycloak/authorization/common/AbstractEvaluationContext.java
new file mode 100644
index 0000000..7588210
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/common/AbstractEvaluationContext.java
@@ -0,0 +1,63 @@
+/*
+ * 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.common;
+
+import org.keycloak.authorization.attribute.Attributes;
+import org.keycloak.authorization.identity.Identity;
+import org.keycloak.authorization.policy.evaluation.EvaluationContext;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.representations.AccessToken;
+
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public abstract class AbstractEvaluationContext implements EvaluationContext {
+
+    private final KeycloakSession keycloakSession;
+
+    public AbstractEvaluationContext(KeycloakSession keycloakSession) {
+        this.keycloakSession = keycloakSession;
+    }
+
+    public Map<String, Collection<String>> getBaseAttributes() {
+        HashMap<String, Collection<String>> attributes = new HashMap<>();
+
+        attributes.put("kc.time.date_time", Arrays.asList(new SimpleDateFormat("MM/dd/yyyy hh:mm:ss").format(new Date())));
+        attributes.put("kc.client.network.ip_address", Arrays.asList(this.keycloakSession.getContext().getConnection().getRemoteAddr()));
+        attributes.put("kc.client.network.host", Arrays.asList(this.keycloakSession.getContext().getConnection().getRemoteHost()));
+
+        List<String> userAgents = this.keycloakSession.getContext().getRequestHeaders().getRequestHeader("User-Agent");
+
+        if (userAgents != null) {
+            attributes.put("kc.client.user_agent", userAgents);
+        }
+
+        attributes.put("kc.realm.name", Arrays.asList(this.keycloakSession.getContext().getRealm().getName()));
+
+        return attributes;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authorization/common/UserModelIdentity.java b/services/src/main/java/org/keycloak/authorization/common/UserModelIdentity.java
new file mode 100644
index 0000000..67e6bb4
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authorization/common/UserModelIdentity.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.authorization.common;
+
+import org.keycloak.authorization.attribute.Attributes;
+import org.keycloak.authorization.identity.Identity;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class UserModelIdentity implements Identity {
+    protected RealmModel realm;
+    protected UserModel user;
+
+    public UserModelIdentity(UserModel user) {
+        this.user = user;
+    }
+
+    @Override
+    public String getId() {
+        return user.getId();
+    }
+
+    @Override
+    public Attributes getAttributes() {
+        Map attr = user.getAttributes();
+        return Attributes.from(attr);
+    }
+
+    @Override
+    public boolean hasRealmRole(String roleName) {
+        RoleModel role = realm.getRole(roleName);
+        if (role == null) return false;
+        return user.hasRole(role);
+    }
+
+    @Override
+    public boolean hasClientRole(String clientId, String roleName) {
+        ClientModel client = realm.getClientByClientId(clientId);
+        RoleModel role = client.getRole(roleName);
+        if (role == null) return false;
+        return user.hasRole(role);
+    }
+
+    @Override
+    public boolean hasRole(String roleName) {
+        throw new RuntimeException("Should not execute");
+    }
+
+    @Override
+    public boolean hasClientRole(String roleName) {
+        throw new RuntimeException("Should not execute");
+    }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
index 4a2ce96..4eb2bb4 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
@@ -99,7 +99,7 @@ public class AdapterTest {
     @Rule
     public AdapterTestStrategy testStrategy = new AdapterTestStrategy("http://localhost:8081/auth", "http://localhost:8081", keycloakRule);
 
-    //@Test
+    @Test
     public void testUi() throws Exception {
         Thread.sleep(1000000000);
     }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminLocalTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminLocalTest.java
new file mode 100644
index 0000000..f4a11be
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminLocalTest.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.admin;
+
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.RealmResource;
+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.models.AdminRoles;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.Constants;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.representations.idm.authorization.DecisionEffect;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.PolicyEvaluationRequest;
+import org.keycloak.representations.idm.authorization.PolicyEvaluationResponse;
+import org.keycloak.testsuite.AbstractKeycloakTest;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+//@Ignore
+public class FineGrainAdminLocalTest extends AbstractKeycloakTest {
+
+    @Override
+    public void addTestRealms(List<RealmRepresentation> testRealms) {
+        RealmRepresentation testRealmRep = new RealmRepresentation();
+        testRealmRep.setId(TEST);
+        testRealmRep.setRealm(TEST);
+        testRealmRep.setEnabled(true);
+        testRealms.add(testRealmRep);
+    }
+
+    public static void setupDefaults(KeycloakSession session) {
+        RealmModel realm = session.realms().getRealmByName(TEST);
+
+        ClientModel client = realm.getClientByClientId("realm-management");
+
+        AuthorizationProvider authz = session.getProvider(AuthorizationProvider.class);
+        ResourceServer resourceServer = authz.getStoreFactory().getResourceServerStore().create(client.getId());
+        Scope mapRoleScope = authz.getStoreFactory().getScopeStore().create("map-role", resourceServer);
+        Scope manageScope = authz.getStoreFactory().getScopeStore().create("manage", resourceServer);
+
+        Policy manageUsersPolicy = null;
+        Policy manageClientsPolicy = null;
+        for (RoleModel role : client.getRoles()) {
+            Policy policy = createRolePolicy(authz, resourceServer, role);
+            if (role.getName().equals(AdminRoles.MANAGE_USERS)) {
+                manageUsersPolicy = policy;
+            } else if (role.getName().equals(AdminRoles.MANAGE_CLIENTS)) {
+                manageClientsPolicy = policy;
+            }
+            Resource resource = createRoleResource(authz, resourceServer, role);
+            Set<Scope> scopeset = new HashSet<>();
+            scopeset.add(mapRoleScope);
+            resource.updateScopes(scopeset);
+
+
+            String name = "map.role.permission." + client.getClientId() + "." + role.getName();
+            Policy permission = addScopePermission(authz, resourceServer, name, resource, mapRoleScope, policy);
+
+        }
+        Resource usersResource = authz.getStoreFactory().getResourceStore().create("Users", resourceServer, resourceServer.getClientId());
+        Set<Scope> scopeset = new HashSet<>();
+        scopeset.add(manageScope);
+        usersResource.updateScopes(scopeset);
+        addScopePermission(authz, resourceServer, "Users.manage.permission", usersResource, manageScope, manageUsersPolicy);
+    }
+
+    private static Policy addScopePermission(AuthorizationProvider authz, ResourceServer resourceServer, String name, Resource resource, Scope scope, Policy policy) {
+        Policy permission = authz.getStoreFactory().getPolicyStore().create(name, "scope", resourceServer);
+        String resources = "[\"" + resource.getId() + "\"]";
+        String scopes = "[\"" + scope.getId() + "\"]";
+        String applyPolicies = "[\"" + policy.getId() + "\"]";
+        Map<String, String> config = new HashMap<>();
+        config.put("resources", resources);
+        config.put("scopes", scopes);
+        config.put("applyPolicies", applyPolicies);
+        permission.setConfig(config);
+        permission.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
+        permission.setLogic(Logic.POSITIVE);
+        permission.addResource(resource);
+        permission.addScope(scope);
+        permission.addAssociatedPolicy(policy);
+        return permission;
+    }
+
+    private static Resource createRoleResource(AuthorizationProvider authz, ResourceServer resourceServer, RoleModel role) {
+        String roleName = getRoleResourceName(role);
+        Resource resource =  authz.getStoreFactory().getResourceStore().create(roleName, resourceServer, resourceServer.getClientId());
+        resource.setType("Role");
+        return resource;
+    }
+
+    private static String getRoleResourceName(RoleModel role) {
+        String roleName = "realm";
+        if (role.getContainer() instanceof ClientModel) {
+            ClientModel client = (ClientModel)role.getContainer();
+            roleName = client.getClientId();
+        }
+        roleName = "role.resource." + roleName + "." + role.getName();
+        return roleName;
+    }
+
+
+    private static Policy createRolePolicy(AuthorizationProvider authz, ResourceServer resourceServer, RoleModel role) {
+        String roleName = "realm";
+        if (role.getContainer() instanceof ClientModel) {
+            ClientModel client = (ClientModel) role.getContainer();
+            roleName = client.getClientId() ;
+        }
+        roleName = "role.policy." + roleName + "." + role.getName();
+        Policy policy = authz.getStoreFactory().getPolicyStore().create(roleName, "role", resourceServer);
+
+        String roleValues = "[{\"id\":\"" + role.getId() + "\",\"required\": true}]";
+        policy.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
+        policy.setLogic(Logic.POSITIVE);
+        Map<String, String> config = new HashMap<>();
+        config.put("roles", roleValues);
+        policy.setConfig(config);
+        return policy;
+    }
+
+    public static void setupUsers(KeycloakSession session) {
+        RealmModel realm = session.realms().getRealmByName(TEST);
+        ClientModel client = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID);
+        UserModel admin = session.users().addUser(realm, "admin");
+        admin.grantRole(client.getRole(AdminRoles.REALM_ADMIN));
+        UserModel manageUserOnlyUser = session.users().addUser(realm, "manage-user");
+        RoleModel manageUsersRole = client.getRole(AdminRoles.MANAGE_USERS);
+        manageUserOnlyUser.grantRole(manageUsersRole);
+        UserModel manageRealmUser = session.users().addUser(realm, "manage-realm");
+        manageRealmUser.grantRole(manageUsersRole);
+        RoleModel manageRealmRole = client.getRole(AdminRoles.MANAGE_REALM);
+        manageRealmUser.grantRole(manageRealmRole);
+
+    }
+
+    @Test
+    public void testUI() throws Exception {
+        testingClient.server().run(FineGrainAdminLocalTest::setupDefaults);
+        testingClient.server().run(FineGrainAdminLocalTest::setupUsers);
+        //Thread.sleep(1000000000);
+    }
+
+    public static void evaluateAdminHasManageRealmPermissions(KeycloakSession session) {
+        RealmModel realm = session.realms().getRealmByName(TEST);
+        UserModel admin = session.users().getUserByUsername("admin", realm);
+
+        AuthorizationProvider authz = session.getProvider(AuthorizationProvider.class);
+        ClientModel client = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID);
+        ResourceServer resourceServer = authz.getStoreFactory().getResourceServerStore().findByClient(client.getId());
+
+        RoleModel manageRealmRole = client.getRole(AdminRoles.MANAGE_REALM);
+        Resource roleResource = authz.getStoreFactory().getResourceStore().findByName(getRoleResourceName(manageRealmRole), resourceServer.getId());
+
+
+
+    }
+
+    @Test
+    public void testEvaluation() throws Exception {
+        testingClient.server().run(FineGrainAdminLocalTest::setupDefaults);
+        testingClient.server().run(FineGrainAdminLocalTest::setupUsers);
+
+        RealmResource realm = adminClient.realm(TEST);
+        String resourceServerId = realm.clients().findByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID).get(0).getId();
+        UserRepresentation admin = realm.users().search("admin").get(0);
+        UserRepresentation manageUser = realm.users().search("manage-user").get(0);
+        UserRepresentation manageRealm = realm.users().search("manage-realm").get(0);
+
+        PolicyEvaluationRequest request = new PolicyEvaluationRequest();
+        request.setUserId(admin.getId());
+        request.setClientId(resourceServerId);
+        request.addResource("role.resource." + Constants.REALM_MANAGEMENT_CLIENT_ID + "." + AdminRoles.MANAGE_REALM,
+                "map-role");
+        PolicyEvaluationResponse result = realm.clients().get(resourceServerId).authorization().policies().evaluate(request);
+        Assert.assertEquals(result.getStatus(), DecisionEffect.PERMIT);
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationCompositeRoleTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationCompositeRoleTest.java
new file mode 100644
index 0000000..98d3366
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationCompositeRoleTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.authz;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.RealmResource;
+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.models.AdminRoles;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.Constants;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.representations.idm.authorization.DecisionEffect;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.PolicyEvaluationRequest;
+import org.keycloak.representations.idm.authorization.PolicyEvaluationResponse;
+import org.keycloak.testsuite.AbstractKeycloakTest;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class PolicyEvaluationCompositeRoleTest extends AbstractKeycloakTest {
+    @Override
+    public void addTestRealms(List<RealmRepresentation> testRealms) {
+        RealmRepresentation testRealmRep = new RealmRepresentation();
+        testRealmRep.setId(TEST);
+        testRealmRep.setRealm(TEST);
+        testRealmRep.setEnabled(true);
+        testRealms.add(testRealmRep);
+    }
+
+    public static void setup(KeycloakSession session) {
+        RealmModel realm = session.realms().getRealmByName(TEST);
+        ClientModel client = session.realms().addClient(realm, "myclient");
+        RoleModel role1 = client.addRole("client-role1");
+
+
+        AuthorizationProvider authz = session.getProvider(AuthorizationProvider.class);
+        ResourceServer resourceServer = authz.getStoreFactory().getResourceServerStore().create(client.getId());
+        Policy policy = createRolePolicy(authz, resourceServer, role1);
+
+        Scope scope = authz.getStoreFactory().getScopeStore().create("myscope", resourceServer);
+        Resource resource = authz.getStoreFactory().getResourceStore().create("myresource", resourceServer, resourceServer.getClientId());
+        addScopePermission(authz, resourceServer, "mypermission", resource, scope, policy);
+
+        RoleModel composite = realm.addRole("composite");
+        composite.addCompositeRole(role1);
+
+        UserModel user = session.users().addUser(realm, "user");
+        user.grantRole(composite);
+    }
+
+    private static Policy addScopePermission(AuthorizationProvider authz, ResourceServer resourceServer, String name, Resource resource, Scope scope, Policy policy) {
+        Policy permission = authz.getStoreFactory().getPolicyStore().create(name, "scope", resourceServer);
+        String resources = "[\"" + resource.getId() + "\"]";
+        String scopes = "[\"" + scope.getId() + "\"]";
+        String applyPolicies = "[\"" + policy.getId() + "\"]";
+        Map<String, String> config = new HashMap<>();
+        config.put("resources", resources);
+        config.put("scopes", scopes);
+        config.put("applyPolicies", applyPolicies);
+        permission.setConfig(config);
+        permission.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
+        permission.setLogic(Logic.POSITIVE);
+        permission.addResource(resource);
+        permission.addScope(scope);
+        permission.addAssociatedPolicy(policy);
+        return permission;
+    }
+
+
+    private static Policy createRolePolicy(AuthorizationProvider authz, ResourceServer resourceServer, RoleModel role) {
+        Policy policy = authz.getStoreFactory().getPolicyStore().create(role.getName(), "role", resourceServer);
+
+        String roleValues = "[{\"id\":\"" + role.getId() + "\",\"required\": true}]";
+        policy.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
+        policy.setLogic(Logic.POSITIVE);
+        Map<String, String> config = new HashMap<>();
+        config.put("roles", roleValues);
+        policy.setConfig(config);
+        return policy;
+    }
+
+
+    @Test
+    public void testCreate() throws Exception {
+        testingClient.server().run(PolicyEvaluationCompositeRoleTest::setup);
+
+        RealmResource realm = adminClient.realm(TEST);
+        String resourceServerId = realm.clients().findByClientId("myclient").get(0).getId();
+        UserRepresentation user = realm.users().search("user").get(0);
+
+        PolicyEvaluationRequest request = new PolicyEvaluationRequest();
+        request.setUserId(user.getId());
+        request.setClientId(resourceServerId);
+        request.addResource("myresource", "myscope");
+        PolicyEvaluationResponse result = realm.clients().get(resourceServerId).authorization().policies().evaluate(request);
+        Assert.assertEquals(result.getStatus(), DecisionEffect.PERMIT);
+    }
+
+
+}