mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-01 02:57:59 +00:00
130 lines
2.8 KiB
Go
130 lines
2.8 KiB
Go
package namespace
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
type nsContext struct {
|
|
context.Context
|
|
// Note: this is currently not locked because we think all uses will take
|
|
// place within a single goroutine. If that isn't the case, this should be
|
|
// protected by an atomic.Value.
|
|
cachedNS *Namespace
|
|
}
|
|
|
|
type contextValues struct{}
|
|
|
|
type Namespace struct {
|
|
ID string `json:"id"`
|
|
Path string `json:"path"`
|
|
}
|
|
|
|
const (
|
|
RootNamespaceID = "root"
|
|
)
|
|
|
|
var (
|
|
contextNamespace contextValues = struct{}{}
|
|
ErrNoNamespace error = errors.New("no namespace")
|
|
RootNamespace *Namespace = &Namespace{
|
|
ID: RootNamespaceID,
|
|
Path: "",
|
|
}
|
|
)
|
|
|
|
var AdjustRequest = func(r *http.Request) (*http.Request, int) {
|
|
return r.WithContext(ContextWithNamespace(r.Context(), RootNamespace)), 0
|
|
}
|
|
|
|
func (n *Namespace) HasParent(possibleParent *Namespace) bool {
|
|
switch {
|
|
case n.Path == "":
|
|
return false
|
|
case possibleParent.Path == "":
|
|
return true
|
|
default:
|
|
return strings.HasPrefix(n.Path, possibleParent.Path)
|
|
}
|
|
}
|
|
|
|
func (n *Namespace) TrimmedPath(path string) string {
|
|
return strings.TrimPrefix(path, n.Path)
|
|
}
|
|
|
|
func ContextWithNamespace(ctx context.Context, ns *Namespace) context.Context {
|
|
nsCtx := context.WithValue(ctx, contextNamespace, ns)
|
|
return &nsContext{
|
|
Context: nsCtx,
|
|
cachedNS: ns,
|
|
}
|
|
}
|
|
|
|
func RootContext(ctx context.Context) context.Context {
|
|
if ctx == nil {
|
|
return ContextWithNamespace(context.Background(), RootNamespace)
|
|
}
|
|
return ContextWithNamespace(ctx, RootNamespace)
|
|
}
|
|
|
|
// This function caches the ns to avoid doing a .Value lookup over and over,
|
|
// because it's called a *lot* in the request critical path. .Value is
|
|
// concurrency-safe so uses some kind of locking/atomicity, but it should never
|
|
// be read before first write, plus we don't believe this will be called from
|
|
// different goroutines, so it should be safe.
|
|
func FromContext(ctx context.Context) (*Namespace, error) {
|
|
if ctx == nil {
|
|
return nil, errors.New("context was nil")
|
|
}
|
|
|
|
nsCtx, ok := ctx.(*nsContext)
|
|
if ok {
|
|
if nsCtx.cachedNS != nil {
|
|
return nsCtx.cachedNS, nil
|
|
}
|
|
}
|
|
|
|
nsRaw := ctx.Value(contextNamespace)
|
|
if nsRaw == nil {
|
|
return nil, ErrNoNamespace
|
|
}
|
|
|
|
ns := nsRaw.(*Namespace)
|
|
if ns == nil {
|
|
return nil, ErrNoNamespace
|
|
}
|
|
|
|
if ok {
|
|
nsCtx.cachedNS = ns
|
|
}
|
|
return ns, nil
|
|
}
|
|
|
|
func TestContext() context.Context {
|
|
return ContextWithNamespace(context.Background(), TestNamespace())
|
|
}
|
|
|
|
func TestNamespace() *Namespace {
|
|
return RootNamespace
|
|
}
|
|
|
|
// Canonicalize trims any prefix '/' and adds a trailing '/' to the
|
|
// provided string
|
|
func Canonicalize(nsPath string) string {
|
|
if nsPath == "" {
|
|
return ""
|
|
}
|
|
|
|
// Canonicalize the path to not have a '/' prefix
|
|
nsPath = strings.TrimPrefix(nsPath, "/")
|
|
|
|
// Canonicalize the path to always having a '/' suffix
|
|
if !strings.HasSuffix(nsPath, "/") {
|
|
nsPath += "/"
|
|
}
|
|
|
|
return nsPath
|
|
}
|