I have a custom user role class with CompanyId
as a foreign key. So I could create user roles relative to the company. That means Administrator role or any other role per se, can be in multiple companies. I cannot go for a many-to-many relationship with Company and Role because claims of Administrator for one company may vary for another. So I have added the company Id in user role entity.
My problem is, user manager takes role name as a parameter in AddToRoleAsync()
, I cannot use this since the role name could be redundant. If I'm able to add user role by Id, that would be great. Is there a workaround to achieve this?
My current workaround is to add the company name as a prefix to role name. Like: ABC - Administrator; XYZ - Administrator. I don't like this very much, so won't the users.
If I could create a an overload method for both AddToRoleAsync()
and IsInRoleAync()
to look for role by it's ID and not by name, It would be great.
You can extend the UserManager
class and add another method to assign a role to a user by RoleId
:
public class ApplicationUserManager : UserManager<IdentityUser>
{
private readonly UserStore<IdentityUser, IdentityRole, ApplicationDbContext, string, IdentityUserClaim<string>,
IdentityUserRole<string>, IdentityUserLogin<string>, IdentityUserToken<string>, IdentityRoleClaim<string>> _store;
public ApplicationUserManager(
IUserStore<IdentityUser> store,
IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<IdentityUser> passwordHasher,
IEnumerable<IUserValidator<IdentityUser>> userValidators,
IEnumerable<IPasswordValidator<IdentityUser>> passwordValidators,
ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors,
IServiceProvider services,
ILogger<UserManager<IdentityUser>> logger)
: base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
{
_store = (UserStore<IdentityUser, IdentityRole, ApplicationDbContext, string, IdentityUserClaim<string>,
IdentityUserRole<string>, IdentityUserLogin<string>, IdentityUserToken<string>, IdentityRoleClaim<string>>)store;
}
public virtual async Task<IdentityResult> AddToRoleByRoleIdAsync(IdentityUser user, string roleId)
{
ThrowIfDisposed();
if (user == null)
throw new ArgumentNullException(nameof(user));
if (string.IsNullOrWhiteSpace(roleId))
throw new ArgumentNullException(nameof(roleId));
if (await IsInRoleByRoleIdAsync(user, roleId, CancellationToken))
return IdentityResult.Failed(ErrorDescriber.UserAlreadyInRole(roleId));
_store.Context.Set<IdentityUserRole<string>>().Add(new IdentityUserRole<string> { RoleId = roleId, UserId = user.Id });
return await UpdateUserAsync(user);
}
public async Task<bool> IsInRoleByRoleIdAsync(IdentityUser user, string roleId, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (user == null)
throw new ArgumentNullException(nameof(user));
if (string.IsNullOrWhiteSpace(roleId))
throw new ArgumentNullException(nameof(roleId));
var role = await _store.Context.Set<IdentityRole>().FindAsync(roleId);
if (role == null)
return false;
var userRole = await _store.Context.Set<IdentityUserRole<string>>().FindAsync(new object[] { user.Id, roleId }, cancellationToken);
return userRole != null;
}
}
Then register you own UserManager
:
services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddUserManager<ApplicationUserManager>() // Add ApplicationUserManager
.AddDefaultTokenProviders()
.AddDefaultUI();
And finally:
await _userManager.AddToRoleByRoleIdAsync(user, role.Id);