Add HotRod store for authorization services

Closes #9679
This commit is contained in:
Michal Hajas 2022-02-01 13:43:33 +01:00 committed by Hynek Mlnařík
parent c554a72b18
commit 6b5c417742
21 changed files with 869 additions and 4 deletions

View file

@ -19,6 +19,11 @@ package org.keycloak.models.map.storage.hotRod;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.authorization.model.PermissionTicket;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope;
import org.keycloak.common.Profile;
import org.keycloak.component.AmphibianProviderFactory;
import org.keycloak.models.AuthenticatedClientSessionModel;
@ -32,6 +37,11 @@ import org.keycloak.models.RoleModel;
import org.keycloak.models.UserLoginFailureModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity;
import org.keycloak.models.map.authorization.entity.MapPolicyEntity;
import org.keycloak.models.map.authorization.entity.MapResourceEntity;
import org.keycloak.models.map.authorization.entity.MapResourceServerEntity;
import org.keycloak.models.map.authorization.entity.MapScopeEntity;
import org.keycloak.models.map.authSession.MapAuthenticationSessionEntity;
import org.keycloak.models.map.authSession.MapRootAuthenticationSessionEntity;
import org.keycloak.models.map.clientscope.MapClientScopeEntity;
@ -53,6 +63,16 @@ import org.keycloak.models.map.role.MapRoleEntity;
import org.keycloak.models.map.storage.hotRod.authSession.HotRodAuthenticationSessionEntityDelegate;
import org.keycloak.models.map.storage.hotRod.authSession.HotRodRootAuthenticationSessionEntity;
import org.keycloak.models.map.storage.hotRod.authSession.HotRodRootAuthenticationSessionEntityDelegate;
import org.keycloak.models.map.storage.hotRod.authorization.HotRodPermissionTicketEntity;
import org.keycloak.models.map.storage.hotRod.authorization.HotRodPermissionTicketEntityDelegate;
import org.keycloak.models.map.storage.hotRod.authorization.HotRodPolicyEntity;
import org.keycloak.models.map.storage.hotRod.authorization.HotRodPolicyEntityDelegate;
import org.keycloak.models.map.storage.hotRod.authorization.HotRodResourceEntity;
import org.keycloak.models.map.storage.hotRod.authorization.HotRodResourceEntityDelegate;
import org.keycloak.models.map.storage.hotRod.authorization.HotRodResourceServerEntity;
import org.keycloak.models.map.storage.hotRod.authorization.HotRodResourceServerEntityDelegate;
import org.keycloak.models.map.storage.hotRod.authorization.HotRodScopeEntity;
import org.keycloak.models.map.storage.hotRod.authorization.HotRodScopeEntityDelegate;
import org.keycloak.models.map.storage.hotRod.loginFailure.HotRodUserLoginFailureEntity;
import org.keycloak.models.map.storage.hotRod.loginFailure.HotRodUserLoginFailureEntityDelegate;
import org.keycloak.models.map.storage.hotRod.role.HotRodRoleEntity;
@ -113,15 +133,21 @@ public class HotRodMapStorageProviderFactory implements AmphibianProviderFactory
private final static DeepCloner CLONER = new DeepCloner.Builder()
.constructor(MapRootAuthenticationSessionEntity.class, HotRodRootAuthenticationSessionEntityDelegate::new)
.constructor(MapAuthenticationSessionEntity.class, HotRodAuthenticationSessionEntityDelegate::new)
.constructor(MapClientEntity.class, HotRodClientEntityDelegate::new)
.constructor(MapProtocolMapperEntity.class, HotRodProtocolMapperEntityDelegate::new)
.constructor(MapClientScopeEntity.class, HotRodClientScopeEntityDelegate::new)
.constructor(MapGroupEntity.class, HotRodGroupEntityDelegate::new)
.constructor(MapRoleEntity.class, HotRodRoleEntityDelegate::new)
.constructor(MapUserEntity.class, HotRodUserEntityDelegate::new)
.constructor(MapUserCredentialEntity.class, HotRodUserCredentialEntityDelegate::new)
.constructor(MapUserFederatedIdentityEntity.class, HotRodUserFederatedIdentityEntityDelegate::new)
.constructor(MapUserConsentEntity.class, HotRodUserConsentEntityDelegate::new)
.constructor(MapUserLoginFailureEntity.class, HotRodUserLoginFailureEntityDelegate::new)
.constructor(MapRealmEntity.class, HotRodRealmEntityDelegate::new)
@ -136,8 +162,16 @@ public class HotRodMapStorageProviderFactory implements AmphibianProviderFactory
.constructor(MapRequiredActionProviderEntity.class, HotRodRequiredActionProviderEntityDelegate::new)
.constructor(MapRequiredCredentialEntity.class, HotRodRequiredCredentialEntityDelegate::new)
.constructor(MapWebAuthnPolicyEntity.class, HotRodWebAuthnPolicyEntityDelegate::new)
.constructor(MapUserSessionEntity.class, HotRodUserSessionEntityDelegate::new)
.constructor(MapAuthenticatedClientSessionEntity.class, HotRodAuthenticatedClientSessionEntityDelegate::new)
.constructor(MapResourceServerEntity.class, HotRodResourceServerEntityDelegate::new)
.constructor(MapResourceEntity.class, HotRodResourceEntityDelegate::new)
.constructor(MapScopeEntity.class, HotRodScopeEntityDelegate::new)
.constructor(MapPolicyEntity.class, HotRodPolicyEntityDelegate::new)
.constructor(MapPermissionTicketEntity.class, HotRodPermissionTicketEntityDelegate::new)
.build();
public static final Map<Class<?>, HotRodEntityDescriptor<?, ?>> ENTITY_DESCRIPTOR_MAP = new HashMap<>();
@ -200,6 +234,57 @@ public class HotRodMapStorageProviderFactory implements AmphibianProviderFactory
new HotRodEntityDescriptor<>(AuthenticatedClientSessionModel.class,
HotRodAuthenticatedClientSessionEntity.class,
HotRodAuthenticatedClientSessionEntityDelegate::new));
// authz
ENTITY_DESCRIPTOR_MAP.put(ResourceServer.class,
new HotRodEntityDescriptor<HotRodResourceServerEntity, HotRodResourceServerEntityDelegate>(ResourceServer.class,
HotRodResourceServerEntity.class,
HotRodResourceServerEntityDelegate::new) {
@Override
public String getCacheName() {
return "authz";
}
});
ENTITY_DESCRIPTOR_MAP.put(Resource.class,
new HotRodEntityDescriptor<HotRodResourceEntity, HotRodResourceEntityDelegate>(Resource.class,
HotRodResourceEntity.class,
HotRodResourceEntityDelegate::new){
@Override
public String getCacheName() {
return "authz";
}
});
ENTITY_DESCRIPTOR_MAP.put(Scope.class,
new HotRodEntityDescriptor<HotRodScopeEntity, HotRodScopeEntityDelegate>(Scope.class,
HotRodScopeEntity.class,
HotRodScopeEntityDelegate::new){
@Override
public String getCacheName() {
return "authz";
}
});
ENTITY_DESCRIPTOR_MAP.put(Policy.class,
new HotRodEntityDescriptor<HotRodPolicyEntity, HotRodPolicyEntityDelegate>(Policy.class,
HotRodPolicyEntity.class,
HotRodPolicyEntityDelegate::new){
@Override
public String getCacheName() {
return "authz";
}
});
ENTITY_DESCRIPTOR_MAP.put(PermissionTicket.class,
new HotRodEntityDescriptor<HotRodPermissionTicketEntity, HotRodPermissionTicketEntityDelegate>(PermissionTicket.class,
HotRodPermissionTicketEntity.class,
HotRodPermissionTicketEntityDelegate::new){
@Override
public String getCacheName() {
return "authz";
}
});
}
@Override

