20 changed files with 708 additions and 149 deletions
@ -1,2 +0,0 @@ |
|||
#Payment__PayPal__ClientId=PAYPAL_CLIENT_ID |
|||
#Payment__PayPal__Secret=PAYPAL_SECRET |
|||
@ -1,45 +1,55 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using EShopOnAbp.IdentityService.Keycloak; |
|||
using EShopOnAbp.IdentityService.Keycloak.Service; |
|||
using Keycloak.Net.Models.Roles; |
|||
using Microsoft.Extensions.Logging; |
|||
using Volo.Abp.BackgroundJobs; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.ObjectMapping; |
|||
|
|||
namespace EShopOnAbp.IdentityService.BackgroundJobs.Roles; |
|||
|
|||
public class KeycloakRoleUpdatingJob : AsyncBackgroundJob<IdentityRoleUpdatingArgs>, ITransientDependency |
|||
{ |
|||
private readonly KeycloakService _keycloakService; |
|||
private readonly IKeycloakService _keycloakService; |
|||
private readonly ILogger<KeycloakRoleUpdatingJob> _logger; |
|||
private readonly IObjectMapper _objectMapper; |
|||
|
|||
public KeycloakRoleUpdatingJob(KeycloakService keycloakService, ILogger<KeycloakRoleUpdatingJob> logger) |
|||
public KeycloakRoleUpdatingJob(IKeycloakService keycloakService, ILogger<KeycloakRoleUpdatingJob> logger, |
|||
IObjectMapper objectMapper) |
|||
{ |
|||
_keycloakService = keycloakService; |
|||
_logger = logger; |
|||
_objectMapper = objectMapper; |
|||
} |
|||
|
|||
public override async Task ExecuteAsync(IdentityRoleUpdatingArgs args) |
|||
{ |
|||
try |
|||
{ |
|||
var existingRole = (await _keycloakService.GetRolesAsync()).FirstOrDefault(q => q.Name == args.Name); |
|||
var existingRole = (await _keycloakService.GetRolesAsync()).FirstOrDefault(q => q.Name == args.OldName); |
|||
if (existingRole == null) |
|||
{ |
|||
_logger.LogWarning($"Role with the name:{args.Name} couldn't be found to update!"); |
|||
_logger.LogWarning($"Role with the name:{args.OldName} couldn't be found to update!"); |
|||
return; |
|||
} |
|||
|
|||
existingRole.Name = args.Name; |
|||
if (args.OldName != args.NewName) |
|||
{ |
|||
existingRole.Name = args.NewName; |
|||
|
|||
await _keycloakService.UpdateRoleAsync(existingRole.Id, existingRole); |
|||
await _keycloakService.UpdateRoleAsync(existingRole.Id, |
|||
_objectMapper.Map<CachedKeycloakRole, Role>(existingRole) |
|||
); |
|||
} |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
_logger.LogWarning($"Could not delete the role with the name:{args.Name} from Keycloak server!"); |
|||
_logger.LogWarning($"Could not update the role with the name:{args.OldName} from Keycloak server!"); |
|||
throw; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public record IdentityRoleUpdatingArgs(string Name); |
|||
public record IdentityRoleUpdatingArgs(string OldName, string NewName); |
|||
@ -0,0 +1,21 @@ |
|||
using AutoMapper; |
|||
using EShopOnAbp.IdentityService.Keycloak.Service; |
|||
using Keycloak.Net.Models.Roles; |
|||
using Keycloak.Net.Models.Users; |
|||
|
|||
namespace EShopOnAbp.IdentityService; |
|||
|
|||
public class EShopOnAbpIdentityServiceAutoMapperProfile : Profile |
|||
{ |
|||
public EShopOnAbpIdentityServiceAutoMapperProfile() |
|||
{ |
|||
CreateMap<User, CachedKeycloakUser>().ReverseMap(); |
|||
CreateMap<UserAccess, CachedUserAccess>().ReverseMap(); |
|||
CreateMap<UserConsent, CachedUserConsent>().ReverseMap(); |
|||
CreateMap<Credentials, CachedCredentials>().ReverseMap(); |
|||
CreateMap<FederatedIdentity, CachedFederatedIdentity>(); |
|||
|
|||
CreateMap<Role, CachedKeycloakRole>().ReverseMap(); |
|||
CreateMap<RoleComposite, CachedRoleComposite>().ReverseMap(); |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
// using System;
|
|||
// using System.Linq;
|
|||
// using System.Threading.Tasks;
|
|||
// using EShopOnAbp.IdentityService.Keycloak;
|
|||
// using Microsoft.Extensions.Logging;
|
|||
// using Volo.Abp.DependencyInjection;
|
|||
// using Volo.Abp.Domain.Entities.Events.Distributed;
|
|||
// using Volo.Abp.EventBus.Distributed;
|
|||
// using Volo.Abp.Identity;
|
|||
//
|
|||
// namespace EShopOnAbp.IdentityService.EventHandlers.Roles;
|
|||
//
|
|||
// public class KeycloakRolesEventHandler : IDistributedEventHandler<EntityCreatedEto<IdentityRoleEto>>,
|
|||
// ITransientDependency
|
|||
// {
|
|||
// private readonly KeycloakService _keycloakService;
|
|||
// private readonly ILogger<KeycloakRolesEventHandler> _logger;
|
|||
//
|
|||
// public KeycloakRolesEventHandler(KeycloakService keycloakService, ILogger<KeycloakRolesEventHandler> logger)
|
|||
// {
|
|||
// _keycloakService = keycloakService;
|
|||
// _logger = logger;
|
|||
// }
|
|||
//
|
|||
// public async Task HandleEventAsync(EntityCreatedEto<IdentityRoleEto> eventData)
|
|||
// {
|
|||
// try
|
|||
// {
|
|||
// var existingRole = (await _keycloakService.GetRolesAsync()).FirstOrDefault(q => q.Name == eventData.Entity.Name);
|
|||
// if (existingRole != null)
|
|||
// {
|
|||
// return;
|
|||
// }
|
|||
//
|
|||
// var isSuccess = await _keycloakService.CreateRoleAsync(eventData.Entity.Name);
|
|||
// if (isSuccess)
|
|||
// {
|
|||
// _logger.LogInformation($"Role created:{eventData.Entity.Name}");
|
|||
// }
|
|||
// }
|
|||
// catch (Exception e)
|
|||
// {
|
|||
// _logger.LogError($"Keycloak role creation with the name:{eventData.Entity.Name} failed!");
|
|||
// throw;
|
|||
// }
|
|||
// }
|
|||
// }
|
|||
@ -1,85 +0,0 @@ |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Keycloak.Net; |
|||
using Keycloak.Net.Models.Roles; |
|||
using Keycloak.Net.Models.Users; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace EShopOnAbp.IdentityService.Keycloak; |
|||
|
|||
public class KeycloakService : ITransientDependency |
|||
{ |
|||
private readonly KeycloakClient _keycloakClient; |
|||
private readonly KeycloakClientOptions _keycloakOptions; |
|||
|
|||
public KeycloakService(IOptions<KeycloakClientOptions> keycloakOptions) |
|||
{ |
|||
_keycloakOptions = keycloakOptions.Value; |
|||
|
|||
_keycloakClient = new KeycloakClient( |
|||
_keycloakOptions.Url, |
|||
_keycloakOptions.AdminUserName, |
|||
_keycloakOptions.AdminPassword |
|||
); |
|||
} |
|||
|
|||
public Task<IEnumerable<User>> GetUsersAsync(string search = null, string username = null, string email = null, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return _keycloakClient.GetUsersAsync(_keycloakOptions.RealmName, search: search, username: username, |
|||
email: email, cancellationToken: cancellationToken); |
|||
} |
|||
|
|||
public Task<User> GetUserAsync(string userId, CancellationToken cancellationToken = default) |
|||
{ |
|||
return _keycloakClient.GetUserAsync(_keycloakOptions.RealmName, userId, cancellationToken: cancellationToken); |
|||
} |
|||
|
|||
public Task<bool> CreateUserAsync(User user, CancellationToken cancellationToken = default) |
|||
{ |
|||
return _keycloakClient.CreateUserAsync(_keycloakOptions.RealmName, user, cancellationToken); |
|||
} |
|||
|
|||
public Task<bool> UpdateUserAsync(string userId, User user, CancellationToken cancellationToken = default) |
|||
{ |
|||
return _keycloakClient.UpdateUserAsync(_keycloakOptions.RealmName, userId, user, cancellationToken); |
|||
} |
|||
|
|||
public Task<bool> DeleteUserAsync(string userId, CancellationToken cancellationToken = default) |
|||
{ |
|||
return _keycloakClient.DeleteUserAsync(_keycloakOptions.RealmName, userId, cancellationToken); |
|||
} |
|||
|
|||
public Task<IEnumerable<Role>> GetRolesAsync(CancellationToken cancellationToken = default) |
|||
{ |
|||
return _keycloakClient.GetRolesAsync(_keycloakOptions.RealmName, cancellationToken: cancellationToken); |
|||
} |
|||
|
|||
public Task<bool> AddRolesToUserAsync(string userId, IEnumerable<Role> roles, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return _keycloakClient.AddRealmRoleMappingsToUserAsync(_keycloakOptions.RealmName, userId, roles, |
|||
cancellationToken); |
|||
} |
|||
|
|||
public Task<bool> CreateRoleAsync(string name, CancellationToken cancellationToken = default) |
|||
{ |
|||
Role role = new Role |
|||
{ |
|||
Name = name |
|||
}; |
|||
return _keycloakClient.CreateRoleAsync(_keycloakOptions.RealmName, role, cancellationToken); |
|||
} |
|||
|
|||
public Task<bool> DeleteRoleByIdAsync(string id, CancellationToken cancellationToken = default) |
|||
{ |
|||
return _keycloakClient.DeleteRoleByIdAsync(_keycloakOptions.RealmName, id, cancellationToken); |
|||
} |
|||
|
|||
public Task<bool> UpdateRoleAsync(string id, Role role, CancellationToken cancellationToken = default) |
|||
{ |
|||
return _keycloakClient.UpdateRoleByIdAsync(_keycloakOptions.RealmName, id, role, cancellationToken); |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.Caching; |
|||
|
|||
namespace EShopOnAbp.IdentityService.Keycloak.Service; |
|||
|
|||
[CacheName("KeycloakRole")] |
|||
public class CachedKeycloakRole |
|||
{ |
|||
public string Id { get; set; } |
|||
public string Name { get; set; } |
|||
public string Description { get; set; } |
|||
public bool? Composite { get; set; } |
|||
public CachedRoleComposite Composites { get; set; } |
|||
public bool? ClientRole { get; set; } |
|||
public string ContainerId { get; set; } |
|||
public IDictionary<string, object> Attributes { get; set; } |
|||
} |
|||
|
|||
public class CachedRoleComposite |
|||
{ |
|||
public IDictionary<string, string> Client { get; set; } |
|||
public IEnumerable<string> Realm { get; set; } |
|||
} |
|||
@ -0,0 +1,127 @@ |
|||
// using System.Collections.Generic;
|
|||
// using System.Linq;
|
|||
// using System.Threading;
|
|||
// using System.Threading.Tasks;
|
|||
// using Keycloak.Net.Models.Roles;
|
|||
// using Keycloak.Net.Models.Users;
|
|||
// using Volo.Abp.Caching;
|
|||
// using Volo.Abp.DependencyInjection;
|
|||
//
|
|||
// namespace EShopOnAbp.IdentityService.Keycloak.Service;
|
|||
//
|
|||
// [ExposeServices(typeof(CachedKeycloakService))]
|
|||
// public class CachedKeycloakService : IKeycloakService
|
|||
// {
|
|||
// protected const string UsersCacheKey = "KeycloakUsers";
|
|||
// protected const string RolesCacheKey = "KeycloakRoles";
|
|||
// private readonly IKeycloakService _keycloakService;
|
|||
// private readonly IDistributedCache<List<User>> _keycloakUsersCache;
|
|||
// private readonly IDistributedCache<List<Role>> _keycloakRolesCache;
|
|||
//
|
|||
// public CachedKeycloakService(IKeycloakService keycloakService,
|
|||
// IDistributedCache<List<User>> keycloakUsersCache,
|
|||
// IDistributedCache<List<Role>> keycloakRolesCache)
|
|||
// {
|
|||
// _keycloakService = keycloakService;
|
|||
// _keycloakUsersCache = keycloakUsersCache;
|
|||
// _keycloakRolesCache = keycloakRolesCache;
|
|||
// }
|
|||
//
|
|||
// public async Task<IEnumerable<User>> GetUsersAsync(string search = null, string username = null,
|
|||
// string email = null,
|
|||
// CancellationToken cancellationToken = default)
|
|||
// {
|
|||
// var users = await _keycloakUsersCache.GetAsync(UsersCacheKey, token: cancellationToken);
|
|||
// if (users == null)
|
|||
// {
|
|||
// users = (await _keycloakService.GetUsersAsync(search, username, email, cancellationToken)).ToList();
|
|||
// await _keycloakUsersCache.SetAsync(UsersCacheKey, users, token: cancellationToken);
|
|||
// }
|
|||
//
|
|||
// return users;
|
|||
// }
|
|||
//
|
|||
// public async Task<bool> CreateUserAsync(User user, CancellationToken cancellationToken = default)
|
|||
// {
|
|||
// var result = await _keycloakService.CreateUserAsync(user, cancellationToken);
|
|||
// if (result)
|
|||
// {
|
|||
// await _keycloakUsersCache.RemoveAsync(UsersCacheKey, token: cancellationToken);
|
|||
// }
|
|||
//
|
|||
// return result;
|
|||
// }
|
|||
//
|
|||
// public async Task<bool> UpdateUserAsync(string userId, User user, CancellationToken cancellationToken = default)
|
|||
// {
|
|||
// var result = await _keycloakService.UpdateUserAsync(userId, user, cancellationToken);
|
|||
// if (result)
|
|||
// {
|
|||
// await _keycloakUsersCache.RemoveAsync(UsersCacheKey, token: cancellationToken);
|
|||
// }
|
|||
//
|
|||
// return result;
|
|||
// }
|
|||
//
|
|||
// public async Task<bool> DeleteUserAsync(string userId, CancellationToken cancellationToken = default)
|
|||
// {
|
|||
// var result = await _keycloakService.DeleteUserAsync(userId, cancellationToken);
|
|||
// if (result)
|
|||
// {
|
|||
// await _keycloakUsersCache.RemoveAsync(UsersCacheKey, token: cancellationToken);
|
|||
// }
|
|||
//
|
|||
// return result;
|
|||
// }
|
|||
//
|
|||
// public async Task<IEnumerable<Role>> GetRolesAsync(CancellationToken cancellationToken = default)
|
|||
// {
|
|||
// var roles = await _keycloakRolesCache.GetAsync(RolesCacheKey, token: cancellationToken);
|
|||
// if (roles == null)
|
|||
// {
|
|||
// roles = (await _keycloakService.GetRolesAsync(cancellationToken: cancellationToken)).ToList();
|
|||
// await _keycloakRolesCache.SetAsync(RolesCacheKey, roles, token: cancellationToken);
|
|||
// }
|
|||
//
|
|||
// return roles;
|
|||
// }
|
|||
//
|
|||
// public Task<bool> AddRolesToUserAsync(string userId, IEnumerable<Role> roles,
|
|||
// CancellationToken cancellationToken = default)
|
|||
// {
|
|||
// return _keycloakService.AddRolesToUserAsync(userId, roles, cancellationToken);
|
|||
// }
|
|||
//
|
|||
// public async Task<bool> CreateRoleAsync(string name, CancellationToken cancellationToken = default)
|
|||
// {
|
|||
// var result = await _keycloakService.CreateRoleAsync(name, cancellationToken);
|
|||
// if (result)
|
|||
// {
|
|||
// await _keycloakRolesCache.RemoveAsync(RolesCacheKey, token: cancellationToken);
|
|||
// }
|
|||
//
|
|||
// return result;
|
|||
// }
|
|||
//
|
|||
// public async Task<bool> DeleteRoleByIdAsync(string id, CancellationToken cancellationToken = default)
|
|||
// {
|
|||
// var result = await _keycloakService.DeleteRoleByIdAsync(id, cancellationToken);
|
|||
// if (result)
|
|||
// {
|
|||
// await _keycloakRolesCache.RemoveAsync(RolesCacheKey, token: cancellationToken);
|
|||
// }
|
|||
//
|
|||
// return result;
|
|||
// }
|
|||
//
|
|||
// public async Task<bool> UpdateRoleAsync(string id, Role role, CancellationToken cancellationToken = default)
|
|||
// {
|
|||
// var result = await _keycloakService.UpdateRoleAsync(id, role, cancellationToken);
|
|||
// if (result)
|
|||
// {
|
|||
// await _keycloakRolesCache.RemoveAsync(RolesCacheKey, token: cancellationToken);
|
|||
// }
|
|||
//
|
|||
// return result;
|
|||
// }
|
|||
// }
|
|||
@ -0,0 +1,75 @@ |
|||
using System.Collections.Generic; |
|||
using System.Collections.ObjectModel; |
|||
using Volo.Abp.Caching; |
|||
|
|||
namespace EShopOnAbp.IdentityService.Keycloak.Service; |
|||
|
|||
[CacheName("KeycloakUser")] |
|||
public class CachedKeycloakUser |
|||
{ |
|||
public string Id { get; set; } |
|||
public long CreatedTimestamp { get; set; } |
|||
public string UserName { get; set; } |
|||
public bool? Enabled { get; set; } |
|||
public bool? Totp { get; set; } |
|||
public bool? EmailVerified { get; set; } |
|||
public string FirstName { get; set; } |
|||
public string LastName { get; set; } |
|||
public string Email { get; set; } |
|||
public Collection<string> DisableableCredentialTypes { get; set; } |
|||
public Collection<string> RequiredActions { get; set; } |
|||
public int? NotBefore { get; set; } |
|||
public Dictionary<string, IEnumerable<string>> Attributes { get; set; } |
|||
public IDictionary<string, object> ClientRoles { get; set; } |
|||
public string FederationLink { get; set; } |
|||
public IEnumerable<string> Groups { get; set; } |
|||
public string Origin { get; set; } |
|||
public string[] RealmRoles { get; set; } |
|||
public string Self { get; set; } |
|||
public string ServiceAccountClientId { get; set; } |
|||
public CachedUserAccess Access { get; set; } |
|||
public IEnumerable<CachedUserConsent> ClientConsents { get; set; } |
|||
public IEnumerable<CachedCredentials> Credentials { get; set; } |
|||
public IEnumerable<CachedFederatedIdentity> FederatedIdentities { get; set; } |
|||
} |
|||
|
|||
public class CachedUserConsent |
|||
{ |
|||
public string ClientId { get; set; } |
|||
public IEnumerable<string> GrantedClientScopes { get; set; } |
|||
public long? CreatedDate { get; set; } |
|||
public long? LastUpdatedDate { get; set; } |
|||
} |
|||
|
|||
public class CachedUserAccess |
|||
{ |
|||
public bool? ManageGroupMembership { get; set; } |
|||
public bool? View { get; set; } |
|||
public bool? MapRoles { get; set; } |
|||
public bool? Impersonate { get; set; } |
|||
public bool? Manage { get; set; } |
|||
} |
|||
|
|||
public class CachedCredentials |
|||
{ |
|||
public string Algorithm { get; set; } |
|||
public IDictionary<string, string> Config { get; set; } |
|||
public int? Counter { get; set; } |
|||
public long? CreatedDate { get; set; } |
|||
public string Device { get; set; } |
|||
public int? Digits { get; set; } |
|||
public int? HashIterations { get; set; } |
|||
public string HashSaltedValue { get; set; } |
|||
public int? Period { get; set; } |
|||
public string Salt { get; set; } |
|||
public bool? Temporary { get; set; } |
|||
public string Type { get; set; } |
|||
public string Value { get; set; } |
|||
} |
|||
|
|||
public class CachedFederatedIdentity |
|||
{ |
|||
public string IdentityProvider { get; set; } |
|||
public string UserId { get; set; } |
|||
public string UserName { get; set; } |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Keycloak.Net.Models.Roles; |
|||
using Keycloak.Net.Models.Users; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace EShopOnAbp.IdentityService.Keycloak.Service; |
|||
|
|||
/* |
|||
* This will be an external service from the Keycloak package. |
|||
* Keeping it under Application layer because the Keycloak.Net.Core package requires .Net6 target framework. |
|||
* Application.Contracts targets netstandard2.0 |
|||
*/ |
|||
public interface IKeycloakService : ITransientDependency |
|||
{ |
|||
Task<List<CachedKeycloakUser>> GetUsersAsync(string search = null, string username = null, string email = null, CancellationToken cancellationToken = default); |
|||
|
|||
Task<bool> CreateUserAsync(User user, CancellationToken cancellationToken = default); |
|||
|
|||
Task<bool> UpdateUserAsync(string userId, User user, CancellationToken cancellationToken = default); |
|||
|
|||
Task<bool> DeleteUserAsync(string userId, CancellationToken cancellationToken = default); |
|||
|
|||
Task<List<CachedKeycloakRole>> GetRolesAsync(CancellationToken cancellationToken = default); |
|||
|
|||
Task<bool> AddRealmRolesToUserAsync(string userId, IEnumerable<Role> roles, CancellationToken cancellationToken = default); |
|||
public Task<IEnumerable<CachedKeycloakRole>> GetRealmRolesOfUserAsync(string userId, CancellationToken cancellationToken = default); |
|||
|
|||
Task RemoveRealmRolesFromUserAsync(string userId, IEnumerable<Role> roles, CancellationToken cancellationToken = default); |
|||
|
|||
Task<bool> CreateRoleAsync(string name, CancellationToken cancellationToken = default); |
|||
|
|||
Task<bool> DeleteRoleByIdAsync(string id, CancellationToken cancellationToken = default); |
|||
|
|||
Task<bool> UpdateRoleAsync(string id, Role role, CancellationToken cancellationToken = default); |
|||
} |
|||
@ -1,4 +1,4 @@ |
|||
namespace EShopOnAbp.IdentityService.Keycloak; |
|||
namespace EShopOnAbp.IdentityService.Keycloak.Service; |
|||
public class KeycloakClientOptions |
|||
{ |
|||
public string Url { get; set; } |
|||
@ -0,0 +1,182 @@ |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Keycloak.Net; |
|||
using Keycloak.Net.Models.Roles; |
|||
using Keycloak.Net.Models.Users; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.Caching; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.ObjectMapping; |
|||
|
|||
namespace EShopOnAbp.IdentityService.Keycloak.Service; |
|||
|
|||
/* |
|||
* This will be an external service from the Keycloak package |
|||
*/ |
|||
[ExposeServices(typeof(IKeycloakService), typeof(KeycloakService))] |
|||
public class KeycloakService : IKeycloakService |
|||
{ |
|||
protected const string UsersCacheKey = "KeycloakUsers"; |
|||
protected const string RolesCacheKey = "KeycloakRoles"; |
|||
private readonly IObjectMapper _objectMapper; |
|||
private readonly IDistributedCache<List<CachedKeycloakUser>, string> _keycloakUsersCache; |
|||
private readonly IDistributedCache<List<CachedKeycloakRole>, string> _keycloakRolesCache; |
|||
private readonly IDistributedCache<List<CachedKeycloakRole>, string> _userRolesCache; |
|||
|
|||
private readonly KeycloakClient _keycloakClient; |
|||
private readonly KeycloakClientOptions _keycloakOptions; |
|||
|
|||
public KeycloakService( |
|||
IOptions<KeycloakClientOptions> keycloakOptions, |
|||
IObjectMapper objectMapper, |
|||
IDistributedCache<List<CachedKeycloakUser>, string> keycloakUsersCache, |
|||
IDistributedCache<List<CachedKeycloakRole>, string> keycloakRolesCache, |
|||
IDistributedCache<List<CachedKeycloakRole>, string> userRolesCache) |
|||
{ |
|||
_objectMapper = objectMapper; |
|||
_keycloakUsersCache = keycloakUsersCache; |
|||
_keycloakRolesCache = keycloakRolesCache; |
|||
_userRolesCache = userRolesCache; |
|||
|
|||
_keycloakOptions = keycloakOptions.Value; |
|||
_keycloakClient = new KeycloakClient( |
|||
_keycloakOptions.Url, |
|||
_keycloakOptions.AdminUserName, |
|||
_keycloakOptions.AdminPassword |
|||
); |
|||
} |
|||
|
|||
public async Task<List<CachedKeycloakUser>> GetUsersAsync(string search = null, string username = null, |
|||
string email = null, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
var users = await _keycloakUsersCache.GetAsync(UsersCacheKey, token: cancellationToken); |
|||
if (users == null) |
|||
{ |
|||
var result = await _keycloakClient.GetUsersAsync(_keycloakOptions.RealmName, search: search, |
|||
username: username, |
|||
email: email, cancellationToken: cancellationToken); |
|||
users = _objectMapper.Map<List<User>, List<CachedKeycloakUser>>(result.ToList()); |
|||
await _keycloakUsersCache.SetAsync(UsersCacheKey, users, token: cancellationToken); |
|||
} |
|||
|
|||
return users; |
|||
} |
|||
|
|||
public async Task<bool> CreateUserAsync(User user, CancellationToken cancellationToken = default) |
|||
{ |
|||
var result = await _keycloakClient.CreateUserAsync(_keycloakOptions.RealmName, user, cancellationToken); |
|||
if (result) |
|||
{ |
|||
await _keycloakUsersCache.RemoveAsync(UsersCacheKey, token: cancellationToken); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public async Task<bool> UpdateUserAsync(string userId, User user, CancellationToken cancellationToken = default) |
|||
{ |
|||
var result = await _keycloakClient.UpdateUserAsync(_keycloakOptions.RealmName, userId, user, cancellationToken); |
|||
if (result) |
|||
{ |
|||
await _keycloakUsersCache.RemoveAsync(UsersCacheKey, token: cancellationToken); |
|||
} |
|||
|
|||
// _keycloakClient.DeleteRealmRoleMappingsFromUserAsync()
|
|||
return result; |
|||
} |
|||
|
|||
public async Task<bool> DeleteUserAsync(string userId, CancellationToken cancellationToken = default) |
|||
{ |
|||
var result = await _keycloakClient.DeleteUserAsync(_keycloakOptions.RealmName, userId, cancellationToken); |
|||
if (result) |
|||
{ |
|||
await _keycloakUsersCache.RemoveAsync(UsersCacheKey, token: cancellationToken); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public async Task<List<CachedKeycloakRole>> GetRolesAsync(CancellationToken cancellationToken = default) |
|||
{ |
|||
var roles = await _keycloakRolesCache.GetAsync(RolesCacheKey, token: cancellationToken); |
|||
if (roles == null) |
|||
{ |
|||
var result = |
|||
(await _keycloakClient.GetRolesAsync(_keycloakOptions.RealmName, cancellationToken: cancellationToken)) |
|||
.ToList(); |
|||
roles = _objectMapper.Map<List<Role>, List<CachedKeycloakRole>>(result.ToList()); |
|||
|
|||
await _keycloakRolesCache.SetAsync(RolesCacheKey, roles, token: cancellationToken); |
|||
} |
|||
|
|||
return roles; |
|||
} |
|||
|
|||
public Task<bool> AddRealmRolesToUserAsync(string userId, IEnumerable<Role> roles, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return _keycloakClient.AddRealmRoleMappingsToUserAsync(_keycloakOptions.RealmName, userId, roles, |
|||
cancellationToken); |
|||
} |
|||
|
|||
// Updating user with roles is not working
|
|||
public async Task<IEnumerable<CachedKeycloakRole>> GetRealmRolesOfUserAsync(string userId, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
var userRoles = await _userRolesCache.GetAsync(userId, token: cancellationToken); |
|||
if (userRoles == null) |
|||
{ |
|||
var roles = (await _keycloakClient.GetRealmRoleMappingsForUserAsync(_keycloakOptions.RealmName, userId, |
|||
cancellationToken)) |
|||
.ToList(); |
|||
userRoles = _objectMapper.Map<List<Role>, List<CachedKeycloakRole>>(roles); |
|||
await _userRolesCache.SetAsync(userId, userRoles, token: cancellationToken); |
|||
} |
|||
|
|||
return userRoles; |
|||
} |
|||
|
|||
public Task RemoveRealmRolesFromUserAsync(string userId, IEnumerable<Role> roles, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return _keycloakClient.DeleteRealmRoleMappingsFromUserAsync(_keycloakOptions.RealmName, userId, roles, |
|||
cancellationToken); |
|||
} |
|||
|
|||
public async Task<bool> CreateRoleAsync(string name, CancellationToken cancellationToken = default) |
|||
{ |
|||
var result = await _keycloakClient.CreateRoleAsync(_keycloakOptions.RealmName, new Role() { Name = name }, |
|||
cancellationToken); |
|||
if (result) |
|||
{ |
|||
await _keycloakRolesCache.RemoveAsync(RolesCacheKey, token: cancellationToken); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public async Task<bool> DeleteRoleByIdAsync(string id, CancellationToken cancellationToken = default) |
|||
{ |
|||
var result = await _keycloakClient.DeleteRoleByIdAsync(_keycloakOptions.RealmName, id, cancellationToken); |
|||
if (result) |
|||
{ |
|||
await _keycloakRolesCache.RemoveAsync(RolesCacheKey, token: cancellationToken); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public async Task<bool> UpdateRoleAsync(string id, Role role, CancellationToken cancellationToken = default) |
|||
{ |
|||
var result = await _keycloakClient.UpdateRoleByIdAsync(_keycloakOptions.RealmName, id, role, cancellationToken); |
|||
if (result) |
|||
{ |
|||
await _keycloakRolesCache.RemoveAsync(RolesCacheKey, token: cancellationToken); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Security.Cryptography; |
|||
using System.Text; |
|||
using System.Text.Json; |
|||
using Keycloak.Net.Models.Roles; |
|||
using Keycloak.Net.Models.Users; |
|||
|
|||
namespace EShopOnAbp.IdentityService.Keycloak.Service; |
|||
|
|||
public static class KeycloakServiceExtensions |
|||
{ |
|||
public static string GenerateCacheKeyBasedOnValues(this IEnumerable<Role> roles) |
|||
{ |
|||
return GenerateUniqueCacheKeyBasedOnList(roles); |
|||
} |
|||
|
|||
public static string GenerateCacheKeyBasedOnValues(this IEnumerable<User> users) |
|||
{ |
|||
return GenerateUniqueCacheKeyBasedOnList(users); |
|||
} |
|||
|
|||
private static string GenerateUniqueCacheKeyBasedOnList<T>(IEnumerable<T> list) |
|||
{ |
|||
string serializedList = JsonSerializer.Serialize(list); |
|||
byte[] bytes = Encoding.UTF8.GetBytes(serializedList); |
|||
byte[] hash = SHA256.Create().ComputeHash(bytes); |
|||
string hashString = BitConverter.ToString(hash).Replace("-", ""); |
|||
return hashString; |
|||
} |
|||
} |
|||
Loading…
Reference in new issue