Namespace Tharga.Team.Service
Classes
- AccessLevelProxy<T>
DispatchProxy that intercepts service method calls and enforces RequireAccessLevelAttribute by reading TeamKey and AccessLevel claims from the current principal (resolved via ITeamPrincipalAccessor, so it works for both HTTP and interactive Blazor callers). Methods without the attribute are blocked (fail-closed). Logs audit entries when IAuditLogger is available.
- AccessLevelServiceCollectionExtensions
Extension methods for registering services with automatic access level enforcement.
- ApiKeyAdministrationService
Default implementation of IApiKeyAdministrationService using MongoDB storage.
- ApiKeyAuthenticationHandler
Authentication handler that validates API keys from the X-API-KEY header.
- ApiKeyConstants
Well-known constants for API key authentication.
- ApiKeyEntity
Default MongoDB entity for API keys.
- ApiKeyLifecycleDecorator
Decorator that wraps IApiKeyAdministrationService and notifies the registered IApiKeyLifecycleHandler(s) after a key is created, recycled, or deleted. The raw private token (available on the create/refresh result) is forwarded on create/recycle; delete is signalled tokenless. Read/lock/scope/role operations pass through without notification.
Handlers run after the underlying mutation succeeds; a handler exception propagates out of the originating operation (capture failures are not swallowed). The token is never logged.
- ApiKeyManagementService
Delegates to IApiKeyAdministrationService and enforces caller-scoped authorization on top: scope enforcement via ScopeProxy<T>, plus owner-scoping for private API keys (resolved from the authenticated principal's claims — not trusting any caller-supplied value).
- ApiKeyRegistration
Extension methods for registering API key authentication.
- AuthorizationTeamServiceDecorator
Decorator over Tharga.Team.ITeamService that enforces the team-operation authorization model in the service layer (so the same checks protect the Blazor circuit and any consumer's REST controller). Reads the caller's claims via TeamAuthorizer:
- Create — authenticated AND
AllowTeamCreation(no scope; self-service). - Delete — (Owner of the team AND
AllowTeamCreation) ORteams:delete(system). - Rename / Consent —
team:manageon the team. - Custom-role CRUD — the configurable custom-role manage scope on the team (default
team:manage). - Member invite/remove/role/scope-overrides/display-name —
member:manageon the team. - Leave — no scope; the operation names no user but the caller. See LeaveTeamAsync(string).
- Remove user from all teams —
users:manage(system; backs user deletion). - Transfer ownership — passed through (Owner-only is enforced by the inner service).
Reads, consent-team lookup, last-seen touch, invite-code lookup and invitation responses pass through (self-service / not gated here).
- Create — authenticated AND
- AuthorizationUserManagementServiceDecorator
Decorator over IUserManagementService that requires the Manage system scope on every operation — verification, directory-only listing, and deletion are all cross-team administration.
- AuthorizationUserServiceDecorator
Decorator over IUserService that enforces user-store authorization in the service layer (so the same checks protect the Blazor circuit and any consumer's REST controller):
- Resolve current user / seed-own-name (invitation accept) — pass through (self-service).
- Set display name — allowed on the caller's own record, otherwise
users:manage. - Enumerate users, read by key, write activity/directory fields, delete —
users:manage(system).
The automatic LastSeen stamping and oid backfill are internal self-calls inside UserServiceBase and never pass through this decorator.
- CacheInvalidatingUserServiceDecorator
Decorator over IUserService that drops the cached copy of a user after any call that changes them — whoever implemented the write.
- ControllersRegistration
Extension methods for registering API controllers with OpenAPI and Swagger.
- HttpContextTeamPrincipalAccessor
Default ITeamPrincipalAccessor that resolves the caller from the current HTTP request (IHttpContextAccessor). Used for controller/API callers; returns null outside a request.
- InvitationThrottleRegistration
Wraps the registered ITeamInvitationService so repeated failed resolves from one source are delayed and the first crossing is audited.
- ScopeProxy<T>
DispatchProxy that intercepts service method calls and enforces RequireScopeAttribute by checking scope claims on the current principal (resolved via ITeamPrincipalAccessor, so it works for both HTTP and interactive Blazor callers). Methods without the attribute throw InvalidOperationException (fail-closed). Also verifies a TeamKey claim is present. Logs audit entries when IAuditLogger is available.
- ScopeServiceCollectionExtensions
Extension methods for registering scopes and scope-protected services.
- SystemApiKeyManagementService
Delegates system API key operations to IApiKeyAdministrationService, stamping the creating user's identity from their claims rather than trusting a caller-supplied value.
- TeamAccess
Ambient record of the authorization decision covering the current call flow, read by TeamAccessInterceptor at the database boundary.
- TeamAccessContext
The record that an authorization decision was made for the current call flow.
- TeamAccessInterceptor
Refuses any database operation that no authorization decision covers.
- TeamAuthorizer
Service-layer authorization primitives for team operations, read from the caller's claims via ITeamPrincipalAccessor (so they work for HTTP/API callers and interactive Blazor circuits alike). The authorization decorator over
ITeamServicecomposes these per operation:- In-team scopes (Manage, MemberManage, …)
authorize only the caller's own team — the
TeamKeyclaim must equal the targetteamKey, closing the "admin of team A acts on team B" hole. - System scopes (Delete) authorize across any team — no team binding.
Claims are the source of truth: scope claims are emitted from the caller's access level / roles / overrides for their team (or from a system key's scope list), so a present scope claim already reflects the underlying membership.
- In-team scopes (Manage, MemberManage, …)
authorize only the caller's own team — the
- TeamContext
Which team a request is about, and what the caller may do there.
- TeamContextMiddleware
Reads the team header and, when a system caller names a team it may act on, adds the claims for it.
- TeamContextOptions
Configuration for how a request names the team it acts on.
- TeamContextResolver
Resolves the team a request acts on, from the credential and an optional header. The single place that answers it — REST and MCP both call this rather than each working it out.
- TeamLifecycleOptions
Service-layer view of the self-service team-lifecycle policy, used by the authorization decorator. Populated at registration time from the Blazor option of the same name (which lives in a higher layer).
- TeamPurgeCascade
Destroys a team's data in the toolkit's own stores, before the team itself is purged.
- ThargaControllerOptions
Options for configuring Tharga controller registration.
- UserManagementService
Default IUserManagementService implementation. Directory operations require a registered IUserDirectoryService; deletion composes the storage seams — remove from all teams, then delete the user record — and only then attempts the (opt-in) directory delete, so a directory failure never leaves the local store half-deleted. Authorization and audit are applied by decorators.
Interfaces
- IApiKeyRepository
Repository interface for API key persistence. Auto-registered by Tharga.MongoDB.
- IApiKeyRepositoryCollection
Collection interface for disk-backed API key storage. Auto-registered by Tharga.MongoDB.
- ITeamPrincipalAccessor
Resolves the current caller's ClaimsPrincipal for scope / access-level enforcement. Abstracting this lets enforcement work outside an HTTP request — e.g. in an interactive Blazor Server circuit where there is no
HttpContextbut the principal is available viaAuthenticationStateProvider. The default implementation readsIHttpContextAccessor.
Enums
- ServiceScopeKind
Whether a service's operations act on one team or across the whole system. Declared once per service at registration rather than per method, so a method added later inherits the rule instead of needing an annotation somebody has to remember.
- TeamAccessKind
Why a database call is permitted to run.
- TeamContextRefusal
Why a request could not act on the team it named.