Table of Contents

Class SystemTeamScopes

Namespace
Tharga.Team
Assembly
Tharga.Team.dll

System-level (cross-team) scope constants for team operations. Unlike the in-team TeamScopes (which authorize only the caller's own team), these authorize across any team and are granted to system API keys or privileged roles. The toolkit defines them; the consumer applies them as they see fit (e.g. o.ConfigureSystemRoles mapping a role to the scope, or a system API key's scope list).

public static class SystemTeamScopes
Inheritance
SystemTeamScopes
Inherited Members

Fields

Delete

Authorizes deleting any team, regardless of membership and regardless of the AllowTeamCreation self-service option. The unconditional, cross-team delete path.

public const string Delete = "teams:delete"

Field Value

string

Manage

Authorizes renaming any team and setting or clearing its icon, regardless of membership. The oversight equivalent of the in-team team:manage, for the two operations that are presentational.

public const string Manage = "teams:manage"

Field Value

string

Remarks

Deliberately narrower than in-team team:manage, which also covers consent and custom roles. Those are authorization: consent is a team's own statement about what it exposes inbound, and an operator overriding it is a far larger claim than fixing a typo in a name. Rename and icon change how a team looks; consent changes who can reach it.

Extending this scope to consent would need a deliberate decision and its own name. An architecture-level rule cannot express "these two members of that scope but not those two", so a test asserts the boundary instead.

Purge

Authorizes permanently removing a soft-deleted team, including dropping its storage.

public const string Purge = "teams:purge"

Field Value

string

Remarks

Separate from Delete because it is the only irreversible one, and because it is the only one needing whatever privilege the storage adapter requires to destroy a team's data — for the MongoDB adapter in a per-team-database deployment, dropDatabase. That privilege is one most managed deployments will not grant permanently: Atlas's readWriteAnyDatabase does not include it.

Splitting it means a deployment can withhold both this scope and the database grant and still delete teams normally, which is what Eplicta FortDocs asked for in Tharga/Team#224.

Restoring needs no scope of its own. It is strictly less destructive than the delete it undoes, so Delete covers it — anyone trusted to remove a team is trusted to change their mind. A third scope would be a grant nobody has asked for and one more thing to map.

Read

Authorizes enumerating any team via ITeamService.GetAllTeamsAsync, regardless of membership — the discovery path for oversight roles (support, administration).

public const string Read = "teams:read"

Field Value

string

Remarks

Discovery only. Holding this grants no access inside a team: selecting a team the caller is not a member of still yields only the scopes that team has consented to, and none if it has consented to nothing. Contrast with the in-team Read, which authorizes reading the caller's own team.

Granting it needs a store that can list every team. It switches the consuming UI — TeamComponent, TeamSelector, the Users view — into cross-team oversight mode, which lists teams from GetAllTeamsAsync instead of from the caller's memberships. The built-in store (TeamServiceRepositoryBase) implements it. A team service deriving TeamServiceBase directly must override GetAllTeamsInternalAsync; without it the call throws NotSupportedException, and a startup check reports the gap as soon as anything can grant this scope. The default used to return nothing instead, and the symptom was every holder, Owners included, being told they are not a member of a team.

SetOwner

Authorizes making an existing member the sole owner of any team, whatever its current owner count — none, one, or several.

public const string SetOwner = "teams:set-owner"

Field Value

string

Remarks

Why this is its own scope. The rule this codebase already applies is that a new scope is warranted when an operation is irreversible or crosses a tenant boundary. This does the second: the caller is by definition not a member of the team they are acting on. It is also privilege escalation by construction — it hands someone Owner and takes it from whoever held it — so it earns its own grant and its own audit entry rather than riding on a scope granted for something else.

Deliberately not Delete: authorizing repair with the right to destroy would mean the only way to fix a team is to hold the right to delete it. Deliberately not the in-team team:manage either, which is TeamKey-bound and so cannot fit a non-member.

This grant can depose a sitting owner, and is meant to. The two cases it exists for are a team synced from a legacy system carrying several owners, which must be reduced to one, and a handover the sitting owner cannot perform themselves — they have left, or the account is gone. ITeamService.TransferOwnershipAsync remains the in-team path and still requires the caller to be the owner; this is the operator path, and the two are not alternatives.

Renamed from teams:assign-owner (3.9.0–3.13.0), which authorized only the ownerless-repair case. The name changed with the capability rather than being widened in place, so a host that granted the old string does not silently acquire the ability to depose owners. The old name authorizes nothing.