mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-10 15:32:05 -03:30
Refactoring how policies are activated based on user-defined events and conditions
Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
parent
cee9b6803b
commit
b65356f3c8
@ -19,10 +19,7 @@ package org.keycloak.models.policy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
@ -33,22 +30,18 @@ import jakarta.persistence.criteria.Subquery;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.connections.jpa.JpaConnectionProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.jpa.entities.UserEntity;
|
||||
import org.keycloak.models.policy.conditions.IdentityProviderPolicyConditionFactory;
|
||||
import org.keycloak.models.policy.conditions.IdentityProviderPolicyConditionProvider;
|
||||
|
||||
public abstract class AbstractUserResourcePolicyProvider implements ResourcePolicyProvider {
|
||||
public abstract class AbstractUserResourcePolicyProvider extends EventBasedResourcePolicyProvider {
|
||||
|
||||
private final ComponentModel policyModel;
|
||||
private final EntityManager em;
|
||||
private final KeycloakSession session;
|
||||
|
||||
public AbstractUserResourcePolicyProvider(KeycloakSession session, ComponentModel model) {
|
||||
this.policyModel = model;
|
||||
super(session, model);
|
||||
this.em = session.getProvider(JpaConnectionProvider.class).getEntityManager();
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -66,7 +59,7 @@ public abstract class AbstractUserResourcePolicyProvider implements ResourcePoli
|
||||
subquery.where(
|
||||
cb.and(
|
||||
cb.equal(stateRoot.get("resourceId"), userRoot.get("id")),
|
||||
cb.equal(stateRoot.get("policyId"), policyModel.getId())
|
||||
cb.equal(stateRoot.get("policyId"), getModel().getId())
|
||||
)
|
||||
);
|
||||
Predicate notExistsPredicate = cb.not(cb.exists(subquery));
|
||||
@ -80,7 +73,7 @@ public abstract class AbstractUserResourcePolicyProvider implements ResourcePoli
|
||||
}
|
||||
|
||||
private List<Predicate> getConditionsPredicate(CriteriaBuilder cb, CriteriaQuery<String> query, Root<UserEntity> path) {
|
||||
List<String> conditions = policyModel.getConfig().getOrDefault("conditions", List.of());
|
||||
List<String> conditions = getModel().getConfig().getOrDefault("conditions", List.of());
|
||||
|
||||
if (conditions.isEmpty()) {
|
||||
return List.of();
|
||||
@ -100,23 +93,6 @@ public abstract class AbstractUserResourcePolicyProvider implements ResourcePoli
|
||||
return predicates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(ResourceType type) {
|
||||
return ResourceType.USERS.equals(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean activateOnEvent(ResourcePolicyEvent event) {
|
||||
boolean b = this.supports(event.getResourceType())
|
||||
&& this.getSupportedOperationsForActivation().contains(event.getOperation());
|
||||
|
||||
if (!b) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return evaluate(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean resetOnEvent(ResourcePolicyEvent event) {
|
||||
boolean b = this.supports(event.getResourceType())
|
||||
@ -140,20 +116,6 @@ public abstract class AbstractUserResourcePolicyProvider implements ResourcePoli
|
||||
return !evaluate(event);
|
||||
}
|
||||
|
||||
private boolean evaluate(ResourcePolicyEvent event) {
|
||||
List<String> conditions = policyModel.getConfig().getOrDefault("conditions", List.of());
|
||||
|
||||
for (String providerId : conditions) {
|
||||
ResourcePolicyConditionProvider condition = resolveCondition(providerId);
|
||||
|
||||
if (!condition.evaluate(event)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// no-op
|
||||
@ -179,20 +141,12 @@ public abstract class AbstractUserResourcePolicyProvider implements ResourcePoli
|
||||
return em;
|
||||
}
|
||||
|
||||
protected ComponentModel getModel() {
|
||||
return policyModel;
|
||||
}
|
||||
|
||||
protected KeycloakSession getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
protected RealmModel getRealm() {
|
||||
return getSession().getContext().getRealm();
|
||||
}
|
||||
|
||||
protected List<String> getBrokerAliases() {
|
||||
List<String> conditions = policyModel.getConfig().getOrDefault("conditions", List.of());
|
||||
List<String> conditions = getModel().getConfig().getOrDefault("conditions", List.of());
|
||||
|
||||
for (String providerId : conditions) {
|
||||
ResourcePolicyConditionProvider condition = resolveCondition(providerId);
|
||||
@ -204,29 +158,4 @@ public abstract class AbstractUserResourcePolicyProvider implements ResourcePoli
|
||||
|
||||
return List.of();
|
||||
}
|
||||
|
||||
private ResourcePolicyConditionProvider resolveCondition(String providerId) {
|
||||
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
|
||||
ResourcePolicyConditionProviderFactory<ResourcePolicyConditionProvider> providerFactory = (ResourcePolicyConditionProviderFactory<ResourcePolicyConditionProvider>) sessionFactory.getProviderFactory(ResourcePolicyConditionProvider.class, providerId);
|
||||
|
||||
if (providerFactory == null) {
|
||||
throw new IllegalStateException("Could not find condition provider: " + providerId);
|
||||
}
|
||||
|
||||
Map<String, List<String>> config = new HashMap<>();
|
||||
|
||||
for (Entry<String, List<String>> configEntry : policyModel.getConfig().entrySet()) {
|
||||
if (configEntry.getKey().startsWith(providerId)) {
|
||||
config.put(configEntry.getKey().substring(providerId.length() + 1), configEntry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
ResourcePolicyConditionProvider condition = providerFactory.create(session, config);
|
||||
|
||||
if (condition == null) {
|
||||
throw new IllegalStateException("Factory " + providerFactory.getClass() + " returned a null provider");
|
||||
}
|
||||
|
||||
return condition;
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,6 +31,10 @@ public class EventBasedResourcePolicyProvider implements ResourcePolicyProvider
|
||||
|
||||
@Override
|
||||
public boolean activateOnEvent(ResourcePolicyEvent event) {
|
||||
if (!supports(event.getResourceType())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<String> events = model.getConfig().getOrDefault("events", List.of());
|
||||
ResourceOperationType operation = event.getOperation();
|
||||
|
||||
@ -38,7 +42,34 @@ public class EventBasedResourcePolicyProvider implements ResourcePolicyProvider
|
||||
return false;
|
||||
}
|
||||
|
||||
List<String> conditions = model.getConfig().getOrDefault("conditions", List.of());
|
||||
return evaluate(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deactivateOnEvent(ResourcePolicyEvent event) {
|
||||
if (!supports(event.getResourceType())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean resetOnEvent(ResourcePolicyEvent event) {
|
||||
if (!supports(event.getResourceType())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
protected boolean evaluate(ResourcePolicyEvent event) {
|
||||
List<String> conditions = getModel().getConfig().getOrDefault("conditions", List.of());
|
||||
|
||||
for (String providerId : conditions) {
|
||||
ResourcePolicyConditionProvider condition = resolveCondition(providerId);
|
||||
@ -51,22 +82,7 @@ public class EventBasedResourcePolicyProvider implements ResourcePolicyProvider
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deactivateOnEvent(ResourcePolicyEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean resetOnEvent(ResourcePolicyEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
private ResourcePolicyConditionProvider resolveCondition(String providerId) {
|
||||
protected ResourcePolicyConditionProvider resolveCondition(String providerId) {
|
||||
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
|
||||
ResourcePolicyConditionProviderFactory<ResourcePolicyConditionProvider> providerFactory = (ResourcePolicyConditionProviderFactory<ResourcePolicyConditionProvider>) sessionFactory.getProviderFactory(ResourcePolicyConditionProvider.class, providerId);
|
||||
|
||||
@ -90,4 +106,12 @@ public class EventBasedResourcePolicyProvider implements ResourcePolicyProvider
|
||||
|
||||
return condition;
|
||||
}
|
||||
|
||||
protected ComponentModel getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
protected KeycloakSession getSession() {
|
||||
return session;
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,6 +34,7 @@ import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.policy.DeleteUserActionProviderFactory;
|
||||
import org.keycloak.models.policy.ResourceOperationType;
|
||||
import org.keycloak.models.policy.ResourcePolicy;
|
||||
import org.keycloak.models.policy.ResourcePolicyManager;
|
||||
import org.keycloak.models.policy.ResourcePolicyStateProvider;
|
||||
@ -119,6 +120,7 @@ public class BrokeredUserSessionRefreshTimePolicyTest {
|
||||
public void tesRunActionOnFederatedUser() {
|
||||
consumerRealm.admin().resources().policies().create(ResourcePolicyRepresentation.create()
|
||||
.of(UserSessionRefreshTimeResourcePolicyProviderFactory.ID)
|
||||
.onEvent(ResourceOperationType.LOGIN.toString())
|
||||
.onCoditions(ResourcePolicyConditionRepresentation.create()
|
||||
.of(IdentityProviderPolicyConditionFactory.ID)
|
||||
.withConfig(IdentityProviderPolicyConditionFactory.EXPECTED_ALIASES, IDP_OIDC_ALIAS)
|
||||
@ -189,6 +191,7 @@ public class BrokeredUserSessionRefreshTimePolicyTest {
|
||||
public void testAddRemoveFedIdentityAffectsPolicyAssociation() {
|
||||
consumerRealm.admin().resources().policies().create(ResourcePolicyRepresentation.create()
|
||||
.of(UserSessionRefreshTimeResourcePolicyProviderFactory.ID)
|
||||
.onEvent(ResourceOperationType.ADD_FEDERATED_IDENTITY.toString())
|
||||
.onCoditions(ResourcePolicyConditionRepresentation.create()
|
||||
.of(IdentityProviderPolicyConditionFactory.ID)
|
||||
.withConfig(IdentityProviderPolicyConditionFactory.EXPECTED_ALIASES, IDP_OIDC_ALIAS)
|
||||
|
||||
@ -196,6 +196,7 @@ public class ResourcePolicyManagementTest {
|
||||
public void testPolicyDoesNotFallThroughActionsInSingleRun() {
|
||||
managedRealm.admin().resources().policies().create(ResourcePolicyRepresentation.create()
|
||||
.of(UserCreationTimeResourcePolicyProviderFactory.ID)
|
||||
.onEvent(ResourceOperationType.CREATE.toString())
|
||||
.withActions(
|
||||
ResourcePolicyActionRepresentation.create().of(NotifyUserActionProviderFactory.ID)
|
||||
.after(Duration.ofDays(5))
|
||||
@ -360,6 +361,7 @@ public class ResourcePolicyManagementTest {
|
||||
// create a test policy
|
||||
managedRealm.admin().resources().policies().create(ResourcePolicyRepresentation.create()
|
||||
.of(UserCreationTimeResourcePolicyProviderFactory.ID)
|
||||
.onEvent(ResourceOperationType.CREATE.toString())
|
||||
.name("test-policy")
|
||||
.withConfig("enabled", "true")
|
||||
.withActions(
|
||||
@ -467,6 +469,7 @@ public class ResourcePolicyManagementTest {
|
||||
public void testRecurringPolicy() {
|
||||
managedRealm.admin().resources().policies().create(ResourcePolicyRepresentation.create()
|
||||
.of(UserCreationTimeResourcePolicyProviderFactory.ID)
|
||||
.onEvent(ResourceOperationType.CREATE.toString())
|
||||
.withConfig("recurring", "true")
|
||||
.withActions(
|
||||
ResourcePolicyActionRepresentation.create().of(NotifyUserActionProviderFactory.ID)
|
||||
|
||||
@ -7,6 +7,7 @@ import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.policy.DisableUserActionProviderFactory;
|
||||
import org.keycloak.models.policy.NotifyUserActionProviderFactory;
|
||||
import org.keycloak.models.policy.ResourceOperationType;
|
||||
import org.keycloak.models.policy.ResourcePolicyManager;
|
||||
import org.keycloak.models.policy.UserCreationTimeResourcePolicyProviderFactory;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
@ -57,6 +58,7 @@ public class UserCreationTimePolicyTest {
|
||||
public void testDisableUserBasedOnCreationDate() {
|
||||
managedRealm.admin().resources().policies().create(ResourcePolicyRepresentation.create()
|
||||
.of(UserCreationTimeResourcePolicyProviderFactory.ID)
|
||||
.onEvent(ResourceOperationType.CREATE.toString())
|
||||
.withActions(
|
||||
ResourcePolicyActionRepresentation.create().of(NotifyUserActionProviderFactory.ID)
|
||||
.after(Duration.ofDays(5))
|
||||
|
||||
@ -33,6 +33,7 @@ import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserProvider;
|
||||
import org.keycloak.models.policy.DisableUserActionProviderFactory;
|
||||
import org.keycloak.models.policy.NotifyUserActionProviderFactory;
|
||||
import org.keycloak.models.policy.ResourceOperationType;
|
||||
import org.keycloak.models.policy.ResourcePolicyManager;
|
||||
import org.keycloak.models.policy.UserSessionRefreshTimeResourcePolicyProviderFactory;
|
||||
import org.keycloak.representations.resources.policies.ResourcePolicyActionRepresentation;
|
||||
@ -86,6 +87,7 @@ public class UserSessionRefreshTimePolicyTest {
|
||||
public void testDisabledUserAfterInactivityPeriod() {
|
||||
managedRealm.admin().resources().policies().create(ResourcePolicyRepresentation.create()
|
||||
.of(UserSessionRefreshTimeResourcePolicyProviderFactory.ID)
|
||||
.onEvent(ResourceOperationType.LOGIN.toString())
|
||||
.withActions(
|
||||
ResourcePolicyActionRepresentation.create().of(NotifyUserActionProviderFactory.ID)
|
||||
.after(Duration.ofDays(5))
|
||||
@ -164,12 +166,14 @@ public class UserSessionRefreshTimePolicyTest {
|
||||
public void testMultiplePolicies() {
|
||||
managedRealm.admin().resources().policies().create(ResourcePolicyRepresentation.create()
|
||||
.of(UserSessionRefreshTimeResourcePolicyProviderFactory.ID)
|
||||
.onEvent(ResourceOperationType.LOGIN.toString())
|
||||
.withActions(
|
||||
ResourcePolicyActionRepresentation.create().of(NotifyUserActionProviderFactory.ID)
|
||||
.after(Duration.ofDays(5))
|
||||
.withConfig("message_key", "notifier1")
|
||||
.build()
|
||||
).of(UserSessionRefreshTimeResourcePolicyProviderFactory.ID)
|
||||
.onEvent(ResourceOperationType.LOGIN.toString())
|
||||
.withActions(
|
||||
ResourcePolicyActionRepresentation.create().of(NotifyUserActionProviderFactory.ID)
|
||||
.after(Duration.ofDays(10))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user