View file

@ -17,6 +17,8 @@
package org.keycloak.models.map.storage.hotRod;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.GroupModel;
@ -78,6 +80,13 @@ public class IckleQueryMapModelCriteriaBuilder<E extends AbstractHotRodEntity, M
INFINISPAN_NAME_OVERRIDES.put(UserSessionModel.SearchableFields.IS_OFFLINE, "offline");
INFINISPAN_NAME_OVERRIDES.put(UserSessionModel.SearchableFields.CLIENT_ID, "authenticatedClientSessions.key");
INFINISPAN_NAME_OVERRIDES.put(AuthenticatedClientSessionModel.SearchableFields.IS_OFFLINE, "offline");
INFINISPAN_NAME_OVERRIDES.put(Resource.SearchableFields.SCOPE_ID, "scopeIds");
INFINISPAN_NAME_OVERRIDES.put(Policy.SearchableFields.RESOURCE_ID, "resourceIds");
INFINISPAN_NAME_OVERRIDES.put(Policy.SearchableFields.SCOPE_ID, "scopeIds");
INFINISPAN_NAME_OVERRIDES.put(Policy.SearchableFields.ASSOCIATED_POLICY_ID, "associatedPolicyIds");
INFINISPAN_NAME_OVERRIDES.put(Policy.SearchableFields.CONFIG, "configs");
}
static {
@ -86,6 +95,8 @@ public class IckleQueryMapModelCriteriaBuilder<E extends AbstractHotRodEntity, M
ANALYZED_MODEL_FIELDS.add(UserModel.SearchableFields.FIRST_NAME);
ANALYZED_MODEL_FIELDS.add(UserModel.SearchableFields.LAST_NAME);
ANALYZED_MODEL_FIELDS.add(UserModel.SearchableFields.EMAIL);
ANALYZED_MODEL_FIELDS.add(Policy.SearchableFields.TYPE);
ANALYZED_MODEL_FIELDS.add(Resource.SearchableFields.TYPE);
}
public IckleQueryMapModelCriteriaBuilder(Class<E> hotRodEntityClass, StringBuilder whereClauseBuilder, Map<String, Object> parameters) {

View file

@ -17,6 +17,7 @@
package org.keycloak.models.map.storage.hotRod;
import org.keycloak.authorization.model.Policy;
import org.keycloak.models.ClientModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
@ -56,6 +57,7 @@ public class IckleQueryWhereClauses {
WHERE_CLAUSE_PRODUCER_OVERRIDES.put(UserModel.SearchableFields.IDP_AND_USER, IckleQueryWhereClauses::whereClauseForUserIdpAlias);
WHERE_CLAUSE_PRODUCER_OVERRIDES.put(UserModel.SearchableFields.CONSENT_CLIENT_FEDERATION_LINK, IckleQueryWhereClauses::whereClauseForConsentClientFederationLink);
WHERE_CLAUSE_PRODUCER_OVERRIDES.put(UserSessionModel.SearchableFields.CORRESPONDING_SESSION_ID, IckleQueryWhereClauses::whereClauseForCorrespondingSessionId);
WHERE_CLAUSE_PRODUCER_OVERRIDES.put(Policy.SearchableFields.CONFIG, IckleQueryWhereClauses::whereClauseForPolicyConfig);
}
@FunctionalInterface
@ -172,4 +174,34 @@ public class IckleQueryWhereClauses {
return "(" + nameClause + ")" + " AND " + "(" + valueClause + ")";
}
private static String whereClauseForPolicyConfig(String modelFieldName, ModelCriteriaBuilder.Operator op, Object[] values, Map<String, Object> parameters) {
if (values == null || values.length == 0) {
throw new CriterionNotSupportedException(Policy.SearchableFields.CONFIG, op, "Invalid arguments, expected (config_name, config_value_operator_arguments), got: " + Arrays.toString(values));
}
final Object attrName = values[0];
if (!(attrName instanceof String)) {
throw new CriterionNotSupportedException(Policy.SearchableFields.CONFIG, op, "Invalid arguments, expected (String config_name), got: " + Arrays.toString(values));
}
String attrNameS = (String) attrName;
Object[] realValues = new Object[values.length - 1];
System.arraycopy(values, 1, realValues, 0, values.length - 1);
boolean isNotExists = op.equals(ModelCriteriaBuilder.Operator.NOT_EXISTS);
if (isNotExists || op.equals(ModelCriteriaBuilder.Operator.EXISTS)) {
ModelCriteriaBuilder.Operator o = isNotExists ? ModelCriteriaBuilder.Operator.NE : ModelCriteriaBuilder.Operator.EQ;
return IckleQueryOperators.combineExpressions(o, modelFieldName + ".key", new String[] { attrNameS }, parameters);
}
String nameClause = IckleQueryOperators.combineExpressions(ModelCriteriaBuilder.Operator.EQ, modelFieldName + ".key", new String[] { attrNameS }, parameters);
if (realValues.length == 0) {
return nameClause;
}
String valueClause = IckleQueryOperators.combineExpressions(op, modelFieldName + ".value", realValues, parameters);
return "(" + nameClause + ")" + " AND " + "(" + valueClause + ")";
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright 2022 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.models.map.storage.hotRod.authorization;
import org.infinispan.protostream.annotations.ProtoEnumValue;
public enum HotRodDecisionStrategy {
/**
* Defines that at least one policy must evaluate to a positive decision in order to the overall decision be also positive.
*/
@ProtoEnumValue(number = 0)
AFFIRMATIVE,
/**
* Defines that all policies must evaluate to a positive decision in order to the overall decision be also positive.
*/
@ProtoEnumValue(number = 1)
UNANIMOUS,
/**
* Defines that the number of positive decisions must be greater than the number of negative decisions. If the number of positive and negative is the same,
* the final decision will be negative.
*/
@ProtoEnumValue(number = 2)
CONSENSUS
}

View file

@ -0,0 +1,34 @@
/*
* Copyright 2022 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.models.map.storage.hotRod.authorization;
import org.infinispan.protostream.annotations.ProtoEnumValue;
public enum HotRodLogic {
/**
* Defines that this policy follows a positive logic. In other words, the final decision is the policy outcome.
*/
@ProtoEnumValue(number = 0)
POSITIVE,
/**
* Defines that this policy uses a logical negation. In other words, the final decision would be a negative of the policy outcome.
*/
@ProtoEnumValue(number = 1)
NEGATIVE,
}

View file

@ -0,0 +1,102 @@
/*
* Copyright 2022 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.models.map.storage.hotRod.authorization;
import org.infinispan.protostream.annotations.ProtoDoc;
import org.infinispan.protostream.annotations.ProtoField;
import org.keycloak.models.map.annotations.GenerateHotRodEntityImplementation;
import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity;
import org.keycloak.models.map.storage.hotRod.common.AbstractHotRodEntity;
import org.keycloak.models.map.storage.hotRod.common.UpdatableHotRodEntityDelegateImpl;
@GenerateHotRodEntityImplementation(
implementInterface = "org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity",
inherits = "org.keycloak.models.map.storage.hotRod.authorization.HotRodPermissionTicketEntity.AbstractHotRodPermissionTicketEntity"
)
@ProtoDoc("@Indexed")
public class HotRodPermissionTicketEntity extends AbstractHotRodEntity {
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
@ProtoField(number = 1, required = true)
public int entityVersion = 1;
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
@ProtoField(number = 2)
public String id;
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
@ProtoField(number = 3)
public String realmId;
@ProtoField(number = 4)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
public String owner;
@ProtoField(number = 5)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
public String requester;
@ProtoField(number = 6)
public Long createdTimestamp;
@ProtoField(number = 7)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
public Long grantedTimestamp;
@ProtoField(number = 8)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
public String resourceId;
@ProtoField(number = 9)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
public String scopeId;
@ProtoField(number = 10)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
public String resourceServerId;
@ProtoField(number = 11)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
public String policyId;
public static abstract class AbstractHotRodPermissionTicketEntity extends UpdatableHotRodEntityDelegateImpl<HotRodPermissionTicketEntity> implements MapPermissionTicketEntity {
@Override
public String getId() {
return getHotRodEntity().id;
}
@Override
public void setId(String id) {
HotRodPermissionTicketEntity entity = getHotRodEntity();
if (entity.id != null) throw new IllegalStateException("Id cannot be changed");
entity.id = id;
entity.updated |= id != null;
}
}
@Override
public boolean equals(Object o) {
return HotRodPermissionTicketEntityDelegate.entityEquals(this, o);
}
@Override
public int hashCode() {
return HotRodPermissionTicketEntityDelegate.entityHashCode(this);
}
}

View file

@ -0,0 +1,40 @@
/*
* Copyright 2022 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.models.map.storage.hotRod.authorization;
import org.infinispan.protostream.annotations.ProtoEnumValue;
public enum HotRodPolicyEnforcementMode {
/**
* Requests are denied by default even when there is no policy associated with a given resource.
*/
@ProtoEnumValue(number = 0)
ENFORCING,
/**
* Requests are allowed even when there is no policy associated with a given resource.
*/
@ProtoEnumValue(number = 1)
PERMISSIVE,
/**
* Completely disables the evaluation of policies and allow access to any resource.
*/
@ProtoEnumValue(number = 2)
DISABLED
}

