prototype

This commit is contained in:
Bill Burke 2017-04-24 10:05:39 -04:00
parent 9452d37926
commit 58868ca99f
7 changed files with 118 additions and 52 deletions

View file

@ -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;
}
}

View file

@ -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();
} }

View file

@ -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());
}
} }

View file

@ -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);
} }
} }

View file

@ -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;
} }

View file

@ -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>.

View file

@ -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