-
Notifications
You must be signed in to change notification settings - Fork 28
/
conventions.go
147 lines (132 loc) · 4.44 KB
/
conventions.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
// Copyright 2016 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 conventions implements unenforced conventions for Vanadium.
//
// This is still work in progress, the conventions may change. Please do not
// rely on these conventions till this comment is removed!
package conventions
import (
"strings"
"v.io/v23/naming"
"v.io/v23/security"
)
// Blessing represents structured information encoded in a blessing name.
type Blessing struct {
IdentityProvider string // Name of the identity provider
User string // UserID attested to by the identity provider
Application string // ApplicationID attested to by the identity provider (may be empty)
Rest string // Remaining extensions of the blessing.
}
// String returns the blessing name represented by this structure.
func (b *Blessing) String() string {
switch b.inferFormat() {
case formatUser:
return join(b.IdentityProvider, "u", b.User, b.Rest)
case formatAppUser:
return join(b.IdentityProvider, "o", b.Application, b.User, b.Rest)
default:
return ""
}
}
// Home returns the "Home directory" in the global namespace for this Blessing.
func (b *Blessing) Home() string {
switch b.inferFormat() {
case formatUser:
return naming.Join("home", naming.EncodeAsNameElement(join(b.IdentityProvider, "u", b.User)))
case formatAppUser:
return naming.Join("home", naming.EncodeAsNameElement(join(b.IdentityProvider, "o", b.Application, b.User)))
default:
return ""
}
}
// UserPattern returns a BlessingPattern that would be matched by a blessing obtainable by the user (irrespective of application).
func (b *Blessing) UserPattern() security.BlessingPattern {
return pattern(b.IdentityProvider, "u", b.User)
}
// AppUserPattern returns a BlessingPattern that would be matched by a blessing for the user using the same application.
func (b *Blessing) AppUserPattern() security.BlessingPattern {
// TODO(ashankar): This should change to join(b.IdentityProvider, "a", b.Application, "u", b.User)}
return pattern(b.IdentityProvider, "o", b.Application, b.User)
}
// AppPattern returns a BlessingPattern that would be matched by all users of the same application.
func (b *Blessing) AppPattern() security.BlessingPattern {
return pattern(b.IdentityProvider, "o", b.Application)
}
func (b *Blessing) inferFormat() format {
if len(b.Application) == 0 {
return formatUser
}
return formatAppUser
}
func join(elems ...string) string {
if len(elems) > 0 && elems[len(elems)-1] == "" {
elems = elems[:len(elems)-1]
}
return strings.Join(elems, security.ChainSeparator)
}
func pattern(elems ...string) security.BlessingPattern {
for _, e := range elems {
if len(e) == 0 {
return security.NoExtension
}
}
return security.BlessingPattern(join(elems...))
}
// format defines the format of the convention used by a blessing name.
type format int
const (
formatInfer format = iota // unknown convention, try to infer it
formatUser // e.g., dev.v.io:u:bugs@bunny.com
formatAppUser // e.g., dev.v.io:o:appid:bugs@bunny.com
)
// ParseBlessingNames extracts structured information from the provided blessing names.
// Blessing names that do not adhere to the conventions of this package are ignored.
//
// Typically the set of names to provide would be obtained via a call to
// security.RemoteBlessingNames or security.LocalBlessingNames.
func ParseBlessingNames(blessingNames ...string) []Blessing {
var ret []Blessing
for _, n := range blessingNames {
if b, ok := parseOne(n); ok {
ret = append(ret, b)
}
}
return ret
}
func parseOne(blessingName string) (Blessing, bool) {
parts := strings.SplitN(blessingName, security.ChainSeparator, 4)
if len(parts) < 3 {
return Blessing{}, false
}
var rest string
if len(parts) > 3 {
rest = parts[3]
}
switch parts[1] {
case "u":
return Blessing{
IdentityProvider: parts[0],
User: parts[2],
Rest: rest,
}, true
case "o":
if len(rest) == 0 {
return Blessing{}, false
}
// TODO(ashankar): Change this - requiring a 'u' component after the app name?
moreparts := strings.SplitN(rest, security.ChainSeparator, 2)
if len(moreparts) > 1 {
rest = moreparts[1]
} else {
rest = ""
}
return Blessing{
IdentityProvider: parts[0],
User: moreparts[0],
Application: parts[2],
Rest: rest,
}, true
}
return Blessing{}, false
}