Home > Software design >  When does Identity's UserManager not support lockouts?
When does Identity's UserManager not support lockouts?

Time:02-20

ASP.NET Core Identity has UserManager.SupportsUserLockout(), which for me is true.

Description:

Gets a flag indicating whether the backing user store supports user lock-outs.

Is it ever possible for the user manager NOT to support lockouts?

CodePudding user response:

Is it ever possible for the user manager NOT to support lockouts?

Yes

Under any of these 2 circumstances:

  • UserManager<TUser> is subclassed and the virtual bool SupportsUserLockout property is overridden and that implementation returns false.
  • UserManager<TUser>.Store (your IUserStore<TUser> implementation) does not implement the optional IUserLockoutStore<TUser> interface.

  • If you're using the "stock" in-box ASP.NET Core Identity functionality, without using any custom IUserStore implementation (and without subclassing UserManager<TUser>), then your runtime IUserStore<TUser> will be some subclass of abstract class UserStoreBase<...> which does implement IUserLockoutStore<TUser>.
    • e.g. ASP.NET Core Identity for Entity Framework uses Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserOnlyStore which inherits the IUserLockoutStore<TUser> interface implementation.
  • Because in C#/.NET, interfaces are strictly additive (one cannot "remove" an interface from a class - you can only reimplement it) it means if you don't want to subclass UserManager<TUser> then you will cannot use a subclass UserStoreBase<> as the basis for your reimplementation of IUserStore<> - you'll have to start from scratch, basically.
    • (Which in my mind, at least, means that ASP.NET Core Identity's default combination of UserManager and UserStoreBase<...> is a bad OOP design because it requires implementations to actively go through a lot of effort to correctly not support something, instead of making everything opt-in.
    • Hey kids, inheritance is bad, mmm'key (okay, not "bad", but at least is so fraught with problems it should be avoided.
      • Subclass inheritance is a blunt instrument, especially in C# and Java, where it's impossible to separate a type's "interface"1 from implementation, and can't describe a type's interface in a type algebraic way - so we can't define a class Derived that subclasses a parent class Base : ISomeInterface such that class Derived no longer implements ISomeInterface (the best we can do is either abuse implicit conversions to a separate type, or "hide" members with [EditorBrowsable] and reimplement the interface explicitly with throw new NotSupportedException(), which is horrible, but even Microsoft does it in some places in .NET's base libraries, such as in Stream and TextWriter subclasses).
      • (Also, don't (ab)use inheritance just to have "common members" in different types. I agree that it does suck that C#/.NET still doesn't support mixins, but the drudgery of copy pasting forwarder properties and methods around is less painful in the long-term than dealing with the consequences of inappropriate use of inheritance.

1: By "interface" I don't mean interface types; I mean the set of public members of a class or struct, i.e. a type's exposed surface.


(In an earlier edit of my post, I suggested that IConfigureOptions<LockoutOptions> could be used to configure SupportsUserLockout, however I was wrong: as there is no option to outright disable the lockout system, but if you were to subclass both LockoutOptions to add a bool Enabled property and subclass UserManager<TUser> to specify override bool SupportsUserLockout to return LockoutOptions.Enabled (ideally, from an immutable copy, not the original mutable options object) then that would work.

  • Related