prototype
This commit is contained in:
parent
9452d37926
commit
58868ca99f
7 changed files with 118 additions and 52 deletions
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.keycloak.authorization.policy.evaluation;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class DecisionResult extends DecisionResultCollector {
|
||||||
|
protected List<Result> results;
|
||||||
|
protected Throwable error;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onComplete(List<Result> results) {
|
||||||
|
this.results = results;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Throwable cause) {
|
||||||
|
this.error = cause;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean completed() {
|
||||||
|
return results != null && error == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Result> getResults() {
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Throwable getError() {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,6 +38,7 @@ import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
import org.jboss.resteasy.spi.HttpRequest;
|
import org.jboss.resteasy.spi.HttpRequest;
|
||||||
import org.keycloak.authorization.AuthorizationProvider;
|
import org.keycloak.authorization.AuthorizationProvider;
|
||||||
|
import org.keycloak.authorization.policy.evaluation.DecisionResult;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
import org.keycloak.representations.idm.authorization.PolicyEvaluationRequest;
|
import org.keycloak.representations.idm.authorization.PolicyEvaluationRequest;
|
||||||
import org.keycloak.authorization.admin.representation.PolicyEvaluationResponseBuilder;
|
import org.keycloak.authorization.admin.representation.PolicyEvaluationResponseBuilder;
|
||||||
|
@ -86,21 +87,6 @@ public class PolicyEvaluationService {
|
||||||
this.auth = auth;
|
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) {
|
public static <T> List<T> asList(T... a) {
|
||||||
List<T> list = new LinkedList<T>();
|
List<T> list = new LinkedList<T>();
|
||||||
|
@ -116,12 +102,12 @@ public class PolicyEvaluationService {
|
||||||
CloseableKeycloakIdentity identity = createIdentity(evaluationRequest);
|
CloseableKeycloakIdentity identity = createIdentity(evaluationRequest);
|
||||||
try {
|
try {
|
||||||
EvaluationContext evaluationContext = createEvaluationContext(evaluationRequest, identity);
|
EvaluationContext evaluationContext = createEvaluationContext(evaluationRequest, identity);
|
||||||
Decision decisionCollector = new Decision();
|
DecisionResult decisionCollector = new DecisionResult();
|
||||||
authorization.evaluators().from(createPermissions(evaluationRequest, evaluationContext, authorization), evaluationContext).evaluate(decisionCollector);
|
authorization.evaluators().from(createPermissions(evaluationRequest, evaluationContext, authorization), evaluationContext).evaluate(decisionCollector);
|
||||||
if (decisionCollector.error != null) {
|
if (!decisionCollector.completed()) {
|
||||||
throw decisionCollector.error;
|
throw decisionCollector.getError();
|
||||||
}
|
}
|
||||||
return Response.ok(PolicyEvaluationResponseBuilder.build(decisionCollector.results, resourceServer, authorization, identity)).build();
|
return Response.ok(PolicyEvaluationResponseBuilder.build(decisionCollector.getResults(), resourceServer, authorization, identity)).build();
|
||||||
} finally {
|
} finally {
|
||||||
identity.close();
|
identity.close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,12 +35,19 @@ import java.util.Map;
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractEvaluationContext implements EvaluationContext {
|
public class DefaultEvaluationContext implements EvaluationContext {
|
||||||
|
|
||||||
private final KeycloakSession keycloakSession;
|
protected final KeycloakSession keycloakSession;
|
||||||
|
protected final Identity identity;
|
||||||
|
|
||||||
public AbstractEvaluationContext(KeycloakSession keycloakSession) {
|
public DefaultEvaluationContext(Identity identity, KeycloakSession keycloakSession) {
|
||||||
this.keycloakSession = keycloakSession;
|
this.keycloakSession = keycloakSession;
|
||||||
|
this.identity = identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Identity getIdentity() {
|
||||||
|
return identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Collection<String>> getBaseAttributes() {
|
public Map<String, Collection<String>> getBaseAttributes() {
|
||||||
|
@ -60,4 +67,9 @@ public abstract class AbstractEvaluationContext implements EvaluationContext {
|
||||||
|
|
||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Attributes getAttributes() {
|
||||||
|
return Attributes.from(getBaseAttributes());
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -18,34 +18,28 @@
|
||||||
|
|
||||||
package org.keycloak.authorization.common;
|
package org.keycloak.authorization.common;
|
||||||
|
|
||||||
import org.keycloak.authorization.attribute.Attributes;
|
|
||||||
import org.keycloak.authorization.identity.Identity;
|
import org.keycloak.authorization.identity.Identity;
|
||||||
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
|
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
import java.util.Map;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
*/
|
*/
|
||||||
public class KeycloakEvaluationContext implements EvaluationContext {
|
public class KeycloakEvaluationContext extends DefaultEvaluationContext {
|
||||||
|
|
||||||
private final KeycloakIdentity identity;
|
private final KeycloakIdentity identity;
|
||||||
private final KeycloakSession keycloakSession;
|
|
||||||
|
|
||||||
public KeycloakEvaluationContext(KeycloakSession keycloakSession) {
|
public KeycloakEvaluationContext(KeycloakSession keycloakSession) {
|
||||||
this(new KeycloakIdentity(keycloakSession), keycloakSession);
|
this(new KeycloakIdentity(keycloakSession), keycloakSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeycloakEvaluationContext(KeycloakIdentity identity, KeycloakSession keycloakSession) {
|
public KeycloakEvaluationContext(KeycloakIdentity identity, KeycloakSession keycloakSession) {
|
||||||
|
super(identity, keycloakSession);
|
||||||
this.identity = identity;
|
this.identity = identity;
|
||||||
this.keycloakSession = keycloakSession;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -54,27 +48,13 @@ public class KeycloakEvaluationContext implements EvaluationContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Attributes getAttributes() {
|
public Map<String, Collection<String>> getBaseAttributes() {
|
||||||
HashMap<String, Collection<String>> attributes = new HashMap<>();
|
Map<String, Collection<String>> attributes = super.getBaseAttributes();
|
||||||
|
|
||||||
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()));
|
|
||||||
|
|
||||||
AccessToken accessToken = this.identity.getAccessToken();
|
AccessToken accessToken = this.identity.getAccessToken();
|
||||||
|
|
||||||
if (accessToken != null) {
|
if (accessToken != null) {
|
||||||
attributes.put("kc.client.id", Arrays.asList(accessToken.getIssuedFor()));
|
attributes.put("kc.client.id", Arrays.asList(accessToken.getIssuedFor()));
|
||||||
}
|
}
|
||||||
|
return attributes;
|
||||||
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.from(attributes);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,8 @@ public class UserModelIdentity implements Identity {
|
||||||
protected RealmModel realm;
|
protected RealmModel realm;
|
||||||
protected UserModel user;
|
protected UserModel user;
|
||||||
|
|
||||||
public UserModelIdentity(UserModel user) {
|
public UserModelIdentity(RealmModel realm, UserModel user) {
|
||||||
|
this.realm = realm;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
package org.keycloak.authorization.util;
|
package org.keycloak.authorization.util;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -45,6 +47,10 @@ import org.keycloak.representations.idm.authorization.Permission;
|
||||||
*/
|
*/
|
||||||
public final class Permissions {
|
public final class Permissions {
|
||||||
|
|
||||||
|
public static List<ResourcePermission> permission(ResourceServer server, Resource resource, Scope scope) {
|
||||||
|
return Arrays.asList(new ResourcePermission(resource, Arrays.asList(scope), server));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of permissions for all resources and scopes that belong to the given <code>resourceServer</code> and
|
* Returns a list of permissions for all resources and scopes that belong to the given <code>resourceServer</code> and
|
||||||
* <code>identity</code>.
|
* <code>identity</code>.
|
||||||
|
|
|
@ -21,10 +21,18 @@ import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.admin.client.resource.RealmResource;
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
import org.keycloak.authorization.AuthorizationProvider;
|
import org.keycloak.authorization.AuthorizationProvider;
|
||||||
|
import org.keycloak.authorization.Decision;
|
||||||
|
import org.keycloak.authorization.common.DefaultEvaluationContext;
|
||||||
|
import org.keycloak.authorization.common.UserModelIdentity;
|
||||||
import org.keycloak.authorization.model.Policy;
|
import org.keycloak.authorization.model.Policy;
|
||||||
import org.keycloak.authorization.model.Resource;
|
import org.keycloak.authorization.model.Resource;
|
||||||
import org.keycloak.authorization.model.ResourceServer;
|
import org.keycloak.authorization.model.ResourceServer;
|
||||||
import org.keycloak.authorization.model.Scope;
|
import org.keycloak.authorization.model.Scope;
|
||||||
|
import org.keycloak.authorization.permission.ResourcePermission;
|
||||||
|
import org.keycloak.authorization.permission.evaluator.PermissionEvaluator;
|
||||||
|
import org.keycloak.authorization.policy.evaluation.DecisionResult;
|
||||||
|
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
|
||||||
|
import org.keycloak.authorization.util.Permissions;
|
||||||
import org.keycloak.models.AdminRoles;
|
import org.keycloak.models.AdminRoles;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
|
@ -43,6 +51,7 @@ import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -53,7 +62,7 @@ import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
@Ignore
|
//@Ignore
|
||||||
public class FineGrainAdminLocalTest extends AbstractKeycloakTest {
|
public class FineGrainAdminLocalTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -170,15 +179,16 @@ public class FineGrainAdminLocalTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
//@Test
|
||||||
public void testUI() throws Exception {
|
public void testUI() throws Exception {
|
||||||
testingClient.server().run(FineGrainAdminLocalTest::setupDefaults);
|
testingClient.server().run(FineGrainAdminLocalTest::setupDefaults);
|
||||||
testingClient.server().run(FineGrainAdminLocalTest::setupUsers);
|
testingClient.server().run(FineGrainAdminLocalTest::setupUsers);
|
||||||
//Thread.sleep(1000000000);
|
Thread.sleep(1000000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void evaluateAdminHasManageRealmPermissions(KeycloakSession session) {
|
public static void evaluateAdminHasManageRealmPermissions(KeycloakSession session) {
|
||||||
RealmModel realm = session.realms().getRealmByName(TEST);
|
RealmModel realm = session.realms().getRealmByName(TEST);
|
||||||
|
session.getContext().setRealm(realm);
|
||||||
UserModel admin = session.users().getUserByUsername("admin", realm);
|
UserModel admin = session.users().getUserByUsername("admin", realm);
|
||||||
|
|
||||||
AuthorizationProvider authz = session.getProvider(AuthorizationProvider.class);
|
AuthorizationProvider authz = session.getProvider(AuthorizationProvider.class);
|
||||||
|
@ -187,9 +197,29 @@ public class FineGrainAdminLocalTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
RoleModel manageRealmRole = client.getRole(AdminRoles.MANAGE_REALM);
|
RoleModel manageRealmRole = client.getRole(AdminRoles.MANAGE_REALM);
|
||||||
Resource roleResource = authz.getStoreFactory().getResourceStore().findByName(getRoleResourceName(manageRealmRole), resourceServer.getId());
|
Resource roleResource = authz.getStoreFactory().getResourceStore().findByName(getRoleResourceName(manageRealmRole), resourceServer.getId());
|
||||||
|
Scope mapRoleScope = authz.getStoreFactory().getScopeStore().findByName("map-role", resourceServer.getId());
|
||||||
|
|
||||||
|
UserModelIdentity identity = new UserModelIdentity(realm, admin);
|
||||||
|
EvaluationContext context = new DefaultEvaluationContext(identity, session);
|
||||||
|
DecisionResult decisionCollector = new DecisionResult();
|
||||||
|
|
||||||
|
List<ResourcePermission> permissions = Permissions.permission(resourceServer, roleResource, mapRoleScope);
|
||||||
|
PermissionEvaluator from = authz.evaluators().from(permissions, context);
|
||||||
|
from.evaluate(decisionCollector);
|
||||||
|
if (!decisionCollector.completed()) {
|
||||||
|
decisionCollector.getError().printStackTrace();
|
||||||
|
}
|
||||||
|
Assert.assertTrue(decisionCollector.completed());
|
||||||
|
Assert.assertEquals(decisionCollector.getResults().get(0).getEffect(), Decision.Effect.PERMIT);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEvaluation2() throws Exception {
|
||||||
|
testingClient.server().run(FineGrainAdminLocalTest::setupDefaults);
|
||||||
|
testingClient.server().run(FineGrainAdminLocalTest::setupUsers);
|
||||||
|
testingClient.server().run(FineGrainAdminLocalTest::evaluateAdminHasManageRealmPermissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in a new issue