Table of Contents

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) OR teams:delete (system).
  • Rename / Consent — team:manage on 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:manage on 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).

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 ITeamService composes these per operation:

  • In-team scopes (Manage, MemberManage, …) authorize only the caller's own team — the TeamKey claim must equal the target teamKey, 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.

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 HttpContext but the principal is available via AuthenticationStateProvider. The default implementation reads IHttpContextAccessor.

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.