Below comes a Value Object to represent UserId in ASP.NET application. It implements Value Identity, so equality is defined by matching of the state calculated at the moment of comparison. It depends on

public interface IAuthenticator
    int CurrentUserId { get; }      // may be impersonated     
    int InteractiveUserId { get; }  // "real" user, may impersonate

which extracts state from ClaimIdentity.

Here is a definition:

public struct UserId 
    public static IAuthenticator Authenticator { get; set; }

    public static readonly UserId Anonymous = 
        new UserId(() => 0);

    public static readonly UserId Current = 
        new UserId(() => Authenticator?.CurrentUserId ?? 0);

    public static readonly UserId Interactive = 
        new UserId(() => Authenticator?.InteractiveUserId ?? 0);

    public UserId(int value)
        : this(() => value)

    UserId(Func<int> getValue)
        : this()
        GetValue = getValue;

    public bool Authenticated => this == Current && this != Anonymous;
    public bool Impersonated => this == Current && Current != Interactive;
    public bool Impersonating => this == Interactive && Current != Interactive;

    Func<int> GetValue { get; }
    int Value => GetValue?.Invoke() ?? 0;

    public static implicit operator int(UserId id) => id.Value;

    public static bool operator ==(UserId left, UserId right) =>
        left.Value == right.Value;

    public static bool operator !=(UserId left, UserId right) =>
        left.Value != right.Value;

    // Equals, GetHashCode are omitted for brevity

We can do the following now:

   if(UserId.Current.Authenticated) ...
   if(UserId.Current.Impersonated) ...
   if(UserId.Interactive.Impersonating) ...


   var uid = new UserId(33);
   if(uid.Authenticated) ...
   if(uid.Impersonated) ...
   if(uid.Impersonating) ...

And provide overloads in extension methods:

   interface IAuthorizer
      void Authorize<TResource, TAction>(int resourceId, UserId userId);

   static class AuthorizerExtensions
      public static void Authorize<TResource, TAction>(
           this IAuthorizer authorizer, int resourceId) =>
           authorizer.Authorize<TResource, TAction>(resourceId, UserId.Current);              

It is intended to be used here. GitHub repository (just a sketch).

Do these terms (Anonymous, Current, Interactive, Authenticated, Impersonated, Impersonating) properly used here and look understandable? Do they have an expected behavior?


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.