Skip to content

Commit

Permalink
add update of users identityProviderLinks
Browse files Browse the repository at this point in the history
  • Loading branch information
ntruchsess committed Aug 4, 2023
1 parent f960e25 commit b2cc0ec
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 6,337 deletions.
12 changes: 6 additions & 6 deletions src/keycloak/Keycloak.Library/Users/KeycloakClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -169,26 +169,26 @@ public async Task<IEnumerable<FederatedIdentity>> GetUserSocialLoginsAsync(strin
.GetJsonAsync<IEnumerable<FederatedIdentity>>()
.ConfigureAwait(false);

public async Task AddUserSocialLoginProviderAsync(string realm, string userId, string provider, FederatedIdentity federatedIdentity) =>
await (await GetBaseUrlAsync(realm).ConfigureAwait(false))
public async Task AddUserSocialLoginProviderAsync(string realm, string userId, string provider, FederatedIdentity federatedIdentity, CancellationToken cancellationToken = default) =>
await (await GetBaseUrlAsync(realm, cancellationToken).ConfigureAwait(false))
.AppendPathSegment("/admin/realms/")
.AppendPathSegment(realm, true)
.AppendPathSegment("/users/")
.AppendPathSegment(userId, true)
.AppendPathSegment("/federated-identity/")
.AppendPathSegment(provider, true)
.PostJsonAsync(federatedIdentity)
.PostJsonAsync(federatedIdentity, cancellationToken)
.ConfigureAwait(false);

public async Task RemoveUserSocialLoginProviderAsync(string realm, string userId, string provider) =>
await (await GetBaseUrlAsync(realm).ConfigureAwait(false))
public async Task RemoveUserSocialLoginProviderAsync(string realm, string userId, string provider, CancellationToken cancellationToken = default) =>
await (await GetBaseUrlAsync(realm, cancellationToken).ConfigureAwait(false))
.AppendPathSegment("/admin/realms/")
.AppendPathSegment(realm, true)
.AppendPathSegment("/users/")
.AppendPathSegment(userId, true)
.AppendPathSegment("/federated-identity/")
.AppendPathSegment(provider, true)
.DeleteAsync()
.DeleteAsync(cancellationToken)
.ConfigureAwait(false);

public async Task<IEnumerable<Group>> GetUserGroupsAsync(string realm, string userId) =>
Expand Down
105 changes: 89 additions & 16 deletions src/keycloak/Keycloak.Seeding/BusinessLogic/UsersUpdater.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,20 @@ public async Task UpdateUsers(string keycloakInstanceName, CancellationToken can
seedUser,
cancellationToken).ConfigureAwait(false);

await UpdateClientAndRealmRoles(keycloak, realm, userId, seedUser, clientsDictionary, cancellationToken).ConfigureAwait(false);
await UpdateClientAndRealmRoles(
keycloak,
realm,
userId,
seedUser,
clientsDictionary,
cancellationToken).ConfigureAwait(false);

await UpdateFederatedIdentities(
keycloak,
realm,
userId,
seedUser.FederatedIdentities ?? Enumerable.Empty<FederatedIdentityModel>(),
cancellationToken).ConfigureAwait(false);
}
}

