27bb03bbc0
* adding copyright header * fix fmt and a test
634 lines
16 KiB
Go
634 lines
16 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package vault
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/go-uuid"
|
|
"github.com/hashicorp/vault/helper/namespace"
|
|
"github.com/hashicorp/vault/sdk/logical"
|
|
)
|
|
|
|
func TestRouter_Mount(t *testing.T) {
|
|
r := NewRouter()
|
|
_, barrier, _ := mockBarrier(t)
|
|
view := NewBarrierView(barrier, "logical/")
|
|
|
|
meUUID, err := uuid.GenerateUUID()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
mountEntry := &MountEntry{
|
|
Path: "prod/aws/",
|
|
UUID: meUUID,
|
|
Accessor: "awsaccessor",
|
|
NamespaceID: namespace.RootNamespaceID,
|
|
namespace: namespace.RootNamespace,
|
|
}
|
|
|
|
n := &NoopBackend{}
|
|
err = r.Mount(n, "prod/aws/", mountEntry, view)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
meUUID, err = uuid.GenerateUUID()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err = r.Mount(n, "prod/aws/", &MountEntry{UUID: meUUID, NamespaceID: namespace.RootNamespaceID, namespace: namespace.RootNamespace}, view)
|
|
if !strings.Contains(err.Error(), "cannot mount under existing mount") {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
meUUID, err = uuid.GenerateUUID()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if path := r.MatchingMount(namespace.RootContext(nil), "prod/aws/foo"); path != "prod/aws/" {
|
|
t.Fatalf("bad: %s", path)
|
|
}
|
|
|
|
if v := r.MatchingStorageByAPIPath(namespace.RootContext(nil), "prod/aws/foo"); v.(*BarrierView) != view {
|
|
t.Fatalf("bad: %v", v)
|
|
}
|
|
|
|
if path := r.MatchingMount(namespace.RootContext(nil), "stage/aws/foo"); path != "" {
|
|
t.Fatalf("bad: %s", path)
|
|
}
|
|
|
|
if v := r.MatchingStorageByAPIPath(namespace.RootContext(nil), "stage/aws/foo"); v != nil {
|
|
t.Fatalf("bad: %v", v)
|
|
}
|
|
|
|
mountEntryFetched := r.MatchingMountByUUID(mountEntry.UUID)
|
|
if mountEntryFetched == nil || !reflect.DeepEqual(mountEntry, mountEntryFetched) {
|
|
t.Fatalf("failed to fetch mount entry using its ID; expected: %#v\n actual: %#v\n", mountEntry, mountEntryFetched)
|
|
}
|
|
|
|
_, mount, prefix, ok := r.MatchingAPIPrefixByStoragePath(namespace.RootContext(nil), "logical/foo")
|
|
if !ok {
|
|
t.Fatalf("missing storage prefix")
|
|
}
|
|
if mount != "prod/aws/" || prefix != "logical/" {
|
|
t.Fatalf("Bad: %v - %v", mount, prefix)
|
|
}
|
|
|
|
req := &logical.Request{
|
|
Path: "prod/aws/foo",
|
|
}
|
|
req.SetTokenEntry(&logical.TokenEntry{
|
|
ID: "foo",
|
|
})
|
|
resp, err := r.Route(namespace.RootContext(nil), req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatalf("bad: %v", resp)
|
|
}
|
|
if req.TokenEntry() == nil || req.TokenEntry().ID != "foo" {
|
|
t.Fatalf("unexpected value for token entry: %v", req.TokenEntry())
|
|
}
|
|
|
|
// Verify the path
|
|
if len(n.Paths) != 1 || n.Paths[0] != "foo" {
|
|
t.Fatalf("bad: %v", n.Paths)
|
|
}
|
|
|
|
subMountEntry := &MountEntry{
|
|
Path: "prod/",
|
|
UUID: meUUID,
|
|
Accessor: "prodaccessor",
|
|
NamespaceID: namespace.RootNamespaceID,
|
|
namespace: namespace.RootNamespace,
|
|
}
|
|
|
|
if r.MountConflict(namespace.RootContext(nil), "prod/aws/") == "" {
|
|
t.Fatalf("bad: prod/aws/")
|
|
}
|
|
|
|
// No error is shown here because MountConflict is checked before Mount
|
|
err = r.Mount(n, "prod/", subMountEntry, view)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if r.MountConflict(namespace.RootContext(nil), "prod/test") == "" {
|
|
t.Fatalf("bad: prod/test/")
|
|
}
|
|
}
|
|
|
|
func TestRouter_MountCredential(t *testing.T) {
|
|
r := NewRouter()
|
|
_, barrier, _ := mockBarrier(t)
|
|
view := NewBarrierView(barrier, credentialBarrierPrefix)
|
|
|
|
meUUID, err := uuid.GenerateUUID()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
mountEntry := &MountEntry{
|
|
Path: "aws",
|
|
UUID: meUUID,
|
|
Accessor: "awsaccessor",
|
|
NamespaceID: namespace.RootNamespaceID,
|
|
namespace: namespace.RootNamespace,
|
|
}
|
|
|
|
n := &NoopBackend{}
|
|
err = r.Mount(n, "auth/aws/", mountEntry, view)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
meUUID, err = uuid.GenerateUUID()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
err = r.Mount(n, "auth/aws/", &MountEntry{UUID: meUUID, NamespaceID: namespace.RootNamespaceID, namespace: namespace.RootNamespace}, view)
|
|
if !strings.Contains(err.Error(), "cannot mount under existing mount") {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
if path := r.MatchingMount(namespace.RootContext(nil), "auth/aws/foo"); path != "auth/aws/" {
|
|
t.Fatalf("bad: %s", path)
|
|
}
|
|
|
|
if v := r.MatchingStorageByAPIPath(namespace.RootContext(nil), "auth/aws/foo"); v.(*BarrierView) != view {
|
|
t.Fatalf("bad: %v", v)
|
|
}
|
|
|
|
if path := r.MatchingMount(namespace.RootContext(nil), "auth/stage/aws/foo"); path != "" {
|
|
t.Fatalf("bad: %s", path)
|
|
}
|
|
|
|
if v := r.MatchingStorageByAPIPath(namespace.RootContext(nil), "auth/stage/aws/foo"); v != nil {
|
|
t.Fatalf("bad: %v", v)
|
|
}
|
|
|
|
mountEntryFetched := r.MatchingMountByUUID(mountEntry.UUID)
|
|
if mountEntryFetched == nil || !reflect.DeepEqual(mountEntry, mountEntryFetched) {
|
|
t.Fatalf("failed to fetch mount entry using its ID; expected: %#v\n actual: %#v\n", mountEntry, mountEntryFetched)
|
|
}
|
|
|
|
_, mount, prefix, ok := r.MatchingAPIPrefixByStoragePath(namespace.RootContext(nil), "auth/foo")
|
|
if !ok {
|
|
t.Fatalf("missing storage prefix")
|
|
}
|
|
if mount != "auth/aws" || prefix != credentialBarrierPrefix {
|
|
t.Fatalf("Bad: %v - %v", mount, prefix)
|
|
}
|
|
|
|
req := &logical.Request{
|
|
Path: "auth/aws/foo",
|
|
}
|
|
resp, err := r.Route(namespace.RootContext(nil), req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatalf("bad: %v", resp)
|
|
}
|
|
|
|
// Verify the path
|
|
if len(n.Paths) != 1 || n.Paths[0] != "foo" {
|
|
t.Fatalf("bad: %v", n.Paths)
|
|
}
|
|
}
|
|
|
|
func TestRouter_Unmount(t *testing.T) {
|
|
r := NewRouter()
|
|
_, barrier, _ := mockBarrier(t)
|
|
view := NewBarrierView(barrier, "logical/")
|
|
|
|
meUUID, err := uuid.GenerateUUID()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
n := &NoopBackend{}
|
|
err = r.Mount(n, "prod/aws/", &MountEntry{Path: "prod/aws/", UUID: meUUID, Accessor: "awsaccessor", NamespaceID: namespace.RootNamespaceID, namespace: namespace.RootNamespace}, view)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
err = r.Unmount(namespace.RootContext(nil), "prod/aws/")
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
req := &logical.Request{
|
|
Path: "prod/aws/foo",
|
|
}
|
|
_, err = r.Route(namespace.RootContext(nil), req)
|
|
if !strings.Contains(err.Error(), "unsupported path") {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
if _, _, _, ok := r.MatchingAPIPrefixByStoragePath(namespace.RootContext(nil), "logical/foo"); ok {
|
|
t.Fatalf("should not have matching storage prefix")
|
|
}
|
|
}
|
|
|
|
func TestRouter_Remount(t *testing.T) {
|
|
r := NewRouter()
|
|
_, barrier, _ := mockBarrier(t)
|
|
view := NewBarrierView(barrier, "logical/")
|
|
|
|
meUUID, err := uuid.GenerateUUID()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
n := &NoopBackend{}
|
|
me := &MountEntry{Path: "prod/aws/", UUID: meUUID, Accessor: "awsaccessor", NamespaceID: namespace.RootNamespaceID, namespace: namespace.RootNamespace}
|
|
err = r.Mount(n, "prod/aws/", me, view)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
me.Path = "stage/aws/"
|
|
err = r.Remount(namespace.RootContext(nil), "prod/aws/", "stage/aws/")
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
err = r.Remount(namespace.RootContext(nil), "prod/aws/", "stage/aws/")
|
|
if !strings.Contains(err.Error(), "no mount at") {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
req := &logical.Request{
|
|
Path: "prod/aws/foo",
|
|
}
|
|
_, err = r.Route(namespace.RootContext(nil), req)
|
|
if !strings.Contains(err.Error(), "unsupported path") {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
req = &logical.Request{
|
|
Path: "stage/aws/foo",
|
|
}
|
|
_, err = r.Route(namespace.RootContext(nil), req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Verify the path
|
|
if len(n.Paths) != 1 || n.Paths[0] != "foo" {
|
|
t.Fatalf("bad: %v", n.Paths)
|
|
}
|
|
|
|
// Check the resolve from storage still works
|
|
_, mount, prefix, _ := r.MatchingAPIPrefixByStoragePath(namespace.RootContext(nil), "logical/foobar")
|
|
if mount != "stage/aws/" {
|
|
t.Fatalf("bad mount: %s", mount)
|
|
}
|
|
if prefix != "logical/" {
|
|
t.Fatalf("Bad prefix: %s", prefix)
|
|
}
|
|
}
|
|
|
|
func TestRouter_RootPath(t *testing.T) {
|
|
r := NewRouter()
|
|
_, barrier, _ := mockBarrier(t)
|
|
view := NewBarrierView(barrier, "logical/")
|
|
|
|
meUUID, err := uuid.GenerateUUID()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
n := &NoopBackend{
|
|
Root: []string{
|
|
"root",
|
|
"policy/*",
|
|
},
|
|
}
|
|
err = r.Mount(n, "prod/aws/", &MountEntry{UUID: meUUID, Accessor: "awsaccessor", NamespaceID: namespace.RootNamespaceID, namespace: namespace.RootNamespace}, view)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
type tcase struct {
|
|
path string
|
|
expect bool
|
|
}
|
|
tcases := []tcase{
|
|
{"random", false},
|
|
{"prod/aws/foo", false},
|
|
{"prod/aws/root", true},
|
|
{"prod/aws/root-more", false},
|
|
{"prod/aws/policy", false},
|
|
{"prod/aws/policy/", true},
|
|
{"prod/aws/policy/ops", true},
|
|
}
|
|
|
|
for _, tc := range tcases {
|
|
out := r.RootPath(namespace.RootContext(nil), tc.path)
|
|
if out != tc.expect {
|
|
t.Fatalf("bad: path: %s expect: %v got %v", tc.path, tc.expect, out)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRouter_LoginPath(t *testing.T) {
|
|
r := NewRouter()
|
|
_, barrier, _ := mockBarrier(t)
|
|
view := NewBarrierView(barrier, "auth/")
|
|
|
|
meUUID, err := uuid.GenerateUUID()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
n := &NoopBackend{
|
|
Login: []string{
|
|
"login",
|
|
"oauth/*",
|
|
"glob1*",
|
|
"+/wildcard/glob2*",
|
|
"end1/+",
|
|
"end2/+/",
|
|
"end3/+/*",
|
|
"middle1/+/bar",
|
|
"middle2/+/+/bar",
|
|
"+/begin",
|
|
"+/around/+/",
|
|
},
|
|
}
|
|
err = r.Mount(n, "auth/foo/", &MountEntry{UUID: meUUID, Accessor: "authfooaccessor", NamespaceID: namespace.RootNamespaceID, namespace: namespace.RootNamespace}, view)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
type tcase struct {
|
|
path string
|
|
expect bool
|
|
}
|
|
tcases := []tcase{
|
|
{"random", false},
|
|
{"auth/foo/bar", false},
|
|
{"auth/foo/login", true},
|
|
{"auth/foo/login/", false},
|
|
{"auth/invalid/login", false},
|
|
{"auth/foo/oauth", false},
|
|
{"auth/foo/oauth/", true},
|
|
{"auth/foo/oauth/redirect", true},
|
|
{"auth/foo/oauth/redirect/", true},
|
|
{"auth/foo/oauth/redirect/bar", true},
|
|
{"auth/foo/glob1", true},
|
|
{"auth/foo/glob1/", true},
|
|
{"auth/foo/glob1/redirect", true},
|
|
|
|
// Wildcard cases
|
|
|
|
// "+/wildcard/glob2*"
|
|
{"auth/foo/bar/wildcard/glo", false},
|
|
{"auth/foo/bar/wildcard/glob2", true},
|
|
{"auth/foo/bar/wildcard/glob2222", true},
|
|
{"auth/foo/bar/wildcard/glob2/", true},
|
|
{"auth/foo/bar/wildcard/glob2/baz", true},
|
|
|
|
// "end1/+"
|
|
{"auth/foo/end1", false},
|
|
{"auth/foo/end1/", true},
|
|
{"auth/foo/end1/bar", true},
|
|
{"auth/foo/end1/bar/", false},
|
|
{"auth/foo/end1/bar/baz", false},
|
|
// "end2/+/"
|
|
{"auth/foo/end2", false},
|
|
{"auth/foo/end2/", false},
|
|
{"auth/foo/end2/bar", false},
|
|
{"auth/foo/end2/bar/", true},
|
|
{"auth/foo/end2/bar/baz", false},
|
|
// "end3/+/*"
|
|
{"auth/foo/end3", false},
|
|
{"auth/foo/end3/", false},
|
|
{"auth/foo/end3/bar", false},
|
|
{"auth/foo/end3/bar/", true},
|
|
{"auth/foo/end3/bar/baz", true},
|
|
{"auth/foo/end3/bar/baz/", true},
|
|
{"auth/foo/end3/bar/baz/qux", true},
|
|
{"auth/foo/end3/bar/baz/qux/qoo", true},
|
|
{"auth/foo/end3/bar/baz/qux/qoo/qaa", true},
|
|
// "middle1/+/bar",
|
|
{"auth/foo/middle1/bar", false},
|
|
{"auth/foo/middle1/bar/", false},
|
|
{"auth/foo/middle1/bar/qux", false},
|
|
{"auth/foo/middle1/bar/bar", true},
|
|
{"auth/foo/middle1/bar/bar/", false},
|
|
// "middle2/+/+/bar",
|
|
{"auth/foo/middle2/bar", false},
|
|
{"auth/foo/middle2/bar/", false},
|
|
{"auth/foo/middle2/bar/baz", false},
|
|
{"auth/foo/middle2/bar/baz/", false},
|
|
{"auth/foo/middle2/bar/baz/bar", true},
|
|
{"auth/foo/middle2/bar/baz/bar/", false},
|
|
// "+/begin"
|
|
{"auth/foo/bar/begin", true},
|
|
{"auth/foo/bar/begin/", false},
|
|
{"auth/foo/begin", false},
|
|
// "+/around/+/"
|
|
{"auth/foo/bar/around", false},
|
|
{"auth/foo/bar/around/", false},
|
|
{"auth/foo/bar/around/baz", false},
|
|
{"auth/foo/bar/around/baz/", true},
|
|
{"auth/foo/bar/around/baz/qux", false},
|
|
}
|
|
|
|
for _, tc := range tcases {
|
|
out := r.LoginPath(namespace.RootContext(nil), tc.path)
|
|
if out != tc.expect {
|
|
t.Fatalf("bad: path: %s expect: %v got %v", tc.path, tc.expect, out)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRouter_Taint(t *testing.T) {
|
|
r := NewRouter()
|
|
_, barrier, _ := mockBarrier(t)
|
|
view := NewBarrierView(barrier, "logical/")
|
|
|
|
meUUID, err := uuid.GenerateUUID()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
n := &NoopBackend{}
|
|
err = r.Mount(n, "prod/aws/", &MountEntry{UUID: meUUID, Accessor: "awsaccessor", NamespaceID: namespace.RootNamespaceID, namespace: namespace.RootNamespace}, view)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
err = r.Taint(namespace.RootContext(nil), "prod/aws/")
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
req := &logical.Request{
|
|
Operation: logical.ReadOperation,
|
|
Path: "prod/aws/foo",
|
|
}
|
|
_, err = r.Route(namespace.RootContext(nil), req)
|
|
if err.Error() != "unsupported path" {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
// Rollback and Revoke should work
|
|
req.Operation = logical.RollbackOperation
|
|
_, err = r.Route(namespace.RootContext(nil), req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
req.Operation = logical.RevokeOperation
|
|
_, err = r.Route(namespace.RootContext(nil), req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestRouter_Untaint(t *testing.T) {
|
|
r := NewRouter()
|
|
_, barrier, _ := mockBarrier(t)
|
|
view := NewBarrierView(barrier, "logical/")
|
|
|
|
meUUID, err := uuid.GenerateUUID()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
n := &NoopBackend{}
|
|
err = r.Mount(n, "prod/aws/", &MountEntry{UUID: meUUID, Accessor: "awsaccessor", NamespaceID: namespace.RootNamespaceID, namespace: namespace.RootNamespace}, view)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
err = r.Taint(namespace.RootContext(nil), "prod/aws/")
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
err = r.Untaint(namespace.RootContext(nil), "prod/aws/")
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
|
|
req := &logical.Request{
|
|
Operation: logical.ReadOperation,
|
|
Path: "prod/aws/foo",
|
|
}
|
|
_, err = r.Route(namespace.RootContext(nil), req)
|
|
if err != nil {
|
|
t.Fatalf("err: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestPathsToRadix(t *testing.T) {
|
|
// Provide real paths
|
|
paths := []string{
|
|
"foo",
|
|
"foo/*",
|
|
"sub/bar*",
|
|
}
|
|
r := pathsToRadix(paths)
|
|
|
|
raw, ok := r.Get("foo")
|
|
if !ok || raw.(bool) != false {
|
|
t.Fatalf("bad: %v (foo)", raw)
|
|
}
|
|
|
|
raw, ok = r.Get("foo/")
|
|
if !ok || raw.(bool) != true {
|
|
t.Fatalf("bad: %v (foo/)", raw)
|
|
}
|
|
|
|
raw, ok = r.Get("sub/bar")
|
|
if !ok || raw.(bool) != true {
|
|
t.Fatalf("bad: %v (sub/bar)", raw)
|
|
}
|
|
}
|
|
|
|
func TestParseUnauthenticatedPaths(t *testing.T) {
|
|
// inputs
|
|
paths := []string{
|
|
"foo",
|
|
"foo/*",
|
|
"sub/bar*",
|
|
}
|
|
wildcardPaths := []string{
|
|
"end/+",
|
|
"+/begin/*",
|
|
"middle/+/bar*",
|
|
}
|
|
allPaths := append(paths, wildcardPaths...)
|
|
|
|
p, err := parseUnauthenticatedPaths(allPaths)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// outputs
|
|
wildcardPathsEntry := []wildcardPath{
|
|
{segments: []string{"end", "+"}, isPrefix: false},
|
|
{segments: []string{"+", "begin", ""}, isPrefix: true},
|
|
{segments: []string{"middle", "+", "bar"}, isPrefix: true},
|
|
}
|
|
expected := &loginPathsEntry{
|
|
paths: pathsToRadix(paths),
|
|
wildcardPaths: wildcardPathsEntry,
|
|
}
|
|
|
|
if !reflect.DeepEqual(expected, p) {
|
|
t.Fatalf("expected: %#v\n actual: %#v\n", expected, p)
|
|
}
|
|
}
|
|
|
|
func TestParseUnauthenticatedPaths_Error(t *testing.T) {
|
|
type tcase struct {
|
|
paths []string
|
|
err string
|
|
}
|
|
tcases := []tcase{
|
|
{
|
|
[]string{"/foo/+*"},
|
|
"path \"/foo/+*\": invalid use of wildcards ('+*' is forbidden)",
|
|
},
|
|
{
|
|
[]string{"/foo/*/*"},
|
|
"path \"/foo/*/*\": invalid use of wildcards (multiple '*' is forbidden)",
|
|
},
|
|
{
|
|
[]string{"*/foo/*"},
|
|
"path \"*/foo/*\": invalid use of wildcards (multiple '*' is forbidden)",
|
|
},
|
|
{
|
|
[]string{"*/foo/"},
|
|
"path \"*/foo/\": invalid use of wildcards ('*' is only allowed at the end of a path)",
|
|
},
|
|
{
|
|
[]string{"/foo+"},
|
|
"path \"/foo+\": invalid use of wildcards ('+' is not allowed next to a non-slash)",
|
|
},
|
|
{
|
|
[]string{"/+foo"},
|
|
"path \"/+foo\": invalid use of wildcards ('+' is not allowed next to a non-slash)",
|
|
},
|
|
{
|
|
[]string{"/++"},
|
|
"path \"/++\": invalid use of wildcards ('+' is not allowed next to a non-slash)",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tcases {
|
|
_, err := parseUnauthenticatedPaths(tc.paths)
|
|
if err == nil || err != nil && !strings.Contains(err.Error(), tc.err) {
|
|
t.Fatalf("bad: path: %s expect: %v got %v", tc.paths, tc.err, err)
|
|
}
|
|
}
|
|
}
|