openapi: Fix logic for labeling unauthenticated/sudo paths (#19600)

This commit is contained in:
Anton Averchenkov 2023-03-20 13:25:09 -04:00 committed by GitHub
parent 1fe1c756ab
commit 4d10063cbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 162 additions and 37 deletions

3
changelog/19600.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:bug
openapi: Fix logic for labeling unauthenticated/sudo paths.
```

View File

@ -547,14 +547,55 @@ func constructRequestResponseName(path, prefix, suffix string) string {
return b.String() return b.String()
} }
// specialPathMatch checks whether the given path matches one of the special
// paths, taking into account * and + wildcards (e.g. foo/+/bar/*)
func specialPathMatch(path string, specialPaths []string) bool { func specialPathMatch(path string, specialPaths []string) bool {
// Test for exact or prefix match of special paths. // pathMatchesByParts determines if the path matches the special path's
// pattern, accounting for the '+' and '*' wildcards
pathMatchesByParts := func(pathParts []string, specialPathParts []string) bool {
if len(pathParts) < len(specialPathParts) {
return false
}
for i := 0; i < len(specialPathParts); i++ {
var (
part = pathParts[i]
pattern = specialPathParts[i]
)
if pattern == "+" {
continue
}
if pattern == "*" {
return true
}
if strings.HasSuffix(pattern, "*") && strings.HasPrefix(part, pattern[0:len(pattern)-1]) {
return true
}
if pattern != part {
return false
}
}
return len(pathParts) == len(specialPathParts)
}
pathParts := strings.Split(path, "/")
for _, sp := range specialPaths { for _, sp := range specialPaths {
if sp == path || // exact match
(strings.HasSuffix(sp, "*") && strings.HasPrefix(path, sp[0:len(sp)-1])) { if sp == path {
return true
}
// match *
if strings.HasSuffix(sp, "*") && strings.HasPrefix(path, sp[0:len(sp)-1]) {
return true
}
// match +
if strings.Contains(sp, "+") && pathMatchesByParts(pathParts, strings.Split(sp, "/")) {
return true return true
} }
} }
return false return false
} }

View File

@ -226,42 +226,123 @@ func TestOpenAPI_SplitFields(t *testing.T) {
} }
func TestOpenAPI_SpecialPaths(t *testing.T) { func TestOpenAPI_SpecialPaths(t *testing.T) {
tests := []struct { tests := map[string]struct {
pattern string pattern string
rootPaths []string rootPaths []string
root bool rootExpected bool
unauthPaths []string unauthenticatedPaths []string
unauth bool unauthenticatedExpected bool
}{ }{
{"foo", []string{}, false, []string{"foo"}, true}, "empty": {
{"foo", []string{"foo"}, true, []string{"bar"}, false}, pattern: "foo",
{"foo/bar", []string{"foo"}, false, []string{"foo/*"}, true}, rootPaths: []string{},
{"foo/bar", []string{"foo/*"}, true, []string{"foo"}, false}, rootExpected: false,
{"foo/", []string{"foo/*"}, true, []string{"a", "b", "foo/"}, true}, unauthenticatedPaths: []string{},
{"foo", []string{"foo*"}, true, []string{"a", "fo*"}, true}, unauthenticatedExpected: false,
{"foo/bar", []string{"a", "b", "foo/*"}, true, []string{"foo/baz/*"}, false}, },
"exact-match-unauthenticated": {
pattern: "foo",
rootPaths: []string{},
rootExpected: false,
unauthenticatedPaths: []string{"foo"},
unauthenticatedExpected: true,
},
"exact-match-root": {
pattern: "foo",
rootPaths: []string{"foo"},
rootExpected: true,
unauthenticatedPaths: []string{"bar"},
unauthenticatedExpected: false,
},
"asterisk-match-unauthenticated": {
pattern: "foo/bar",
rootPaths: []string{"foo"},
rootExpected: false,
unauthenticatedPaths: []string{"foo/*"},
unauthenticatedExpected: true,
},
"asterisk-match-root": {
pattern: "foo/bar",
rootPaths: []string{"foo/*"},
rootExpected: true,
unauthenticatedPaths: []string{"foo"},
unauthenticatedExpected: false,
},
"path-ends-with-slash": {
pattern: "foo/",
rootPaths: []string{"foo/*"},
rootExpected: true,
unauthenticatedPaths: []string{"a", "b", "foo*"},
unauthenticatedExpected: true,
},
"asterisk-match-no-slash": {
pattern: "foo",
rootPaths: []string{"foo*"},
rootExpected: true,
unauthenticatedPaths: []string{"a", "fo*"},
unauthenticatedExpected: true,
},
"multiple-root-paths": {
pattern: "foo/bar",
rootPaths: []string{"a", "b", "foo/*"},
rootExpected: true,
unauthenticatedPaths: []string{"foo/baz/*"},
unauthenticatedExpected: false,
},
"plus-match-unauthenticated": {
pattern: "foo/bar/baz",
rootPaths: []string{"foo/bar"},
rootExpected: false,
unauthenticatedPaths: []string{"foo/+/baz"},
unauthenticatedExpected: true,
},
"plus-match-root": {
pattern: "foo/bar/baz",
rootPaths: []string{"foo/+/baz"},
rootExpected: true,
unauthenticatedPaths: []string{"foo/bar"},
unauthenticatedExpected: false,
},
"plus-and-asterisk": {
pattern: "foo/bar/baz/something",
rootPaths: []string{"foo/+/baz/*"},
rootExpected: true,
unauthenticatedPaths: []string{"foo/+/baz*"},
unauthenticatedExpected: true,
},
"double-plus-good": {
pattern: "foo/bar/baz",
rootPaths: []string{"foo/+/+"},
rootExpected: true,
unauthenticatedPaths: []string{"foo/bar"},
unauthenticatedExpected: false,
},
} }
for i, test := range tests { for name, test := range tests {
doc := NewOASDocument("version") t.Run(name, func(t *testing.T) {
path := Path{ doc := NewOASDocument("version")
Pattern: test.pattern, path := Path{
} Pattern: test.pattern,
sp := &logical.Paths{ }
Root: test.rootPaths, specialPaths := &logical.Paths{
Unauthenticated: test.unauthPaths, Root: test.rootPaths,
} Unauthenticated: test.unauthenticatedPaths,
err := documentPath(&path, sp, "kv", logical.TypeLogical, doc) }
if err != nil {
t.Fatal(err) if err := documentPath(&path, specialPaths, "kv", logical.TypeLogical, doc); err != nil {
} t.Fatal(err)
result := test.root }
if doc.Paths["/"+test.pattern].Sudo != result {
t.Fatalf("Test (root) %d: Expected %v got %v", i, test.root, result) actual := doc.Paths["/"+test.pattern].Sudo
} if actual != test.rootExpected {
result = test.unauth t.Fatalf("Test (root): expected: %v; got: %v", test.rootExpected, actual)
if doc.Paths["/"+test.pattern].Unauthenticated != result { }
t.Fatalf("Test (unauth) %d: Expected %v got %v", i, test.unauth, result)
} actual = doc.Paths["/"+test.pattern].Unauthenticated
if actual != test.unauthenticatedExpected {
t.Fatalf("Test (unauth): expected: %v; got: %v", test.unauthenticatedExpected, actual)
}
})
} }
} }