| // Copyright 2015 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package mounttablelib |
| |
| import ( |
| "encoding/json" |
| "io" |
| "os" |
| "sort" |
| "strconv" |
| "strings" |
| |
| "v.io/v23/context" |
| "v.io/v23/conventions" |
| "v.io/v23/security" |
| "v.io/v23/security/access" |
| "v.io/v23/services/mounttable" |
| "v.io/v23/verror" |
| ) |
| |
| // Blessings can't include a comma so we use them in made up user ids. The following distinctions are |
| // made so that we can account for them differently. |
| const localUser = ",LOCAL," // a client that has our public key but no blessing from which we can extract a user name |
| const blessedUser = ",BLESSED," // a client with blessings we trust but from which we can't extract a user name |
| const unknownUser = ",UNKNOWN," // a client which presents no blessing we trust |
| |
| // VersionedPermissions associates a Version with a Permissions |
| type VersionedPermissions struct { |
| V int32 |
| P access.Permissions |
| } |
| |
| func NewVersionedPermissions() *VersionedPermissions { |
| return &VersionedPermissions{P: make(access.Permissions)} |
| } |
| |
| // Set sets the Permissions iff Version matches the current Version. If the set happens, the Version is advanced. |
| // If b is nil, this creates a new VersionedPermissions. |
| func (b *VersionedPermissions) Set(ctx *context.T, verstr string, perm access.Permissions) (*VersionedPermissions, error) { |
| if b == nil { |
| b = new(VersionedPermissions) |
| } |
| if len(verstr) > 0 { |
| gen, err := strconv.ParseInt(verstr, 10, 32) |
| if err != nil { |
| return b, verror.NewErrBadVersion(ctx) |
| } |
| if gen >= 0 && int32(gen) != b.V { |
| return b, verror.NewErrBadVersion(ctx) |
| } |
| } |
| b.P = perm |
| // Increment with possible wrap. |
| b.V++ |
| if b.V < 0 { |
| b.V = 0 |
| } |
| return b, nil |
| } |
| |
| // Get returns the current Version and Permissions. |
| func (b *VersionedPermissions) Get() (string, access.Permissions) { |
| if b == nil { |
| return "", nil |
| } |
| return strconv.FormatInt(int64(b.V), 10), b.P |
| } |
| |
| // AccessListForTag returns the current access list for the given tag. |
| func (b *VersionedPermissions) AccessListForTag(tag string) (access.AccessList, bool) { |
| al, exists := b.P[tag] |
| return al, exists |
| } |
| |
| // Copy copies the receiver. |
| func (b *VersionedPermissions) Copy() *VersionedPermissions { |
| nt := new(VersionedPermissions) |
| nt.P = b.P.Copy() |
| nt.V = b.V |
| return nt |
| } |
| |
| // Add adds the blessing pattern to the tag in the reciever. |
| func (b *VersionedPermissions) Add(pattern security.BlessingPattern, tag string) { |
| b.P.Add(pattern, tag) |
| } |
| |
| // parsePermFile reads a file and parses the contained permissions. |
| func (mt *mountTable) parsePermFile(ctx *context.T, path string) error { |
| ctx.VI(2).Infof("parsePermFile(%s)", path) |
| if path == "" { |
| return nil |
| } |
| // A map from node name to permissions. |
| var pm map[string]access.Permissions |
| f, err := os.Open(path) |
| if err != nil { |
| return err |
| } |
| defer f.Close() |
| decoder := json.NewDecoder(f) |
| for { |
| if err = decoder.Decode(&pm); err != nil { |
| if err == io.EOF { |
| break |
| } |
| return err |
| } |
| cc := &callContext{ctx: ctx, |
| create: true, |
| ignorePerms: true, |
| ignoreLimits: true, |
| } |
| // Sort the map shortest key first. That way configs for nodes higher up in the |
| // name tree happen first ensuring that lower nodes correctly inherit permissions. |
| var keys []string |
| for name := range pm { |
| keys = append(keys, name) |
| } |
| sort.Strings(keys) |
| for _, name := range keys { |
| perms := pm[name] |
| var elems []string |
| isPattern := false |
| |
| // The configuration file allows patterns and also will cause the superuser to |
| // be set to the root's administrator. |
| if len(name) == 0 { |
| // If the config file has is an Admin tag on the root AccessList, the |
| // list of Admin users is the equivalent of a super user for |
| // the whole table. Later SetPermissions do not update the set |
| // of super users. |
| if bp, exists := perms[string(mounttable.Admin)]; exists { |
| mt.superUsers = bp |
| } |
| } else { |
| // AccessList templates terminate with a %% element. These are very |
| // constrained matches, i.e., the trailing element of the name |
| // is copied into every %% in the AccessList. |
| elems = strings.Split(name, "/") |
| if elems[len(elems)-1] == templateVar { |
| isPattern = true |
| elems = elems[:len(elems)-1] |
| } |
| } |
| |
| // Create name and add the Permissions map to it. |
| cc.creator = mt.pickCreator(ctx, nil) |
| n, err := mt.findNode(cc, elems, nil, nil) |
| if err != nil { |
| ctx.Errorf("skipping node for %v; error: %v", elems, err) |
| } |
| if n == nil { |
| continue |
| } |
| if err == nil { |
| ctx.VI(2).Infof("added perms %v to %s", perms, name) |
| if isPattern { |
| n.permsTemplate = perms |
| } else { |
| n.vPerms, _ = n.vPerms.Set(nil, "", perms) |
| n.explicitPermissions = true |
| } |
| } |
| n.parent.Unlock() |
| n.Unlock() |
| } |
| } |
| return nil |
| } |
| |
| // pickCreator returns a string matching the blessing of the user performing the creation. |
| func (mt *mountTable) pickCreator(ctx *context.T, call security.Call) string { |
| ids := conventions.GetClientUserIds(ctx, call) |
| // Replace the slashes with something else or we'll confuse the stats package. |
| return strings.Replace(ids[0], "/", "\\", 0) |
| } |