From 46a4ad6a4ded21294e5561d8094833674a21ec7d Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Fri, 11 Oct 2024 10:58:54 +0200 Subject: [PATCH] auth: add Grants.IsSubset This is a little bit hacky. Previously DecodeGrants would only store the list of grants associated with the current service. This minimizes API breakage by storing all grants as $service/$grant in the map key and stores the local service name in the grant object, and updates Grants.Has() to accept "$grant" and infer that it refers to a local service or accept the fully qualified "$service/$grant" to test against grants for any service -- which IsSubset makes use of to test that one Grant object is a subset of another with respect to all services it has grants for. --- auth/bearer.go | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/auth/bearer.go b/auth/bearer.go index 15105847d59a39b720eb1b0b067bb55fdf54950c..a1bc435a4648bfd15c126d5c4e91865186088c97 100644 --- a/auth/bearer.go +++ b/auth/bearer.go @@ -90,6 +90,7 @@ type Grants struct { all bool grants map[string]string + local string encoded string } @@ -99,6 +100,7 @@ func DecodeGrants(ctx context.Context, grants string) (Grants, error) { return Grants{ all: true, grants: nil, + local: config.ServiceName(ctx), encoded: "", }, nil } @@ -121,18 +123,23 @@ func DecodeGrants(ctx context.Context, grants string) (Grants, error) { } else { access = parts[1] } - if service == config.ServiceName(ctx) { - accessMap[scope] = access - } + name := fmt.Sprintf("%s/%s", service, scope) + accessMap[name] = access } return Grants{ all: false, grants: accessMap, + local: config.ServiceName(ctx), encoded: grants, }, nil } +// Returns true if these grants include access to a specific OAuth grant. func (g *Grants) Has(grant string, mode string) bool { + if !strings.ContainsRune(grant, '/') { + grant = fmt.Sprintf("%s/%s", g.local, grant) + } + if mode != RO && mode != RW { panic("Invalid access mode") } @@ -154,6 +161,31 @@ func (g *Grants) Has(grant string, mode string) bool { } } +// Returns true if this is a universal grant. +func (g *Grants) HasAll() bool { + return g.all +} + +// Returns true of this grant object contains a subset of the permissions of +// another. +func (g *Grants) IsSubset(other *Grants) bool { + if g.all && !other.all { + return false + } + + if other.all { + return true + } + + for scope, access := range g.grants { + if !other.Has(scope, access) { + return false + } + } + + return true +} + func (g *Grants) Encode() string { return g.encoded }