Skip to content

Commit

Permalink
Migrate services.MatchLabels to parse.Matcher
Browse files Browse the repository at this point in the history
This should be backwards-compatible plus add the {{regexp.match(...)}}
and {{regexp.not_match(...)}} functions.
  • Loading branch information
Andrew Lytvynov committed Sep 17, 2020
1 parent e00790c commit 9d71803
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 32 deletions.
18 changes: 13 additions & 5 deletions lib/services/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -1585,11 +1585,19 @@ func MatchLabels(selector Labels, target map[string]string) (bool, string, error
}

if !utils.SliceContainsStr(selectorValues, Wildcard) {
result, err := utils.SliceMatchesRegex(targetVal, selectorValues)
if err != nil {
return false, "", trace.Wrap(err)
} else if !result {
return false, fmt.Sprintf("no value match: got '%v' want: '%v'", targetVal, selectorValues), nil
matched := false
for _, expr := range selectorValues {
m, err := parse.Match(expr)
if err != nil {
return false, "", trace.Wrap(err)
}
if m.Match(targetVal) {
matched = true
break
}
}
if !matched {
return false, fmt.Sprintf("no value match: got %q want: %q", targetVal, selectorValues), nil
}
}
}
Expand Down
58 changes: 58 additions & 0 deletions lib/services/role_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,64 @@ func (s *RoleSuite) TestCheckAccess(c *C) {
{server: serverC2, login: "admin", hasAccess: true},
},
},
{
name: "role matches a regexp label",
roles: []RoleV3{
RoleV3{
Metadata: Metadata{
Name: "name1",
Namespace: defaults.Namespace,
},
Spec: RoleSpecV3{
Options: RoleOptions{
MaxSessionTTL: Duration(20 * time.Hour),
},
Allow: RoleConditions{
Logins: []string{"admin"},
NodeLabels: Labels{"role": []string{`{{regexp.match("worker.*")}}`}},
Namespaces: []string{defaults.Namespace, namespaceC},
},
},
},
},
checks: []check{
{server: serverA, login: "root", hasAccess: false},
{server: serverA, login: "admin", hasAccess: false},
{server: serverB, login: "root", hasAccess: false},
{server: serverB, login: "admin", hasAccess: true},
{server: serverC, login: "root", hasAccess: false},
{server: serverC, login: "admin", hasAccess: false},
},
},
{
name: "role matches a negative regexp label",
roles: []RoleV3{
RoleV3{
Metadata: Metadata{
Name: "name1",
Namespace: defaults.Namespace,
},
Spec: RoleSpecV3{
Options: RoleOptions{
MaxSessionTTL: Duration(20 * time.Hour),
},
Allow: RoleConditions{
Logins: []string{"admin"},
NodeLabels: Labels{"role": []string{`{{regexp.not_match("db.*")}}`}},
Namespaces: []string{defaults.Namespace, namespaceC},
},
},
},
},
checks: []check{
{server: serverA, login: "root", hasAccess: false},
{server: serverA, login: "admin", hasAccess: false},
{server: serverB, login: "root", hasAccess: false},
{server: serverB, login: "admin", hasAccess: true},
{server: serverC, login: "root", hasAccess: false},
{server: serverC, login: "admin", hasAccess: false},
},
},
}
for i, tc := range testCases {

Expand Down
27 changes: 0 additions & 27 deletions lib/utils/replace.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,32 +46,5 @@ func ReplaceRegexp(expression string, replaceWith string, input string) (string,
return expr.ReplaceAllString(input, replaceWith), nil
}

// SliceMatchesRegex checks if input matches any of the expressions. The
// match is always evaluated as a regex either an exact match or regexp.
func SliceMatchesRegex(input string, expressions []string) (bool, error) {
for _, expression := range expressions {
if !strings.HasPrefix(expression, "^") || !strings.HasSuffix(expression, "$") {
// replace glob-style wildcards with regexp wildcards
// for plain strings, and quote all characters that could
// be interpreted in regular expression
expression = "^" + GlobToRegexp(expression) + "$"
}

expr, err := regexp.Compile(expression)
if err != nil {
return false, trace.BadParameter(err.Error())
}

// Since the expression is always surrounded by ^ and $ this is an exact
// match for either a a plain string (for example ^hello$) or for a regexp
// (for example ^hel*o$).
if expr.MatchString(input) {
return true, nil
}
}

return false, nil
}

var replaceWildcard = regexp.MustCompile(`(\\\*)`)
var reExpansion = regexp.MustCompile(`\$[^\$]+`)

0 comments on commit 9d71803

Please sign in to comment.