From 79cdff9f9fc785b5597c5421eda26907ad3c0da6 Mon Sep 17 00:00:00 2001 From: Raphael Husistein Date: Fri, 26 Dec 2025 20:04:23 +0100 Subject: [PATCH 1/3] feat: add bearer token support for sync.go Signed-off-by: Raphael Husistein --- pkg/vendir/config/data.go | 1 + pkg/vendir/fetch/http/sync.go | 47 ++++++- pkg/vendir/fetch/http/sync_test.go | 191 +++++++++++++++++++++++++++++ 3 files changed, 236 insertions(+), 3 deletions(-) create mode 100644 pkg/vendir/fetch/http/sync_test.go diff --git a/pkg/vendir/config/data.go b/pkg/vendir/config/data.go index 62ac44a3..1b9519e8 100644 --- a/pkg/vendir/config/data.go +++ b/pkg/vendir/config/data.go @@ -6,6 +6,7 @@ package config const ( SecretK8sCorev1BasicAuthUsernameKey = "username" SecretK8sCorev1BasicAuthPasswordKey = "password" + SecretK8sCorev1HTTPBearerTokenKey = "token" SecretK8sCoreV1SSHAuthPrivateKey = "ssh-privatekey" SecretSSHAuthKnownHosts = "ssh-knownhosts" // not part of k8s diff --git a/pkg/vendir/fetch/http/sync.go b/pkg/vendir/fetch/http/sync.go index f2eb664a..6189badf 100644 --- a/pkg/vendir/fetch/http/sync.go +++ b/pkg/vendir/fetch/http/sync.go @@ -148,14 +148,55 @@ func (t *Sync) addAuth(req *http.Request) error { switch name { case ctlconf.SecretK8sCorev1BasicAuthUsernameKey: case ctlconf.SecretK8sCorev1BasicAuthPasswordKey: + case ctlconf.SecretK8sCorev1HTTPBearerTokenKey: default: return fmt.Errorf("Unknown secret field '%s' in secret '%s'", name, secret.Metadata.Name) } } - if _, found := secret.Data[ctlconf.SecretK8sCorev1BasicAuthUsernameKey]; found { - req.SetBasicAuth(string(secret.Data[ctlconf.SecretK8sCorev1BasicAuthUsernameKey]), - string(secret.Data[ctlconf.SecretK8sCorev1BasicAuthPasswordKey])) + _, hasUser := secret.Data[ctlconf.SecretK8sCorev1BasicAuthUsernameKey] + _, hasPass := secret.Data[ctlconf.SecretK8sCorev1BasicAuthPasswordKey] + token, hasToken := secret.Data[ctlconf.SecretK8sCorev1HTTPBearerTokenKey] + + // Be strict about basic auth fields: require username and password together. + if hasUser && !hasPass { + return fmt.Errorf( + "Secret '%s' contains '%s' but is missing '%s'", + secret.Metadata.Name, + ctlconf.SecretK8sCorev1BasicAuthUsernameKey, + ctlconf.SecretK8sCorev1BasicAuthPasswordKey, + ) + } + if hasPass && !hasUser { + return fmt.Errorf( + "Secret '%s' contains '%s' but is missing '%s'", + secret.Metadata.Name, + ctlconf.SecretK8sCorev1BasicAuthPasswordKey, + ctlconf.SecretK8sCorev1BasicAuthUsernameKey, + ) + } + + // Do not allow mixing basic auth and bearer token in the same secret. + if hasToken && (hasUser || hasPass) { + return fmt.Errorf( + "Secret '%s' must not contain both basic auth (username/password) and token", + secret.Metadata.Name, + ) + } + + // Bearer token auth + if hasToken { + // If you want to allow a fully preformatted value, drop "Bearer ". + req.Header.Set("Authorization", "Bearer "+string(token)) + return nil + } + + // Basic auth + if hasUser { + req.SetBasicAuth( + string(secret.Data[ctlconf.SecretK8sCorev1BasicAuthUsernameKey]), + string(secret.Data[ctlconf.SecretK8sCorev1BasicAuthPasswordKey]), + ) } return nil diff --git a/pkg/vendir/fetch/http/sync_test.go b/pkg/vendir/fetch/http/sync_test.go new file mode 100644 index 00000000..1e5d419b --- /dev/null +++ b/pkg/vendir/fetch/http/sync_test.go @@ -0,0 +1,191 @@ +package http + +import ( + "bytes" + "fmt" + "net/http" + "net/http/httptest" + "testing" + + ctlconf "carvel.dev/vendir/pkg/vendir/config" +) + +/* +Fake RefFetcher (implements ctlfetch.RefFetcher) +*/ + +type fakeRefFetcher struct { + secrets map[string]ctlconf.Secret + configMaps map[string]ctlconf.ConfigMap +} + +func (f fakeRefFetcher) GetSecret(name string) (ctlconf.Secret, error) { + s, ok := f.secrets[name] + if !ok { + return ctlconf.Secret{}, fmt.Errorf("secret %q not found", name) + } + return s, nil +} + +func (f fakeRefFetcher) GetConfigMap(name string) (ctlconf.ConfigMap, error) { + // Not used by these tests, but required by the interface. + if f.configMaps == nil { + return ctlconf.ConfigMap{}, fmt.Errorf("configmap %q not found", name) + } + cm, ok := f.configMaps[name] + if !ok { + return ctlconf.ConfigMap{}, fmt.Errorf("configmap %q not found", name) + } + return cm, nil +} + +/* +Helper: build the correct SecretRef type for DirectoryContentsHTTP. +In vendir, HTTP uses a dedicated type (not a generic ctlconf.SecretRef). +*/ +func secretRef(name string) *ctlconf.DirectoryContentsLocalRef { + return &ctlconf.DirectoryContentsLocalRef{Name: name} +} + +/* +Tests +*/ + +func TestHTTPAuth_BasicAuth_Succeeds(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + user, pass, ok := r.BasicAuth() + if !ok || user != "admin" || pass != "password" { + w.WriteHeader(http.StatusUnauthorized) + return + } + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("ok")) + })) + defer srv.Close() + + ref := fakeRefFetcher{ + secrets: map[string]ctlconf.Secret{ + "http-auth": { + Metadata: ctlconf.GenericMetadata{Name: "http-auth"}, + Data: map[string][]byte{ + ctlconf.SecretK8sCorev1BasicAuthUsernameKey: []byte("admin"), + ctlconf.SecretK8sCorev1BasicAuthPasswordKey: []byte("password"), + }, + }, + }, + } + + s := NewSync(ctlconf.DirectoryContentsHTTP{ + URL: srv.URL, + SecretRef: secretRef("http-auth"), + }, ref) + + var dst bytes.Buffer + if err := s.downloadFile(&dst); err != nil { + t.Fatalf("expected basic auth download to succeed, got error: %v", err) + } +} + +func TestHTTPAuth_BearerToken_Succeeds(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if got := r.Header.Get("Authorization"); got != "Bearer abc123" { + w.WriteHeader(http.StatusUnauthorized) + return + } + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("ok")) + })) + defer srv.Close() + + ref := fakeRefFetcher{ + secrets: map[string]ctlconf.Secret{ + "http-auth": { + Metadata: ctlconf.GenericMetadata{Name: "http-auth"}, + Data: map[string][]byte{ + ctlconf.SecretK8sCorev1HTTPBearerTokenKey: []byte("abc123"), + }, + }, + }, + } + + s := NewSync(ctlconf.DirectoryContentsHTTP{ + URL: srv.URL, + SecretRef: secretRef("http-auth"), + }, ref) + + var dst bytes.Buffer + if err := s.downloadFile(&dst); err != nil { + t.Fatalf("expected bearer auth download to succeed, got error: %v", err) + } +} + +func TestHTTPAuth_MixedAuth_Fails(t *testing.T) { + ref := fakeRefFetcher{ + secrets: map[string]ctlconf.Secret{ + "http-auth": { + Metadata: ctlconf.GenericMetadata{Name: "http-auth"}, + Data: map[string][]byte{ + ctlconf.SecretK8sCorev1BasicAuthUsernameKey: []byte("admin"), + ctlconf.SecretK8sCorev1BasicAuthPasswordKey: []byte("password"), + ctlconf.SecretK8sCorev1HTTPBearerTokenKey: []byte("abc123"), + }, + }, + }, + } + + s := NewSync(ctlconf.DirectoryContentsHTTP{ + URL: "http://example.com", + SecretRef: secretRef("http-auth"), + }, ref) + + req, _ := http.NewRequest("GET", "http://example.com", nil) + if err := s.addAuth(req); err == nil { + t.Fatalf("expected error for mixed auth, got nil") + } +} + +func TestHTTPAuth_UsernameWithoutPassword_Fails(t *testing.T) { + ref := fakeRefFetcher{ + secrets: map[string]ctlconf.Secret{ + "http-auth": { + Metadata: ctlconf.GenericMetadata{Name: "http-auth"}, + Data: map[string][]byte{ + ctlconf.SecretK8sCorev1BasicAuthUsernameKey: []byte("admin"), + }, + }, + }, + } + + s := NewSync(ctlconf.DirectoryContentsHTTP{ + URL: "http://example.com", + SecretRef: secretRef("http-auth"), + }, ref) + + req, _ := http.NewRequest("GET", "http://example.com", nil) + if err := s.addAuth(req); err == nil { + t.Fatalf("expected error when username is set without password") + } +} + +func TestHTTPAuth_PasswordWithoutUsername_Fails(t *testing.T) { + ref := fakeRefFetcher{ + secrets: map[string]ctlconf.Secret{ + "http-auth": { + Metadata: ctlconf.GenericMetadata{Name: "http-auth"}, + Data: map[string][]byte{ + ctlconf.SecretK8sCorev1BasicAuthPasswordKey: []byte("password"), + }, + }, + }, + } + + s := NewSync(ctlconf.DirectoryContentsHTTP{ + URL: "http://example.com", + SecretRef: secretRef("http-auth"), + }, ref) + + req, _ := http.NewRequest("GET", "http://example.com", nil) + if err := s.addAuth(req); err == nil { + t.Fatalf("expected error when password is set without username") + } +} From 6a3f4b575a7248075f44782ba1f197318c98c0af Mon Sep 17 00:00:00 2001 From: Raphael Husistein Date: Tue, 14 Apr 2026 17:52:23 +0200 Subject: [PATCH 2/3] fix: enable usage of basic auth without password and update sync_test.go to keep the tests consistent through the project Signed-off-by: Raphael Husistein --- pkg/vendir/fetch/http/sync.go | 18 +-- pkg/vendir/fetch/http/sync_test.go | 230 ++++++++++++++--------------- 2 files changed, 118 insertions(+), 130 deletions(-) diff --git a/pkg/vendir/fetch/http/sync.go b/pkg/vendir/fetch/http/sync.go index 6189badf..d00f2b19 100644 --- a/pkg/vendir/fetch/http/sync.go +++ b/pkg/vendir/fetch/http/sync.go @@ -158,15 +158,7 @@ func (t *Sync) addAuth(req *http.Request) error { _, hasPass := secret.Data[ctlconf.SecretK8sCorev1BasicAuthPasswordKey] token, hasToken := secret.Data[ctlconf.SecretK8sCorev1HTTPBearerTokenKey] - // Be strict about basic auth fields: require username and password together. - if hasUser && !hasPass { - return fmt.Errorf( - "Secret '%s' contains '%s' but is missing '%s'", - secret.Metadata.Name, - ctlconf.SecretK8sCorev1BasicAuthUsernameKey, - ctlconf.SecretK8sCorev1BasicAuthPasswordKey, - ) - } + // Basic auth requires a username if password is provided, but password is optional. if hasPass && !hasUser { return fmt.Errorf( "Secret '%s' contains '%s' but is missing '%s'", @@ -177,7 +169,7 @@ func (t *Sync) addAuth(req *http.Request) error { } // Do not allow mixing basic auth and bearer token in the same secret. - if hasToken && (hasUser || hasPass) { + if hasToken && hasUser { return fmt.Errorf( "Secret '%s' must not contain both basic auth (username/password) and token", secret.Metadata.Name, @@ -186,16 +178,16 @@ func (t *Sync) addAuth(req *http.Request) error { // Bearer token auth if hasToken { - // If you want to allow a fully preformatted value, drop "Bearer ". req.Header.Set("Authorization", "Bearer "+string(token)) return nil } - // Basic auth + // Basic auth — password is optional, defaults to empty string if hasUser { + password := string(secret.Data[ctlconf.SecretK8sCorev1BasicAuthPasswordKey]) req.SetBasicAuth( string(secret.Data[ctlconf.SecretK8sCorev1BasicAuthUsernameKey]), - string(secret.Data[ctlconf.SecretK8sCorev1BasicAuthPasswordKey]), + password, ) } diff --git a/pkg/vendir/fetch/http/sync_test.go b/pkg/vendir/fetch/http/sync_test.go index 1e5d419b..c2a18d4d 100644 --- a/pkg/vendir/fetch/http/sync_test.go +++ b/pkg/vendir/fetch/http/sync_test.go @@ -1,19 +1,21 @@ -package http +// Copyright 2024 The Carvel Authors. +// SPDX-License-Identifier: Apache-2.0 + +package http_test import ( - "bytes" "fmt" "net/http" "net/http/httptest" + "os" + "path/filepath" "testing" ctlconf "carvel.dev/vendir/pkg/vendir/config" + vendirhttp "carvel.dev/vendir/pkg/vendir/fetch/http" + "github.com/stretchr/testify/require" ) -/* -Fake RefFetcher (implements ctlfetch.RefFetcher) -*/ - type fakeRefFetcher struct { secrets map[string]ctlconf.Secret configMaps map[string]ctlconf.ConfigMap @@ -28,10 +30,10 @@ func (f fakeRefFetcher) GetSecret(name string) (ctlconf.Secret, error) { } func (f fakeRefFetcher) GetConfigMap(name string) (ctlconf.ConfigMap, error) { - // Not used by these tests, but required by the interface. if f.configMaps == nil { return ctlconf.ConfigMap{}, fmt.Errorf("configmap %q not found", name) } + cm, ok := f.configMaps[name] if !ok { return ctlconf.ConfigMap{}, fmt.Errorf("configmap %q not found", name) @@ -39,153 +41,147 @@ func (f fakeRefFetcher) GetConfigMap(name string) (ctlconf.ConfigMap, error) { return cm, nil } -/* -Helper: build the correct SecretRef type for DirectoryContentsHTTP. -In vendir, HTTP uses a dedicated type (not a generic ctlconf.SecretRef). -*/ +type fakeTempArea struct { + baseDir string +} + +func (f fakeTempArea) NewTempDir(prefix string) (string, error) { + return os.MkdirTemp(f.baseDir, prefix) +} + +func (f fakeTempArea) NewTempFile(prefix string) (*os.File, error) { + return os.CreateTemp(f.baseDir, prefix) +} + func secretRef(name string) *ctlconf.DirectoryContentsLocalRef { return &ctlconf.DirectoryContentsLocalRef{Name: name} } -/* -Tests -*/ - -func TestHTTPAuth_BasicAuth_Succeeds(t *testing.T) { - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - user, pass, ok := r.BasicAuth() - if !ok || user != "admin" || pass != "password" { - w.WriteHeader(http.StatusUnauthorized) - return - } - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("ok")) - })) - defer srv.Close() - - ref := fakeRefFetcher{ - secrets: map[string]ctlconf.Secret{ - "http-auth": { +type syncTest struct { + name string + secret ctlconf.Secret + expectedBody string + expectedError string + validateReq func(t *testing.T, r *http.Request) +} + +func TestSync_HTTPAuth(t *testing.T) { + allTests := []syncTest{ + { + name: "when basic auth username and password are provided, it succeeds", + secret: ctlconf.Secret{ Metadata: ctlconf.GenericMetadata{Name: "http-auth"}, Data: map[string][]byte{ ctlconf.SecretK8sCorev1BasicAuthUsernameKey: []byte("admin"), ctlconf.SecretK8sCorev1BasicAuthPasswordKey: []byte("password"), }, }, + expectedBody: "ok", + validateReq: func(t *testing.T, r *http.Request) { + user, pass, ok := r.BasicAuth() + require.True(t, ok) + require.Equal(t, "admin", user) + require.Equal(t, "password", pass) + }, }, - } - - s := NewSync(ctlconf.DirectoryContentsHTTP{ - URL: srv.URL, - SecretRef: secretRef("http-auth"), - }, ref) - - var dst bytes.Buffer - if err := s.downloadFile(&dst); err != nil { - t.Fatalf("expected basic auth download to succeed, got error: %v", err) - } -} - -func TestHTTPAuth_BearerToken_Succeeds(t *testing.T) { - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if got := r.Header.Get("Authorization"); got != "Bearer abc123" { - w.WriteHeader(http.StatusUnauthorized) - return - } - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte("ok")) - })) - defer srv.Close() - - ref := fakeRefFetcher{ - secrets: map[string]ctlconf.Secret{ - "http-auth": { + { + name: "when bearer token is provided, it succeeds", + secret: ctlconf.Secret{ Metadata: ctlconf.GenericMetadata{Name: "http-auth"}, Data: map[string][]byte{ ctlconf.SecretK8sCorev1HTTPBearerTokenKey: []byte("abc123"), }, }, + expectedBody: "ok", + validateReq: func(t *testing.T, r *http.Request) { + require.Equal(t, "Bearer abc123", r.Header.Get("Authorization")) + }, }, - } - - s := NewSync(ctlconf.DirectoryContentsHTTP{ - URL: srv.URL, - SecretRef: secretRef("http-auth"), - }, ref) - - var dst bytes.Buffer - if err := s.downloadFile(&dst); err != nil { - t.Fatalf("expected bearer auth download to succeed, got error: %v", err) - } -} - -func TestHTTPAuth_MixedAuth_Fails(t *testing.T) { - ref := fakeRefFetcher{ - secrets: map[string]ctlconf.Secret{ - "http-auth": { + { + name: "when username is provided without password, it uses empty password and succeeds", + secret: ctlconf.Secret{ Metadata: ctlconf.GenericMetadata{Name: "http-auth"}, Data: map[string][]byte{ ctlconf.SecretK8sCorev1BasicAuthUsernameKey: []byte("admin"), - ctlconf.SecretK8sCorev1BasicAuthPasswordKey: []byte("password"), - ctlconf.SecretK8sCorev1HTTPBearerTokenKey: []byte("abc123"), }, }, + expectedBody: "ok", + validateReq: func(t *testing.T, r *http.Request) { + user, pass, ok := r.BasicAuth() + require.True(t, ok) + require.Equal(t, "admin", user) + require.Equal(t, "", pass) + }, }, - } - - s := NewSync(ctlconf.DirectoryContentsHTTP{ - URL: "http://example.com", - SecretRef: secretRef("http-auth"), - }, ref) - - req, _ := http.NewRequest("GET", "http://example.com", nil) - if err := s.addAuth(req); err == nil { - t.Fatalf("expected error for mixed auth, got nil") - } -} - -func TestHTTPAuth_UsernameWithoutPassword_Fails(t *testing.T) { - ref := fakeRefFetcher{ - secrets: map[string]ctlconf.Secret{ - "http-auth": { + { + name: "when basic auth and bearer token are mixed, it fails", + secret: ctlconf.Secret{ Metadata: ctlconf.GenericMetadata{Name: "http-auth"}, Data: map[string][]byte{ ctlconf.SecretK8sCorev1BasicAuthUsernameKey: []byte("admin"), + ctlconf.SecretK8sCorev1BasicAuthPasswordKey: []byte("password"), + ctlconf.SecretK8sCorev1HTTPBearerTokenKey: []byte("abc123"), }, }, + expectedError: "must not contain both basic auth", }, - } - - s := NewSync(ctlconf.DirectoryContentsHTTP{ - URL: "http://example.com", - SecretRef: secretRef("http-auth"), - }, ref) - - req, _ := http.NewRequest("GET", "http://example.com", nil) - if err := s.addAuth(req); err == nil { - t.Fatalf("expected error when username is set without password") - } -} - -func TestHTTPAuth_PasswordWithoutUsername_Fails(t *testing.T) { - ref := fakeRefFetcher{ - secrets: map[string]ctlconf.Secret{ - "http-auth": { + { + name: "when password is provided without username, it fails", + secret: ctlconf.Secret{ Metadata: ctlconf.GenericMetadata{Name: "http-auth"}, Data: map[string][]byte{ ctlconf.SecretK8sCorev1BasicAuthPasswordKey: []byte("password"), }, }, + expectedError: "is missing 'username'", }, } - s := NewSync(ctlconf.DirectoryContentsHTTP{ - URL: "http://example.com", - SecretRef: secretRef("http-auth"), - }, ref) + for _, test := range allTests { + t.Run(test.name, func(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if test.expectedError != "" { + t.Fatalf("server should not be reached when auth setup fails") + } + + test.validateReq(t, r) + + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte(test.expectedBody)) + require.NoError(t, err) + })) + defer srv.Close() + + ref := fakeRefFetcher{ + secrets: map[string]ctlconf.Secret{ + "http-auth": test.secret, + }, + } + + subject := vendirhttp.NewSync(ctlconf.DirectoryContentsHTTP{ + URL: srv.URL, + SecretRef: secretRef("http-auth"), + DisableUnpack: true, + }, ref) + + tempRoot, err := os.MkdirTemp("", "vendir-http-test") + require.NoError(t, err) + defer os.RemoveAll(tempRoot) + + dstPath := filepath.Join(tempRoot, "dst") + _, err = subject.Sync(dstPath, fakeTempArea{baseDir: tempRoot}) + + if test.expectedError != "" { + require.Error(t, err) + require.Contains(t, err.Error(), test.expectedError) + return + } + + require.NoError(t, err) - req, _ := http.NewRequest("GET", "http://example.com", nil) - if err := s.addAuth(req); err == nil { - t.Fatalf("expected error when password is set without username") + bs, err := os.ReadFile(filepath.Join(dstPath, filepath.Base(srv.URL))) + require.NoError(t, err) + require.Equal(t, test.expectedBody, string(bs)) + }) } } From 36ffbd500141d86428318120c8eb4022d6472c39 Mon Sep 17 00:00:00 2001 From: Raphael Husistein Date: Tue, 14 Apr 2026 18:24:33 +0200 Subject: [PATCH 3/3] fix: check for empty token Signed-off-by: Raphael Husistein --- pkg/vendir/fetch/http/sync.go | 9 +++++++++ pkg/vendir/fetch/http/sync_test.go | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/pkg/vendir/fetch/http/sync.go b/pkg/vendir/fetch/http/sync.go index d00f2b19..b7250940 100644 --- a/pkg/vendir/fetch/http/sync.go +++ b/pkg/vendir/fetch/http/sync.go @@ -158,6 +158,15 @@ func (t *Sync) addAuth(req *http.Request) error { _, hasPass := secret.Data[ctlconf.SecretK8sCorev1BasicAuthPasswordKey] token, hasToken := secret.Data[ctlconf.SecretK8sCorev1HTTPBearerTokenKey] + // Validate that token is not empty if provided. + if hasToken && len(token) == 0 { + return fmt.Errorf( + "Secret '%s' contains empty '%s'", + secret.Metadata.Name, + ctlconf.SecretK8sCorev1HTTPBearerTokenKey, + ) + } + // Basic auth requires a username if password is provided, but password is optional. if hasPass && !hasUser { return fmt.Errorf( diff --git a/pkg/vendir/fetch/http/sync_test.go b/pkg/vendir/fetch/http/sync_test.go index c2a18d4d..18784d87 100644 --- a/pkg/vendir/fetch/http/sync_test.go +++ b/pkg/vendir/fetch/http/sync_test.go @@ -125,6 +125,16 @@ func TestSync_HTTPAuth(t *testing.T) { }, expectedError: "must not contain both basic auth", }, + { + name: "when bearer token is empty, it fails", + secret: ctlconf.Secret{ + Metadata: ctlconf.GenericMetadata{Name: "http-auth"}, + Data: map[string][]byte{ + ctlconf.SecretK8sCorev1HTTPBearerTokenKey: []byte(""), + }, + }, + expectedError: "contains empty 'token'", + }, { name: "when password is provided without username, it fails", secret: ctlconf.Secret{