| // Copyright (c) HashiCorp, Inc. |
| // SPDX-License-Identifier: MPL-2.0 |
| |
| package namespace |
| |
| import ( |
| "context" |
| "errors" |
| "fmt" |
| "strings" |
| |
| "github.com/hashicorp/vault/sdk/helper/consts" |
| ) |
| |
| type contextValues struct{} |
| |
| type Namespace struct { |
| ID string `json:"id" mapstructure:"id"` |
| Path string `json:"path" mapstructure:"path"` |
| CustomMetadata map[string]string `json:"custom_metadata" mapstructure:"custom_metadata"` |
| } |
| |
| func (n *Namespace) String() string { |
| return fmt.Sprintf("ID: %s. Path: %s", n.ID, n.Path) |
| } |
| |
| const ( |
| RootNamespaceID = "root" |
| ) |
| |
| var ( |
| contextNamespace contextValues = struct{}{} |
| ErrNoNamespace error = errors.New("no namespace") |
| RootNamespace *Namespace = &Namespace{ |
| ID: RootNamespaceID, |
| Path: "", |
| CustomMetadata: make(map[string]string), |
| } |
| ) |
| |
| func (n *Namespace) HasParent(possibleParent *Namespace) bool { |
| switch { |
| case possibleParent.Path == "": |
| return true |
| case n.Path == "": |
| return false |
| 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 { |
| return context.WithValue(ctx, contextNamespace, ns) |
| } |
| |
| func RootContext(ctx context.Context) context.Context { |
| if ctx == nil { |
| return ContextWithNamespace(context.Background(), RootNamespace) |
| } |
| return ContextWithNamespace(ctx, RootNamespace) |
| } |
| |
| // FromContext retrieves the namespace from a context, or an error |
| // if there is no namespace in the context. |
| func FromContext(ctx context.Context) (*Namespace, error) { |
| if ctx == nil { |
| return nil, errors.New("context was nil") |
| } |
| |
| nsRaw := ctx.Value(contextNamespace) |
| if nsRaw == nil { |
| return nil, ErrNoNamespace |
| } |
| |
| ns := nsRaw.(*Namespace) |
| if ns == nil { |
| return nil, ErrNoNamespace |
| } |
| |
| return ns, nil |
| } |
| |
| // 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 |
| } |
| |
| func SplitIDFromString(input string) (string, string) { |
| prefix := "" |
| slashIdx := strings.LastIndex(input, "/") |
| |
| switch { |
| case strings.HasPrefix(input, consts.LegacyBatchTokenPrefix): |
| prefix = consts.LegacyBatchTokenPrefix |
| input = input[2:] |
| |
| case strings.HasPrefix(input, consts.LegacyServiceTokenPrefix): |
| prefix = consts.LegacyServiceTokenPrefix |
| input = input[2:] |
| case strings.HasPrefix(input, consts.BatchTokenPrefix): |
| prefix = consts.BatchTokenPrefix |
| input = input[4:] |
| case strings.HasPrefix(input, consts.ServiceTokenPrefix): |
| prefix = consts.ServiceTokenPrefix |
| input = input[4:] |
| |
| case slashIdx > 0: |
| // Leases will never have a b./s. to start |
| if slashIdx == len(input)-1 { |
| return input, "" |
| } |
| prefix = input[:slashIdx+1] |
| input = input[slashIdx+1:] |
| } |
| |
| idx := strings.LastIndex(input, ".") |
| if idx == -1 { |
| return prefix + input, "" |
| } |
| if idx == len(input)-1 { |
| return prefix + input, "" |
| } |
| |
| return prefix + input[:idx], input[idx+1:] |
| } |
| |
| // MountPathDetails contains the details of a mount's location, |
| // consisting of the namespace of the mount and the path of the |
| // mount within the namespace |
| type MountPathDetails struct { |
| Namespace *Namespace |
| MountPath string |
| } |
| |
| func (mpd *MountPathDetails) GetRelativePath(currNs *Namespace) string { |
| subNsPath := strings.TrimPrefix(mpd.Namespace.Path, currNs.Path) |
| return subNsPath + mpd.MountPath |
| } |
| |
| func (mpd *MountPathDetails) GetFullPath() string { |
| return mpd.Namespace.Path + mpd.MountPath |
| } |