Convert MapUserEntity to interface
This commit is contained in:
parent
9b18688ce2
commit
9849df3757
14 changed files with 512 additions and 811 deletions
|
@ -44,6 +44,13 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.models.map.storage.MapStorageProvider;
|
import org.keycloak.models.map.storage.MapStorageProvider;
|
||||||
import org.keycloak.models.map.storage.MapStorageProviderFactory;
|
import org.keycloak.models.map.storage.MapStorageProviderFactory;
|
||||||
|
import org.keycloak.models.map.user.MapUserConsentEntity;
|
||||||
|
import org.keycloak.models.map.user.MapUserConsentEntityImpl;
|
||||||
|
import org.keycloak.models.map.user.MapUserCredentialEntity;
|
||||||
|
import org.keycloak.models.map.user.MapUserCredentialEntityImpl;
|
||||||
|
import org.keycloak.models.map.user.MapUserEntityImpl;
|
||||||
|
import org.keycloak.models.map.user.MapUserFederatedIdentityEntity;
|
||||||
|
import org.keycloak.models.map.user.MapUserFederatedIdentityEntityImpl;
|
||||||
import org.keycloak.models.map.storage.ModelEntityUtil;
|
import org.keycloak.models.map.storage.ModelEntityUtil;
|
||||||
import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
||||||
import org.keycloak.provider.ProviderConfigProperty;
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
|
@ -80,10 +87,14 @@ public class ConcurrentHashMapStorageProviderFactory implements AmphibianProvide
|
||||||
|
|
||||||
private final static DeepCloner CLONER = new DeepCloner.Builder()
|
private final static DeepCloner CLONER = new DeepCloner.Builder()
|
||||||
.genericCloner(Serialization::from)
|
.genericCloner(Serialization::from)
|
||||||
.constructor(MapClientEntityImpl.class, MapClientEntityImpl::new)
|
.constructor(MapClientEntityImpl.class, MapClientEntityImpl::new)
|
||||||
.constructor(MapProtocolMapperEntity.class, MapProtocolMapperEntityImpl::new)
|
.constructor(MapProtocolMapperEntity.class, MapProtocolMapperEntityImpl::new)
|
||||||
.constructor(MapGroupEntityImpl.class, MapGroupEntityImpl::new)
|
.constructor(MapGroupEntityImpl.class, MapGroupEntityImpl::new)
|
||||||
.constructor(MapRoleEntityImpl.class, MapRoleEntityImpl::new)
|
.constructor(MapRoleEntityImpl.class, MapRoleEntityImpl::new)
|
||||||
|
.constructor(MapUserEntityImpl.class, MapUserEntityImpl::new)
|
||||||
|
.constructor(MapUserCredentialEntityImpl.class, MapUserCredentialEntityImpl::new)
|
||||||
|
.constructor(MapUserFederatedIdentityEntityImpl.class, MapUserFederatedIdentityEntityImpl::new)
|
||||||
|
.constructor(MapUserConsentEntityImpl.class, MapUserConsentEntityImpl::new)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
private static final Map<String, StringKeyConvertor> KEY_CONVERTORS = new HashMap<>();
|
private static final Map<String, StringKeyConvertor> KEY_CONVERTORS = new HashMap<>();
|
||||||
|
|
|
@ -44,6 +44,7 @@ import org.keycloak.models.map.loginFailure.MapUserLoginFailureEntity;
|
||||||
import org.keycloak.models.map.realm.MapRealmEntity;
|
import org.keycloak.models.map.realm.MapRealmEntity;
|
||||||
import org.keycloak.models.map.role.MapRoleEntity;
|
import org.keycloak.models.map.role.MapRoleEntity;
|
||||||
import org.keycloak.models.map.storage.QueryParameters;
|
import org.keycloak.models.map.storage.QueryParameters;
|
||||||
|
import org.keycloak.models.map.user.MapUserConsentEntity;
|
||||||
import org.keycloak.storage.SearchableModelField;
|
import org.keycloak.storage.SearchableModelField;
|
||||||
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
@ -52,7 +53,6 @@ import java.util.Map;
|
||||||
import org.keycloak.models.map.storage.chm.MapModelCriteriaBuilder.UpdatePredicatesFunc;
|
import org.keycloak.models.map.storage.chm.MapModelCriteriaBuilder.UpdatePredicatesFunc;
|
||||||
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
|
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
|
||||||
import org.keycloak.models.map.user.MapUserEntity;
|
import org.keycloak.models.map.user.MapUserEntity;
|
||||||
import org.keycloak.models.map.user.UserConsentEntity;
|
|
||||||
import org.keycloak.models.map.userSession.MapAuthenticatedClientSessionEntity;
|
import org.keycloak.models.map.userSession.MapAuthenticatedClientSessionEntity;
|
||||||
import org.keycloak.models.map.userSession.MapUserSessionEntity;
|
import org.keycloak.models.map.userSession.MapUserSessionEntity;
|
||||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||||
|
@ -279,7 +279,7 @@ public class MapFieldPredicates {
|
||||||
String providerId = ensureEqSingleValue(UserModel.SearchableFields.CONSENT_CLIENT_FEDERATION_LINK, "provider_id", op, values);
|
String providerId = ensureEqSingleValue(UserModel.SearchableFields.CONSENT_CLIENT_FEDERATION_LINK, "provider_id", op, values);
|
||||||
String providerIdS = new StorageId((String) providerId, "").getId();
|
String providerIdS = new StorageId((String) providerId, "").getId();
|
||||||
Function<MapUserEntity, ?> getter;
|
Function<MapUserEntity, ?> getter;
|
||||||
getter = ue -> ue.getUserConsents().map(UserConsentEntity::getClientId).anyMatch(v -> v != null && v.startsWith(providerIdS));
|
getter = ue -> Optional.ofNullable(ue.getUserConsents()).orElseGet(Collections::emptyMap).values().stream().map(MapUserConsentEntity::getClientId).anyMatch(v -> v != null && v.startsWith(providerIdS));
|
||||||
|
|
||||||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||||
}
|
}
|
||||||
|
@ -330,7 +330,7 @@ public class MapFieldPredicates {
|
||||||
private static MapModelCriteriaBuilder<Object, MapUserEntity, UserModel> checkGrantedUserRole(MapModelCriteriaBuilder<Object, MapUserEntity, UserModel> mcb, Operator op, Object[] values) {
|
private static MapModelCriteriaBuilder<Object, MapUserEntity, UserModel> checkGrantedUserRole(MapModelCriteriaBuilder<Object, MapUserEntity, UserModel> mcb, Operator op, Object[] values) {
|
||||||
String roleIdS = ensureEqSingleValue(UserModel.SearchableFields.ASSIGNED_ROLE, "role_id", op, values);
|
String roleIdS = ensureEqSingleValue(UserModel.SearchableFields.ASSIGNED_ROLE, "role_id", op, values);
|
||||||
Function<MapUserEntity, ?> getter;
|
Function<MapUserEntity, ?> getter;
|
||||||
getter = ue -> ue.getRolesMembership().contains(roleIdS);
|
getter = ue -> Optional.ofNullable(ue.getRolesMembership()).orElseGet(Collections::emptySet).contains(roleIdS);
|
||||||
|
|
||||||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||||
}
|
}
|
||||||
|
@ -433,10 +433,10 @@ public class MapFieldPredicates {
|
||||||
Function<MapUserEntity, ?> getter;
|
Function<MapUserEntity, ?> getter;
|
||||||
if (op == Operator.IN && values != null && values.length == 1 && (values[0] instanceof Collection)) {
|
if (op == Operator.IN && values != null && values.length == 1 && (values[0] instanceof Collection)) {
|
||||||
Collection<?> c = (Collection<?>) values[0];
|
Collection<?> c = (Collection<?>) values[0];
|
||||||
getter = ue -> ue.getGroupsMembership().stream().anyMatch(c::contains);
|
getter = ue -> Optional.ofNullable(ue.getGroupsMembership()).orElseGet(Collections::emptySet).stream().anyMatch(c::contains);
|
||||||
} else {
|
} else {
|
||||||
String groupIdS = ensureEqSingleValue(UserModel.SearchableFields.ASSIGNED_GROUP, "group_id", op, values);
|
String groupIdS = ensureEqSingleValue(UserModel.SearchableFields.ASSIGNED_GROUP, "group_id", op, values);
|
||||||
getter = ue -> ue.getGroupsMembership().contains(groupIdS);
|
getter = ue -> Optional.ofNullable(ue.getGroupsMembership()).orElseGet(Collections::emptySet).contains(groupIdS);
|
||||||
}
|
}
|
||||||
|
|
||||||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||||
|
@ -453,7 +453,7 @@ public class MapFieldPredicates {
|
||||||
private static MapModelCriteriaBuilder<Object, MapUserEntity, UserModel> checkUserConsentsWithClientScope(MapModelCriteriaBuilder<Object, MapUserEntity, UserModel> mcb, Operator op, Object[] values) {
|
private static MapModelCriteriaBuilder<Object, MapUserEntity, UserModel> checkUserConsentsWithClientScope(MapModelCriteriaBuilder<Object, MapUserEntity, UserModel> mcb, Operator op, Object[] values) {
|
||||||
String clientScopeIdS = ensureEqSingleValue(UserModel.SearchableFields.CONSENT_FOR_CLIENT, "client_scope_id", op, values);
|
String clientScopeIdS = ensureEqSingleValue(UserModel.SearchableFields.CONSENT_FOR_CLIENT, "client_scope_id", op, values);
|
||||||
Function<MapUserEntity, ?> getter;
|
Function<MapUserEntity, ?> getter;
|
||||||
getter = ue -> ue.getUserConsents().anyMatch(consent -> consent.getGrantedClientScopesIds().contains(clientScopeIdS));
|
getter = ue -> Optional.ofNullable(ue.getUserConsents()).orElseGet(Collections::emptyMap).values().stream().anyMatch(consent -> Optional.ofNullable(consent.getGrantedClientScopesIds()).orElseGet(Collections::emptySet).contains(clientScopeIdS));
|
||||||
|
|
||||||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||||
}
|
}
|
||||||
|
@ -469,15 +469,15 @@ public class MapFieldPredicates {
|
||||||
final Object idpAlias = values[0];
|
final Object idpAlias = values[0];
|
||||||
Function<MapUserEntity, ?> getter;
|
Function<MapUserEntity, ?> getter;
|
||||||
if (values.length == 1) {
|
if (values.length == 1) {
|
||||||
getter = ue -> ue.getFederatedIdentities()
|
getter = ue -> Optional.ofNullable(ue.getFederatedIdentities()).orElseGet(Collections::emptyMap).values().stream()
|
||||||
.anyMatch(aue -> Objects.equals(idpAlias, aue.getIdentityProvider()));
|
.anyMatch(aue -> Objects.equals(idpAlias, aue.getIdentityProvider()));
|
||||||
} else if (idpAlias == null) {
|
} else if (idpAlias == null) {
|
||||||
final Object idpUserId = values[1];
|
final Object idpUserId = values[1];
|
||||||
getter = ue -> ue.getFederatedIdentities()
|
getter = ue -> Optional.ofNullable(ue.getFederatedIdentities()).orElseGet(Collections::emptyMap).values().stream()
|
||||||
.anyMatch(aue -> Objects.equals(idpUserId, aue.getUserId()));
|
.anyMatch(aue -> Objects.equals(idpUserId, aue.getUserId()));
|
||||||
} else {
|
} else {
|
||||||
final Object idpUserId = values[1];
|
final Object idpUserId = values[1];
|
||||||
getter = ue -> ue.getFederatedIdentities()
|
getter = ue -> Optional.ofNullable(ue.getFederatedIdentities()).orElseGet(Collections::emptyMap).values().stream()
|
||||||
.anyMatch(aue -> Objects.equals(idpAlias, aue.getIdentityProvider()) && Objects.equals(idpUserId, aue.getUserId()));
|
.anyMatch(aue -> Objects.equals(idpAlias, aue.getIdentityProvider()) && Objects.equals(idpUserId, aue.getUserId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
|
||||||
|
@ -76,7 +77,8 @@ public abstract class MapUserAdapter extends AbstractUserModel<MapUserEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return entity.isEnabled();
|
Boolean enabled = entity.isEnabled();
|
||||||
|
return enabled != null && enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -147,19 +149,20 @@ public abstract class MapUserAdapter extends AbstractUserModel<MapUserEntity> {
|
||||||
@Override
|
@Override
|
||||||
public String getFirstAttribute(String name) {
|
public String getFirstAttribute(String name) {
|
||||||
return getSpecialAttributeValue(name)
|
return getSpecialAttributeValue(name)
|
||||||
.orElseGet(() -> entity.getAttribute(name).stream().findFirst()
|
.orElseGet(() -> Optional.ofNullable(entity.getAttribute(name)).orElseGet(Collections::emptyList).stream().findFirst()
|
||||||
.orElse(null));
|
.orElse(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<String> getAttributeStream(String name) {
|
public Stream<String> getAttributeStream(String name) {
|
||||||
return getSpecialAttributeValue(name).map(Collections::singletonList)
|
return getSpecialAttributeValue(name).map(Collections::singletonList)
|
||||||
.orElseGet(() -> entity.getAttribute(name)).stream();
|
.orElseGet(() -> Optional.ofNullable(entity.getAttribute(name)).orElseGet(Collections::emptyList)).stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, List<String>> getAttributes() {
|
public Map<String, List<String>> getAttributes() {
|
||||||
MultivaluedHashMap<String, String> result = new MultivaluedHashMap<>(entity.getAttributes());
|
Map<String, List<String>> attributes = entity.getAttributes();
|
||||||
|
MultivaluedHashMap<String, String> result = attributes == null ? new MultivaluedHashMap<>() : new MultivaluedHashMap<>(attributes);
|
||||||
result.add(UserModel.FIRST_NAME, entity.getFirstName());
|
result.add(UserModel.FIRST_NAME, entity.getFirstName());
|
||||||
result.add(UserModel.LAST_NAME, entity.getLastName());
|
result.add(UserModel.LAST_NAME, entity.getLastName());
|
||||||
result.add(UserModel.EMAIL, entity.getEmail());
|
result.add(UserModel.EMAIL, entity.getEmail());
|
||||||
|
@ -170,7 +173,8 @@ public abstract class MapUserAdapter extends AbstractUserModel<MapUserEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<String> getRequiredActionsStream() {
|
public Stream<String> getRequiredActionsStream() {
|
||||||
return entity.getRequiredActions().stream();
|
Set<String> requiredActions = entity.getRequiredActions();
|
||||||
|
return requiredActions == null ? Stream.empty() : requiredActions.stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -233,7 +237,8 @@ public abstract class MapUserAdapter extends AbstractUserModel<MapUserEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmailVerified() {
|
public boolean isEmailVerified() {
|
||||||
return entity.isEmailVerified();
|
Boolean emailVerified = entity.isEmailVerified();
|
||||||
|
return emailVerified != null && emailVerified;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -243,7 +248,9 @@ public abstract class MapUserAdapter extends AbstractUserModel<MapUserEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<GroupModel> getGroupsStream() {
|
public Stream<GroupModel> getGroupsStream() {
|
||||||
return session.groups().getGroupsStream(realm, entity.getGroupsMembership().stream());
|
Set<String> groups = entity.getGroupsMembership();
|
||||||
|
if (groups == null || groups.isEmpty()) return Stream.empty();
|
||||||
|
return session.groups().getGroupsStream(realm, groups.stream());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -258,7 +265,8 @@ public abstract class MapUserAdapter extends AbstractUserModel<MapUserEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isMemberOf(GroupModel group) {
|
public boolean isMemberOf(GroupModel group) {
|
||||||
return entity.getGroupsMembership().contains(group.getId());
|
Set<String> groups = entity.getGroupsMembership();
|
||||||
|
return groups != null && groups.contains(group.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -294,7 +302,8 @@ public abstract class MapUserAdapter extends AbstractUserModel<MapUserEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasDirectRole(RoleModel role) {
|
public boolean hasDirectRole(RoleModel role) {
|
||||||
return entity.getRolesMembership().contains(role.getId());
|
Set<String> roles = entity.getRolesMembership();
|
||||||
|
return roles != null && entity.getRolesMembership().contains(role.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -309,6 +318,8 @@ public abstract class MapUserAdapter extends AbstractUserModel<MapUserEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<RoleModel> getRoleMappingsStream() {
|
public Stream<RoleModel> getRoleMappingsStream() {
|
||||||
|
Set<String> roles = entity.getRolesMembership();
|
||||||
|
if (roles == null || roles.isEmpty()) return Stream.empty();
|
||||||
return entity.getRolesMembership().stream().map(realm::getRoleById);
|
return entity.getRolesMembership().stream().map(realm::getRoleById);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 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.user;
|
||||||
|
|
||||||
|
import org.keycloak.common.util.Time;
|
||||||
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.ClientScopeModel;
|
||||||
|
import org.keycloak.models.ModelException;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserConsentModel;
|
||||||
|
import org.keycloak.models.map.annotations.GenerateEntityImplementations;
|
||||||
|
import org.keycloak.models.map.common.DeepCloner;
|
||||||
|
import org.keycloak.models.map.common.UpdatableEntity;
|
||||||
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@GenerateEntityImplementations
|
||||||
|
@DeepCloner.Root
|
||||||
|
public interface MapUserConsentEntity extends UpdatableEntity {
|
||||||
|
|
||||||
|
public static MapUserConsentEntity fromModel(UserConsentModel model) {
|
||||||
|
long currentTime = Time.currentTimeMillis();
|
||||||
|
|
||||||
|
MapUserConsentEntity consentEntity = new MapUserConsentEntityImpl();
|
||||||
|
consentEntity.setClientId(model.getClient().getId());
|
||||||
|
consentEntity.setCreatedDate(currentTime);
|
||||||
|
consentEntity.setLastUpdatedDate(currentTime);
|
||||||
|
|
||||||
|
model.getGrantedClientScopes()
|
||||||
|
.stream()
|
||||||
|
.map(ClientScopeModel::getId)
|
||||||
|
.forEach(consentEntity::addGrantedClientScopesId);
|
||||||
|
|
||||||
|
return consentEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UserConsentModel toModel(RealmModel realm, MapUserConsentEntity entity) {
|
||||||
|
if (entity == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientModel client = realm.getClientById(entity.getClientId());
|
||||||
|
if (client == null) {
|
||||||
|
throw new ModelException("Client with id " + entity.getClientId() + " is not available");
|
||||||
|
}
|
||||||
|
UserConsentModel model = new UserConsentModel(client);
|
||||||
|
model.setCreatedDate(entity.getCreatedDate());
|
||||||
|
model.setLastUpdatedDate(entity.getLastUpdatedDate());
|
||||||
|
|
||||||
|
|
||||||
|
Set<String> grantedClientScopesIds = entity.getGrantedClientScopesIds();
|
||||||
|
|
||||||
|
if (grantedClientScopesIds != null && !grantedClientScopesIds.isEmpty()) {
|
||||||
|
grantedClientScopesIds.stream()
|
||||||
|
.map(scopeId -> KeycloakModelUtils.findClientScopeById(realm, client, scopeId))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.forEach(model::addGrantedClientScope);
|
||||||
|
}
|
||||||
|
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getClientId();
|
||||||
|
void setClientId(String clientId);
|
||||||
|
|
||||||
|
Set<String> getGrantedClientScopesIds();
|
||||||
|
void addGrantedClientScopesId(String scope);
|
||||||
|
void setGrantedClientScopesIds(Set<String> scopesIds);
|
||||||
|
void removeGrantedClientScopesId(String scopesId);
|
||||||
|
|
||||||
|
Long getCreatedDate();
|
||||||
|
void setCreatedDate(Long createdDate);
|
||||||
|
|
||||||
|
Long getLastUpdatedDate();
|
||||||
|
void setLastUpdatedDate(Long lastUpdatedDate);
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 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.user;
|
||||||
|
|
||||||
|
import org.keycloak.credential.CredentialModel;
|
||||||
|
import org.keycloak.models.map.annotations.GenerateEntityImplementations;
|
||||||
|
import org.keycloak.models.map.common.DeepCloner;
|
||||||
|
import org.keycloak.models.map.common.UpdatableEntity;
|
||||||
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
@GenerateEntityImplementations
|
||||||
|
@DeepCloner.Root
|
||||||
|
public interface MapUserCredentialEntity extends UpdatableEntity {
|
||||||
|
|
||||||
|
Comparator<MapUserCredentialEntity> ORDER_BY_PRIORITY = Comparator.comparing(MapUserCredentialEntity::getPriority);
|
||||||
|
|
||||||
|
public static MapUserCredentialEntity fromModel(CredentialModel model) {
|
||||||
|
MapUserCredentialEntity credentialEntity = new MapUserCredentialEntityImpl();
|
||||||
|
String id = model.getId() == null ? KeycloakModelUtils.generateId() : model.getId();
|
||||||
|
credentialEntity.setId(id);
|
||||||
|
credentialEntity.setCreatedDate(model.getCreatedDate());
|
||||||
|
credentialEntity.setUserLabel(model.getUserLabel());
|
||||||
|
credentialEntity.setType(model.getType());
|
||||||
|
credentialEntity.setSecretData(model.getSecretData());
|
||||||
|
credentialEntity.setCredentialData(model.getCredentialData());
|
||||||
|
|
||||||
|
return credentialEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CredentialModel toModel(MapUserCredentialEntity entity) {
|
||||||
|
CredentialModel model = new CredentialModel();
|
||||||
|
model.setId(entity.getId());
|
||||||
|
model.setType(entity.getType());
|
||||||
|
model.setCreatedDate(entity.getCreatedDate());
|
||||||
|
model.setUserLabel(entity.getUserLabel());
|
||||||
|
model.setSecretData(entity.getSecretData());
|
||||||
|
model.setCredentialData(entity.getCredentialData());
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getId();
|
||||||
|
void setId(String id);
|
||||||
|
|
||||||
|
String getType();
|
||||||
|
void setType(String type);
|
||||||
|
|
||||||
|
String getUserLabel();
|
||||||
|
void setUserLabel(String userLabel);
|
||||||
|
|
||||||
|
Long getCreatedDate();
|
||||||
|
void setCreatedDate(Long createdDate);
|
||||||
|
|
||||||
|
String getSecretData();
|
||||||
|
void setSecretData(String secretData);
|
||||||
|
|
||||||
|
String getCredentialData();
|
||||||
|
void setCredentialData(String credentialData);
|
||||||
|
|
||||||
|
Integer getPriority();
|
||||||
|
void setPriority(Integer priority);
|
||||||
|
}
|
|
@ -1,13 +1,13 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2020 Red Hat, Inc. and/or its affiliates
|
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||||
* and other contributors as indicated by the @author tags.
|
* and other contributors as indicated by the @author tags.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@ -17,363 +17,140 @@
|
||||||
|
|
||||||
package org.keycloak.models.map.user;
|
package org.keycloak.models.map.user;
|
||||||
|
|
||||||
import org.keycloak.models.ModelDuplicateException;
|
import org.keycloak.models.map.annotations.GenerateEntityImplementations;
|
||||||
|
import org.keycloak.models.map.annotations.IgnoreForEntityImplementationGenerator;
|
||||||
import org.keycloak.models.map.common.AbstractEntity;
|
import org.keycloak.models.map.common.AbstractEntity;
|
||||||
|
import org.keycloak.models.map.common.DeepCloner;
|
||||||
import org.keycloak.models.map.common.EntityWithAttributes;
|
import org.keycloak.models.map.common.EntityWithAttributes;
|
||||||
import org.keycloak.models.map.common.UpdatableEntity;
|
import org.keycloak.models.map.common.UpdatableEntity;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.Comparator;
|
||||||
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.Objects;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
@GenerateEntityImplementations(
|
||||||
*
|
inherits = "org.keycloak.models.map.user.MapUserEntity.AbstractUserEntity"
|
||||||
* @author mhajas
|
)
|
||||||
*/
|
@DeepCloner.Root
|
||||||
public class MapUserEntity extends UpdatableEntity.Impl implements AbstractEntity, EntityWithAttributes {
|
public interface MapUserEntity extends UpdatableEntity, AbstractEntity, EntityWithAttributes {
|
||||||
|
|
||||||
private String id;
|
public abstract class AbstractUserEntity extends UpdatableEntity.Impl implements MapUserEntity {
|
||||||
private String realmId;
|
|
||||||
|
|
||||||
private String username;
|
private String id;
|
||||||
private String firstName;
|
|
||||||
private Long createdTimestamp;
|
|
||||||
private String lastName;
|
|
||||||
private String email;
|
|
||||||
private boolean enabled;
|
|
||||||
private boolean emailVerified;
|
|
||||||
// This is necessary to be able to dynamically switch unique email constraints on and off in the realm settings
|
|
||||||
private String emailConstraint = KeycloakModelUtils.generateId();
|
|
||||||
private Map<String, List<String>> attributes = new HashMap<>();
|
|
||||||
private Set<String> requiredActions = new HashSet<>();
|
|
||||||
private final Map<String, UserCredentialEntity> credentials = new HashMap<>();
|
|
||||||
private final List<String> credentialsOrder = new LinkedList<>();
|
|
||||||
private final Map<String, UserFederatedIdentityEntity> federatedIdentities = new HashMap<>();
|
|
||||||
private final Map<String, UserConsentEntity> userConsents = new HashMap<>();
|
|
||||||
private Set<String> groupsMembership = new HashSet<>();
|
|
||||||
private Set<String> rolesMembership = new HashSet<>();
|
|
||||||
private String federationLink;
|
|
||||||
private String serviceAccountClientLink;
|
|
||||||
private int notBefore;
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Flag signalizing that any of the setters has been meaningfully used.
|
public boolean isUpdated() {
|
||||||
*/
|
return this.updated
|
||||||
|
|| Optional.ofNullable(getUserConsents()).orElseGet(Collections::emptyMap).values().stream().anyMatch(MapUserConsentEntity::isUpdated)
|
||||||
public MapUserEntity() {}
|
|| Optional.ofNullable(getCredentials()).orElseGet(Collections::emptyMap).values().stream().anyMatch(MapUserCredentialEntity::isUpdated)
|
||||||
|
|| Optional.ofNullable(getFederatedIdentities()).orElseGet(Collections::emptyMap).values().stream().anyMatch(MapUserFederatedIdentityEntity::isUpdated);
|
||||||
public MapUserEntity(String id, String realmId) {
|
|
||||||
this.id = id;
|
|
||||||
this.realmId = realmId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return this.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setId(String id) {
|
|
||||||
if (this.id != null) throw new IllegalStateException("Id cannot be changed");
|
|
||||||
this.id = id;
|
|
||||||
this.updated |= id != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isUpdated() {
|
|
||||||
return this.updated
|
|
||||||
|| userConsents.values().stream().anyMatch(UserConsentEntity::isUpdated)
|
|
||||||
|| credentials.values().stream().anyMatch(UserCredentialEntity::isUpdated)
|
|
||||||
|| federatedIdentities.values().stream().anyMatch(UserFederatedIdentityEntity::isUpdated);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getRealmId() {
|
|
||||||
return realmId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRealmId(String realmId) {
|
|
||||||
this.updated |= !Objects.equals(this.realmId, realmId);
|
|
||||||
this.realmId = realmId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUsername() {
|
|
||||||
return username;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUsername(String username) {
|
|
||||||
this.updated |= !Objects.equals(this.username, username);
|
|
||||||
this.username = username;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFirstName() {
|
|
||||||
return firstName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFirstName(String firstName) {
|
|
||||||
this.updated |= !Objects.equals(this.firstName, firstName);
|
|
||||||
this.firstName = firstName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getCreatedTimestamp() {
|
|
||||||
return createdTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCreatedTimestamp(Long createdTimestamp) {
|
|
||||||
this.updated |= !Objects.equals(this.createdTimestamp, createdTimestamp);
|
|
||||||
this.createdTimestamp = createdTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getLastName() {
|
|
||||||
return lastName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLastName(String lastName) {
|
|
||||||
this.updated |= !Objects.equals(this.lastName, lastName);
|
|
||||||
this.lastName = lastName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getEmail() {
|
|
||||||
return email;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEmail(String email, boolean duplicateEmailsAllowed) {
|
|
||||||
this.updated |= !Objects.equals(this.email, email);
|
|
||||||
this.email = email;
|
|
||||||
this.emailConstraint = email == null || duplicateEmailsAllowed ? KeycloakModelUtils.generateId() : email;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEnabled(boolean enabled) {
|
|
||||||
this.updated |= !Objects.equals(this.enabled, enabled);
|
|
||||||
this.enabled = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEmailVerified() {
|
|
||||||
return emailVerified;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEmailVerified(boolean emailVerified) {
|
|
||||||
this.updated |= !Objects.equals(this.emailVerified, emailVerified);
|
|
||||||
this.emailVerified = emailVerified;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getEmailConstraint() {
|
|
||||||
return emailConstraint;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEmailConstraint(String emailConstraint) {
|
|
||||||
this.updated |= !Objects.equals(this.emailConstraint, emailConstraint);
|
|
||||||
this.emailConstraint = emailConstraint;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, List<String>> getAttributes() {
|
|
||||||
return attributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> getAttribute(String name) {
|
|
||||||
return attributes.getOrDefault(name, Collections.emptyList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAttributes(Map<String, List<String>> attributes) {
|
|
||||||
this.updated |= !Objects.equals(this.attributes, attributes);
|
|
||||||
this.attributes = attributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAttribute(String name, List<String> value) {
|
|
||||||
this.updated |= !Objects.equals(this.attributes.put(name, value), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeAttribute(String name) {
|
|
||||||
this.updated |= this.attributes.remove(name) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<String> getRequiredActions() {
|
|
||||||
return requiredActions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRequiredActions(Set<String> requiredActions) {
|
|
||||||
this.updated |= !Objects.equals(this.requiredActions, requiredActions);
|
|
||||||
this.requiredActions = requiredActions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addRequiredAction(String requiredAction) {
|
|
||||||
this.updated |= this.requiredActions.add(requiredAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeRequiredAction(String requiredAction) {
|
|
||||||
this.updated |= this.requiredActions.remove(requiredAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateCredential(UserCredentialEntity credentialEntity) {
|
|
||||||
this.updated |= credentials.replace(credentialEntity.getId(), credentialEntity) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addCredential(UserCredentialEntity credentialEntity) {
|
|
||||||
if (credentials.containsKey(credentialEntity.getId())) {
|
|
||||||
throw new ModelDuplicateException("A CredentialModel with given id already exists");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updated = true;
|
@Override
|
||||||
credentials.put(credentialEntity.getId(), credentialEntity);
|
public void clearUpdatedFlag() {
|
||||||
credentialsOrder.add(credentialEntity.getId());
|
this.updated = false;
|
||||||
}
|
Optional.ofNullable(getUserConsents()).orElseGet(Collections::emptyMap).values().forEach(UpdatableEntity::clearUpdatedFlag);
|
||||||
|
Optional.ofNullable(getCredentials()).orElseGet(Collections::emptyMap).values().forEach(UpdatableEntity::clearUpdatedFlag);
|
||||||
public boolean removeCredential(String credentialId) {
|
Optional.ofNullable(getFederatedIdentities()).orElseGet(Collections::emptyMap).values().forEach(UpdatableEntity::clearUpdatedFlag);
|
||||||
if (!credentials.containsKey(credentialId)) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updated = true;
|
@Override
|
||||||
this.credentials.remove(credentialId);
|
public String getId() {
|
||||||
this.credentialsOrder.remove(credentialId);
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
@Override
|
||||||
}
|
public void setId(String id) {
|
||||||
|
if (this.id != null) throw new IllegalStateException("Id cannot be changed");
|
||||||
public UserCredentialEntity getCredential(String id) {
|
this.id = id;
|
||||||
return credentials.get(id);
|
this.updated |= id != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Stream<UserCredentialEntity> getCredentials() {
|
@Override
|
||||||
return credentialsOrder.stream()
|
public void setEmail(String email, boolean duplicateEmailsAllowed) {
|
||||||
.map(credentials::get);
|
this.setEmail(email);
|
||||||
}
|
this.setEmailConstraint(email == null || duplicateEmailsAllowed ? KeycloakModelUtils.generateId() : email);
|
||||||
|
}
|
||||||
public int getCredentialIndex(String credentialId) {
|
|
||||||
return credentialsOrder.indexOf(credentialId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void moveCredential(int currentPosition, int newPosition) {
|
|
||||||
this.updated |= currentPosition != newPosition;
|
|
||||||
credentialsOrder.add(newPosition, credentialsOrder.remove(currentPosition));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Stream<UserFederatedIdentityEntity> getFederatedIdentities() {
|
String getRealmId();
|
||||||
return federatedIdentities.values().stream();
|
void setRealmId(String realmId);
|
||||||
}
|
|
||||||
|
|
||||||
public void setFederatedIdentities(Collection<UserFederatedIdentityEntity> federatedIdentities) {
|
String getUsername();
|
||||||
this.updated = true;
|
void setUsername(String username);
|
||||||
this.federatedIdentities.clear();
|
|
||||||
this.federatedIdentities.putAll(federatedIdentities.stream()
|
|
||||||
.collect(Collectors.toMap(UserFederatedIdentityEntity::getIdentityProvider, Function.identity())));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addFederatedIdentity(UserFederatedIdentityEntity federatedIdentity) {
|
|
||||||
String idpId = federatedIdentity.getIdentityProvider();
|
|
||||||
this.updated |= !Objects.equals(this.federatedIdentities.put(idpId, federatedIdentity), federatedIdentity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UserFederatedIdentityEntity getFederatedIdentity(String federatedIdentity) {
|
String getFirstName();
|
||||||
return this.federatedIdentities.get(federatedIdentity);
|
void setFirstName(String firstName);
|
||||||
}
|
|
||||||
|
|
||||||
public boolean removeFederatedIdentity(String providerId) {
|
|
||||||
boolean removed = federatedIdentities.remove(providerId) != null;
|
|
||||||
this.updated |= removed;
|
|
||||||
return removed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateFederatedIdentity(UserFederatedIdentityEntity federatedIdentityModel) {
|
Long getCreatedTimestamp();
|
||||||
this.updated |= federatedIdentities.replace(federatedIdentityModel.getIdentityProvider(), federatedIdentityModel) != null;
|
void setCreatedTimestamp(Long createdTimestamp);
|
||||||
}
|
|
||||||
|
|
||||||
public Stream<UserConsentEntity> getUserConsents() {
|
String getLastName();
|
||||||
return userConsents.values().stream();
|
void setLastName(String lastName);
|
||||||
}
|
|
||||||
|
|
||||||
public UserConsentEntity getUserConsent(String clientId) {
|
String getEmail();
|
||||||
return this.userConsents.get(clientId);
|
void setEmail(String email);
|
||||||
}
|
@IgnoreForEntityImplementationGenerator
|
||||||
|
void setEmail(String email, boolean duplicateEmailsAllowed);
|
||||||
|
|
||||||
|
Boolean isEnabled();
|
||||||
public void addUserConsent(UserConsentEntity userConsentEntity) {
|
void setEnabled(Boolean enabled);
|
||||||
String clientId = userConsentEntity.getClientId();
|
|
||||||
this.updated |= !Objects.equals(this.userConsents.put(clientId, userConsentEntity), userConsentEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean removeUserConsent(String clientId) {
|
Boolean isEmailVerified();
|
||||||
boolean removed = userConsents.remove(clientId) != null;
|
void setEmailVerified(Boolean emailVerified);
|
||||||
this.updated |= removed;
|
|
||||||
return removed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<String> getGroupsMembership() {
|
String getEmailConstraint();
|
||||||
return groupsMembership;
|
void setEmailConstraint(String emailConstraint);
|
||||||
}
|
|
||||||
|
|
||||||
public void setGroupsMembership(Set<String> groupsMembership) {
|
Map<String, List<String>> getAttributes();
|
||||||
this.updated |= Objects.equals(groupsMembership, this.groupsMembership);
|
List<String> getAttribute(String name);
|
||||||
this.groupsMembership = groupsMembership;
|
void setAttributes(Map<String, List<String>> attributes);
|
||||||
}
|
void setAttribute(String name, List<String> value);
|
||||||
|
void removeAttribute(String name);
|
||||||
public void addGroupsMembership(String groupId) {
|
|
||||||
this.updated |= this.groupsMembership.add(groupId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeGroupsMembership(String groupId) {
|
Set<String> getRequiredActions();
|
||||||
this.updated |= this.groupsMembership.remove(groupId);
|
void setRequiredActions(Set<String> requiredActions);
|
||||||
}
|
void addRequiredAction(String requiredAction);
|
||||||
|
void removeRequiredAction(String requiredAction);
|
||||||
|
|
||||||
public Set<String> getRolesMembership() {
|
Map<String, MapUserCredentialEntity> getCredentials();
|
||||||
return rolesMembership;
|
void setCredential(String id, MapUserCredentialEntity credentialEntity);
|
||||||
}
|
Boolean removeCredential(String credentialId);
|
||||||
|
MapUserCredentialEntity getCredential(String id);
|
||||||
|
|
||||||
public void setRolesMembership(Set<String> rolesMembership) {
|
Map<String, MapUserFederatedIdentityEntity> getFederatedIdentities();
|
||||||
this.updated |= Objects.equals(rolesMembership, this.rolesMembership);
|
void setFederatedIdentities(Map<String, MapUserFederatedIdentityEntity> federatedIdentities);
|
||||||
this.rolesMembership = rolesMembership;
|
void setFederatedIdentity(String id, MapUserFederatedIdentityEntity federatedIdentity);
|
||||||
}
|
MapUserFederatedIdentityEntity getFederatedIdentity(String federatedIdentity);
|
||||||
|
Boolean removeFederatedIdentity(String providerId);
|
||||||
|
|
||||||
public void addRolesMembership(String roleId) {
|
Map<String, MapUserConsentEntity> getUserConsents();
|
||||||
this.updated |= this.rolesMembership.add(roleId);
|
MapUserConsentEntity getUserConsent(String clientId);
|
||||||
}
|
void setUserConsent(String id, MapUserConsentEntity userConsentEntity);
|
||||||
|
Boolean removeUserConsent(String clientId);
|
||||||
|
|
||||||
public void removeRolesMembership(String roleId) {
|
Set<String> getGroupsMembership();
|
||||||
this.updated |= this.rolesMembership.remove(roleId);
|
void setGroupsMembership(Set<String> groupsMembership);
|
||||||
}
|
void addGroupsMembership(String groupId);
|
||||||
|
void removeGroupsMembership(String groupId);
|
||||||
|
|
||||||
public String getFederationLink() {
|
Set<String> getRolesMembership();
|
||||||
return federationLink;
|
void setRolesMembership(Set<String> rolesMembership);
|
||||||
}
|
void addRolesMembership(String roleId);
|
||||||
|
void removeRolesMembership(String roleId);
|
||||||
|
|
||||||
public void setFederationLink(String federationLink) {
|
String getFederationLink();
|
||||||
this.updated |= !Objects.equals(this.federationLink, federationLink);
|
void setFederationLink(String federationLink);
|
||||||
this.federationLink = federationLink;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getServiceAccountClientLink() {
|
String getServiceAccountClientLink();
|
||||||
return serviceAccountClientLink;
|
void setServiceAccountClientLink(String serviceAccountClientLink);
|
||||||
}
|
|
||||||
|
|
||||||
public void setServiceAccountClientLink(String serviceAccountClientLink) {
|
|
||||||
this.updated |= !Objects.equals(this.serviceAccountClientLink, serviceAccountClientLink);
|
|
||||||
this.serviceAccountClientLink = serviceAccountClientLink;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getNotBefore() {
|
|
||||||
return notBefore;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNotBefore(int notBefore) {
|
|
||||||
this.updated |= !Objects.equals(this.notBefore, notBefore);
|
|
||||||
this.notBefore = notBefore;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Integer getNotBefore();
|
||||||
|
void setNotBefore(Integer notBefore);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 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.user;
|
||||||
|
|
||||||
|
import org.keycloak.models.FederatedIdentityModel;
|
||||||
|
import org.keycloak.models.map.annotations.GenerateEntityImplementations;
|
||||||
|
import org.keycloak.models.map.common.DeepCloner;
|
||||||
|
import org.keycloak.models.map.common.UpdatableEntity;
|
||||||
|
|
||||||
|
@GenerateEntityImplementations
|
||||||
|
@DeepCloner.Root
|
||||||
|
public interface MapUserFederatedIdentityEntity extends UpdatableEntity {
|
||||||
|
|
||||||
|
public static MapUserFederatedIdentityEntity fromModel(FederatedIdentityModel model) {
|
||||||
|
if (model == null) return null;
|
||||||
|
MapUserFederatedIdentityEntity entity = new MapUserFederatedIdentityEntityImpl();
|
||||||
|
entity.setIdentityProvider(model.getIdentityProvider());
|
||||||
|
entity.setUserId(model.getUserId());
|
||||||
|
entity.setUserName(model.getUserName().toLowerCase());
|
||||||
|
entity.setToken(model.getToken());
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FederatedIdentityModel toModel(MapUserFederatedIdentityEntity entity) {
|
||||||
|
if (entity == null) return null;
|
||||||
|
return new FederatedIdentityModel(entity.getIdentityProvider(), entity.getUserId(), entity.getUserName(), entity.getToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
String getToken();
|
||||||
|
void setToken(String token);
|
||||||
|
|
||||||
|
String getUserId();
|
||||||
|
void setUserId(String userId);
|
||||||
|
|
||||||
|
String getIdentityProvider();
|
||||||
|
void setIdentityProvider(String identityProvider);
|
||||||
|
|
||||||
|
String getUserName();
|
||||||
|
void setUserName(String userName);
|
||||||
|
}
|
|
@ -45,13 +45,17 @@ import org.keycloak.models.map.storage.MapKeycloakTransaction;
|
||||||
import org.keycloak.models.map.storage.MapStorage;
|
import org.keycloak.models.map.storage.MapStorage;
|
||||||
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
|
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
|
||||||
import org.keycloak.models.map.storage.criteria.DefaultModelCriteria;
|
import org.keycloak.models.map.storage.criteria.DefaultModelCriteria;
|
||||||
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.storage.StorageId;
|
import org.keycloak.storage.StorageId;
|
||||||
import org.keycloak.storage.UserStorageProvider;
|
import org.keycloak.storage.UserStorageProvider;
|
||||||
import org.keycloak.storage.client.ClientStorageProvider;
|
import org.keycloak.storage.client.ClientStorageProvider;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
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.Objects;
|
import java.util.Objects;
|
||||||
|
@ -75,6 +79,8 @@ import static org.keycloak.models.map.storage.criteria.DefaultModelCriteria.crit
|
||||||
|
|
||||||
public class MapUserProvider implements UserProvider.Streams, UserCredentialStore.Streams {
|
public class MapUserProvider implements UserProvider.Streams, UserCredentialStore.Streams {
|
||||||
|
|
||||||
|
// Typical priority difference between 2 credentials
|
||||||
|
public static final int PRIORITY_DIFFERENCE = 10;
|
||||||
private static final Logger LOG = Logger.getLogger(MapUserProvider.class);
|
private static final Logger LOG = Logger.getLogger(MapUserProvider.class);
|
||||||
private final KeycloakSession session;
|
private final KeycloakSession session;
|
||||||
final MapKeycloakTransaction<MapUserEntity, UserModel> tx;
|
final MapKeycloakTransaction<MapUserEntity, UserModel> tx;
|
||||||
|
@ -139,15 +145,18 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
||||||
|
|
||||||
getEntityById(realm, user.getId())
|
getEntityById(realm, user.getId())
|
||||||
.ifPresent(userEntity ->
|
.ifPresent(userEntity ->
|
||||||
userEntity.addFederatedIdentity(UserFederatedIdentityEntity.fromModel(socialLink)));
|
userEntity.setFederatedIdentity(socialLink.getIdentityProvider(), MapUserFederatedIdentityEntity.fromModel(socialLink)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean removeFederatedIdentity(RealmModel realm, UserModel user, String socialProvider) {
|
public boolean removeFederatedIdentity(RealmModel realm, UserModel user, String socialProvider) {
|
||||||
LOG.tracef("removeFederatedIdentity(%s, %s, %s)%s", realm, user.getId(), socialProvider, getShortStackTrace());
|
LOG.tracef("removeFederatedIdentity(%s, %s, %s)%s", realm, user.getId(), socialProvider, getShortStackTrace());
|
||||||
return getEntityById(realm, user.getId())
|
|
||||||
.map(entity -> entity.removeFederatedIdentity(socialProvider))
|
Optional<MapUserEntity> entityById = getEntityById(realm, user.getId());
|
||||||
.orElse(false);
|
if (!entityById.isPresent()) return false;
|
||||||
|
|
||||||
|
Boolean result = entityById.get().removeFederatedIdentity(socialProvider);
|
||||||
|
return result == null ? true : result; // TODO: make removeFederatedIdentity return Boolean so the caller can correctly handle "I don't know" null answer
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -166,15 +175,18 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
||||||
public void updateFederatedIdentity(RealmModel realm, UserModel federatedUser, FederatedIdentityModel federatedIdentityModel) {
|
public void updateFederatedIdentity(RealmModel realm, UserModel federatedUser, FederatedIdentityModel federatedIdentityModel) {
|
||||||
LOG.tracef("updateFederatedIdentity(%s, %s, %s)%s", realm, federatedUser.getId(), federatedIdentityModel.getIdentityProvider(), getShortStackTrace());
|
LOG.tracef("updateFederatedIdentity(%s, %s, %s)%s", realm, federatedUser.getId(), federatedIdentityModel.getIdentityProvider(), getShortStackTrace());
|
||||||
getEntityById(realm, federatedUser.getId())
|
getEntityById(realm, federatedUser.getId())
|
||||||
.ifPresent(entity -> entity.updateFederatedIdentity(UserFederatedIdentityEntity.fromModel(federatedIdentityModel)));
|
.ifPresent(entity -> entity.setFederatedIdentity(federatedIdentityModel.getIdentityProvider(), MapUserFederatedIdentityEntity.fromModel(federatedIdentityModel)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<FederatedIdentityModel> getFederatedIdentitiesStream(RealmModel realm, UserModel user) {
|
public Stream<FederatedIdentityModel> getFederatedIdentitiesStream(RealmModel realm, UserModel user) {
|
||||||
LOG.tracef("getFederatedIdentitiesStream(%s, %s)%s", realm, user.getId(), getShortStackTrace());
|
LOG.tracef("getFederatedIdentitiesStream(%s, %s)%s", realm, user.getId(), getShortStackTrace());
|
||||||
return getEntityById(realm, user.getId())
|
return getEntityById(realm, user.getId())
|
||||||
.map(MapUserEntity::getFederatedIdentities).orElseGet(Stream::empty)
|
.map(MapUserEntity::getFederatedIdentities)
|
||||||
.map(UserFederatedIdentityEntity::toModel);
|
.map(Map::values)
|
||||||
|
.map(Collection::stream)
|
||||||
|
.orElseGet(Stream::empty)
|
||||||
|
.map(MapUserFederatedIdentityEntity::toModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -182,7 +194,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
||||||
LOG.tracef("getFederatedIdentity(%s, %s, %s)%s", realm, user.getId(), socialProvider, getShortStackTrace());
|
LOG.tracef("getFederatedIdentity(%s, %s, %s)%s", realm, user.getId(), socialProvider, getShortStackTrace());
|
||||||
return getEntityById(realm, user.getId())
|
return getEntityById(realm, user.getId())
|
||||||
.map(userEntity -> userEntity.getFederatedIdentity(socialProvider))
|
.map(userEntity -> userEntity.getFederatedIdentity(socialProvider))
|
||||||
.map(UserFederatedIdentityEntity::toModel)
|
.map(MapUserFederatedIdentityEntity::toModel)
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,7 +225,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
||||||
LOG.tracef("addConsent(%s, %s, %s)%s", realm, userId, consent, getShortStackTrace());
|
LOG.tracef("addConsent(%s, %s, %s)%s", realm, userId, consent, getShortStackTrace());
|
||||||
|
|
||||||
getEntityByIdOrThrow(realm, userId)
|
getEntityByIdOrThrow(realm, userId)
|
||||||
.addUserConsent(UserConsentEntity.fromModel(consent));
|
.setUserConsent(consent.getClient().getId(), MapUserConsentEntity.fromModel(consent));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -221,7 +233,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
||||||
LOG.tracef("getConsentByClient(%s, %s, %s)%s", realm, userId, clientInternalId, getShortStackTrace());
|
LOG.tracef("getConsentByClient(%s, %s, %s)%s", realm, userId, clientInternalId, getShortStackTrace());
|
||||||
return getEntityById(realm, userId)
|
return getEntityById(realm, userId)
|
||||||
.map(userEntity -> userEntity.getUserConsent(clientInternalId))
|
.map(userEntity -> userEntity.getUserConsent(clientInternalId))
|
||||||
.map(consent -> UserConsentEntity.toModel(realm, consent))
|
.map(consent -> MapUserConsentEntity.toModel(realm, consent))
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,8 +242,10 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
||||||
LOG.tracef("getConsentByClientStream(%s, %s)%s", realm, userId, getShortStackTrace());
|
LOG.tracef("getConsentByClientStream(%s, %s)%s", realm, userId, getShortStackTrace());
|
||||||
return getEntityById(realm, userId)
|
return getEntityById(realm, userId)
|
||||||
.map(MapUserEntity::getUserConsents)
|
.map(MapUserEntity::getUserConsents)
|
||||||
|
.map(Map::values)
|
||||||
|
.map(Collection::stream)
|
||||||
.orElse(Stream.empty())
|
.orElse(Stream.empty())
|
||||||
.map(consent -> UserConsentEntity.toModel(realm, consent));
|
.map(consent -> MapUserConsentEntity.toModel(realm, consent));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -239,7 +253,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
||||||
LOG.tracef("updateConsent(%s, %s, %s)%s", realm, userId, consent, getShortStackTrace());
|
LOG.tracef("updateConsent(%s, %s, %s)%s", realm, userId, consent, getShortStackTrace());
|
||||||
|
|
||||||
MapUserEntity user = getEntityByIdOrThrow(realm, userId);
|
MapUserEntity user = getEntityByIdOrThrow(realm, userId);
|
||||||
UserConsentEntity userConsentEntity = user.getUserConsent(consent.getClient().getId());
|
MapUserConsentEntity userConsentEntity = user.getUserConsent(consent.getClient().getId());
|
||||||
if (userConsentEntity == null) {
|
if (userConsentEntity == null) {
|
||||||
throw new ModelException("Consent not found for client [" + consent.getClient().getId() + "] and user [" + userId + "]");
|
throw new ModelException("Consent not found for client [" + consent.getClient().getId() + "] and user [" + userId + "]");
|
||||||
}
|
}
|
||||||
|
@ -256,9 +270,12 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
||||||
@Override
|
@Override
|
||||||
public boolean revokeConsentForClient(RealmModel realm, String userId, String clientInternalId) {
|
public boolean revokeConsentForClient(RealmModel realm, String userId, String clientInternalId) {
|
||||||
LOG.tracef("revokeConsentForClient(%s, %s, %s)%s", realm, userId, clientInternalId, getShortStackTrace());
|
LOG.tracef("revokeConsentForClient(%s, %s, %s)%s", realm, userId, clientInternalId, getShortStackTrace());
|
||||||
return getEntityById(realm, userId)
|
|
||||||
.map(userEntity -> userEntity.removeUserConsent(clientInternalId))
|
Optional<MapUserEntity> entityById = getEntityById(realm, userId);
|
||||||
.orElse(false);
|
if (!entityById.isPresent()) return false;
|
||||||
|
|
||||||
|
Boolean result = entityById.get().removeUserConsent(clientInternalId);
|
||||||
|
return result == null ? true : result; // TODO: make revokeConsentForClient return Boolean so the caller can correctly handle "I don't know" null answer
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -270,9 +287,11 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
||||||
@Override
|
@Override
|
||||||
public int getNotBeforeOfUser(RealmModel realm, UserModel user) {
|
public int getNotBeforeOfUser(RealmModel realm, UserModel user) {
|
||||||
LOG.tracef("getNotBeforeOfUser(%s, %s)%s", realm, user.getId(), getShortStackTrace());
|
LOG.tracef("getNotBeforeOfUser(%s, %s)%s", realm, user.getId(), getShortStackTrace());
|
||||||
return getEntityById(realm, user.getId())
|
Integer notBefore = getEntityById(realm, user.getId())
|
||||||
.orElseThrow(this::userDoesntExistException)
|
.orElseThrow(this::userDoesntExistException)
|
||||||
.getNotBefore();
|
.getNotBefore();
|
||||||
|
|
||||||
|
return notBefore == null ? 0 : notBefore;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -313,7 +332,10 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
||||||
throw new ModelDuplicateException("User exists: " + id);
|
throw new ModelDuplicateException("User exists: " + id);
|
||||||
}
|
}
|
||||||
|
|
||||||
MapUserEntity entity = new MapUserEntity(id, realm.getId());
|
MapUserEntity entity = new MapUserEntityImpl();
|
||||||
|
entity.setId(id);
|
||||||
|
entity.setRealmId(realm.getId());
|
||||||
|
entity.setEmailConstraint(KeycloakModelUtils.generateId());
|
||||||
entity.setUsername(username.toLowerCase());
|
entity.setUsername(username.toLowerCase());
|
||||||
entity.setCreatedTimestamp(Time.currentTimeMillis());
|
entity.setCreatedTimestamp(Time.currentTimeMillis());
|
||||||
|
|
||||||
|
@ -423,8 +445,11 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
||||||
.compare(SearchableFields.CONSENT_WITH_CLIENT_SCOPE, Operator.EQ, clientScopeId);
|
.compare(SearchableFields.CONSENT_WITH_CLIENT_SCOPE, Operator.EQ, clientScopeId);
|
||||||
|
|
||||||
try (Stream<MapUserEntity> s = tx.read(withCriteria(mcb))) {
|
try (Stream<MapUserEntity> s = tx.read(withCriteria(mcb))) {
|
||||||
s.flatMap(MapUserEntity::getUserConsents)
|
s.map(MapUserEntity::getUserConsents)
|
||||||
.forEach(consent -> consent.removeGrantedClientScopesIds(clientScopeId));
|
.filter(Objects::nonNull)
|
||||||
|
.map(Map::values)
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.forEach(consent -> consent.removeGrantedClientScopesId(clientScopeId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -449,8 +474,10 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
||||||
|
|
||||||
private Consumer<MapUserEntity> removeConsentsForExternalClient(String idPrefix) {
|
private Consumer<MapUserEntity> removeConsentsForExternalClient(String idPrefix) {
|
||||||
return userEntity -> {
|
return userEntity -> {
|
||||||
List<String> consentClientIds = userEntity.getUserConsents()
|
Map<String, MapUserConsentEntity> userConsents = userEntity.getUserConsents();
|
||||||
.map(UserConsentEntity::getClientId)
|
if (userConsents == null || userConsents.isEmpty()) return;
|
||||||
|
List<String> consentClientIds = userConsents.values().stream()
|
||||||
|
.map(MapUserConsentEntity::getClientId)
|
||||||
.filter(clientId -> clientId != null && clientId.startsWith(idPrefix))
|
.filter(clientId -> clientId != null && clientId.startsWith(idPrefix))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
@ -730,7 +757,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
||||||
|
|
||||||
private Consumer<MapUserEntity> updateCredential(CredentialModel credentialModel) {
|
private Consumer<MapUserEntity> updateCredential(CredentialModel credentialModel) {
|
||||||
return user -> {
|
return user -> {
|
||||||
UserCredentialEntity credentialEntity = user.getCredential(credentialModel.getId());
|
MapUserCredentialEntity credentialEntity = user.getCredential(credentialModel.getId());
|
||||||
if (credentialEntity == null) return;
|
if (credentialEntity == null) return;
|
||||||
|
|
||||||
credentialEntity.setCreatedDate(credentialModel.getCreatedDate());
|
credentialEntity.setCreatedDate(credentialModel.getCreatedDate());
|
||||||
|
@ -744,20 +771,35 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
||||||
@Override
|
@Override
|
||||||
public CredentialModel createCredential(RealmModel realm, UserModel user, CredentialModel cred) {
|
public CredentialModel createCredential(RealmModel realm, UserModel user, CredentialModel cred) {
|
||||||
LOG.tracef("createCredential(%s, %s, %s)%s", realm, user.getId(), cred.getId(), getShortStackTrace());
|
LOG.tracef("createCredential(%s, %s, %s)%s", realm, user.getId(), cred.getId(), getShortStackTrace());
|
||||||
UserCredentialEntity credentialEntity = UserCredentialEntity.fromModel(cred);
|
MapUserEntity userEntity = getEntityByIdOrThrow(realm, user.getId());
|
||||||
|
MapUserCredentialEntity credentialEntity = MapUserCredentialEntity.fromModel(cred);
|
||||||
|
|
||||||
getEntityByIdOrThrow(realm, user.getId())
|
if (userEntity.getCredential(cred.getId()) != null) {
|
||||||
.addCredential(credentialEntity);
|
throw new ModelDuplicateException("A CredentialModel with given id already exists");
|
||||||
|
}
|
||||||
|
|
||||||
return UserCredentialEntity.toModel(credentialEntity);
|
Map<String, MapUserCredentialEntity> credentials = userEntity.getCredentials();
|
||||||
|
int priority = PRIORITY_DIFFERENCE;
|
||||||
|
|
||||||
|
if (credentials != null && !credentials.isEmpty()) {
|
||||||
|
priority = credentials.values().stream().max(MapUserCredentialEntity.ORDER_BY_PRIORITY).map(MapUserCredentialEntity::getPriority).orElse(0) + PRIORITY_DIFFERENCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
credentialEntity.setPriority(priority);
|
||||||
|
userEntity.setCredential(credentialEntity.getId(), credentialEntity);
|
||||||
|
|
||||||
|
return MapUserCredentialEntity.toModel(credentialEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean removeStoredCredential(RealmModel realm, UserModel user, String id) {
|
public boolean removeStoredCredential(RealmModel realm, UserModel user, String id) {
|
||||||
LOG.tracef("removeStoredCredential(%s, %s, %s)%s", realm, user.getId(), id, getShortStackTrace());
|
LOG.tracef("removeStoredCredential(%s, %s, %s)%s", realm, user.getId(), id, getShortStackTrace());
|
||||||
return getEntityById(realm, user.getId())
|
|
||||||
.map(mapUserEntity -> mapUserEntity.removeCredential(id))
|
Optional<MapUserEntity> entityById = getEntityById(realm, user.getId());
|
||||||
.orElse(false);
|
if (!entityById.isPresent()) return false;
|
||||||
|
|
||||||
|
Boolean result = entityById.get().removeCredential(id);
|
||||||
|
return result == null ? true : result; // TODO: make removeStoredCredential return Boolean so the caller can correctly handle "I don't know" null answer
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -765,17 +807,21 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
||||||
LOG.tracef("getStoredCredentialById(%s, %s, %s)%s", realm, user.getId(), id, getShortStackTrace());
|
LOG.tracef("getStoredCredentialById(%s, %s, %s)%s", realm, user.getId(), id, getShortStackTrace());
|
||||||
return getEntityById(realm, user.getId())
|
return getEntityById(realm, user.getId())
|
||||||
.map(mapUserEntity -> mapUserEntity.getCredential(id))
|
.map(mapUserEntity -> mapUserEntity.getCredential(id))
|
||||||
.map(UserCredentialEntity::toModel)
|
.map(MapUserCredentialEntity::toModel)
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<CredentialModel> getStoredCredentialsStream(RealmModel realm, UserModel user) {
|
public Stream<CredentialModel> getStoredCredentialsStream(RealmModel realm, UserModel user) {
|
||||||
LOG.tracef("getStoredCredentialsStream(%s, %s)%s", realm, user.getId(), getShortStackTrace());
|
LOG.tracef("getStoredCredentialsStream(%s, %s)%s", realm, user.getId(), getShortStackTrace());
|
||||||
|
|
||||||
return getEntityById(realm, user.getId())
|
return getEntityById(realm, user.getId())
|
||||||
.map(MapUserEntity::getCredentials)
|
.map(MapUserEntity::getCredentials)
|
||||||
|
.map(Map::values)
|
||||||
|
.map(Collection::stream)
|
||||||
.orElseGet(Stream::empty)
|
.orElseGet(Stream::empty)
|
||||||
.map(UserCredentialEntity::toModel);
|
.sorted(MapUserCredentialEntity.ORDER_BY_PRIORITY)
|
||||||
|
.map(MapUserCredentialEntity::toModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -795,38 +841,59 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean moveCredentialTo(RealmModel realm, UserModel user, String id, String newPreviousCredentialId) {
|
public boolean moveCredentialTo(RealmModel realm, UserModel user, String id, String newPreviousCredentialId) {
|
||||||
LOG.tracef("moveCredentialTo(%s, %s, %s, %s)%s", realm, user.getId(), id, newPreviousCredentialId, getShortStackTrace());
|
|
||||||
String userId = user.getId();
|
MapUserEntity userEntity = getEntityByIdOrThrow(realm, user.getId());
|
||||||
MapUserEntity userEntity = getEntityById(realm, userId).orElse(null);
|
|
||||||
if (userEntity == null) {
|
// 1 - Create new list and move everything to it.
|
||||||
LOG.warnf("User with id: [%s] not found", userId);
|
Map<String, MapUserCredentialEntity> credentialEntityMap = userEntity.getCredentials();
|
||||||
|
List<MapUserCredentialEntity> newList = credentialEntityMap == null ? new LinkedList<>()
|
||||||
|
: credentialEntityMap.values().stream()
|
||||||
|
.sorted(MapUserCredentialEntity.ORDER_BY_PRIORITY)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// 2 - Find indexes of our and newPrevious credential
|
||||||
|
int ourCredentialIndex = -1;
|
||||||
|
int newPreviousCredentialIndex = -1;
|
||||||
|
MapUserCredentialEntity ourCredential = null;
|
||||||
|
int i = 0;
|
||||||
|
for (MapUserCredentialEntity credential : newList) {
|
||||||
|
if (id.equals(credential.getId())) {
|
||||||
|
ourCredentialIndex = i;
|
||||||
|
ourCredential = credential;
|
||||||
|
} else if(newPreviousCredentialId != null && newPreviousCredentialId.equals(credential.getId())) {
|
||||||
|
newPreviousCredentialIndex = i;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ourCredentialIndex == -1) {
|
||||||
|
LOG.warnf("Not found credential with id [%s] of user [%s]", id, user.getUsername());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find index of credential which should be before id in the list
|
if (newPreviousCredentialId != null && newPreviousCredentialIndex == -1) {
|
||||||
int newPreviousCredentialIdIndex = -1; // If newPreviousCredentialId == null we need to put id credential to index 0
|
LOG.warnf("Can't move up credential with id [%s] of user [%s]", id, user.getUsername());
|
||||||
if (newPreviousCredentialId != null) {
|
return false;
|
||||||
newPreviousCredentialIdIndex = userEntity.getCredentialIndex(newPreviousCredentialId);
|
}
|
||||||
if (newPreviousCredentialIdIndex == -1) { // If not null previous credential not found, print warning and return false
|
|
||||||
LOG.warnf("Credential with id: [%s] for user: [%s] not found", newPreviousCredentialId, userId);
|
// 3 - Compute index where we move our credential
|
||||||
return false;
|
int toMoveIndex = newPreviousCredentialId==null ? 0 : newPreviousCredentialIndex + 1;
|
||||||
|
|
||||||
|
// 4 - Insert our credential to new position, remove it from the old position
|
||||||
|
newList.add(toMoveIndex, ourCredential);
|
||||||
|
int indexToRemove = toMoveIndex < ourCredentialIndex ? ourCredentialIndex + 1 : ourCredentialIndex;
|
||||||
|
newList.remove(indexToRemove);
|
||||||
|
|
||||||
|
// 5 - newList contains credentials in requested order now. Iterate through whole list and change priorities accordingly.
|
||||||
|
int expectedPriority = 0;
|
||||||
|
for (MapUserCredentialEntity credential : newList) {
|
||||||
|
expectedPriority += PRIORITY_DIFFERENCE;
|
||||||
|
if (credential.getPriority() != expectedPriority) {
|
||||||
|
credential.setPriority(expectedPriority);
|
||||||
|
|
||||||
|
LOG.tracef("Priority of credential [%s] of user [%s] changed to [%d]", credential.getId(), user.getUsername(), expectedPriority);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find current index of credential (id) which will be moved
|
|
||||||
int currentPositionOfId = userEntity.getCredentialIndex(id);
|
|
||||||
if (currentPositionOfId == -1) {
|
|
||||||
LOG.warnf("Credential with id: [%s] for user: [%s] not found", id, userId);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If id is before newPreviousCredentialId in priority list, it will be moved to position -1
|
|
||||||
if (currentPositionOfId < newPreviousCredentialIdIndex) {
|
|
||||||
newPreviousCredentialIdIndex -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move credential to desired index
|
|
||||||
userEntity.moveCredential(currentPositionOfId, newPreviousCredentialIdIndex + 1);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,124 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020 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.user;
|
|
||||||
|
|
||||||
import org.keycloak.common.util.Time;
|
|
||||||
import org.keycloak.models.ClientModel;
|
|
||||||
import org.keycloak.models.ClientScopeModel;
|
|
||||||
import org.keycloak.models.ModelException;
|
|
||||||
import org.keycloak.models.RealmModel;
|
|
||||||
import org.keycloak.models.UserConsentModel;
|
|
||||||
import org.keycloak.models.map.common.UpdatableEntity;
|
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
|
|
||||||
public class UserConsentEntity extends UpdatableEntity.Impl {
|
|
||||||
|
|
||||||
private String clientId;
|
|
||||||
private final Set<String> grantedClientScopesIds = new HashSet<>();
|
|
||||||
private Long createdDate;
|
|
||||||
private Long lastUpdatedDate;
|
|
||||||
|
|
||||||
private UserConsentEntity() {}
|
|
||||||
|
|
||||||
public static UserConsentEntity fromModel(UserConsentModel model) {
|
|
||||||
long currentTime = Time.currentTimeMillis();
|
|
||||||
|
|
||||||
UserConsentEntity consentEntity = new UserConsentEntity();
|
|
||||||
consentEntity.setClientId(model.getClient().getId());
|
|
||||||
consentEntity.setCreatedDate(currentTime);
|
|
||||||
consentEntity.setLastUpdatedDate(currentTime);
|
|
||||||
|
|
||||||
model.getGrantedClientScopes()
|
|
||||||
.stream()
|
|
||||||
.map(ClientScopeModel::getId)
|
|
||||||
.forEach(consentEntity::addGrantedClientScopeId);
|
|
||||||
|
|
||||||
return consentEntity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UserConsentModel toModel(RealmModel realm, UserConsentEntity entity) {
|
|
||||||
if (entity == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientModel client = realm.getClientById(entity.getClientId());
|
|
||||||
if (client == null) {
|
|
||||||
throw new ModelException("Client with id " + entity.getClientId() + " is not available");
|
|
||||||
}
|
|
||||||
UserConsentModel model = new UserConsentModel(client);
|
|
||||||
model.setCreatedDate(entity.getCreatedDate());
|
|
||||||
model.setLastUpdatedDate(entity.getLastUpdatedDate());
|
|
||||||
|
|
||||||
entity.getGrantedClientScopesIds().stream()
|
|
||||||
.map(scopeId -> KeycloakModelUtils.findClientScopeById(realm, client, scopeId))
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.forEach(model::addGrantedClientScope);
|
|
||||||
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getClientId() {
|
|
||||||
return clientId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setClientId(String clientId) {
|
|
||||||
this.updated = !Objects.equals(this.clientId, clientId);
|
|
||||||
this.clientId = clientId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<String> getGrantedClientScopesIds() {
|
|
||||||
return grantedClientScopesIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addGrantedClientScopeId(String scope) {
|
|
||||||
this.updated |= grantedClientScopesIds.add(scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGrantedClientScopesIds(Set<String> scopesIds) {
|
|
||||||
this.updated |= !Objects.equals(grantedClientScopesIds, scopesIds);
|
|
||||||
this.grantedClientScopesIds.clear();
|
|
||||||
this.grantedClientScopesIds.addAll(scopesIds);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeGrantedClientScopesIds(String scopesId) {
|
|
||||||
this.updated |= this.grantedClientScopesIds.remove(scopesId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getCreatedDate() {
|
|
||||||
return createdDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCreatedDate(Long createdDate) {
|
|
||||||
this.updated |= !Objects.equals(this.createdDate, createdDate);
|
|
||||||
this.createdDate = createdDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getLastUpdatedDate() {
|
|
||||||
return lastUpdatedDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLastUpdatedDate(Long lastUpdatedDate) {
|
|
||||||
this.updated |= !Objects.equals(this.lastUpdatedDate, lastUpdatedDate);
|
|
||||||
this.lastUpdatedDate = lastUpdatedDate;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,114 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020 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.user;
|
|
||||||
|
|
||||||
import org.keycloak.credential.CredentialModel;
|
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
|
||||||
import org.keycloak.models.map.common.UpdatableEntity;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public class UserCredentialEntity extends UpdatableEntity.Impl {
|
|
||||||
|
|
||||||
private String id;
|
|
||||||
private String type;
|
|
||||||
private String userLabel;
|
|
||||||
private Long createdDate;
|
|
||||||
private String secretData;
|
|
||||||
private String credentialData;
|
|
||||||
|
|
||||||
UserCredentialEntity() {}
|
|
||||||
|
|
||||||
public static UserCredentialEntity fromModel(CredentialModel model) {
|
|
||||||
UserCredentialEntity credentialEntity = new UserCredentialEntity();
|
|
||||||
String id = model.getId() == null ? KeycloakModelUtils.generateId() : model.getId();
|
|
||||||
credentialEntity.setId(id);
|
|
||||||
credentialEntity.setCreatedDate(model.getCreatedDate());
|
|
||||||
credentialEntity.setUserLabel(model.getUserLabel());
|
|
||||||
credentialEntity.setType(model.getType());
|
|
||||||
credentialEntity.setSecretData(model.getSecretData());
|
|
||||||
credentialEntity.setCredentialData(model.getCredentialData());
|
|
||||||
|
|
||||||
return credentialEntity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CredentialModel toModel(UserCredentialEntity entity) {
|
|
||||||
CredentialModel model = new CredentialModel();
|
|
||||||
model.setId(entity.getId());
|
|
||||||
model.setType(entity.getType());
|
|
||||||
model.setCreatedDate(entity.getCreatedDate());
|
|
||||||
model.setUserLabel(entity.getUserLabel());
|
|
||||||
model.setSecretData(entity.getSecretData());
|
|
||||||
model.setCredentialData(entity.getCredentialData());
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(String id) {
|
|
||||||
this.updated |= !Objects.equals(this.id, id);
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setType(String type) {
|
|
||||||
this.updated |= !Objects.equals(this.type, type);
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUserLabel() {
|
|
||||||
return userLabel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUserLabel(String userLabel) {
|
|
||||||
this.updated |= !Objects.equals(this.userLabel, userLabel);
|
|
||||||
this.userLabel = userLabel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getCreatedDate() {
|
|
||||||
return createdDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCreatedDate(Long createdDate) {
|
|
||||||
this.updated |= !Objects.equals(this.createdDate, createdDate);
|
|
||||||
this.createdDate = createdDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSecretData() {
|
|
||||||
return secretData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSecretData(String secretData) {
|
|
||||||
this.updated |= !Objects.equals(this.secretData, secretData);
|
|
||||||
this.secretData = secretData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCredentialData() {
|
|
||||||
return credentialData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCredentialData(String credentialData) {
|
|
||||||
this.updated |= !Objects.equals(this.credentialData, credentialData);
|
|
||||||
this.credentialData = credentialData;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,84 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020 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.user;
|
|
||||||
|
|
||||||
import org.keycloak.models.FederatedIdentityModel;
|
|
||||||
import org.keycloak.models.map.common.UpdatableEntity;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public class UserFederatedIdentityEntity extends UpdatableEntity.Impl {
|
|
||||||
private String token;
|
|
||||||
private String userId;
|
|
||||||
private String identityProvider;
|
|
||||||
private String userName;
|
|
||||||
|
|
||||||
private UserFederatedIdentityEntity() {}
|
|
||||||
|
|
||||||
public static UserFederatedIdentityEntity fromModel(FederatedIdentityModel model) {
|
|
||||||
if (model == null) return null;
|
|
||||||
UserFederatedIdentityEntity entity = new UserFederatedIdentityEntity();
|
|
||||||
entity.setIdentityProvider(model.getIdentityProvider());
|
|
||||||
entity.setUserId(model.getUserId());
|
|
||||||
entity.setUserName(model.getUserName().toLowerCase());
|
|
||||||
entity.setToken(model.getToken());
|
|
||||||
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FederatedIdentityModel toModel(UserFederatedIdentityEntity entity) {
|
|
||||||
if (entity == null) return null;
|
|
||||||
return new FederatedIdentityModel(entity.getIdentityProvider(), entity.getUserId(), entity.getUserName(), entity.getToken());
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getToken() {
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setToken(String token) {
|
|
||||||
this.updated |= !Objects.equals(this.token, token);
|
|
||||||
this.token = token;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUserId() {
|
|
||||||
return userId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUserId(String userId) {
|
|
||||||
this.updated |= !Objects.equals(this.userId, userId);
|
|
||||||
this.userId = userId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getIdentityProvider() {
|
|
||||||
return identityProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setIdentityProvider(String identityProvider) {
|
|
||||||
this.updated |= !Objects.equals(this.identityProvider, identityProvider);
|
|
||||||
this.identityProvider = identityProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUserName() {
|
|
||||||
return userName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUserName(String userName) {
|
|
||||||
this.updated |= !Objects.equals(this.userName, userName);
|
|
||||||
this.userName = userName;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020 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.user;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.hamcrest.Matchers;
|
|
||||||
import static org.junit.Assert.assertThat;
|
|
||||||
import org.keycloak.credential.CredentialModel;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class AbstractUserEntityCredentialsOrderTest {
|
|
||||||
|
|
||||||
private MapUserEntity user;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void init() {
|
|
||||||
user = new MapUserEntity("1", "realmId");
|
|
||||||
|
|
||||||
for (int i = 1; i <= 5; i++) {
|
|
||||||
UserCredentialEntity credentialModel = new UserCredentialEntity();
|
|
||||||
credentialModel.setId(Integer.toString(i));
|
|
||||||
|
|
||||||
user.addCredential(credentialModel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertOrder(Integer... ids) {
|
|
||||||
List<Integer> currentList = user.getCredentials().map(entity -> Integer.valueOf(entity.getId())).collect(Collectors.toList());
|
|
||||||
assertThat(currentList, Matchers.contains(ids));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCorrectOrder() {
|
|
||||||
assertOrder(1, 2, 3, 4, 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMoveToZero() {
|
|
||||||
user.moveCredential(2, 0);
|
|
||||||
assertOrder(3, 1, 2, 4, 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMoveBack() {
|
|
||||||
user.moveCredential(3, 1);
|
|
||||||
assertOrder(1, 4, 2, 3, 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMoveForward() {
|
|
||||||
user.moveCredential(1, 3);
|
|
||||||
assertOrder(1, 3, 4, 2, 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSamePosition() {
|
|
||||||
user.moveCredential(1, 1);
|
|
||||||
assertOrder(1, 2, 3, 4, 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSamePositionZero() {
|
|
||||||
user.moveCredential(0, 0);
|
|
||||||
assertOrder(1, 2, 3, 4, 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -31,6 +31,17 @@ import java.util.stream.Stream;
|
||||||
public interface UserCredentialStore extends Provider {
|
public interface UserCredentialStore extends Provider {
|
||||||
void updateCredential(RealmModel realm, UserModel user, CredentialModel cred);
|
void updateCredential(RealmModel realm, UserModel user, CredentialModel cred);
|
||||||
CredentialModel createCredential(RealmModel realm, UserModel user, CredentialModel cred);
|
CredentialModel createCredential(RealmModel realm, UserModel user, CredentialModel cred);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes credential with the {@code id} for the {@code user}.
|
||||||
|
*
|
||||||
|
* @param realm realm.
|
||||||
|
* @param user user
|
||||||
|
* @param id id
|
||||||
|
* @return {@code true} if the credential was removed, {@code false} otherwise
|
||||||
|
*
|
||||||
|
* TODO: Make this method return Boolean so that store can return "I don't know" answer, this can be used for example in async stores
|
||||||
|
*/
|
||||||
boolean removeStoredCredential(RealmModel realm, UserModel user, String id);
|
boolean removeStoredCredential(RealmModel realm, UserModel user, String id);
|
||||||
CredentialModel getStoredCredentialById(RealmModel realm, UserModel user, String id);
|
CredentialModel getStoredCredentialById(RealmModel realm, UserModel user, String id);
|
||||||
|
|
||||||
|
|
|
@ -202,6 +202,8 @@ public interface UserProvider extends Provider,
|
||||||
* @param userId id of the user
|
* @param userId id of the user
|
||||||
* @param clientInternalId id of the client
|
* @param clientInternalId id of the client
|
||||||
* @return {@code true} if the consent was removed, {@code false} otherwise
|
* @return {@code true} if the consent was removed, {@code false} otherwise
|
||||||
|
*
|
||||||
|
* TODO: Make this method return Boolean so that store can return "I don't know" answer, this can be used for example in async stores
|
||||||
*/
|
*/
|
||||||
boolean revokeConsentForClient(RealmModel realm, String userId, String clientInternalId);
|
boolean revokeConsentForClient(RealmModel realm, String userId, String clientInternalId);
|
||||||
|
|
||||||
|
@ -224,6 +226,8 @@ public interface UserProvider extends Provider,
|
||||||
* @param user the user model
|
* @param user the user model
|
||||||
* @param socialProvider alias of the identity provider, see {@link IdentityProviderModel#getAlias()}
|
* @param socialProvider alias of the identity provider, see {@link IdentityProviderModel#getAlias()}
|
||||||
* @return {@code true} if the association was removed, {@code false} otherwise
|
* @return {@code true} if the association was removed, {@code false} otherwise
|
||||||
|
*
|
||||||
|
* TODO: Make this method return Boolean so that store can return "I don't know" answer, this can be used for example in async stores
|
||||||
*/
|
*/
|
||||||
boolean removeFederatedIdentity(RealmModel realm, UserModel user, String socialProvider);
|
boolean removeFederatedIdentity(RealmModel realm, UserModel user, String socialProvider);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue