/
versionedpermissions.go
179 lines (164 loc) · 5.11 KB
/
versionedpermissions.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// 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)
}