Make sure federationLink always map to the storage provider associated with federated users
Closes #31670 Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
parent
f9d3895550
commit
eeae50fb43
17 changed files with 45 additions and 42 deletions
|
@ -67,7 +67,6 @@ public class UserRepresentation extends AbstractUserRepresentation{
|
|||
this.setUserProfileMetadata(rep.getUserProfileMetadata());
|
||||
|
||||
this.self = rep.getSelf();
|
||||
this.origin = rep.getOrigin();
|
||||
this.createdTimestamp = rep.getCreatedTimestamp();
|
||||
this.enabled = rep.isEnabled();
|
||||
this.totp = rep.isTotp();
|
||||
|
@ -220,13 +219,21 @@ public class UserRepresentation extends AbstractUserRepresentation{
|
|||
* Returns id of UserStorageProvider that loaded this user
|
||||
*
|
||||
* @return NULL if user stored locally
|
||||
* @deprecated Use {@link #getFederationLink()} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public String getOrigin() {
|
||||
return origin;
|
||||
return federationLink;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param origin the origin
|
||||
* @deprecated Use {@link #setFederationLink(String)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public void setOrigin(String origin) {
|
||||
this.origin = origin;
|
||||
// deprecated
|
||||
}
|
||||
|
||||
public Set<String> getDisableableCredentialTypes() {
|
||||
|
|
|
@ -129,3 +129,9 @@ The `_LEGACY` cookies also served another purpose, which was to allow login from
|
|||
not recommended at all in production deployments of Keycloak, it is fairly frequent to access Keycloak over `http` outside
|
||||
of `localhost`. As an alternative to the `_LEGACY` cookies Keycloak now doesn't set the `secure` flag and sets `SameSite=Lax`
|
||||
instead of `SameSite=None` when it detects an insecure context is used.
|
||||
|
||||
= Property `origin` in the `UserRepresentation` is deprecated
|
||||
|
||||
The `origin` property in the `UserRepresentation` is deprecated and planned to be removed in future releases.
|
||||
|
||||
Instead, prefer using the `federationLink` property to obtain the provider to which a user is linked with.
|
||||
|
|
|
@ -25,10 +25,10 @@ export const FederatedUserLink = ({ user }: FederatedUserLinkProps) => {
|
|||
() =>
|
||||
access.hasAccess("view-realm")
|
||||
? adminClient.components.findOne({
|
||||
id: (user.federationLink || user.origin)!,
|
||||
id: user.federationLink!,
|
||||
})
|
||||
: adminClient.userStorageProvider.name({
|
||||
id: (user.federationLink || user.origin)!,
|
||||
id: user.federationLink!,
|
||||
}),
|
||||
setComponent,
|
||||
[],
|
||||
|
|
|
@ -354,7 +354,7 @@ export const UserCredentials = ({ user, setUser }: UserCredentialsProps) => {
|
|||
toggleDeleteDialog();
|
||||
};
|
||||
|
||||
const useFederatedCredentials = user.federationLink || user.origin;
|
||||
const useFederatedCredentials = user.federationLink;
|
||||
const [credentialTypes, setCredentialTypes] = useState<string[]>([]);
|
||||
|
||||
useFetch(
|
||||
|
|
|
@ -208,7 +208,7 @@ export const UserForm = ({
|
|||
label="requiredUserActions"
|
||||
help="requiredUserActionsHelp"
|
||||
/>
|
||||
{(user?.federationLink || user?.origin) && canViewFederationLink && (
|
||||
{user?.federationLink && canViewFederationLink && (
|
||||
<FormGroup
|
||||
label={t("federationLink")}
|
||||
labelIcon={
|
||||
|
|
|
@ -27,7 +27,6 @@ export default interface UserRepresentation {
|
|||
firstName?: string;
|
||||
groups?: string[];
|
||||
lastName?: string;
|
||||
origin?: string;
|
||||
realmRoles?: string[];
|
||||
self?: string;
|
||||
serviceAccountClientId?: string;
|
||||
|
|
|
@ -350,13 +350,11 @@ public class UserCacheSession implements UserCache, OnCreateComponent, OnUpdateC
|
|||
}
|
||||
}
|
||||
|
||||
StorageId storageId = delegate.getFederationLink() != null ?
|
||||
new StorageId(delegate.getFederationLink(), delegate.getId()) : new StorageId(delegate.getId());
|
||||
CachedUser cached = null;
|
||||
UserAdapter adapter = null;
|
||||
CachedUser cached;
|
||||
UserAdapter adapter;
|
||||
|
||||
if (!storageId.isLocal()) {
|
||||
ComponentModel component = realm.getComponent(storageId.getProviderId());
|
||||
if (delegate.getFederationLink() != null) {
|
||||
ComponentModel component = realm.getComponent(delegate.getFederationLink());
|
||||
UserStorageProviderModel model = new UserStorageProviderModel(component);
|
||||
if (!model.isEnabled()) {
|
||||
return new ReadOnlyUserModelDelegate(delegate) {
|
||||
|
|
|
@ -61,7 +61,7 @@ public class UserCredentialManager extends AbstractStorageManager<UserStoragePro
|
|||
|
||||
List<CredentialInput> toValidate = new LinkedList<>(inputs);
|
||||
|
||||
String providerId = StorageId.isLocalStorage(user.getId()) ? user.getFederationLink() : StorageId.providerId(user.getId());
|
||||
String providerId = user.getFederationLink();
|
||||
if (providerId != null) {
|
||||
UserStorageProviderModel model = getStorageProviderModel(realm, providerId);
|
||||
if (model == null || !model.isEnabled()) return false;
|
||||
|
@ -80,8 +80,8 @@ public class UserCredentialManager extends AbstractStorageManager<UserStoragePro
|
|||
|
||||
@Override
|
||||
public boolean updateCredential(CredentialInput input) {
|
||||
String providerId = StorageId.isLocalStorage(user.getId()) ? user.getFederationLink() : StorageId.providerId(user.getId());
|
||||
if (!StorageId.isLocalStorage(user.getId())) throwExceptionIfInvalidUser(user);
|
||||
String providerId = user.getFederationLink();
|
||||
|
||||
if (providerId != null) {
|
||||
UserStorageProviderModel model = getStorageProviderModel(realm, providerId);
|
||||
|
@ -152,8 +152,8 @@ public class UserCredentialManager extends AbstractStorageManager<UserStoragePro
|
|||
|
||||
@Override
|
||||
public void disableCredentialType(String credentialType) {
|
||||
String providerId = StorageId.isLocalStorage(user.getId()) ? user.getFederationLink() : StorageId.providerId(user.getId());
|
||||
if (!StorageId.isLocalStorage(user.getId())) throwExceptionIfInvalidUser(user);
|
||||
String providerId = user.getFederationLink();
|
||||
if (providerId != null) {
|
||||
UserStorageProviderModel model = getStorageProviderModel(realm, providerId);
|
||||
if (model == null || !model.isEnabled()) return;
|
||||
|
@ -172,7 +172,7 @@ public class UserCredentialManager extends AbstractStorageManager<UserStoragePro
|
|||
@Override
|
||||
public Stream<String> getDisableableCredentialTypesStream() {
|
||||
Stream<String> types = Stream.empty();
|
||||
String providerId = StorageId.isLocalStorage(user) ? user.getFederationLink() : StorageId.resolveProviderId(user);
|
||||
String providerId = user.getFederationLink();
|
||||
if (providerId != null) {
|
||||
UserStorageProviderModel model = getStorageProviderModel(realm, providerId);
|
||||
if (model == null || !model.isEnabled()) return types;
|
||||
|
@ -231,7 +231,7 @@ public class UserCredentialManager extends AbstractStorageManager<UserStoragePro
|
|||
}
|
||||
|
||||
private UserStorageCredentialConfigured isConfiguredThroughUserStorage(RealmModel realm, UserModel user, String type) {
|
||||
String providerId = StorageId.isLocalStorage(user) ? user.getFederationLink() : StorageId.resolveProviderId(user);
|
||||
String providerId = user.getFederationLink();
|
||||
if (providerId != null) {
|
||||
UserStorageProviderModel model = getStorageProviderModel(realm, providerId);
|
||||
if (model == null || !model.isEnabled()) return UserStorageCredentialConfigured.USER_STORAGE_DISABLED;
|
||||
|
|
|
@ -240,7 +240,7 @@ public abstract class AbstractUserAdapter extends UserModelDefaultMethods {
|
|||
*/
|
||||
@Override
|
||||
public String getFederationLink() {
|
||||
return null;
|
||||
return StorageId.providerId(getId());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -247,7 +247,7 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
|
|||
*/
|
||||
@Override
|
||||
public String getFederationLink() {
|
||||
return null;
|
||||
return StorageId.providerId(getId());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -220,8 +220,6 @@ public class ModelToRepresentation {
|
|||
public static UserRepresentation toRepresentation(KeycloakSession session, RealmModel realm, UserModel user) {
|
||||
UserRepresentation rep = new UserRepresentation();
|
||||
rep.setId(user.getId());
|
||||
String providerId = StorageId.resolveProviderId(user);
|
||||
rep.setOrigin(providerId);
|
||||
rep.setUsername(user.getUsername());
|
||||
rep.setCreatedTimestamp(user.getCreatedTimestamp());
|
||||
rep.setLastName(user.getLastName());
|
||||
|
|
|
@ -28,10 +28,12 @@ import org.keycloak.models.UserModelDefaultMethods;
|
|||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.utils.RoleUtils;
|
||||
import org.keycloak.storage.ReadOnlyException;
|
||||
import org.keycloak.storage.StorageId;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
@ -235,7 +237,7 @@ public abstract class AbstractInMemoryUserAdapter extends UserModelDefaultMethod
|
|||
|
||||
@Override
|
||||
public String getFederationLink() {
|
||||
return federationLink;
|
||||
return Optional.ofNullable(federationLink).orElse(StorageId.providerId(getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -333,11 +333,6 @@ public class DefaultAttributes extends HashMap<String, List<String>> implements
|
|||
}
|
||||
|
||||
String providerId = user.getFederationLink();
|
||||
|
||||
if (providerId == null) {
|
||||
providerId = StorageId.providerId(user.getId());
|
||||
}
|
||||
|
||||
UserProvider userProvider = session.users();
|
||||
|
||||
if (userProvider instanceof UserProfileDecorator) {
|
||||
|
@ -460,7 +455,7 @@ public class DefaultAttributes extends HashMap<String, List<String>> implements
|
|||
Stream<String> valuesStream = Optional.ofNullable(values).orElse(EMPTY_VALUE).stream().filter(Objects::nonNull);
|
||||
|
||||
// do not normalize the username if a federated user because we need to respect the format from the external identity store
|
||||
if ((UserModel.USERNAME.equals(name) && isLocalUser()) || UserModel.EMAIL.equals(name)) {
|
||||
if ((UserModel.USERNAME.equals(name) && !isFederated()) || UserModel.EMAIL.equals(name)) {
|
||||
valuesStream = valuesStream.map(KeycloakModelUtils::toLowerCaseSafe);
|
||||
}
|
||||
|
||||
|
@ -571,7 +566,7 @@ public class DefaultAttributes extends HashMap<String, List<String>> implements
|
|||
};
|
||||
}
|
||||
|
||||
private boolean isLocalUser() {
|
||||
return user == null || user.isLocal();
|
||||
private boolean isFederated() {
|
||||
return user != null && user.isFederated();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,11 +17,9 @@
|
|||
|
||||
package org.keycloak.models;
|
||||
|
||||
import static org.keycloak.utils.StringUtil.isBlank;
|
||||
import static org.keycloak.utils.StringUtil.isNotBlank;
|
||||
|
||||
import org.keycloak.provider.ProviderEvent;
|
||||
import org.keycloak.storage.StorageId;
|
||||
import org.keycloak.utils.StringUtil;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
@ -221,10 +219,10 @@ public interface UserModel extends RoleMapperModel {
|
|||
* Indicates if this {@link UserModel} maps to a local account or an account
|
||||
* federated from an external user storage.
|
||||
*
|
||||
* @return {@code true} if a local account. Otherwise, {@code false}.
|
||||
* @return {@code true} if a federated account. Otherwise, {@code false}.
|
||||
*/
|
||||
default boolean isLocal() {
|
||||
return isBlank(getFederationLink()) && StorageId.isLocalStorage(getId());
|
||||
default boolean isFederated() {
|
||||
return isNotBlank(getFederationLink());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -62,7 +62,7 @@ public class ImmutableAttributeValidator implements SimpleValidator {
|
|||
Stream<String> rawValues = user.getAttributeStream(inputHint).filter(Objects::nonNull);
|
||||
|
||||
// force usernames to lower-case to avoid validation errors if the external storage is using a different format
|
||||
if (user.isLocal() && UserModel.USERNAME.equals(inputHint)) {
|
||||
if (!user.isFederated() && UserModel.USERNAME.equals(inputHint)) {
|
||||
rawValues = rawValues.map(String::toLowerCase);
|
||||
}
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ public class LDAPProvidersIntegrationNoImportTest extends LDAPProvidersIntegrati
|
|||
|
||||
// TODO: It should be possibly LDAP_ID (LDAP UUID) used as an externalId inside storageId...
|
||||
Assert.assertEquals(storageId.getExternalId(), user.getUsername());
|
||||
Assert.assertNull(user.getFederationLink());
|
||||
Assert.assertNotNull(user.getFederationLink());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -421,8 +421,8 @@ public class UserStorageTest extends AbstractAuthTest {
|
|||
|
||||
memuser = user(uid).toRepresentation();
|
||||
assertNotNull(memuser);
|
||||
assertNotNull(memuser.getOrigin());
|
||||
ComponentRepresentation origin = testRealmResource().components().component(memuser.getOrigin()).toRepresentation();
|
||||
assertNotNull(memuser.getFederationLink());
|
||||
ComponentRepresentation origin = testRealmResource().components().component(memuser.getFederationLink()).toRepresentation();
|
||||
Assert.assertEquals("memory", origin.getName());
|
||||
|
||||
testRealmResource().users().get(memuser.getId()).remove();
|
||||
|
|
Loading…
Reference in a new issue