Class ScopeDefinition
- Namespace
- Tharga.Team
- Assembly
- Tharga.Team.dll
Defines a scope with its default minimum access level and an optional human-readable description (shown as a tooltip in the scope picker).
public record ScopeDefinition : IEquatable<ScopeDefinition>
- Inheritance
-
ScopeDefinition
- Implements
- Inherited Members
Remarks
A scope name follows a grammar, and it is load-bearing rather than cosmetic.
{feature}:{action} team:read, apikey:manage, audit:read, mcp:discover
{feature}:{reach}:{action} support:unassigned:read, support:all:read
Feature is the area acted on and action is what may be done — the two halves the audit log already names, filters and charts by. Reach names the population a grant covers, and is present only where that population is not the one the scope is held against. A team scope needs no reach segment: it is held against a team, and that team is its reach. A system scope has no team to imply one, which is why every reach-bearing scope today is a system scope.
The first colon is the one that matters. AuditEntry.ParseScope splits there and nowhere
else, so support:all:read is audited as feature support, action all:read. That is
why reach goes in the middle: moving it into the feature — support-all:read — would fork one
feature into two in the audit log's feature filter and its charts, and would fall outside a
support:* notification route.
Two scopes express reach differently and are not the shape to copy. teams:read
pluralises the feature to mean "every team", against team:read's one; apikey:system-manage
qualifies the action instead. Both predate the grammar, and neither is worth renaming: a renamed scope
is invisible to the compiler and silently authorizes nothing.
Constructors
ScopeDefinition(string, AccessLevel, string, bool)
public ScopeDefinition(string Name, AccessLevel DefaultMinimumLevel, string Description, bool GrantOnly)
Parameters
NamestringDefaultMinimumLevelAccessLevelDescriptionstringGrantOnlybool
ScopeDefinition(string, AccessLevel, string, bool, string)
Defines a scope with its default minimum access level and an optional human-readable description (shown as a tooltip in the scope picker).
public ScopeDefinition(string Name, AccessLevel DefaultMinimumLevel, string Description = null, bool GrantOnly = false, string Group = null)
Parameters
NamestringThe scope name, as checked by RequireScopeAttribute. See the remarks for the grammar it follows.
DefaultMinimumLevelAccessLevelThe least-privileged access level that is granted this scope automatically. Meaningless when
GrantOnlyis true, since no access level grants such a scope.DescriptionstringHuman-readable description, shown in the scope catalogue and pickers.
GrantOnlyboolWhen true the scope is registered for documentation and validation only: no access level grants it, a tenant-defined custom role may not reference it, and the scope-override pickers do not offer it. It is held solely through a code-registered tenant role or an explicit scope override, so holding it is a recorded decision rather than a consequence of being a team Owner or Administrator. Register one with RegisterGrantOnly(string, string).
GroupstringThe key of the ScopeGroup the scope is shown under, or null for an ungrouped scope. Display only: it has no bearing on who holds the scope or how it is checked.
Remarks
A scope name follows a grammar, and it is load-bearing rather than cosmetic.
{feature}:{action} team:read, apikey:manage, audit:read, mcp:discover
{feature}:{reach}:{action} support:unassigned:read, support:all:read
Feature is the area acted on and action is what may be done — the two halves the audit log already names, filters and charts by. Reach names the population a grant covers, and is present only where that population is not the one the scope is held against. A team scope needs no reach segment: it is held against a team, and that team is its reach. A system scope has no team to imply one, which is why every reach-bearing scope today is a system scope.
The first colon is the one that matters. AuditEntry.ParseScope splits there and nowhere
else, so support:all:read is audited as feature support, action all:read. That is
why reach goes in the middle: moving it into the feature — support-all:read — would fork one
feature into two in the audit log's feature filter and its charts, and would fall outside a
support:* notification route.
Two scopes express reach differently and are not the shape to copy. teams:read
pluralises the feature to mean "every team", against team:read's one; apikey:system-manage
qualifies the action instead. Both predate the grammar, and neither is worth renaming: a renamed scope
is invisible to the compiler and silently authorizes nothing.
Properties
DefaultMinimumLevel
The least-privileged access level that is granted this scope automatically. Meaningless when
GrantOnly is true, since no access level grants such a scope.
public AccessLevel DefaultMinimumLevel { get; init; }
Property Value
Description
Human-readable description, shown in the scope catalogue and pickers.
public string Description { get; init; }
Property Value
GrantOnly
When true the scope is registered for documentation and validation only: no access level grants it, a tenant-defined custom role may not reference it, and the scope-override pickers do not offer it. It is held solely through a code-registered tenant role or an explicit scope override, so holding it is a recorded decision rather than a consequence of being a team Owner or Administrator. Register one with RegisterGrantOnly(string, string).
public bool GrantOnly { get; init; }
Property Value
Group
The key of the ScopeGroup the scope is shown under, or null for an ungrouped scope. Display only: it has no bearing on who holds the scope or how it is checked.
public string Group { get; init; }
Property Value
Name
The scope name, as checked by RequireScopeAttribute. See the remarks for the grammar it follows.
public string Name { get; init; }