navidrome/core/apiauth/scopes.go
Deluan 0628721006 refactor(api): simplify API v1 auth after dropping access tokens
- Fold Allowed into Expand and drop the ErrInsufficientScope sentinel; the
  gate's scopeError is now the only source of insufficient_scope.
- Replace the two-value authKind with a public flag, inline loadUser, and
  pass the grant to touch.
- Read a declared request body once before validation, so the JSON checks
  and the handler no longer depend on kin-openapi restoring the exact bytes.
- Merge the Service tests into one file and drop specs that only covered
  the removed liveness cache. The revoked-during-password-change spec now
  revokes after the gate authenticates, so it reaches ChangePassword again.

Signed-off-by: Deluan <deluan@navidrome.org>
2026-09-28 21:47:16 -04:00

63 lines
1.5 KiB
Go

package apiauth
import (
"slices"
"strings"
)
const (
ScopeAll = "all"
ScopeRead = "read"
ScopePassword = "password"
ScopeAdmin = "admin"
)
// KnownScopes lists the scopes of modules this server implements; `all` expands to these.
var KnownScopes = []string{ScopeRead, ScopePassword}
func known(s string) bool {
return slices.Contains(KnownScopes, s)
}
func grantable(s string, isAdmin bool) bool {
return known(s) && (s != ScopeAdmin || isAdmin)
}
func normalize(in []string) []string {
out := slices.Clone(in)
slices.Sort(out)
return slices.Compact(out)
}
// Entitled returns the scopes a new grant stores.
func Entitled(requested []string, isAdmin bool) []string {
if requested == nil {
return []string{ScopeAll}
}
var out []string
for _, s := range requested {
if s == ScopeAll || grantable(s, isAdmin) {
out = append(out, s)
}
}
return normalize(out)
}
// Expand turns a grant's stored scopes into the concrete scopes it carries right now.
func Expand(granted []string, isAdmin bool) []string {
var out []string
for _, s := range granted {
if s == ScopeAll {
out = append(out, KnownScopes...)
continue
}
out = append(out, s)
}
out = slices.DeleteFunc(out, func(s string) bool { return !grantable(s, isAdmin) })
return normalize(out)
}
func Satisfies(scopes []string, required string) bool {
return slices.Contains(scopes, required) ||
(!strings.HasSuffix(required, ":write") && slices.Contains(scopes, required+":write"))
}