View file

@ -0,0 +1,128 @@
/*
* Copyright 2022 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.models.map.storage.hotRod.authorization;
import org.infinispan.protostream.annotations.ProtoDoc;
import org.infinispan.protostream.annotations.ProtoField;
import org.keycloak.models.map.annotations.GenerateHotRodEntityImplementation;
import org.keycloak.models.map.authorization.entity.MapPolicyEntity;
import org.keycloak.models.map.storage.hotRod.common.AbstractHotRodEntity;
import org.keycloak.models.map.storage.hotRod.common.HotRodStringPair;
import org.keycloak.models.map.storage.hotRod.common.UpdatableHotRodEntityDelegateImpl;
import java.util.Objects;
import java.util.Set;
@GenerateHotRodEntityImplementation(
implementInterface = "org.keycloak.models.map.authorization.entity.MapPolicyEntity",
inherits = "org.keycloak.models.map.storage.hotRod.authorization.HotRodPolicyEntity.AbstractHotRodPolicyEntity"
)
@ProtoDoc("@Indexed")
public class HotRodPolicyEntity extends AbstractHotRodEntity {
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
@ProtoField(number = 1, required = true)
public int entityVersion = 1;
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
@ProtoField(number = 2)
public String id;
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
@ProtoField(number = 3)
public String realmId;
@ProtoField(number = 4)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
public String name;
@ProtoField(number = 5)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
public String nameLowercase;
@ProtoField(number = 6)
public String description;
@ProtoField(number = 7)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES, analyze = Analyze.YES, analyzer = @Analyzer(definition = \"filename\"))")
public String type;
@ProtoField(number = 8)
public HotRodDecisionStrategy decisionStrategy;
@ProtoField(number = 9)
public HotRodLogic logic;
@ProtoField(number = 10)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
public Set<HotRodStringPair> configs;
@ProtoField(number = 11)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
public String resourceServerId;
@ProtoField(number = 12)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
public Set<String> associatedPolicyIds;
@ProtoField(number = 13)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
public Set<String> resourceIds;
@ProtoField(number = 14)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
public Set<String> scopeIds;
@ProtoField(number = 15)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
public String owner;
public static abstract class AbstractHotRodPolicyEntity extends UpdatableHotRodEntityDelegateImpl<HotRodPolicyEntity> implements MapPolicyEntity {
@Override
public String getId() {
return getHotRodEntity().id;
}
@Override
public void setId(String id) {
HotRodPolicyEntity entity = getHotRodEntity();
if (entity.id != null) throw new IllegalStateException("Id cannot be changed");
entity.id = id;
entity.updated |= id != null;
}
@Override
public void setName(String name) {
HotRodPolicyEntity entity = getHotRodEntity();
entity.updated |= ! Objects.equals(entity.name, name);
entity.name = name;
entity.nameLowercase = name == null ? null : name.toLowerCase();
}
}
@Override
public boolean equals(Object o) {
return HotRodPolicyEntityDelegate.entityEquals(this, o);
}
@Override
public int hashCode() {
return HotRodPolicyEntityDelegate.entityHashCode(this);
}
}

View file

@ -0,0 +1,124 @@
/*
* Copyright 2022 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.models.map.storage.hotRod.authorization;
import org.infinispan.protostream.annotations.ProtoDoc;
import org.infinispan.protostream.annotations.ProtoField;
import org.keycloak.models.map.annotations.GenerateHotRodEntityImplementation;
import org.keycloak.models.map.authorization.entity.MapResourceEntity;
import org.keycloak.models.map.storage.hotRod.common.AbstractHotRodEntity;
import org.keycloak.models.map.storage.hotRod.common.HotRodAttributeEntityNonIndexed;
import org.keycloak.models.map.storage.hotRod.common.UpdatableHotRodEntityDelegateImpl;
import java.util.Objects;
import java.util.Set;
@GenerateHotRodEntityImplementation(
implementInterface = "org.keycloak.models.map.authorization.entity.MapResourceEntity",
inherits = "org.keycloak.models.map.storage.hotRod.authorization.HotRodResourceEntity.AbstractHotRodResourceEntity"
)
@ProtoDoc("@Indexed")
public class HotRodResourceEntity extends AbstractHotRodEntity {
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
@ProtoField(number = 1, required = true)
public int entityVersion = 1;
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
@ProtoField(number = 2)
public String id;
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
@ProtoField(number = 3)
public String realmId;
@ProtoField(number = 4)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
public String name;
@ProtoField(number = 5)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
public String nameLowercase;
@ProtoField(number = 6)
public String displayName;
@ProtoField(number = 7)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
public Set<String> uris;
@ProtoField(number = 8)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES, analyze = Analyze.YES, analyzer = @Analyzer(definition = \"filename\"))")
public String type;
@ProtoField(number = 9)
public String iconUri;
@ProtoField(number = 10)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
public String owner;
@ProtoField(number = 11)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
public Boolean ownerManagedAccess;
@ProtoField(number = 12)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
public String resourceServerId;
@ProtoField(number = 13)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
public Set<String> scopeIds;
@ProtoField(number = 14)
public Set<HotRodAttributeEntityNonIndexed> attributes;
public static abstract class AbstractHotRodResourceEntity extends UpdatableHotRodEntityDelegateImpl<HotRodResourceEntity> implements MapResourceEntity {
@Override
public String getId() {
return getHotRodEntity().id;
}
@Override
public void setId(String id) {
HotRodResourceEntity entity = getHotRodEntity();
if (entity.id != null) throw new IllegalStateException("Id cannot be changed");
entity.id = id;
entity.updated |= id != null;
}
@Override
public void setName(String name) {
HotRodResourceEntity entity = getHotRodEntity();
entity.updated |= ! Objects.equals(entity.name, name);
entity.name = name;
entity.nameLowercase = name == null ? null : name.toLowerCase();
}
}
@Override
public boolean equals(Object o) {
return HotRodResourceEntityDelegate.entityEquals(this, o);
}
@Override
public int hashCode() {
return HotRodResourceEntityDelegate.entityHashCode(this);
}
}

View file

@ -0,0 +1,84 @@
/*
* Copyright 2022 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.models.map.storage.hotRod.authorization;
import org.infinispan.protostream.annotations.ProtoDoc;
import org.infinispan.protostream.annotations.ProtoField;
import org.keycloak.models.map.annotations.GenerateHotRodEntityImplementation;
import org.keycloak.models.map.authorization.entity.MapResourceServerEntity;
import org.keycloak.models.map.storage.hotRod.common.AbstractHotRodEntity;
import org.keycloak.models.map.storage.hotRod.common.UpdatableHotRodEntityDelegateImpl;
@GenerateHotRodEntityImplementation(
implementInterface = "org.keycloak.models.map.authorization.entity.MapResourceServerEntity",
inherits = "org.keycloak.models.map.storage.hotRod.authorization.HotRodResourceServerEntity.AbstractHotRodResourceServerEntity"
)
public class HotRodResourceServerEntity extends AbstractHotRodEntity {
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
@ProtoField(number = 1, required = true)
public int entityVersion = 1;
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
@ProtoField(number = 2)
public String id;
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
@ProtoField(number = 3)
public String realmId;
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
@ProtoField(number = 4)
public String clientId;
@ProtoField(number = 5)
public Boolean allowRemoteResourceManagement;
@ProtoField(number = 6)
public HotRodPolicyEnforcementMode policyEnforcementMode;
@ProtoField(number = 7)
public HotRodDecisionStrategy decisionStrategy;
public static abstract class AbstractHotRodResourceServerEntity extends UpdatableHotRodEntityDelegateImpl<HotRodResourceServerEntity> implements MapResourceServerEntity {
@Override
public String getId() {
return getHotRodEntity().id;
}
@Override
public void setId(String id) {
HotRodResourceServerEntity entity = getHotRodEntity();
if (entity.id != null) throw new IllegalStateException("Id cannot be changed");
entity.id = id;
entity.updated |= id != null;
}
}
@Override
public boolean equals(Object o) {
return HotRodResourceServerEntityDelegate.entityEquals(this, o);
}
@Override
public int hashCode() {
return HotRodResourceServerEntityDelegate.entityHashCode(this);
}
}

View file

@ -0,0 +1,99 @@
/*
* Copyright 2022 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.models.map.storage.hotRod.authorization;
import org.infinispan.protostream.annotations.ProtoDoc;
import org.infinispan.protostream.annotations.ProtoField;
import org.keycloak.models.map.annotations.GenerateHotRodEntityImplementation;
import org.keycloak.models.map.authorization.entity.MapScopeEntity;
import org.keycloak.models.map.storage.hotRod.common.AbstractHotRodEntity;
import org.keycloak.models.map.storage.hotRod.common.UpdatableHotRodEntityDelegateImpl;
import java.util.Objects;
@GenerateHotRodEntityImplementation(
implementInterface = "org.keycloak.models.map.authorization.entity.MapScopeEntity",
inherits = "org.keycloak.models.map.storage.hotRod.authorization.HotRodScopeEntity.AbstractHotRodScopeEntity"
)
@ProtoDoc("@Indexed")
public class HotRodScopeEntity extends AbstractHotRodEntity {
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
@ProtoField(number = 1, required = true)
public int entityVersion = 1;
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
@ProtoField(number = 2)
public String id;
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
@ProtoField(number = 3)
public String realmId;
@ProtoField(number = 4)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
public String name;
@ProtoField(number = 5)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
public String nameLowercase;
@ProtoField(number = 6)
public String displayName;
@ProtoField(number = 7)
public String iconUri;
@ProtoField(number = 8)
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
public String resourceServerId;
public static abstract class AbstractHotRodScopeEntity extends UpdatableHotRodEntityDelegateImpl<HotRodScopeEntity> implements MapScopeEntity {
@Override
public String getId() {
return getHotRodEntity().id;
}
@Override
public void setId(String id) {
HotRodScopeEntity entity = getHotRodEntity();
if (entity.id != null) throw new IllegalStateException("Id cannot be changed");
entity.id = id;
entity.updated |= id != null;
}
@Override
public void setName(String name) {
HotRodScopeEntity entity = getHotRodEntity();
entity.updated |= ! Objects.equals(entity.name, name);
entity.name = name;
entity.nameLowercase = name == null ? null : name.toLowerCase();
}
}
@Override
public boolean equals(Object o) {
return HotRodScopeEntityDelegate.entityEquals(this, o);
}
@Override
public int hashCode() {
return HotRodScopeEntityDelegate.entityHashCode(this);
}
}

View file

@ -21,11 +21,17 @@ import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.map.common.AbstractEntity;
import org.keycloak.models.map.storage.hotRod.authSession.HotRodAuthenticationSessionEntity;
import org.keycloak.models.map.storage.hotRod.authorization.HotRodDecisionStrategy;
import org.keycloak.models.map.storage.hotRod.authorization.HotRodLogic;
import org.keycloak.models.map.storage.hotRod.authorization.HotRodPolicyEnforcementMode;
import org.keycloak.models.map.storage.hotRod.realm.entity.HotRodLocalizationTexts;
import org.keycloak.models.map.storage.hotRod.realm.entity.HotRodRequirement;
import org.keycloak.models.map.storage.hotRod.user.HotRodUserConsentEntity;
import org.keycloak.models.map.storage.hotRod.user.HotRodUserFederatedIdentityEntity;
import org.keycloak.models.map.storage.hotRod.userSession.HotRodSessionState;
import org.keycloak.representations.idm.authorization.DecisionStrategy;
import org.keycloak.representations.idm.authorization.Logic;
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
import java.util.HashMap;
import java.util.List;
@ -165,4 +171,28 @@ public class HotRodTypesUtils {
public static HotRodSessionState migrateStateToHotRodSessionState(UserSessionModel.State state) {
return HotRodSessionState.valueOf(state.name());
}
public static HotRodDecisionStrategy migrateDecisionStrategyToHotRodDecisionStrategy(DecisionStrategy p0) {
return p0 == null ? null : HotRodDecisionStrategy.values()[p0.ordinal()];
}
public static DecisionStrategy migrateHotRodDecisionStrategyToDecisionStrategy(HotRodDecisionStrategy p0) {
return p0 == null ? null : DecisionStrategy.values()[p0.ordinal()];
}
public static HotRodPolicyEnforcementMode migratePolicyEnforcementModeToHotRodPolicyEnforcementMode(PolicyEnforcementMode p0) {
return p0 == null ? null : HotRodPolicyEnforcementMode.values()[p0.ordinal()];
}
public static PolicyEnforcementMode migrateHotRodPolicyEnforcementModeToPolicyEnforcementMode(HotRodPolicyEnforcementMode p0) {
return p0 == null ? null : PolicyEnforcementMode.values()[p0.ordinal()];
}
public static HotRodLogic migrateLogicToHotRodLogic(Logic p0) {
return p0 == null ? null : HotRodLogic.values()[p0.ordinal()];
}
public static Logic migrateHotRodLogicToLogic(HotRodLogic p0) {
return p0 == null ? null : Logic.values()[p0.ordinal()];
}
}

View file

@ -22,6 +22,14 @@ import org.infinispan.protostream.annotations.AutoProtoSchemaBuilder;
import org.keycloak.models.map.storage.hotRod.authSession.HotRodAuthenticationSessionEntity;
import org.keycloak.models.map.storage.hotRod.authSession.HotRodExecutionStatus;
import org.keycloak.models.map.storage.hotRod.authSession.HotRodRootAuthenticationSessionEntity;
import org.keycloak.models.map.storage.hotRod.authorization.HotRodDecisionStrategy;
import org.keycloak.models.map.storage.hotRod.authorization.HotRodLogic;
import org.keycloak.models.map.storage.hotRod.authorization.HotRodPermissionTicketEntity;
import org.keycloak.models.map.storage.hotRod.authorization.HotRodPolicyEnforcementMode;
import org.keycloak.models.map.storage.hotRod.authorization.HotRodPolicyEntity;
import org.keycloak.models.map.storage.hotRod.authorization.HotRodResourceEntity;
import org.keycloak.models.map.storage.hotRod.authorization.HotRodResourceServerEntity;
import org.keycloak.models.map.storage.hotRod.authorization.HotRodScopeEntity;
import org.keycloak.models.map.storage.hotRod.client.HotRodClientEntity;
import org.keycloak.models.map.storage.hotRod.client.HotRodProtocolMapperEntity;
import org.keycloak.models.map.storage.hotRod.clientscope.HotRodClientScopeEntity;
@ -105,6 +113,16 @@ import org.keycloak.models.map.storage.hotRod.userSession.HotRodUserSessionEntit
// Client sessions
HotRodAuthenticatedClientSessionEntity.class,
// Authz
HotRodResourceServerEntity.class,
HotRodResourceEntity.class,
HotRodScopeEntity.class,
HotRodPolicyEntity.class,
HotRodPermissionTicketEntity.class,
HotRodDecisionStrategy.class,
HotRodLogic.class,
HotRodPolicyEnforcementMode.class,
// Common
HotRodPair.class,
HotRodStringPair.class,

View file

@ -88,5 +88,17 @@
</indexing>
<encoding media-type="application/x-protostream"/>
</distributed-cache>
<distributed-cache name="authz" mode="SYNC">
<encoding media-type="application/x-protostream"/>
<indexing>
<indexed-entities>
<indexed-entity>kc.HotRodResourceEntity</indexed-entity>
<indexed-entity>kc.HotRodScopeEntity</indexed-entity>
<indexed-entity>kc.HotRodPolicyEntity</indexed-entity>
<indexed-entity>kc.HotRodPermissionTicketEntity</indexed-entity>
<indexed-entity>kc.HotRodStringPair</indexed-entity>
</indexed-entities>
</indexing>
</distributed-cache>
</cache-container>
</infinispan>

View file

@ -90,5 +90,17 @@
</indexing>
<encoding media-type="application/x-protostream"/>
</distributed-cache>
<distributed-cache name="authz" mode="SYNC">
<encoding media-type="application/x-protostream"/>
<indexing>
<indexed-entities>
<indexed-entity>kc.HotRodResourceEntity</indexed-entity>
<indexed-entity>kc.HotRodScopeEntity</indexed-entity>
<indexed-entity>kc.HotRodPolicyEntity</indexed-entity>
<indexed-entity>kc.HotRodPermissionTicketEntity</indexed-entity>
<indexed-entity>kc.HotRodStringPair</indexed-entity>
</indexed-entities>
</indexing>
</distributed-cache>
</cache-container>
</infinispan>

View file

@ -107,7 +107,7 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
// @UniqueConstraint(columnNames = {"OWNER", "REQUESTER", "RESOURCE_SERVER_ID", "RESOURCE_ID", "SCOPE_ID"})
DefaultModelCriteria<PermissionTicket> mcb = forResourceServer(resourceServer)
.compare(SearchableFields.OWNER, Operator.EQ, owner)
.compare(SearchableFields.RESOURCE_ID, Operator.EQ, resource)
.compare(SearchableFields.RESOURCE_ID, Operator.EQ, resource.getId())
.compare(SearchableFields.REQUESTER, Operator.EQ, requester);
if (scope != null) {

View file

@ -32,7 +32,7 @@ public interface Scope {
public static final SearchableModelField<Scope> ID = new SearchableModelField<>("id", String.class);
public static final SearchableModelField<Scope> NAME = new SearchableModelField<>("name", String.class);
public static final SearchableModelField<Scope> RESOURCE_SERVER_ID = new SearchableModelField<>("resourceServerId", String.class);
public static final SearchableModelField<Scope> REALM_ID = new SearchableModelField<>("resourceServerId", String.class);
public static final SearchableModelField<Scope> REALM_ID = new SearchableModelField<>("realmId", String.class);
}
public static enum FilterOption {

View file

@ -264,7 +264,7 @@
"username": "${keycloak.connectionsHotRod.username:myuser}",
"password": "${keycloak.connectionsHotRod.password:qwer1234!}",
"enableSecurity": "${keycloak.connectionsHotRod.enableSecurity:true}",
"reindexCaches": "${keycloak.connectionsHotRod.reindexCaches:auth-sessions,clients,client-scopes,groups,users,user-login-failures,user-sessions,client-sessions,roles,realms}"
"reindexCaches": "${keycloak.connectionsHotRod.reindexCaches:auth-sessions,clients,client-scopes,groups,users,user-login-failures,user-sessions,client-sessions,roles,realms,authz}"
}
},

View file

@ -1505,6 +1505,7 @@
<keycloak.loginFailure.map.storage.provider>hotrod</keycloak.loginFailure.map.storage.provider>
<keycloak.realm.map.storage.provider>hotrod</keycloak.realm.map.storage.provider>
<keycloak.userSession.map.storage.provider>hotrod</keycloak.userSession.map.storage.provider>
<keycloak.authorization.map.storage.provider>hotrod</keycloak.authorization.map.storage.provider>
</systemPropertyVariables>
</configuration>
</plugin>

View file

@ -86,5 +86,17 @@
</indexing>
<encoding media-type="application/x-protostream"/>
</distributed-cache>
<distributed-cache name="authz" mode="SYNC">
<encoding media-type="application/x-protostream"/>
<indexing>
<indexed-entities>
<indexed-entity>kc.HotRodResourceEntity</indexed-entity>
<indexed-entity>kc.HotRodScopeEntity</indexed-entity>
<indexed-entity>kc.HotRodPolicyEntity</indexed-entity>
<indexed-entity>kc.HotRodPermissionTicketEntity</indexed-entity>
<indexed-entity>kc.HotRodStringPair</indexed-entity>
</indexed-entities>
</indexing>
</distributed-cache>
</cache-container>
</infinispan>

View file

@ -79,7 +79,7 @@ public class HotRodMapStorage extends KeycloakModelParameters {
.spi("realm").provider(MapRealmProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
.spi("role").provider(MapRoleProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
.spi(DeploymentStateSpi.NAME).provider(MapDeploymentStateProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.spi(StoreFactorySpi.NAME).provider(MapAuthorizationStoreFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
.spi(StoreFactorySpi.NAME).provider(MapAuthorizationStoreFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
.spi("user").provider(MapUserProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
.spi(UserSessionSpi.NAME).provider(MapUserSessionProviderFactory.PROVIDER_ID).config("storage-user-sessions.provider", HotRodMapStorageProviderFactory.PROVIDER_ID)
.config("storage-client-sessions.provider", HotRodMapStorageProviderFactory.PROVIDER_ID)