Class SystemTeamScopes
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
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
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
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
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.
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
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. A startup check fails
loudly if the retired name is still registered — see RetiredScopeCheck — because the failure
would otherwise be a silent refusal at the point of use rather than an error at boot.