2
\$\begingroup\$

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) ...

Also:

   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?

\$\endgroup\$

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.