Class UserServiceBase
- Namespace
- Tharga.Team
- Assembly
- Tharga.Team.dll
public abstract class UserServiceBase : IUserService, IUserCacheInvalidator
- Inheritance
-
UserServiceBase
- Implements
- Derived
- Inherited Members
Constructors
UserServiceBase(AuthenticationStateProvider, ILogger<UserServiceBase>, IIconStore, ITeamCache)
protected UserServiceBase(AuthenticationStateProvider authenticationStateProvider, ILogger<UserServiceBase> logger = null, IIconStore iconStore = null, ITeamCache cache = null)
Parameters
authenticationStateProviderAuthenticationStateProviderResolves the calling principal when none is supplied.
loggerILogger<UserServiceBase>Optional. Used to report activity-stamping failures, which never fail a resolve.
iconStoreIIconStoreOptional. Required only for user icons; see SetUserIconAsync(string, byte[], string).
cacheITeamCacheWhere resolved users are kept. Defaults to the process-local InMemoryTeamCache, which is correct for a single instance only — forward this parameter from your own service's constructor so a shared implementation can be registered, or a multi-instance deployment will not see a user disabled through another instance. See ITeamCache.
Fields
_authenticationStateProvider
protected readonly AuthenticationStateProvider _authenticationStateProvider
Field Value
Properties
LastSeenStampInterval
How often (at most) LastSeen is written on resolve. Null disables stamping; Zero stamps on every resolve. The throttle is per process, so a multi-instance deployment writes at most once per interval per instance.
protected virtual TimeSpan? LastSeenStampInterval { get; }
Property Value
Methods
ClearOwnIconAsync()
Clears the current user's own icon and deletes the stored bytes (self-service).
public virtual Task ClearOwnIconAsync()
Returns
ClearUserIconAsync(string)
Clears a specific user's icon (administrative). Requires Manage.
public virtual Task ClearUserIconAsync(string userKey)
Parameters
userKeystring
Returns
DeleteUserAsync(string)
public virtual Task DeleteUserAsync(string userKey)
Parameters
userKeystring
Returns
GetAllAsync()
protected abstract IAsyncEnumerable<IUser> GetAllAsync()
Returns
GetAsync()
All users. Cross-user enumeration — requires Manage.
public virtual IAsyncEnumerable<IUser> GetAsync()
Returns
GetClaims(ClaimsPrincipal)
The caller, either as supplied or resolved from the circuit. Null when there is no circuit to ask — an MCP request handler, a hosted service, a message handler — because nothing there can name a caller, and crashing is not a better answer than saying so.
protected virtual Task<ClaimsPrincipal> GetClaims(ClaimsPrincipal claimsPrincipal)
Parameters
claimsPrincipalClaimsPrincipal
Returns
GetCurrentUserAsync(ClaimsPrincipal)
public Task<IUser> GetCurrentUserAsync(ClaimsPrincipal claimsPrincipal)
Parameters
claimsPrincipalClaimsPrincipal
Returns
GetCurrentUserAsync(ClaimsPrincipal, bool)
The user for claimsPrincipal, resolved as GetCurrentUserAsync(ClaimsPrincipal)
does, saying whether the resolve is the user being active.
public Task<IUser> GetCurrentUserAsync(ClaimsPrincipal claimsPrincipal, bool recordActivity)
Parameters
claimsPrincipalClaimsPrincipalThe caller; null for the one on the current circuit.
recordActivityboolWhether this resolve counts towards LastSeen. Pass
falsefrom code that reads the user on a schedule rather than because they did something, such as claim revalidation on an idle tab.
Returns
Remarks
Only the last-seen stamp depends on it; keeping the record right from the sign-in (the directory id, a trusted profile refresh) happens either way. The default forwards to GetCurrentUserAsync(ClaimsPrincipal), so a host's own store keeps compiling and records whatever it recorded before.
GetUserAsync(ClaimsPrincipal)
Finds or creates the user for claimsPrincipal, keyed by ResolveUserIdentity(ClaimsPrincipal).
protected abstract Task<IUser> GetUserAsync(ClaimsPrincipal claimsPrincipal)
Parameters
claimsPrincipalClaimsPrincipal
Returns
GetUserByKeyAsync(string)
The user with the given key, or null. The default implementation scans GetAsync(); storage-backed services override it with a direct read.
public virtual Task<IUser> GetUserByKeyAsync(string userKey)
Parameters
userKeystring
Returns
InvalidateUserByKey(string)
Drops any cached copy of the user with this key. A no-op when nothing is cached for it, so it is always safe to call and safe to call twice.
public void InvalidateUserByKey(string userKey)
Parameters
userKeystring
Remarks
See InvalidateUserCache(string) on why blocking here is safe for the built-in cache and not for a remote one.
InvalidateUserByKeyAsync(string)
The asynchronous form, which is what the toolkit calls. Prefer implementing this one: the cache may be a remote store (see ITeamCache), and the synchronous member can then only be served by blocking.
public Task InvalidateUserByKeyAsync(string userKey)
Parameters
userKeystring
Returns
Remarks
Defaulted to the synchronous member so an existing implementation keeps compiling and keeps working.
InvalidateUserCache(string)
Drops the cached user for identity. Retained for hosts that call it; the toolkit's own
paths use RemoveUserAsync(string) directly.
protected void InvalidateUserCache(string identity)
Parameters
identitystring
Remarks
Synchronous, so it waits on the cache. That is free for the built-in InMemoryTeamCache — every member completes synchronously — but a host that has registered a remote ITeamCache should prefer InvalidateUserCacheAsync(string) rather than blocking a request thread on it.
InvalidateUserCacheAsync(string)
Drops the cached user for identity. A no-op when nothing is cached.
protected Task InvalidateUserCacheAsync(string identity)
Parameters
identitystring
Returns
RaiseUserCreated(IUser, ClaimsPrincipal)
Announces a first-sign-in creation. Call only after the record is durably stored, and only for the caller that actually created it - a store losing the insert race re-reads the winner and must not report a creation it did not perform.
protected void RaiseUserCreated(IUser user, ClaimsPrincipal claimsPrincipal)
Parameters
userIUserclaimsPrincipalClaimsPrincipal
ResolveUserIdentity(ClaimsPrincipal)
The identity claimsPrincipal is stored under, honouring the host's configured
identity claim. A store overriding GetUserAsync(ClaimsPrincipal) must look users up by this value.
protected string ResolveUserIdentity(ClaimsPrincipal claimsPrincipal)
Parameters
claimsPrincipalClaimsPrincipal
Returns
SeedUserNameAsync(string, string)
Sets the user's display name only if it is currently null/empty. Used by the invitation-accept flow to promote the admin-entered invitation name into the new user's identity without clobbering an IdP-provided name.
public virtual Task SeedUserNameAsync(string userKey, string name)
Parameters
Returns
SetOwnIconAsync(byte[], string)
Sets the current user's own icon from raw image bytes (self-service): stores them via the registered IIconStore, persists the reference on the user, and deletes any previous icon. Requires a registered icon store and an authenticated caller.
public virtual Task SetOwnIconAsync(byte[] data, string contentType)
Parameters
Returns
SetUserDirectoryIdAsync(string, string)
Links the user to their external-directory id (DirectoryId). Called by the oid backfill (internal self-call) and by directory verification on an email-fallback match (relink). Default is a no-op — stores that track the directory id override it.
public virtual Task SetUserDirectoryIdAsync(string userKey, string directoryId)
Parameters
Returns
SetUserDisabledAsync(string, DateTime?, string)
public virtual Task SetUserDisabledAsync(string userKey, DateTime? disabledAt, string disabledBy)
Parameters
Returns
Remarks
Throws rather than no-opping when unimplemented, exactly as DeleteUserAsync(string) does: silently accepting a disable would report a containment that never happened.
SetUserEmailAsync(string, string)
public virtual Task SetUserEmailAsync(string userKey, string email)
Parameters
Returns
Remarks
Throws rather than no-opping when unimplemented, as SetUserDisabledAsync(string, DateTime?, string) does: silently accepting the change would report an address that was never set.
SetUserIconAsync(string, byte[], string)
Sets a specific user's icon (administrative). The mechanism is the same as
SetOwnIconAsync(byte[], string) but targets userKey; requires
Manage.
public virtual Task SetUserIconAsync(string userKey, byte[] data, string contentType)
Parameters
Returns
SetUserIconReferenceAsync(string, string)
Backs SetOwnIconAsync(byte[], string) / ClearOwnIconAsync() — persists the icon reference (or null to clear) on the user document. Default no-op; stores that track Icon override it.
protected virtual Task SetUserIconReferenceAsync(string userKey, string reference)
Parameters
Returns
SetUserLastSeenAsync(string, DateTime)
Stamps when the user last made an authenticated request. The automatic throttled stamping is an internal self-call that bypasses the authorization decorator; calling this member from outside requires Manage. The default is a no-op — stores that track LastSeen override it.
public virtual Task SetUserLastSeenAsync(string userKey, DateTime lastSeen)
Parameters
Returns
SetUserNameAsync(string, string)
Always sets the user's display name. Used by the user self-edit flow where the caller has explicitly chosen a name for themselves.
public virtual Task SetUserNameAsync(string userKey, string name)
Parameters
Returns
SetUserProfileAsync(string, string, string)
Writes a refreshed name and/or email. name or email is null when that
value is to be left unchanged.
protected virtual Task SetUserProfileAsync(string userKey, string name, string email)
Parameters
Returns
Remarks
Throws rather than no-opping when unimplemented, as DeleteUserAsync(string) does: a host that opted in through ShouldRefreshProfile(ClaimsPrincipal) and whose store cannot write would otherwise see nothing happen and nothing reported. The failure is logged and never fails the resolve.
ShouldRefreshProfile(ClaimsPrincipal)
Whether the stored name and email should follow this sign-in's claims. Default false: a user's
Name and EMail are written once, at creation, and an address set
afterwards stays.
protected virtual bool ShouldRefreshProfile(ClaimsPrincipal claimsPrincipal)
Parameters
claimsPrincipalClaimsPrincipal
Returns
Remarks
Override to trust a sign-in — typically by the identity provider it came through, when that provider verifies who the person is. Evaluated on every resolve, so keep it to reading the principal. When it returns true, a non-empty claim value that differs from the stored one is written through SetUserProfileAsync(string, string, string), the user's cache entry is dropped and UserProfileRefreshedEvent is raised. A name guessed from the email address, which a display name falls back to when the claims carry none, is never written as the name.
Events
UserCreatedEvent
Raised when a user record is created as a side effect of someone signing in for the first time.
public event EventHandler<UserCreatedEventArgs> UserCreatedEvent
Event Type
Remarks
An event rather than a constructor dependency, on purpose. The creation happens in the storage
base (Tharga.Team.MongoDB), which cannot see the audit types - they live in
Tharga.Team.Service. An optional constructor parameter would also be one more thing a host's own
service must remember to forward, which is the hazard TeamCacheWiringCheck exists to catch.
Not the same as an administrator creating a user. That path is already audited by
AuditingUserManagementServiceDecorator. This one has no actor but the new user themselves.
UserProfileRefreshedEvent
Raised when a stored user's name and/or email were refreshed from a sign-in the host trusts. Never raised unless ShouldRefreshProfile(ClaimsPrincipal) is overridden; an event rather than a dependency for the reason UserCreatedEvent gives.
public event EventHandler<UserProfileRefreshedEventArgs> UserProfileRefreshedEvent