pub struct ScopeSet(pub Vec<Scope>);Expand description
A space-separated set of Scopes, as carried on a scope parameter or
a token response.
Canonical by construction: ScopeSet::new sorts, dedupes, and drops
any atom already admitted by another atom of its kind, so two sets
built from the same members in different orders — or with redundant
members — compare equal. Policy is a set of records in a repository,
which has no inherent order, so a ceiling has to be a function of the
rule set alone; a ScopeSet that could represent the same ceiling two
different ways would let vector order silently change what a policy
means, which is exactly the failure this type exists to rule out.
Tuple Fields§
§0: Vec<Scope>Implementations§
Source§impl ScopeSet
impl ScopeSet
Sourcepub fn new(atoms: Vec<Scope>) -> Self
pub fn new(atoms: Vec<Scope>) -> Self
Builds a canonical ScopeSet from a bag of atoms: sorted, deduped,
and with every atom already admitted by another atom of its kind
dropped. Containment among distinct atoms is a strict partial order —
two distinct atoms can never admit each other — so which atom
survives never depends on the input order.
Only an atom of the same kind stands in for another. A ceiling judges
each kind on its own (ScopeSet::narrow), and this server never
grants a transition: scope, so transition:generic does not absorb
the repo: scopes asked for beside it.
Sourcepub fn covers(&self, wanted: &Scope) -> bool
pub fn covers(&self, wanted: &Scope) -> bool
Whether some atom of this set admits wanted, which is what a token
carrying this set may do.
Sourcepub fn permits_rpc(&self, lxm: Option<&str>, aud: &str) -> bool
pub fn permits_rpc(&self, lxm: Option<&str>, aud: &str) -> bool
Whether a token carrying this set may act at another service: call
lxm there through atproto-proxy, or take a token for it from
com.atproto.server.getServiceAuth. aud names the service, as a
DID or a DID and a service fragment.
None asks for a token bound to no method. An rpc:* scope for
aud admits one, and so does transition:generic, as the reference
PDS reads it.
Sourcepub fn parse(s: &str) -> Result<Self, ScopeParseError>
pub fn parse(s: &str) -> Result<Self, ScopeParseError>
Parses a whole scope string: OAuth2’s space-separated list of atoms.
Checks MAX_SCOPE_BYTES before reading any atom, and
MAX_SCOPE_ATOMS against the scopes they name. An atom this grammar
cannot read is kept as Scope::Unknown; see ScopeSet::read.
Sourcepub fn read(
s: &str,
) -> Result<(Self, Vec<(String, ScopeWarning)>), ScopeParseError>
pub fn read( s: &str, ) -> Result<(Self, Vec<(String, ScopeWarning)>), ScopeParseError>
Reads a scope an app asked for: the set, and each warning its atoms’ forms carry, beside the atom as the app wrote it.
Every form the permission spec defines reads without a warning. A form
it does not define is read as well as it can be, with a warning
saying how. An atom that names no scope this grammar reads is kept as
Scope::Unknown, with a warning, rather than failing the string: a
server can drop it and grant the rest. Only the bounds and a character
no scope may hold fail the whole string.
Sourcepub fn parse_grant(s: &str) -> Result<Self, ScopeParseError>
pub fn parse_grant(s: &str) -> Result<Self, ScopeParseError>
Parses a grant this server stored, which may be larger than any
request: each include: in the request became the atoms its
permission set grants.
Checks MAX_GRANT_BYTES and MAX_GRANT_ATOMS before parsing any
atom.
fn read_within( s: &str, max_bytes: usize, max_scopes: usize, ) -> Result<(Self, Vec<(String, ScopeWarning)>), ScopeParseError>
Sourcepub fn intersect(&self, ceiling: &ScopeSet) -> Result<ScopeSet, ScopeRefused>
pub fn intersect(&self, ceiling: &ScopeSet) -> Result<ScopeSet, ScopeRefused>
Is every requested scope admitted by some scope in ceiling?
Returns the whole intersected grant on success. On refusal, returns
the first requested scope that no ceiling scope admits at all —
plan/scope-policy.md’s “refuse rather than silently narrow, and say
which scope”, which needs this exact atom to build the
WWW-Authenticate: Bearer error="insufficient_scope", scope="…"
header.
A requested atom’s grant is the union of its overlap with every
ceiling atom, not the single largest overlap: ceiling is a set with
no inherent order, so a ceiling atom that grants create and one
that grants delete must both survive regardless of which one this
loop visits first. ScopeSet::new then collapses any overlap that
one of its siblings already subsumes.
An overlap counts only in the requested atom’s own kind, as in
ScopeSet::narrow: transition:generic under a repo:* ceiling is
refused, not paid out as repo:*.
Sourcepub fn narrow(&self, ceiling: &ScopeSet) -> (ScopeSet, ScopeSet)
pub fn narrow(&self, ceiling: &ScopeSet) -> (ScopeSet, ScopeSet)
The same overlap, split into what the ceiling granted and what it did not.
ScopeSet::intersect refuses the whole request the moment one
atom falls outside the ceiling, which is what token and every
other caller checking a grant against a ceiling wants. A consent
decision wants the other answer: the grant that is available, plus
the part of the request it does not cover, so the record an agent
approves can name both.
Projection runs from the requested atom onto the ceiling and never
the other way, and only ever within one kind: a granted atom is
always a narrowing of something the request named, of that atom’s own
kind. So a wide atom is never paid out in capabilities of a kind
nobody asked for — transition:generic overlaps repo:* and
rpc:*, and a ceiling holding those grants it nothing.
cut is every requested atom that no granted atom contains — the
atoms the ceiling dropped outright and the atoms it shrank alike.
The grammar has no difference operation, so an atom the ceiling
narrowed (repo:*?action=create&action=delete against a ceiling holding
repo:*?action=create) is named whole rather than as the sliver
removed from it. An empty cut means the ceiling granted the
request exactly as asked, which is the only case
ScopeSet::intersect and this agree on completely.
Trait Implementations§
impl Eq for ScopeSet
impl StructuralPartialEq for ScopeSet
Auto Trait Implementations§
impl Freeze for ScopeSet
impl RefUnwindSafe for ScopeSet
impl Send for ScopeSet
impl Sync for ScopeSet
impl Unpin for ScopeSet
impl UnsafeUnpin for ScopeSet
impl UnwindSafe for ScopeSet
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.