-
Notifications
You must be signed in to change notification settings - Fork 28
/
pattern.go
159 lines (146 loc) · 4.62 KB
/
pattern.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
// 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 security
import (
"fmt"
"regexp"
"strings"
"v.io/v23/naming"
)
// Syntax is /{endpoint}/__({pattern})/{name}
var namePatternRegexp = regexp.MustCompile(`^__\(([^)]*)\)($|/)(.*)`)
// MatchedBy returns true iff one of the presented blessings matches
// p as per the rules described in documentation for the BlessingPattern type.
func (p BlessingPattern) MatchedBy(blessings ...string) bool {
if len(p) == 0 || !p.IsValid() {
return false
}
if p == AllPrincipals {
return true
}
pstr, glob := trimNoExtension(string(p))
if pstr == "" {
return false
}
for _, b := range blessings {
if b == pstr {
return true
}
if glob && strings.HasPrefix(b, pstr) && strings.HasPrefix(b[len(pstr):], ChainSeparator) {
return true
}
}
return false
}
// trimNoExtension removes the trailing NoExtension component from pattern.
// Returns true if nothing was trimmed.
func trimNoExtension(pattern string) (string, bool) {
if suffix := string(NoExtension); pattern == suffix {
return "", false
}
if suffix := ChainSeparator + string(NoExtension); strings.HasSuffix(pattern, suffix) {
return pattern[0 : len(pattern)-len(suffix)], false
}
return pattern, true
}
// splitBlessing splits in into the first component upto the ChainSeparator and
// the rest.
func splitBlessing(in string) (prefix, rest string) {
idx := strings.Index(in, ChainSeparator)
if idx == -1 {
return in, ""
}
return in[0:idx], in[idx+1:]
}
// IsValid returns true iff the BlessingPattern is well formed, as per the
// rules described in documentation for the BlessingPattern type.
func (p BlessingPattern) IsValid() bool {
if len(p) == 0 {
return false
}
if p == AllPrincipals {
return true
}
pstr, _ := trimNoExtension(string(p))
if strings.HasSuffix(pstr, ChainSeparator) {
return false
}
for len(pstr) > 0 {
prefix, rest := splitBlessing(pstr)
if validateExtension(prefix) != nil {
return false
}
pstr = rest
}
return true
}
// MakeNonExtendable returns a pattern that is matched exactly
// by the blessing specified by the given pattern string.
//
// For example:
// onlyAlice := BlessingPattern("google:alice").MakeNonExtendable()
// onlyAlice.MatchedBy("google:alice") // Returns true
// onlyAlice.MatchedBy("google") // Returns false
// onlyAlice.MatchedBy("google:alice:bob") // Returns false
func (p BlessingPattern) MakeNonExtendable() BlessingPattern {
if len(p) == 0 || p == NoExtension {
return NoExtension
}
if strings.HasSuffix(string(p), ChainSeparator+string(NoExtension)) {
return p
}
return BlessingPattern(string(p) + ChainSeparator + string(NoExtension))
}
// PrefixPatterns returns a set of BlessingPatterns that are matched by
// blessings that either directly match the provided pattern or can be
// extended to match the provided pattern.
//
// For example:
// BlessingPattern("google:alice:friend").PrefixPatterns() returns
// ["google:$", "google:alice:$", "google:alice:friend"]
// BlessingPattern("google:alice:friend:$").PrefixPatterns() returns
// ["google:$", "google:alice:$", "google:alice:friend:$"]
//
// The returned set of BlessingPatterns are ordered by the number of
// ":"-separated components in the pattern.
func (p BlessingPattern) PrefixPatterns() []BlessingPattern {
if p == NoExtension {
return []BlessingPattern{p}
}
parts := strings.Split(string(p), ChainSeparator)
if parts[len(parts)-1] == string(NoExtension) {
parts = parts[:len(parts)-2]
} else {
parts = parts[:len(parts)-1]
}
var ret []BlessingPattern
for i := 0; i < len(parts); i++ {
ret = append(ret, BlessingPattern(strings.Join(parts[:i+1], ChainSeparator)).MakeNonExtendable())
}
return append(ret, p)
}
// SplitPatternName takes an object name and parses out the server blessing pattern.
// It returns the pattern specified, and the name with the pattern removed.
func SplitPatternName(origName string) (BlessingPattern, string) {
rooted := naming.Rooted(origName)
ep, name := naming.SplitAddressName(origName)
match := namePatternRegexp.FindStringSubmatch(name)
if len(match) == 0 {
return BlessingPattern(""), origName
}
pattern := BlessingPattern(match[1])
name = naming.Clean(match[3])
if rooted {
name = naming.JoinAddressName(ep, name)
}
return pattern, name
}
// JoinPatternName embeds the specified pattern into a name.
func JoinPatternName(pattern BlessingPattern, name string) string {
if len(pattern) == 0 {
return name
}
ep, rel := naming.SplitAddressName(name)
return naming.JoinAddressName(ep, fmt.Sprintf("__(%s)/%s", pattern, rel))
}