Expand All @@ -78,11 +91,10 @@ private static async Task<string> CreateOrUpdateUserReturningId(KeycloakClient k
throw new ConflictException($"user.Id must not be null: userName {seedUser.Username}");
if (!CompareUser(user, seedUser))
{
var updateUser = CreateUpdateUser(user.Id, seedUser);
await keycloak.UpdateUserAsync(
realm,
user.Id,
updateUser,
CreateUpdateUser(user.Id, seedUser),
cancellationToken).ConfigureAwait(false);
}
return user.Id;
Expand Down Expand Up @@ -157,12 +169,7 @@ private static async Task UpdateUserRoles(Func<Task<IEnumerable<Role>>> getUserR
Attributes = update.Attributes?.ToDictionary(x => x.Key, x => x.Value),
// ClientConsents = update.ClientConsents,
// Credentials = update.Credentials,
FederatedIdentities = update.FederatedIdentities?.Select(x => new FederatedIdentity
{ // TODO: this works only on usercreation, it does not update existing identities
IdentityProvider = x.IdentityProvider,
UserId = x.UserId,
UserName = x.UserName
}),
// FederatedIdentities: doesn't update
// FederationLink = update.FederationLink,
Groups = update.Groups,
// Origin = update.Origin,
Expand All @@ -186,17 +193,83 @@ private static bool CompareUser(User user, UserModel update) =>
user.Attributes.NullOrContentEqual(update.Attributes) &&
// ClientConsents == update.ClientConsents &&
// Credentials == update.Credentials &&
CompareFederatedIdentities(user.FederatedIdentities, update.FederatedIdentities) && // doesn't update
// CompareFederatedIdentities(user.FederatedIdentities, update.FederatedIdentities) && // doesn't update
// FederationLink == update.FederationLink &&
user.Groups.NullOrContentEqual(update.Groups) &&
// Origin == update.Origin &&
// Self == update.Self &&
user.ServiceAccountClientId == update.ServiceAccountClientId;

private static bool CompareFederatedIdentities(IEnumerable<FederatedIdentity>? identities, IEnumerable<FederatedIdentityModel>? updates) =>
identities == null && updates == null ||
identities != null && updates != null &&
identities.Select(x => x.IdentityProvider ?? throw new ConflictException("keycloak federated identity identityProvider must not be null")).NullOrContentEqual(updates.Select(x => x.IdentityProvider ?? throw new ConflictException("seeding federated identity identityProvider must not be null"))) &&
identities.Select(x => x.UserId ?? throw new ConflictException("keycloak federated identity identityProvider must not be null")).NullOrContentEqual(updates.Select(x => x.UserId ?? throw new ConflictException("seeding federated identity identityProvider must not be null"))) &&
identities.Select(x => x.UserName ?? throw new ConflictException("keycloak federated identity identityProvider must not be null")).NullOrContentEqual(updates.Select(x => x.UserName ?? throw new ConflictException("seeding federated identity identityProvider must not be null")));
private static bool CompareFederatedIdentity(FederatedIdentity identity, FederatedIdentityModel update) =>
identity.IdentityProvider == update.IdentityProvider &&
identity.UserId == update.UserId &&
identity.UserName == update.UserName;

private static async Task UpdateFederatedIdentities(KeycloakClient keycloak, string realm, string userId, IEnumerable<FederatedIdentityModel> updates, CancellationToken cancellationToken)
{
var identities = await keycloak.GetUserSocialLoginsAsync(realm, userId).ConfigureAwait(false);
await DeleteObsoleteFederatedIdentities(keycloak, realm, userId, identities, updates, cancellationToken).ConfigureAwait(false);
await CreateMissingFederatedIdentities(keycloak, realm, userId, identities, updates, cancellationToken).ConfigureAwait(false);
await UpdateExistingFederatedIdentities(keycloak, realm, userId, identities, updates, cancellationToken).ConfigureAwait(false);
}

private static async Task DeleteObsoleteFederatedIdentities(KeycloakClient keycloak, string realm, string userId, IEnumerable<FederatedIdentity> identities, IEnumerable<FederatedIdentityModel> updates, CancellationToken cancellationToken)
{
foreach (var identity in identities.ExceptBy(updates.Select(x => x.IdentityProvider), x => x.IdentityProvider))
{
await keycloak.RemoveUserSocialLoginProviderAsync(
realm,
userId,
identity.IdentityProvider ?? throw new ConflictException($"federatedIdentity.IdentityProvider is null {userId}"),
cancellationToken).ConfigureAwait(false);
}
}

private static async Task CreateMissingFederatedIdentities(KeycloakClient keycloak, string realm, string userId, IEnumerable<FederatedIdentity> identities, IEnumerable<FederatedIdentityModel> updates, CancellationToken cancellationToken)
{
foreach (var update in updates.ExceptBy(identities.Select(x => x.IdentityProvider), x => x.IdentityProvider))
{
await keycloak.AddUserSocialLoginProviderAsync(
realm,
userId,
update.IdentityProvider ?? throw new ConflictException($"federatedIdentity.IdentityProvider is null {userId}"),
new ()
{
IdentityProvider = update.IdentityProvider,
UserId = update.UserId ?? throw new ConflictException($"federatedIdentity.UserId is null {userId}, {update.IdentityProvider}"),
UserName = update.UserName ?? throw new ConflictException($"federatedIdentity.UserName is null {userId}, {update.IdentityProvider}")
},
cancellationToken).ConfigureAwait(false);
}
}

private static async Task UpdateExistingFederatedIdentities(KeycloakClient keycloak, string realm, string userId, IEnumerable<FederatedIdentity> identities, IEnumerable<FederatedIdentityModel> updates, CancellationToken cancellationToken)
{
foreach (var (identity, update) in identities
.Join(
updates,
x => x.IdentityProvider,
x => x.IdentityProvider,
(identity, update) => (Identity: identity, Update: update))
.Where(x => !CompareFederatedIdentity(x.Identity, x.Update)))
{
await keycloak.RemoveUserSocialLoginProviderAsync(
realm,
userId,
identity.IdentityProvider ?? throw new ConflictException($"federatedIdentity.IdentityProvider is null {userId}"),
cancellationToken).ConfigureAwait(false);

await keycloak.AddUserSocialLoginProviderAsync(
realm,
userId,
update.IdentityProvider ?? throw new ConflictException($"federatedIdentity.IdentityProvider is null {userId}"),
new ()
{
IdentityProvider = update.IdentityProvider,
UserId = update.UserId ?? throw new ConflictException($"federatedIdentity.UserId is null {userId}, {update.IdentityProvider}"),
UserName = update.UserName ?? throw new ConflictException($"federatedIdentity.UserName is null {userId}, {update.IdentityProvider}")
},
cancellationToken).ConfigureAwait(false);
}
}
}
Loading

0 comments on commit b2cc0ec

Please sign in to comment.