-
-
Notifications
You must be signed in to change notification settings - Fork 58
Adding Features
If you wish to add support for the features that are not added by default that will generally mean extending the user store to add this functionality.
In some cases, this could mean extending the user manager and overriding some of the methods there too if they don't behave the way that you want.
The best resource for understanding how the user manager interacts with the user store is to look at the ASP.NET source
In some cases you may need to have a custom user and extend the default UmbracoIdentityMember to add custom properties to it. That is fully supported and it's why the TMember
generic type is used everywhere.
You can configure these custom stores, managers and users on startup
This is an example of a custom user store implementation that implements the IUserLockoutStore
feature which persists the
public class CustomUmbracoMembersUserStore<TMember> : UmbracoMembersUserStore<TMember>,
IUserLockoutStore<TMember, int>
where TMember : UmbracoIdentityMember, IUser<int>, new()
{
public CustomUmbracoMembersUserStore(ILogger logger, IMemberService memberService, IMemberTypeService memberTypeService, IMemberGroupService memberGroupService, IdentityEnabledMembersMembershipProvider membershipProvider, IExternalLoginStore externalLoginStore)
: base(logger, memberService, memberTypeService, memberGroupService, membershipProvider, externalLoginStore)
{
}
public Task<int> GetAccessFailedCountAsync(TMember user) => Task.FromResult(user.AccessFailedCount);
public Task<bool> GetLockoutEnabledAsync(TMember user) => Task.FromResult(user.LockoutEnabled);
public Task<DateTimeOffset> GetLockoutEndDateAsync(TMember user) => Task.FromResult(new DateTimeOffset(user.LockoutEndDateUtc ?? DateTime.MinValue));
public Task<int> IncrementAccessFailedCountAsync(TMember user)
{
user.AccessFailedCount++;
return Task.FromResult(user.AccessFailedCount);
}
public Task ResetAccessFailedCountAsync(TMember user)
{
user.AccessFailedCount = 0;
return Task.CompletedTask;
}
public Task SetLockoutEnabledAsync(TMember user, bool enabled)
{
user.LockoutEnabled = enabled;
return Task.CompletedTask;
}
public Task SetLockoutEndDateAsync(TMember user, DateTimeOffset lockoutEnd)
{
user.LockoutEndDateUtc = lockoutEnd.UtcDateTime;
return Task.CompletedTask;
}
/// <summary>
/// Override to map custom properties for persistence
/// </summary>
/// <param name="member"></param>
/// <param name="user"></param>
/// <returns></returns>
protected override bool UpdateMemberProperties(IMember member, TMember user)
{
var hasChanged = base.UpdateMemberProperties(member, user);
// NOTE: AccessFailedCount is already handled on the base class
// Assumes your lockoutEnabled member type property is a true/false property type, in which case
// i think it stores 0 or 1 as a int
var lockoutEnabled = member.GetValue<int>("lockoutEnabled") == 1;
if (lockoutEnabled != user.LockoutEnabled)
{
member.SetValue("lockoutEnabled", user.LockoutEnabled ? 1 : 0);
hasChanged = true;
}
// Assumes your lockoutEndDateUtc member type property is a date property type
var lockoutEndDateUtc = member.GetValue<DateTime>("lockoutEndDateUtc");
if (lockoutEndDateUtc != (user.LockoutEndDateUtc ?? DateTime.MinValue))
{
member.SetValue("lockoutEndDateUtc", user.LockoutEndDateUtc);
hasChanged = true;
}
return hasChanged;
}
/// <summary>
/// Override to map custom properties when retrieved from the DB
/// </summary>
/// <param name="member"></param>
/// <returns></returns>
protected override TMember MapFromMember(IMember member)
{
var mapped = base.MapFromMember(member);
mapped.LockoutEnabled = member.GetValue<int>("lockoutEnabled") == 1;
mapped.LockoutEndDateUtc = member.GetValue<DateTime>("lockoutEndDateUtc");
return mapped;
}
}