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?
IsAuthenticated
etc., as described here. See also: codereview.stackexchange.com/q/30914/10582 Microsoft says: "DO name Boolean properties with an affirmative phrase (CanSeek instead of CantSeek). Optionally, you can also prefix Boolean properties with 'Is,' 'Can,' or 'Has,' but only where it adds value.". \$\endgroup\$Created
is more readable thanIsCreated
. Having the prefix is often too verbose, ... It is just as clear to typeMyObject.Enabled =
and have Intellisense ... as it is to haveMyObject.IsEnabled
=, and the second one is more verbose. ... Test out the API in an if-statement.if(collection.Contains(item))
vsif(collection.IsContained(item))
". Should I haveIsAuthenticated
butImpersonated
? My English is far from perfect :( \$\endgroup\$