mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-09-06 08:07:08 +00:00
Merge branch 'main' into dynamic_clients
This commit is contained in:
@@ -10,22 +10,22 @@ import (
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
)
|
||||
|
||||
// This interface is similar to the k8s token authenticator, but works with username/passwords instead
|
||||
// UserAuthenticator is an interface is similar to the k8s token authenticator, but works with username/passwords instead
|
||||
// of a single token string.
|
||||
//
|
||||
// The return values should be as follows.
|
||||
// 1. For a successful authentication:
|
||||
// - A response which includes the username, uid, and groups in the userInfo. The username and uid must not be blank.
|
||||
// - true
|
||||
// - nil error
|
||||
// - A response which includes the username, uid, and groups in the userInfo. The username and uid must not be blank.
|
||||
// - true
|
||||
// - nil error
|
||||
// 2. For an unsuccessful authentication, e.g. bad username or password:
|
||||
// - nil response
|
||||
// - false
|
||||
// - nil error
|
||||
// - nil response
|
||||
// - false
|
||||
// - nil error
|
||||
// 3. For an unexpected error, e.g. a network problem:
|
||||
// - nil response
|
||||
// - false
|
||||
// - an error
|
||||
// - nil response
|
||||
// - false
|
||||
// - an error
|
||||
// Other combinations of return values must be avoided.
|
||||
//
|
||||
// See k8s.io/apiserver/pkg/authentication/authenticator/interfaces.go for the token authenticator
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package certauthority
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -23,10 +23,10 @@ import (
|
||||
func loadFromFiles(t *testing.T, certPath string, keyPath string) (*CA, error) {
|
||||
t.Helper()
|
||||
|
||||
certPEM, err := ioutil.ReadFile(certPath)
|
||||
certPEM, err := os.ReadFile(certPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
keyPEM, err := ioutil.ReadFile(keyPath)
|
||||
keyPEM, err := os.ReadFile(keyPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
ca, err := Load(string(certPEM), string(keyPEM))
|
||||
@@ -206,7 +206,7 @@ func TestPool(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
pool := ca.Pool()
|
||||
require.Len(t, pool.Subjects(), 1) // nolint: staticcheck // not system cert pool
|
||||
require.Len(t, pool.Subjects(), 1)
|
||||
}
|
||||
|
||||
type errSigner struct {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package apiserver
|
||||
@@ -76,7 +76,7 @@ func (c completedConfig) New() (*PinnipedServer, error) {
|
||||
GenericAPIServer: genericServer,
|
||||
}
|
||||
|
||||
var errs []error //nolint: prealloc
|
||||
var errs []error //nolint:prealloc
|
||||
for _, f := range []func() (schema.GroupVersionResource, rest.Storage){
|
||||
func() (schema.GroupVersionResource, rest.Storage) {
|
||||
tokenCredReqGVR := c.ExtraConfig.LoginConciergeGroupVersion.WithResource("tokencredentialrequests")
|
||||
|
||||
@@ -643,7 +643,7 @@ func getTransportForUser(ctx context.Context, userInfo user.Info, delegate, dele
|
||||
}
|
||||
|
||||
func canImpersonateFully(userInfo user.Info) bool {
|
||||
// nolint: gosimple // this structure is on purpose because we plan to expand this function
|
||||
//nolint:gosimple // this structure is on purpose because we plan to expand this function
|
||||
if len(userInfo.GetUID()) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package concierge contains functionality to load/store Config's from/to
|
||||
@@ -8,7 +8,7 @@ package concierge
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"k8s.io/utils/pointer"
|
||||
@@ -43,7 +43,7 @@ const (
|
||||
// This function will decode that base64-encoded data to PEM bytes to be stored
|
||||
// in the Config.
|
||||
func FromPath(ctx context.Context, path string) (*Config, error) {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read file: %w", err)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package concierge
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@@ -585,7 +584,7 @@ func TestFromPath(t *testing.T) {
|
||||
// this is a serial test because it sets the global logger
|
||||
|
||||
// Write yaml to temp file
|
||||
f, err := ioutil.TempFile("", "pinniped-test-config-yaml-*")
|
||||
f, err := os.CreateTemp("", "pinniped-test-config-yaml-*")
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err := os.Remove(f.Name())
|
||||
|
||||
@@ -8,8 +8,8 @@ package supervisor
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"k8s.io/utils/pointer"
|
||||
@@ -36,7 +36,7 @@ const (
|
||||
// defaults (from the Config documentation), and verifies that the config is
|
||||
// valid (Config documentation).
|
||||
func FromPath(ctx context.Context, path string) (*Config, error) {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read file: %w", err)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ package supervisor
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@@ -451,7 +450,7 @@ func TestFromPath(t *testing.T) {
|
||||
// this is a serial test because it sets the global logger
|
||||
|
||||
// Write yaml to temp file
|
||||
f, err := ioutil.TempFile("", "pinniped-test-config-yaml-*")
|
||||
f, err := os.CreateTemp("", "pinniped-test-config-yaml-*")
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err := os.Remove(f.Name())
|
||||
|
||||
@@ -143,7 +143,7 @@ func TestController(t *testing.T) {
|
||||
if tt.initialCache != nil {
|
||||
tt.initialCache(t, cache)
|
||||
}
|
||||
testLog := testlogger.NewLegacy(t) //nolint: staticcheck // old test with lots of log statements
|
||||
testLog := testlogger.NewLegacy(t) //nolint:staticcheck // old test with lots of log statements
|
||||
|
||||
webhooks := informers.Authentication().V1alpha1().WebhookAuthenticators()
|
||||
jwtAuthenticators := informers.Authentication().V1alpha1().JWTAuthenticators()
|
||||
|
||||
@@ -375,7 +375,7 @@ func TestController(t *testing.T) {
|
||||
fakeClient := pinnipedfake.NewSimpleClientset(tt.jwtAuthenticators...)
|
||||
informers := pinnipedinformers.NewSharedInformerFactory(fakeClient, 0)
|
||||
cache := authncache.New()
|
||||
testLog := testlogger.NewLegacy(t) //nolint: staticcheck // old test with lots of log statements
|
||||
testLog := testlogger.NewLegacy(t) //nolint:staticcheck // old test with lots of log statements
|
||||
|
||||
if tt.cache != nil {
|
||||
tt.cache(t, cache, tt.wantClose)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package webhookcachefiller implements a controller for filling an authncache.Cache with each added/updated WebhookAuthenticator.
|
||||
@@ -6,7 +6,6 @@ package webhookcachefiller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
@@ -64,7 +63,7 @@ func (c *controller) Sync(ctx controllerlib.Context) error {
|
||||
return fmt.Errorf("failed to get WebhookAuthenticator %s/%s: %w", ctx.Key.Namespace, ctx.Key.Name, err)
|
||||
}
|
||||
|
||||
webhookAuthenticator, err := newWebhookAuthenticator(&obj.Spec, ioutil.TempFile, clientcmd.WriteToFile)
|
||||
webhookAuthenticator, err := newWebhookAuthenticator(&obj.Spec, os.CreateTemp, clientcmd.WriteToFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to build webhook config: %w", err)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
@@ -88,7 +88,7 @@ func TestController(t *testing.T) {
|
||||
fakeClient := pinnipedfake.NewSimpleClientset(tt.webhooks...)
|
||||
informers := pinnipedinformers.NewSharedInformerFactory(fakeClient, 0)
|
||||
cache := authncache.New()
|
||||
testLog := testlogger.NewLegacy(t) //nolint: staticcheck // old test with lots of log statements
|
||||
testLog := testlogger.NewLegacy(t) //nolint:staticcheck // old test with lots of log statements
|
||||
|
||||
controller := New(cache, informers.Authentication().V1alpha1().WebhookAuthenticators(), testLog.Logger)
|
||||
|
||||
@@ -121,7 +121,7 @@ func TestNewWebhookAuthenticator(t *testing.T) {
|
||||
|
||||
t.Run("marshal failure", func(t *testing.T) {
|
||||
marshalError := func(_ clientcmdapi.Config, _ string) error { return fmt.Errorf("some marshal error") }
|
||||
res, err := newWebhookAuthenticator(&auth1alpha1.WebhookAuthenticatorSpec{}, ioutil.TempFile, marshalError)
|
||||
res, err := newWebhookAuthenticator(&auth1alpha1.WebhookAuthenticatorSpec{}, os.CreateTemp, marshalError)
|
||||
require.Nil(t, res)
|
||||
require.EqualError(t, err, "unable to marshal kubeconfig: some marshal error")
|
||||
})
|
||||
@@ -130,7 +130,7 @@ func TestNewWebhookAuthenticator(t *testing.T) {
|
||||
res, err := newWebhookAuthenticator(&auth1alpha1.WebhookAuthenticatorSpec{
|
||||
Endpoint: "https://example.com",
|
||||
TLS: &auth1alpha1.TLSSpec{CertificateAuthorityData: "invalid-base64"},
|
||||
}, ioutil.TempFile, clientcmd.WriteToFile)
|
||||
}, os.CreateTemp, clientcmd.WriteToFile)
|
||||
require.Nil(t, res)
|
||||
require.EqualError(t, err, "invalid TLS configuration: illegal base64 data at input byte 7")
|
||||
})
|
||||
@@ -139,7 +139,7 @@ func TestNewWebhookAuthenticator(t *testing.T) {
|
||||
res, err := newWebhookAuthenticator(&auth1alpha1.WebhookAuthenticatorSpec{
|
||||
Endpoint: "https://example.com",
|
||||
TLS: &auth1alpha1.TLSSpec{CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte("bad data"))},
|
||||
}, ioutil.TempFile, clientcmd.WriteToFile)
|
||||
}, os.CreateTemp, clientcmd.WriteToFile)
|
||||
require.Nil(t, res)
|
||||
require.EqualError(t, err, "invalid TLS configuration: certificateAuthorityData is not valid PEM: data does not contain any valid RSA or ECDSA certificates")
|
||||
})
|
||||
@@ -147,14 +147,14 @@ func TestNewWebhookAuthenticator(t *testing.T) {
|
||||
t.Run("valid config with no TLS spec", func(t *testing.T) {
|
||||
res, err := newWebhookAuthenticator(&auth1alpha1.WebhookAuthenticatorSpec{
|
||||
Endpoint: "https://example.com",
|
||||
}, ioutil.TempFile, clientcmd.WriteToFile)
|
||||
}, os.CreateTemp, clientcmd.WriteToFile)
|
||||
require.NotNil(t, res)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
caBundle, url := testutil.TLSTestServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
body, err := io.ReadAll(r.Body)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, string(body), "test-token")
|
||||
_, err = w.Write([]byte(`{}`))
|
||||
@@ -166,7 +166,7 @@ func TestNewWebhookAuthenticator(t *testing.T) {
|
||||
CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(caBundle)),
|
||||
},
|
||||
}
|
||||
res, err := newWebhookAuthenticator(spec, ioutil.TempFile, clientcmd.WriteToFile)
|
||||
res, err := newWebhookAuthenticator(spec, os.CreateTemp, clientcmd.WriteToFile)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res)
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"reflect"
|
||||
@@ -92,7 +92,7 @@ func TestImpersonatorConfigControllerOptions(t *testing.T) {
|
||||
nil,
|
||||
caSignerName,
|
||||
nil,
|
||||
plog.Logr(), // nolint: staticcheck // old test with no log assertions
|
||||
plog.Logr(), //nolint:staticcheck // old test with no log assertions
|
||||
)
|
||||
credIssuerInformerFilter = observableWithInformerOption.GetFilterForInformer(credIssuerInformer)
|
||||
servicesInformerFilter = observableWithInformerOption.GetFilterForInformer(servicesInformer)
|
||||
@@ -360,10 +360,13 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
}
|
||||
|
||||
testHTTPServerMutex.Lock() // this is to satisfy the race detector
|
||||
testHTTPServer = &http.Server{Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
_, err := fmt.Fprint(w, fakeServerResponseBody)
|
||||
r.NoError(err)
|
||||
})}
|
||||
testHTTPServer = &http.Server{
|
||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
_, err := fmt.Fprint(w, fakeServerResponseBody)
|
||||
r.NoError(err)
|
||||
}),
|
||||
ReadHeaderTimeout: 10 * time.Second,
|
||||
}
|
||||
testHTTPServerMutex.Unlock()
|
||||
|
||||
// Start serving requests in the background.
|
||||
@@ -480,7 +483,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
r.NoError(err)
|
||||
|
||||
r.Equal(http.StatusOK, resp.StatusCode)
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
r.NoError(resp.Body.Close())
|
||||
r.NoError(err)
|
||||
r.Equal(fakeServerResponseBody, string(body))
|
||||
@@ -560,7 +563,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
|
||||
impersonatorFunc,
|
||||
caSignerName,
|
||||
signingCertProvider,
|
||||
plog.Logr(), // nolint: staticcheck // old test with no log assertions
|
||||
plog.Logr(), //nolint:staticcheck // old test with no log assertions
|
||||
)
|
||||
controllerlib.TestWrap(t, subject, func(syncer controllerlib.Syncer) controllerlib.Syncer {
|
||||
tlsServingCertDynamicCertProvider = syncer.(*impersonatorConfigController).tlsServingCertDynamicCertProvider
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package issuerconfig contains helpers for updating CredentialIssuer status entries.
|
||||
@@ -60,8 +60,7 @@ func mergeStrategy(configToUpdate *v1alpha1.CredentialIssuerStatus, strategy v1a
|
||||
}
|
||||
|
||||
// weights are a set of priorities for each strategy type.
|
||||
//nolint: gochecknoglobals
|
||||
var weights = map[v1alpha1.StrategyType]int{
|
||||
var weights = map[v1alpha1.StrategyType]int{ //nolint:gochecknoglobals
|
||||
v1alpha1.KubeClusterSigningCertificateStrategyType: 2, // most preferred strategy
|
||||
v1alpha1.ImpersonationProxyStrategyType: 1,
|
||||
// unknown strategy types will have weight 0 by default
|
||||
|
||||
@@ -145,12 +145,12 @@ type agentController struct {
|
||||
|
||||
var (
|
||||
// controllerManagerLabels are the Kubernetes labels we expect on the kube-controller-manager Pod.
|
||||
controllerManagerLabels = labels.SelectorFromSet(map[string]string{ // nolint: gochecknoglobals
|
||||
controllerManagerLabels = labels.SelectorFromSet(map[string]string{ //nolint:gochecknoglobals
|
||||
"component": "kube-controller-manager",
|
||||
})
|
||||
|
||||
// agentLabels are the Kubernetes labels we always expect on the kube-controller-manager Pod.
|
||||
agentLabels = labels.SelectorFromSet(map[string]string{ // nolint: gochecknoglobals
|
||||
agentLabels = labels.SelectorFromSet(map[string]string{ //nolint:gochecknoglobals
|
||||
agentPodLabelKey: agentPodLabelValue,
|
||||
})
|
||||
)
|
||||
@@ -179,7 +179,7 @@ func NewAgentController(
|
||||
dynamicCertProvider,
|
||||
&clock.RealClock{},
|
||||
cache.NewExpiring(),
|
||||
plog.Logr(), // nolint: staticcheck // old controller with lots of log statements
|
||||
plog.Logr(), //nolint:staticcheck // old controller with lots of log statements
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1110,7 +1110,7 @@ func TestAgentController(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
if tt.wantAgentDeployment == nil {
|
||||
assert.Empty(t, deployments.Items, "did not expect an agent deployment")
|
||||
} else { // nolint: gocritic
|
||||
} else { //nolint:gocritic
|
||||
if assert.Len(t, deployments.Items, 1, "expected a single agent deployment") {
|
||||
assert.Equal(t, tt.wantAgentDeployment, &deployments.Items[0])
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ func TestLegacyPodCleanerController(t *testing.T) {
|
||||
}
|
||||
|
||||
kubeInformers := informers.NewSharedInformerFactory(kubeClientset, 0)
|
||||
log := testlogger.NewLegacy(t) //nolint: staticcheck // old test with lots of log statements
|
||||
log := testlogger.NewLegacy(t) //nolint:staticcheck // old test with lots of log statements
|
||||
controller := NewLegacyPodCleanerController(
|
||||
AgentConfig{
|
||||
Namespace: "concierge",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package secretgenerator provides a supervisorSecretsController that can ensure existence of a generated secret.
|
||||
// Package generator provides a supervisorSecretsController that can ensure existence of a generated secret.
|
||||
package generator
|
||||
|
||||
import (
|
||||
@@ -24,8 +24,7 @@ import (
|
||||
)
|
||||
|
||||
// generateKey is stubbed out for the purpose of testing. The default behavior is to generate a symmetric key.
|
||||
//nolint:gochecknoglobals
|
||||
var generateKey = generateSymmetricKey
|
||||
var generateKey = generateSymmetricKey //nolint:gochecknoglobals
|
||||
|
||||
type supervisorSecretsController struct {
|
||||
labels map[string]string
|
||||
|
||||
@@ -50,8 +50,7 @@ const (
|
||||
)
|
||||
|
||||
// generateKey is stubbed out for the purpose of testing. The default behavior is to generate an EC key.
|
||||
//nolint:gochecknoglobals
|
||||
var generateKey = generateECKey
|
||||
var generateKey = generateECKey //nolint:gochecknoglobals
|
||||
|
||||
func generateECKey(r io.Reader) (interface{}, error) {
|
||||
return ecdsa.GenerateKey(elliptic.P256(), r)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package supervisorconfig
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -259,7 +259,7 @@ func TestJWKSWriterControllerSync(t *testing.T) {
|
||||
|
||||
const namespace = "tuna-namespace"
|
||||
|
||||
goodKeyPEM, err := ioutil.ReadFile("testdata/good-ec-key.pem")
|
||||
goodKeyPEM, err := os.ReadFile("testdata/good-ec-key.pem")
|
||||
require.NoError(t, err)
|
||||
block, _ := pem.Decode(goodKeyPEM)
|
||||
require.NotNil(t, block, "expected block to be non-nil...is goodKeyPEM a valid PEM?")
|
||||
@@ -747,7 +747,7 @@ func TestJWKSWriterControllerSync(t *testing.T) {
|
||||
func readJWKJSON(t *testing.T, path string) []byte {
|
||||
t.Helper()
|
||||
|
||||
data, err := ioutil.ReadFile(path)
|
||||
data, err := os.ReadFile(path)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Trim whitespace from our testdata so that we match the compact JSON encoding of
|
||||
|
||||
@@ -68,7 +68,7 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
disallowedAdditionalAuthorizeParameters = map[string]bool{ // nolint: gochecknoglobals
|
||||
disallowedAdditionalAuthorizeParameters = map[string]bool{ //nolint:gochecknoglobals
|
||||
// Reject these AdditionalAuthorizeParameters to avoid allowing the user's config to overwrite the parameters
|
||||
// that are always used by Pinniped in authcode authorization requests. The OIDC library used would otherwise
|
||||
// happily treat the user's config as an override. Users can already set the "client_id" and "scope" params
|
||||
|
||||
+2
-2
@@ -91,7 +91,7 @@ func TestOIDCUpstreamWatcherControllerFilterSecret(t *testing.T) {
|
||||
nil,
|
||||
pinnipedInformers.IDP().V1alpha1().OIDCIdentityProviders(),
|
||||
secretInformer,
|
||||
plog.Logr(), // nolint: staticcheck // old test with no log assertions
|
||||
plog.Logr(), //nolint:staticcheck // old test with no log assertions
|
||||
withInformer.WithInformer,
|
||||
)
|
||||
|
||||
@@ -1400,7 +1400,7 @@ oidc: issuer did not match the issuer returned by provider, expected "` + testIs
|
||||
pinnipedInformers := pinnipedinformers.NewSharedInformerFactory(fakePinnipedClient, 0)
|
||||
fakeKubeClient := fake.NewSimpleClientset(tt.inputSecrets...)
|
||||
kubeInformers := informers.NewSharedInformerFactory(fakeKubeClient, 0)
|
||||
testLog := testlogger.NewLegacy(t) // nolint: staticcheck // old test with lots of log statements
|
||||
testLog := testlogger.NewLegacy(t) //nolint:staticcheck // old test with lots of log statements
|
||||
cache := provider.NewDynamicUpstreamIDPProvider()
|
||||
cache.SetOIDCIdentityProviders([]provider.UpstreamOIDCIdentityProviderI{
|
||||
&upstreamoidc.ProviderConfig{Name: "initial-entry"},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package supervisorconfig
|
||||
@@ -6,8 +6,8 @@ package supervisorconfig
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/sclevine/spec"
|
||||
@@ -170,7 +170,7 @@ func TestTLSCertObserverControllerSync(t *testing.T) {
|
||||
}
|
||||
|
||||
var readTestFile = func(path string) []byte {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
data, err := os.ReadFile(path)
|
||||
r.NoError(err)
|
||||
return data
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package controllerinit
|
||||
@@ -29,8 +29,8 @@ type Informer interface {
|
||||
}
|
||||
|
||||
// Prepare returns RunnerBuilder that, when called:
|
||||
// 1. Starts all provided informers and waits for them sync (and fails if they hang)
|
||||
// 2. Returns a Runner that combines the Runner and RunnerWrapper passed into Prepare
|
||||
// 1.) Starts all provided informers and waits for them sync (and fails if they hang), and
|
||||
// 2.) Returns a Runner that combines the Runner and RunnerWrapper passed into Prepare.
|
||||
func Prepare(controllers Runner, controllersWrapper RunnerWrapper, informers ...Informer) RunnerBuilder {
|
||||
return func(ctx context.Context) (Runner, error) {
|
||||
for _, informer := range informers {
|
||||
|
||||
@@ -97,8 +97,7 @@ type Config struct {
|
||||
}
|
||||
|
||||
// PrepareControllers prepares the controllers and their informers and returns a function that will start them when called.
|
||||
//nolint:funlen // Eh, fair, it is a really long function...but it is wiring the world...so...
|
||||
func PrepareControllers(c *Config) (controllerinit.RunnerBuilder, error) {
|
||||
func PrepareControllers(c *Config) (controllerinit.RunnerBuilder, error) { //nolint:funlen // Eh, fair, it is a really long function...but it is wiring the world...so...
|
||||
loginConciergeGroupData, identityConciergeGroupData := groupsuffix.ConciergeAggregatedGroups(c.APIGroupSuffix)
|
||||
|
||||
dref, deployment, _, err := deploymentref.New(c.ServerInstallationInfo)
|
||||
@@ -223,7 +222,7 @@ func PrepareControllers(c *Config) (controllerinit.RunnerBuilder, error) {
|
||||
agentConfig,
|
||||
client,
|
||||
informers.installationNamespaceK8s.Core().V1().Pods(),
|
||||
plog.Logr(), // nolint: staticcheck // old controller with lots of log statements
|
||||
plog.Logr(), //nolint:staticcheck // old controller with lots of log statements
|
||||
),
|
||||
singletonWorker,
|
||||
).
|
||||
@@ -233,7 +232,7 @@ func PrepareControllers(c *Config) (controllerinit.RunnerBuilder, error) {
|
||||
webhookcachefiller.New(
|
||||
c.AuthenticatorCache,
|
||||
informers.pinniped.Authentication().V1alpha1().WebhookAuthenticators(),
|
||||
plog.Logr(), // nolint: staticcheck // old controller with lots of log statements
|
||||
plog.Logr(), //nolint:staticcheck // old controller with lots of log statements
|
||||
),
|
||||
singletonWorker,
|
||||
).
|
||||
@@ -241,7 +240,7 @@ func PrepareControllers(c *Config) (controllerinit.RunnerBuilder, error) {
|
||||
jwtcachefiller.New(
|
||||
c.AuthenticatorCache,
|
||||
informers.pinniped.Authentication().V1alpha1().JWTAuthenticators(),
|
||||
plog.Logr(), // nolint: staticcheck // old controller with lots of log statements
|
||||
plog.Logr(), //nolint:staticcheck // old controller with lots of log statements
|
||||
),
|
||||
singletonWorker,
|
||||
).
|
||||
@@ -250,7 +249,7 @@ func PrepareControllers(c *Config) (controllerinit.RunnerBuilder, error) {
|
||||
c.AuthenticatorCache,
|
||||
informers.pinniped.Authentication().V1alpha1().WebhookAuthenticators(),
|
||||
informers.pinniped.Authentication().V1alpha1().JWTAuthenticators(),
|
||||
plog.Logr(), // nolint: staticcheck // old controller with lots of log statements
|
||||
plog.Logr(), //nolint:staticcheck // old controller with lots of log statements
|
||||
),
|
||||
singletonWorker,
|
||||
).
|
||||
@@ -276,7 +275,7 @@ func PrepareControllers(c *Config) (controllerinit.RunnerBuilder, error) {
|
||||
impersonator.New,
|
||||
c.NamesConfig.ImpersonationSignerSecret,
|
||||
c.ImpersonationSigningCertProvider,
|
||||
plog.Logr(), // nolint: staticcheck // old controller with lots of log statements
|
||||
plog.Logr(), //nolint:staticcheck // old controller with lots of log statements
|
||||
),
|
||||
singletonWorker,
|
||||
).
|
||||
|
||||
@@ -169,7 +169,7 @@ func validateSecret(resource string, secret *corev1.Secret) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
//nolint: gochecknoglobals
|
||||
//nolint:gochecknoglobals
|
||||
var b32 = base32.StdEncoding.WithPadding(base32.NoPadding)
|
||||
|
||||
func (s *secretsStorage) GetName(signature string) string {
|
||||
|
||||
@@ -144,7 +144,7 @@ func TestMerge(t *testing.T) {
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, //nolint: gosec // yeah, I know it is a bad cipher, but AD sucks
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, //nolint:gosec // yeah, I know it is a bad cipher, but AD sucks
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
@@ -169,7 +169,7 @@ func TestMerge(t *testing.T) {
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, //nolint: gosec // yeah, I know it is a bad cipher, but AD sucks
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, //nolint:gosec // yeah, I know it is a bad cipher, but AD sucks
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
@@ -187,7 +187,7 @@ func TestMerge(t *testing.T) {
|
||||
ServerName: "something-to-check-passthrough",
|
||||
MinVersion: tls.VersionTLS12,
|
||||
CipherSuites: []uint16{
|
||||
tls.TLS_RSA_WITH_AES_128_CBC_SHA, //nolint: gosec // yeah, I know it is a bad cipher, this is the legacy config
|
||||
tls.TLS_RSA_WITH_AES_128_CBC_SHA, //nolint:gosec // yeah, I know it is a bad cipher, this is the legacy config
|
||||
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
@@ -219,7 +219,7 @@ func TestMerge(t *testing.T) {
|
||||
ServerName: "a different thing for passthrough",
|
||||
MinVersion: tls.VersionTLS12,
|
||||
CipherSuites: []uint16{
|
||||
tls.TLS_RSA_WITH_AES_128_CBC_SHA, //nolint: gosec // yeah, I know it is a bad cipher, this is the legacy config
|
||||
tls.TLS_RSA_WITH_AES_128_CBC_SHA, //nolint:gosec // yeah, I know it is a bad cipher, this is the legacy config
|
||||
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package deploymentref
|
||||
@@ -23,8 +23,7 @@ import (
|
||||
// We would normally pass a kubernetes.Interface into New(), but the client we want to create in
|
||||
// the calling code depends on the return value of New() (i.e., on the kubeclient.Option for the
|
||||
// OwnerReference).
|
||||
//nolint: gochecknoglobals
|
||||
var getTempClient = func() (kubernetes.Interface, error) {
|
||||
var getTempClient = func() (kubernetes.Interface, error) { //nolint:gochecknoglobals
|
||||
client, err := kubeclient.New()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package downward implements a client interface for interacting with Kubernetes "downwardAPI" volumes.
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -32,20 +32,20 @@ type PodInfo struct {
|
||||
// Load pod metadata from a downwardAPI volume directory.
|
||||
func Load(directory string) (*PodInfo, error) {
|
||||
var result PodInfo
|
||||
ns, err := ioutil.ReadFile(filepath.Join(directory, "namespace"))
|
||||
ns, err := os.ReadFile(filepath.Join(directory, "namespace"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not load namespace: %w", err)
|
||||
}
|
||||
result.Namespace = strings.TrimSpace(string(ns))
|
||||
|
||||
name, err := ioutil.ReadFile(filepath.Join(directory, "name"))
|
||||
name, err := os.ReadFile(filepath.Join(directory, "name"))
|
||||
if err != nil {
|
||||
plog.Warning("could not read 'name' downward API file")
|
||||
} else {
|
||||
result.Name = strings.TrimSpace(string(name))
|
||||
}
|
||||
|
||||
labels, err := ioutil.ReadFile(filepath.Join(directory, "labels"))
|
||||
labels, err := os.ReadFile(filepath.Join(directory, "labels"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not load labels: %w", err)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package dynamiccert
|
||||
@@ -41,7 +41,7 @@ func TestProviderWithDynamicServingCertificateController(t *testing.T) {
|
||||
cert, err := tls.X509KeyPair(certPEM, keyPEM)
|
||||
require.NoError(t, err)
|
||||
|
||||
return pool.Subjects(), []tls.Certificate{cert} // nolint: staticcheck // not system cert pool
|
||||
return pool.Subjects(), []tls.Certificate{cert}
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -69,7 +69,7 @@ func TestProviderWithDynamicServingCertificateController(t *testing.T) {
|
||||
|
||||
certKey.UnsetCertKeyContent()
|
||||
|
||||
return pool.Subjects(), []tls.Certificate{cert} // nolint: staticcheck // not system cert pool
|
||||
return pool.Subjects(), []tls.Certificate{cert}
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -87,7 +87,7 @@ func TestProviderWithDynamicServingCertificateController(t *testing.T) {
|
||||
cert, err := tls.X509KeyPair(certPEM, keyPEM)
|
||||
require.NoError(t, err)
|
||||
|
||||
return newCA.Pool().Subjects(), []tls.Certificate{cert} // nolint: staticcheck // not system cert pool
|
||||
return newCA.Pool().Subjects(), []tls.Certificate{cert}
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -110,7 +110,7 @@ func TestProviderWithDynamicServingCertificateController(t *testing.T) {
|
||||
ok := pool.AppendCertsFromPEM(ca.CurrentCABundleContent())
|
||||
require.True(t, ok, "should have valid non-empty CA bundle")
|
||||
|
||||
return pool.Subjects(), []tls.Certificate{cert} // nolint: staticcheck // not system cert pool
|
||||
return pool.Subjects(), []tls.Certificate{cert}
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -137,7 +137,7 @@ func TestProviderWithDynamicServingCertificateController(t *testing.T) {
|
||||
err = ca.SetCertKeyContent(newOtherCA.Bundle(), caKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
return newOtherCA.Pool().Subjects(), []tls.Certificate{cert} // nolint: staticcheck // not system cert pool
|
||||
return newOtherCA.Pool().Subjects(), []tls.Certificate{cert}
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -221,7 +221,7 @@ func poolSubjects(pool *x509.CertPool) [][]byte {
|
||||
if pool == nil {
|
||||
return nil
|
||||
}
|
||||
return pool.Subjects() // nolint: staticcheck // not system cert pool
|
||||
return pool.Subjects()
|
||||
}
|
||||
|
||||
func TestNewServingCert(t *testing.T) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package execcredcache
|
||||
@@ -6,7 +6,6 @@ package execcredcache
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sort"
|
||||
"time"
|
||||
@@ -51,7 +50,7 @@ type (
|
||||
|
||||
// readCache loads a credCache from a path on disk. If the requested path does not exist, it returns an empty cache.
|
||||
func readCache(path string) (*credCache, error) {
|
||||
cacheYAML, err := ioutil.ReadFile(path)
|
||||
cacheYAML, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
// If the file was not found, generate a freshly initialized empty cache.
|
||||
@@ -87,7 +86,7 @@ func (c *credCache) writeTo(path string) error {
|
||||
// Marshal the cache back to YAML and save it to the file.
|
||||
cacheYAML, err := yaml.Marshal(c)
|
||||
if err == nil {
|
||||
err = ioutil.WriteFile(path, cacheYAML, 0600)
|
||||
err = os.WriteFile(path, cacheYAML, 0600)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package execcredcache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -52,7 +51,7 @@ func TestGet(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "file lock error",
|
||||
makeTestFile: func(t *testing.T, tmp string) { require.NoError(t, ioutil.WriteFile(tmp, []byte(""), 0600)) },
|
||||
makeTestFile: func(t *testing.T, tmp string) { require.NoError(t, os.WriteFile(tmp, []byte(""), 0600)) },
|
||||
trylockFunc: func(t *testing.T) error { return fmt.Errorf("some lock error") },
|
||||
unlockFunc: func(t *testing.T) error { require.Fail(t, "should not be called"); return nil },
|
||||
key: testKey{},
|
||||
@@ -61,7 +60,7 @@ func TestGet(t *testing.T) {
|
||||
{
|
||||
name: "invalid file",
|
||||
makeTestFile: func(t *testing.T, tmp string) {
|
||||
require.NoError(t, ioutil.WriteFile(tmp, []byte("invalid yaml"), 0600))
|
||||
require.NoError(t, os.WriteFile(tmp, []byte("invalid yaml"), 0600))
|
||||
},
|
||||
key: testKey{},
|
||||
wantErrors: []string{
|
||||
@@ -70,7 +69,7 @@ func TestGet(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "invalid file, fail to unlock",
|
||||
makeTestFile: func(t *testing.T, tmp string) { require.NoError(t, ioutil.WriteFile(tmp, []byte("invalid"), 0600)) },
|
||||
makeTestFile: func(t *testing.T, tmp string) { require.NoError(t, os.WriteFile(tmp, []byte("invalid"), 0600)) },
|
||||
trylockFunc: func(t *testing.T) error { return nil },
|
||||
unlockFunc: func(t *testing.T) error { return fmt.Errorf("some unlock error") },
|
||||
key: testKey{},
|
||||
@@ -211,7 +210,7 @@ func TestPutToken(t *testing.T) {
|
||||
{
|
||||
name: "fail to create directory",
|
||||
makeTestFile: func(t *testing.T, tmp string) {
|
||||
require.NoError(t, ioutil.WriteFile(filepath.Dir(tmp), []byte{}, 0600))
|
||||
require.NoError(t, os.WriteFile(filepath.Dir(tmp), []byte{}, 0600))
|
||||
},
|
||||
wantErrors: []string{
|
||||
"could not create credential cache directory: mkdir TEMPDIR: not a directory",
|
||||
|
||||
@@ -236,6 +236,7 @@ const ExpectedAuthorizeCodeSessionJSONFromFuzzing = `{
|
||||
"Host": "",
|
||||
"Path": "",
|
||||
"RawPath": "",
|
||||
"OmitHost": false,
|
||||
"ForceQuery": false,
|
||||
"RawQuery": "",
|
||||
"Fragment": "",
|
||||
@@ -253,6 +254,7 @@ const ExpectedAuthorizeCodeSessionJSONFromFuzzing = `{
|
||||
"Host": "",
|
||||
"Path": "",
|
||||
"RawPath": "",
|
||||
"OmitHost": false,
|
||||
"ForceQuery": false,
|
||||
"RawQuery": "",
|
||||
"Fragment": "",
|
||||
@@ -270,6 +272,7 @@ const ExpectedAuthorizeCodeSessionJSONFromFuzzing = `{
|
||||
"Host": "",
|
||||
"Path": "",
|
||||
"RawPath": "",
|
||||
"OmitHost": false,
|
||||
"ForceQuery": false,
|
||||
"RawQuery": "",
|
||||
"Fragment": "",
|
||||
|
||||
@@ -100,7 +100,7 @@ func TestOpenIdConnectStorage(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, request, newRequest)
|
||||
|
||||
err = storage.DeleteOpenIDConnectSession(ctx, "fancy-code.fancy-signature") //nolint: staticcheck // we know this is deprecated and never called. our GC controller cleans these up.
|
||||
err = storage.DeleteOpenIDConnectSession(ctx, "fancy-code.fancy-signature") //nolint:staticcheck // we know this is deprecated and never called. our GC controller cleans these up.
|
||||
require.NoError(t, err)
|
||||
|
||||
testutil.LogActualJSONFromCreateAction(t, client, 0) // makes it easier to update expected values when needed
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package groupsuffix
|
||||
@@ -175,7 +175,7 @@ func Unreplace(baseAPIGroup, apiGroupSuffix string) (string, bool) {
|
||||
// makes sure that the provided apiGroupSuffix is a valid DNS-1123 subdomain with at least one dot,
|
||||
// to match Kubernetes behavior.
|
||||
func Validate(apiGroupSuffix string) error {
|
||||
var errs []error // nolint: prealloc
|
||||
var errs []error //nolint:prealloc
|
||||
|
||||
if len(strings.Split(apiGroupSuffix, ".")) < 2 {
|
||||
errs = append(errs, constable.Error("must contain '.'"))
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package securityheader
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
@@ -74,7 +74,7 @@ func TestWrap(t *testing.T) {
|
||||
defer resp.Body.Close()
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "hello world", string(respBody))
|
||||
|
||||
|
||||
@@ -15,8 +15,7 @@ import (
|
||||
)
|
||||
|
||||
// defaultServerUrlFor was copied from k8s.io/client-go/rest/url_utils.go.
|
||||
//nolint:revive
|
||||
func defaultServerUrlFor(config *restclient.Config) (*url.URL, string, error) {
|
||||
func defaultServerUrlFor(config *restclient.Config) (*url.URL, string, error) { //nolint:revive
|
||||
hasCA := len(config.CAFile) != 0 || len(config.CAData) != 0
|
||||
hasCert := len(config.CertFile) != 0 || len(config.CertData) != 0
|
||||
defaultTLS := hasCA || hasCert || config.Insecure
|
||||
|
||||
@@ -211,7 +211,7 @@ func AssertSecureTransport(rt http.RoundTripper) error {
|
||||
tlsConfigCopy := tlsConfig.Clone()
|
||||
ptls.Merge(ptls.Secure, tlsConfigCopy) // only mutate the copy
|
||||
|
||||
//nolint: gosec // the empty TLS config here is not used
|
||||
//nolint:gosec // the empty TLS config here is not used
|
||||
if diff := cmp.Diff(tlsConfigCopy, tlsConfig,
|
||||
cmpopts.IgnoreUnexported(tls.Config{}, x509.CertPool{}),
|
||||
cmpopts.IgnoreFields(tls.Config{}, "GetClientCertificate"),
|
||||
|
||||
@@ -949,7 +949,7 @@ func TestUnwrap(t *testing.T) {
|
||||
|
||||
server, restConfig := fakekubeapi.Start(t, nil)
|
||||
|
||||
serverSubjects := server.Client().Transport.(*http.Transport).TLSClientConfig.RootCAs.Subjects() // nolint: staticcheck // not system cert pool
|
||||
serverSubjects := server.Client().Transport.(*http.Transport).TLSClientConfig.RootCAs.Subjects()
|
||||
|
||||
t.Run("regular client", func(t *testing.T) {
|
||||
t.Parallel() // make sure to run in parallel to confirm that our client-go TLS cache busting works (i.e. assert no data races)
|
||||
@@ -1121,7 +1121,7 @@ func testUnwrap(t *testing.T, client *Client, serverSubjects [][]byte) {
|
||||
require.Equal(t, secureTLSConfig.NextProtos, tlsConfig.NextProtos)
|
||||
|
||||
// x509.CertPool has some embedded functions that make it hard to compare so just look at the subjects
|
||||
require.Equal(t, serverSubjects, tlsConfig.RootCAs.Subjects()) // nolint: staticcheck // not system cert pool
|
||||
require.Equal(t, serverSubjects, tlsConfig.RootCAs.Subjects())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package kubeclient
|
||||
@@ -71,7 +71,7 @@ func (r *request) Namespace() string {
|
||||
return r.namespace
|
||||
}
|
||||
|
||||
//nolint: gochecknoglobals
|
||||
//nolint:gochecknoglobals
|
||||
var namespaceGVR = corev1.SchemeGroupVersion.WithResource("namespaces")
|
||||
|
||||
func (r *request) NamespaceScoped() bool {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package kubeclient
|
||||
@@ -6,7 +6,7 @@ package kubeclient
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
@@ -142,7 +142,7 @@ func Test_updatePathNewGVK(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_reqWithoutPrefix(t *testing.T) {
|
||||
body := ioutil.NopCloser(bytes.NewBuffer([]byte("some body")))
|
||||
body := io.NopCloser(bytes.NewBuffer([]byte("some body")))
|
||||
newReq := func(rawurl string) *http.Request {
|
||||
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, rawurl, body)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package kubeclient
|
||||
@@ -6,7 +6,7 @@ package kubeclient
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
@@ -213,7 +213,7 @@ func handleCreateOrUpdate(
|
||||
return true, nil, fmt.Errorf("get body failed: %w", err)
|
||||
}
|
||||
defer body.Close()
|
||||
data, err := ioutil.ReadAll(body)
|
||||
data, err := io.ReadAll(body)
|
||||
if err != nil {
|
||||
return true, nil, fmt.Errorf("read body failed: %w", err)
|
||||
}
|
||||
@@ -296,7 +296,7 @@ func handleResponseNewGVK(
|
||||
|
||||
// always make sure we close the body, even if reading from it fails
|
||||
defer resp.Body.Close()
|
||||
respData, err := ioutil.ReadAll(resp.Body)
|
||||
respData, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read response body: %w", err)
|
||||
}
|
||||
@@ -319,7 +319,7 @@ func handleResponseNewGVK(
|
||||
newResp := &http.Response{}
|
||||
*newResp = *resp
|
||||
|
||||
newResp.Body = ioutil.NopCloser(bytes.NewBuffer(fixedRespData))
|
||||
newResp.Body = io.NopCloser(bytes.NewBuffer(fixedRespData))
|
||||
return newResp, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package kubeclient
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
stderrors "errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -155,7 +154,7 @@ func drainAndMaybeCloseBody(resp *http.Response, close bool) {
|
||||
// from k8s.io/client-go/rest/request.go...
|
||||
const maxBodySlurpSize = 2 << 10
|
||||
if resp.ContentLength <= maxBodySlurpSize {
|
||||
_, _ = io.Copy(ioutil.Discard, &io.LimitedReader{R: resp.Body, N: maxBodySlurpSize})
|
||||
_, _ = io.Copy(io.Discard, &io.LimitedReader{R: resp.Body, N: maxBodySlurpSize})
|
||||
}
|
||||
if close {
|
||||
resp.Body.Close()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package leaderelection
|
||||
@@ -184,7 +184,7 @@ func (t *isLeaderTracker) start() {
|
||||
}
|
||||
|
||||
func (t *isLeaderTracker) stop() (didStop bool) {
|
||||
return t.tracker.CAS(true, false)
|
||||
return t.tracker.CompareAndSwap(true, false)
|
||||
}
|
||||
|
||||
// note that resourcelock.Interface is an internal, unstable interface.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package localuserauthenticator provides a authentication webhook program.
|
||||
@@ -79,8 +79,9 @@ func (w *webhook) start(ctx context.Context, l net.Listener) error {
|
||||
return &cert, err
|
||||
}
|
||||
server := http.Server{
|
||||
Handler: w,
|
||||
TLSConfig: c,
|
||||
Handler: w,
|
||||
TLSConfig: c,
|
||||
ReadHeaderTimeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
errCh := make(chan error)
|
||||
@@ -356,7 +357,7 @@ func run(ctx context.Context) error {
|
||||
startControllers(ctx, dynamicCertProvider, client.Kubernetes, kubeInformers)
|
||||
plog.Debug("controllers are ready")
|
||||
|
||||
// nolint: gosec // Intentionally binding to all network interfaces.
|
||||
//nolint:gosec // Intentionally binding to all network interfaces.
|
||||
l, err := net.Listen("tcp", ":8443")
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot create listener: %w", err)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package localuserauthenticator
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -385,7 +384,7 @@ func TestWebhook(t *testing.T) {
|
||||
url: goodURL,
|
||||
method: http.MethodPost,
|
||||
headers: goodRequestHeaders,
|
||||
body: func() (io.ReadCloser, error) { return ioutil.NopCloser(bytes.NewBuffer([]byte("invalid body"))), nil },
|
||||
body: func() (io.ReadCloser, error) { return io.NopCloser(bytes.NewBuffer([]byte("invalid body"))), nil },
|
||||
wantStatus: http.StatusBadRequest,
|
||||
},
|
||||
}
|
||||
@@ -416,7 +415,7 @@ func TestWebhook(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
responseBody, err := ioutil.ReadAll(rsp.Body)
|
||||
responseBody, err := io.ReadAll(rsp.Body)
|
||||
require.NoError(t, err)
|
||||
if test.wantBody != nil {
|
||||
require.NoError(t, err)
|
||||
@@ -520,7 +519,7 @@ func newTokenReviewBodyWithGVK(token string, gvk *schema.GroupVersionKind) (io.R
|
||||
},
|
||||
}
|
||||
err := json.NewEncoder(buf).Encode(&tr)
|
||||
return ioutil.NopCloser(buf), err
|
||||
return io.NopCloser(buf), err
|
||||
}
|
||||
|
||||
func unauthenticatedResponseJSON() *authenticationv1beta1.TokenReview {
|
||||
|
||||
@@ -58,10 +58,10 @@ func TestAuthorizationEndpoint(t *testing.T) {
|
||||
oidcUpstreamSubject = "abc123-some guid" // has a space character which should get escaped in URL
|
||||
oidcUpstreamSubjectQueryEscaped = "abc123-some+guid"
|
||||
oidcUpstreamUsername = "test-oidc-pinniped-username"
|
||||
oidcUpstreamPassword = "test-oidc-pinniped-password" //nolint: gosec
|
||||
oidcUpstreamPassword = "test-oidc-pinniped-password" //nolint:gosec
|
||||
oidcUpstreamUsernameClaim = "the-user-claim"
|
||||
oidcUpstreamGroupsClaim = "the-groups-claim"
|
||||
oidcPasswordGrantUpstreamRefreshToken = "some-opaque-token" //nolint: gosec
|
||||
oidcPasswordGrantUpstreamRefreshToken = "some-opaque-token" //nolint:gosec
|
||||
oidcUpstreamAccessToken = "some-access-token"
|
||||
|
||||
downstreamIssuer = "https://my-downstream-issuer.com/some-path"
|
||||
|
||||
@@ -121,7 +121,7 @@ func (k KubeStorage) GetOpenIDConnectSession(ctx context.Context, fullAuthcode s
|
||||
}
|
||||
|
||||
func (k KubeStorage) DeleteOpenIDConnectSession(ctx context.Context, fullAuthcode string) error {
|
||||
return k.oidcStorage.DeleteOpenIDConnectSession(ctx, fullAuthcode) //nolint: staticcheck // we know this is deprecated and never called. our GC controller cleans these up.
|
||||
return k.oidcStorage.DeleteOpenIDConnectSession(ctx, fullAuthcode) //nolint:staticcheck // we know this is deprecated and never called. our GC controller cleans these up.
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package loginhtml defines HTML templates used by the Supervisor.
|
||||
//nolint: gochecknoglobals // This package uses globals to ensure that all parsing and minifying happens at init.
|
||||
package loginhtml
|
||||
|
||||
import (
|
||||
@@ -15,6 +14,7 @@ import (
|
||||
"go.pinniped.dev/internal/oidc/provider/csp"
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals // This package uses globals to ensure that all parsing and minifying happens at init.
|
||||
var (
|
||||
//go:embed login_form.css
|
||||
rawCSS string
|
||||
@@ -22,20 +22,20 @@ var (
|
||||
|
||||
//go:embed login_form.gohtml
|
||||
rawHTMLTemplate string
|
||||
|
||||
// Parse the Go templated HTML and inject functions providing the minified inline CSS and JS.
|
||||
parsedHTMLTemplate = template.Must(template.New("login_form.gohtml").Funcs(template.FuncMap{
|
||||
"minifiedCSS": func() template.CSS { return template.CSS(CSS()) },
|
||||
}).Parse(rawHTMLTemplate))
|
||||
|
||||
// Generate the CSP header value once since it's effectively constant.
|
||||
cspValue = strings.Join([]string{
|
||||
`default-src 'none'`,
|
||||
`style-src '` + csp.Hash(minifiedCSS) + `'`,
|
||||
`frame-ancestors 'none'`,
|
||||
}, "; ")
|
||||
)
|
||||
|
||||
// Parse the Go templated HTML and inject functions providing the minified inline CSS and JS.
|
||||
var parsedHTMLTemplate = template.Must(template.New("login_form.gohtml").Funcs(template.FuncMap{
|
||||
"minifiedCSS": func() template.CSS { return template.CSS(CSS()) },
|
||||
}).Parse(rawHTMLTemplate))
|
||||
|
||||
// Generate the CSP header value once since it's effectively constant.
|
||||
var cspValue = strings.Join([]string{
|
||||
`default-src 'none'`,
|
||||
`style-src '` + csp.Hash(minifiedCSS) + `'`,
|
||||
`frame-ancestors 'none'`,
|
||||
}, "; ")
|
||||
|
||||
func panicOnError(s string, err error) string {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
@@ -244,11 +244,12 @@ func FositeOauth2Helper(
|
||||
// passed to a plog function (e.g., plog.Info()).
|
||||
//
|
||||
// Sample usage:
|
||||
// err := someFositeLibraryFunction()
|
||||
// if err != nil {
|
||||
// plog.Info("some error", FositeErrorForLog(err)...)
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// err := someFositeLibraryFunction()
|
||||
// if err != nil {
|
||||
// plog.Info("some error", FositeErrorForLog(err)...)
|
||||
// ...
|
||||
// }
|
||||
func FositeErrorForLog(err error) []interface{} {
|
||||
rfc6749Error := fosite.ErrorToRFC6749Error(err)
|
||||
keysAndValues := make([]interface{}, 0)
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package formposthtml defines HTML templates used by the Supervisor.
|
||||
//nolint: gochecknoglobals // This package uses globals to ensure that all parsing and minifying happens at init.
|
||||
package formposthtml
|
||||
|
||||
import (
|
||||
@@ -15,6 +14,7 @@ import (
|
||||
"go.pinniped.dev/internal/oidc/provider/csp"
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals // This package uses globals to ensure that all parsing and minifying happens at init.
|
||||
var (
|
||||
//go:embed form_post.css
|
||||
rawCSS string
|
||||
@@ -26,24 +26,24 @@ var (
|
||||
|
||||
//go:embed form_post.gohtml
|
||||
rawHTMLTemplate string
|
||||
|
||||
// Parse the Go templated HTML and inject functions providing the minified inline CSS and JS.
|
||||
parsedHTMLTemplate = template.Must(template.New("form_post.gohtml").Funcs(template.FuncMap{
|
||||
"minifiedCSS": func() template.CSS { return template.CSS(minifiedCSS) },
|
||||
"minifiedJS": func() template.JS { return template.JS(minifiedJS) }, //nolint:gosec // This is 100% static input, not attacker-controlled.
|
||||
}).Parse(rawHTMLTemplate))
|
||||
|
||||
// Generate the CSP header value once since it's effectively constant.
|
||||
cspValue = strings.Join([]string{
|
||||
`default-src 'none'`,
|
||||
`script-src '` + csp.Hash(minifiedJS) + `'`,
|
||||
`style-src '` + csp.Hash(minifiedCSS) + `'`,
|
||||
`img-src data:`,
|
||||
`connect-src *`,
|
||||
`frame-ancestors 'none'`,
|
||||
}, "; ")
|
||||
)
|
||||
|
||||
// Parse the Go templated HTML and inject functions providing the minified inline CSS and JS.
|
||||
var parsedHTMLTemplate = template.Must(template.New("form_post.gohtml").Funcs(template.FuncMap{
|
||||
"minifiedCSS": func() template.CSS { return template.CSS(minifiedCSS) },
|
||||
"minifiedJS": func() template.JS { return template.JS(minifiedJS) }, //nolint:gosec // This is 100% static input, not attacker-controlled.
|
||||
}).Parse(rawHTMLTemplate))
|
||||
|
||||
// Generate the CSP header value once since it's effectively constant.
|
||||
var cspValue = strings.Join([]string{
|
||||
`default-src 'none'`,
|
||||
`script-src '` + csp.Hash(minifiedJS) + `'`,
|
||||
`style-src '` + csp.Hash(minifiedCSS) + `'`,
|
||||
`img-src data:`,
|
||||
`connect-src *`,
|
||||
`frame-ancestors 'none'`,
|
||||
}, "; ")
|
||||
|
||||
func panicOnError(s string, err error) string {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"crypto/ecdsa"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
@@ -84,7 +84,7 @@ func TestManager(t *testing.T) {
|
||||
|
||||
// Minimal check to ensure that the right discovery endpoint was called
|
||||
r.Equal(http.StatusOK, recorder.Code)
|
||||
responseBody, err := ioutil.ReadAll(recorder.Body)
|
||||
responseBody, err := io.ReadAll(recorder.Body)
|
||||
r.NoError(err)
|
||||
parsedDiscoveryResult := discovery.Metadata{}
|
||||
err = json.Unmarshal(responseBody, &parsedDiscoveryResult)
|
||||
@@ -105,7 +105,7 @@ func TestManager(t *testing.T) {
|
||||
|
||||
// Minimal check to ensure that the right IDP discovery endpoint was called
|
||||
r.Equal(http.StatusOK, recorder.Code)
|
||||
responseBody, err := ioutil.ReadAll(recorder.Body)
|
||||
responseBody, err := io.ReadAll(recorder.Body)
|
||||
r.NoError(err)
|
||||
r.Equal(
|
||||
fmt.Sprintf(`{"pinniped_identity_providers":[{"name":"%s","type":"%s","flows":%s}]}`+"\n", expectedIDPName, expectedIDPType, expectedFlowsJSON),
|
||||
@@ -230,7 +230,7 @@ func TestManager(t *testing.T) {
|
||||
|
||||
// Minimal check to ensure that the right JWKS endpoint was called
|
||||
r.Equal(http.StatusOK, recorder.Code)
|
||||
responseBody, err := ioutil.ReadAll(recorder.Body)
|
||||
responseBody, err := io.ReadAll(recorder.Body)
|
||||
r.NoError(err)
|
||||
parsedJWKSResult := jose.JSONWebKeySet{}
|
||||
err = json.Unmarshal(responseBody, &parsedJWKSResult)
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
@@ -52,6 +51,7 @@ import (
|
||||
"go.pinniped.dev/internal/here"
|
||||
"go.pinniped.dev/internal/httputil/httperr"
|
||||
"go.pinniped.dev/internal/oidc"
|
||||
"go.pinniped.dev/internal/oidc/clientregistry"
|
||||
"go.pinniped.dev/internal/oidc/jwks"
|
||||
"go.pinniped.dev/internal/oidc/oidcclientvalidator"
|
||||
"go.pinniped.dev/internal/oidc/provider"
|
||||
@@ -119,7 +119,7 @@ var (
|
||||
fositeInvalidPayloadErrorBody = here.Doc(`
|
||||
{
|
||||
"error": "invalid_request",
|
||||
"error_description": "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. The POST body can not be empty."
|
||||
"error_description": "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Unable to parse HTTP body, make sure to send a properly formatted form request body."
|
||||
}
|
||||
`)
|
||||
|
||||
@@ -633,7 +633,7 @@ func TestTokenEndpointAuthcodeExchange(t *testing.T) {
|
||||
name: "payload is not valid form serialization",
|
||||
authcodeExchange: authcodeExchangeInputs{
|
||||
modifyTokenRequest: func(r *http.Request, authCode string) {
|
||||
r.Body = ioutil.NopCloser(strings.NewReader("this newline character is not allowed in a form serialization: \n"))
|
||||
r.Body = io.NopCloser(strings.NewReader("this newline character is not allowed in a form serialization: \n"))
|
||||
},
|
||||
want: tokenEndpointResponseExpectedValues{
|
||||
wantStatus: http.StatusBadRequest,
|
||||
@@ -927,12 +927,13 @@ func TestTokenEndpointTokenExchange(t *testing.T) { // tests for grant_type "urn
|
||||
authcodeExchange authcodeExchangeInputs
|
||||
modifyRequestParams func(t *testing.T, params url.Values)
|
||||
modifyRequestHeaders func(r *http.Request)
|
||||
modifyStorage func(t *testing.T, storage *oidc.KubeStorage, pendingRequest *http.Request)
|
||||
modifyStorage func(t *testing.T, storage *oidc.KubeStorage, secrets v1.SecretInterface, pendingRequest *http.Request)
|
||||
requestedAudience string
|
||||
kubeResources func(t *testing.T, supervisorClient *supervisorfake.Clientset, kubeClient *fake.Clientset)
|
||||
|
||||
wantStatus int
|
||||
wantResponseBodyContains string
|
||||
wantStatus int
|
||||
wantErrorType string
|
||||
wantErrorDescContains string
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
@@ -1038,9 +1039,10 @@ func TestTokenEndpointTokenExchange(t *testing.T) { // tests for grant_type "urn
|
||||
modifyRequestHeaders: func(r *http.Request) {
|
||||
r.SetBasicAuth(dynamicClientID, testutil.PlaintextPassword1)
|
||||
},
|
||||
requestedAudience: "some-workload-cluster",
|
||||
wantStatus: http.StatusBadRequest,
|
||||
wantResponseBodyContains: `The client is not authorized to request a token using this method. The OAuth 2.0 Client is not allowed to use token exchange grant 'urn:ietf:params:oauth:grant-type:token-exchange'.`,
|
||||
requestedAudience: "some-workload-cluster",
|
||||
wantStatus: http.StatusBadRequest,
|
||||
wantErrorType: "unauthorized_client",
|
||||
wantErrorDescContains: `The client is not authorized to request a token using this method. The OAuth 2.0 Client is not allowed to use token exchange grant 'urn:ietf:params:oauth:grant-type:token-exchange'.`,
|
||||
},
|
||||
{
|
||||
name: "dynamic client did not ask for the pinniped:request-audience scope in the original authorization request, so the access token submitted during token exchange lacks the scope",
|
||||
@@ -1067,9 +1069,10 @@ func TestTokenEndpointTokenExchange(t *testing.T) { // tests for grant_type "urn
|
||||
modifyRequestHeaders: func(r *http.Request) {
|
||||
r.SetBasicAuth(dynamicClientID, testutil.PlaintextPassword1)
|
||||
},
|
||||
requestedAudience: "some-workload-cluster",
|
||||
wantStatus: http.StatusForbidden,
|
||||
wantResponseBodyContains: `The resource owner or authorization server denied the request. missing the 'pinniped:request-audience' scope`,
|
||||
requestedAudience: "some-workload-cluster",
|
||||
wantStatus: http.StatusForbidden,
|
||||
wantErrorType: "access_denied",
|
||||
wantErrorDescContains: `The resource owner or authorization server denied the request. Missing the 'pinniped:request-audience' scope.`,
|
||||
},
|
||||
{
|
||||
name: "dynamic client did not ask for the openid scope in the original authorization request, so the access token submitted during token exchange lacks the scope",
|
||||
@@ -1096,9 +1099,10 @@ func TestTokenEndpointTokenExchange(t *testing.T) { // tests for grant_type "urn
|
||||
modifyRequestHeaders: func(r *http.Request) {
|
||||
r.SetBasicAuth(dynamicClientID, testutil.PlaintextPassword1)
|
||||
},
|
||||
requestedAudience: "some-workload-cluster",
|
||||
wantStatus: http.StatusForbidden,
|
||||
wantResponseBodyContains: `The resource owner or authorization server denied the request. missing the 'openid' scope`,
|
||||
requestedAudience: "some-workload-cluster",
|
||||
wantStatus: http.StatusForbidden,
|
||||
wantErrorType: "access_denied",
|
||||
wantErrorDescContains: `The resource owner or authorization server denied the request. Missing the 'openid' scope.`,
|
||||
},
|
||||
{
|
||||
name: "dynamic client did not ask for the username scope in the original authorization request, so the session during token exchange has no username associated with it",
|
||||
@@ -1125,37 +1129,42 @@ func TestTokenEndpointTokenExchange(t *testing.T) { // tests for grant_type "urn
|
||||
modifyRequestHeaders: func(r *http.Request) {
|
||||
r.SetBasicAuth(dynamicClientID, testutil.PlaintextPassword1)
|
||||
},
|
||||
requestedAudience: "some-workload-cluster",
|
||||
wantStatus: http.StatusForbidden,
|
||||
wantResponseBodyContains: `The resource owner or authorization server denied the request. No username found in session. Ensure that the 'username' scope was requested and granted at the authorization endpoint.`,
|
||||
requestedAudience: "some-workload-cluster",
|
||||
wantStatus: http.StatusForbidden,
|
||||
wantErrorType: "access_denied",
|
||||
wantErrorDescContains: `The resource owner or authorization server denied the request. No username found in session. Ensure that the 'username' scope was requested and granted at the authorization endpoint.`,
|
||||
},
|
||||
{
|
||||
name: "missing audience",
|
||||
authcodeExchange: doValidAuthCodeExchange,
|
||||
requestedAudience: "",
|
||||
wantStatus: http.StatusBadRequest,
|
||||
wantResponseBodyContains: "missing audience parameter",
|
||||
name: "missing audience",
|
||||
authcodeExchange: doValidAuthCodeExchange,
|
||||
requestedAudience: "",
|
||||
wantStatus: http.StatusBadRequest,
|
||||
wantErrorType: "invalid_request",
|
||||
wantErrorDescContains: "Missing 'audience' parameter.",
|
||||
},
|
||||
{
|
||||
name: "bad requested audience when it looks like the name of an OIDCClient CR",
|
||||
authcodeExchange: doValidAuthCodeExchange,
|
||||
requestedAudience: "client.oauth.pinniped.dev-some-client-abc123",
|
||||
wantStatus: http.StatusBadRequest,
|
||||
wantResponseBodyContains: "requested audience cannot contain '.pinniped.dev'",
|
||||
name: "bad requested audience when it looks like the name of an OIDCClient CR",
|
||||
authcodeExchange: doValidAuthCodeExchange,
|
||||
requestedAudience: "client.oauth.pinniped.dev-some-client-abc123",
|
||||
wantStatus: http.StatusBadRequest,
|
||||
wantErrorType: "invalid_request",
|
||||
wantErrorDescContains: "requested audience cannot contain '.pinniped.dev'",
|
||||
},
|
||||
{
|
||||
name: "bad requested audience when it contains the substring .pinniped.dev because it is reserved for potential future usage",
|
||||
authcodeExchange: doValidAuthCodeExchange,
|
||||
requestedAudience: "something.pinniped.dev/some_aud",
|
||||
wantStatus: http.StatusBadRequest,
|
||||
wantResponseBodyContains: "requested audience cannot contain '.pinniped.dev'",
|
||||
name: "bad requested audience when it contains the substring .pinniped.dev because it is reserved for potential future usage",
|
||||
authcodeExchange: doValidAuthCodeExchange,
|
||||
requestedAudience: "something.pinniped.dev/some_aud",
|
||||
wantStatus: http.StatusBadRequest,
|
||||
wantErrorType: "invalid_request",
|
||||
wantErrorDescContains: "requested audience cannot contain '.pinniped.dev'",
|
||||
},
|
||||
{
|
||||
name: "bad requested audience when it is the same name as the static public client pinniped-cli",
|
||||
authcodeExchange: doValidAuthCodeExchange,
|
||||
requestedAudience: "pinniped-cli",
|
||||
wantStatus: http.StatusBadRequest,
|
||||
wantResponseBodyContains: "requested audience cannot equal 'pinniped-cli'",
|
||||
name: "bad requested audience when it is the same name as the static public client pinniped-cli",
|
||||
authcodeExchange: doValidAuthCodeExchange,
|
||||
requestedAudience: "pinniped-cli",
|
||||
wantStatus: http.StatusBadRequest,
|
||||
wantErrorType: "invalid_request",
|
||||
wantErrorDescContains: "requested audience cannot equal 'pinniped-cli'",
|
||||
},
|
||||
{
|
||||
name: "missing subject_token",
|
||||
@@ -1164,8 +1173,9 @@ func TestTokenEndpointTokenExchange(t *testing.T) { // tests for grant_type "urn
|
||||
modifyRequestParams: func(t *testing.T, params url.Values) {
|
||||
params.Del("subject_token")
|
||||
},
|
||||
wantStatus: http.StatusBadRequest,
|
||||
wantResponseBodyContains: "missing subject_token parameter",
|
||||
wantStatus: http.StatusBadRequest,
|
||||
wantErrorType: "invalid_request",
|
||||
wantErrorDescContains: "Missing 'subject_token' parameter.",
|
||||
},
|
||||
{
|
||||
name: "wrong subject_token_type",
|
||||
@@ -1174,8 +1184,9 @@ func TestTokenEndpointTokenExchange(t *testing.T) { // tests for grant_type "urn
|
||||
modifyRequestParams: func(t *testing.T, params url.Values) {
|
||||
params.Set("subject_token_type", "invalid")
|
||||
},
|
||||
wantStatus: http.StatusBadRequest,
|
||||
wantResponseBodyContains: `unsupported subject_token_type parameter value`,
|
||||
wantStatus: http.StatusBadRequest,
|
||||
wantErrorType: "invalid_request",
|
||||
wantErrorDescContains: `Unsupported 'subject_token_type' parameter value, must be 'urn:ietf:params:oauth:token-type:access_token'.`,
|
||||
},
|
||||
{
|
||||
name: "wrong requested_token_type",
|
||||
@@ -1184,8 +1195,9 @@ func TestTokenEndpointTokenExchange(t *testing.T) { // tests for grant_type "urn
|
||||
modifyRequestParams: func(t *testing.T, params url.Values) {
|
||||
params.Set("requested_token_type", "invalid")
|
||||
},
|
||||
wantStatus: http.StatusBadRequest,
|
||||
wantResponseBodyContains: `unsupported requested_token_type parameter value`,
|
||||
wantStatus: http.StatusBadRequest,
|
||||
wantErrorType: "invalid_request",
|
||||
wantErrorDescContains: `Unsupported 'requested_token_type' parameter value, must be 'urn:ietf:params:oauth:token-type:jwt'.`,
|
||||
},
|
||||
{
|
||||
name: "unsupported RFC8693 parameter",
|
||||
@@ -1194,8 +1206,9 @@ func TestTokenEndpointTokenExchange(t *testing.T) { // tests for grant_type "urn
|
||||
modifyRequestParams: func(t *testing.T, params url.Values) {
|
||||
params.Set("resource", "some-resource-parameter-value")
|
||||
},
|
||||
wantStatus: http.StatusBadRequest,
|
||||
wantResponseBodyContains: `unsupported parameter resource`,
|
||||
wantStatus: http.StatusBadRequest,
|
||||
wantErrorType: "invalid_request",
|
||||
wantErrorDescContains: `Unsupported parameter 'resource'.`,
|
||||
},
|
||||
{
|
||||
name: "bogus access token",
|
||||
@@ -1204,8 +1217,9 @@ func TestTokenEndpointTokenExchange(t *testing.T) { // tests for grant_type "urn
|
||||
modifyRequestParams: func(t *testing.T, params url.Values) {
|
||||
params.Set("subject_token", "some-bogus-value")
|
||||
},
|
||||
wantStatus: http.StatusBadRequest,
|
||||
wantResponseBodyContains: `Invalid token format`,
|
||||
wantStatus: http.StatusUnauthorized,
|
||||
wantErrorType: "request_unauthorized",
|
||||
wantErrorDescContains: `The request could not be authorized. Invalid 'subject_token' parameter value.`,
|
||||
},
|
||||
{
|
||||
name: "bad client ID",
|
||||
@@ -1214,8 +1228,9 @@ func TestTokenEndpointTokenExchange(t *testing.T) { // tests for grant_type "urn
|
||||
modifyRequestParams: func(t *testing.T, params url.Values) {
|
||||
params.Set("client_id", "some-bogus-value")
|
||||
},
|
||||
wantStatus: http.StatusUnauthorized,
|
||||
wantResponseBodyContains: `Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method).`,
|
||||
wantStatus: http.StatusUnauthorized,
|
||||
wantErrorType: "invalid_client",
|
||||
wantErrorDescContains: `Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method).`,
|
||||
},
|
||||
{
|
||||
name: "dynamic client uses wrong client secret",
|
||||
@@ -1227,9 +1242,10 @@ func TestTokenEndpointTokenExchange(t *testing.T) { // tests for grant_type "urn
|
||||
modifyRequestHeaders: func(r *http.Request) {
|
||||
r.SetBasicAuth(dynamicClientID, "bad client secret")
|
||||
},
|
||||
requestedAudience: "some-workload-cluster",
|
||||
wantStatus: http.StatusUnauthorized,
|
||||
wantResponseBodyContains: `Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method).`,
|
||||
requestedAudience: "some-workload-cluster",
|
||||
wantStatus: http.StatusUnauthorized,
|
||||
wantErrorType: "invalid_client",
|
||||
wantErrorDescContains: `Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method).`,
|
||||
},
|
||||
{
|
||||
name: "dynamic client uses wrong auth method (must use basic auth)",
|
||||
@@ -1243,9 +1259,10 @@ func TestTokenEndpointTokenExchange(t *testing.T) { // tests for grant_type "urn
|
||||
modifyRequestHeaders: func(r *http.Request) {
|
||||
// would usually set the basic auth header here, but we don't for this test case
|
||||
},
|
||||
requestedAudience: "some-workload-cluster",
|
||||
wantStatus: http.StatusUnauthorized,
|
||||
wantResponseBodyContains: `Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client supports client authentication method 'client_secret_basic', but method 'client_secret_post' was requested. You must configure the OAuth 2.0 client's 'token_endpoint_auth_method' value to accept 'client_secret_post'.`,
|
||||
requestedAudience: "some-workload-cluster",
|
||||
wantStatus: http.StatusUnauthorized,
|
||||
wantErrorType: "invalid_client",
|
||||
wantErrorDescContains: `Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). The OAuth 2.0 Client supports client authentication method 'client_secret_basic', but method 'client_secret_post' was requested. You must configure the OAuth 2.0 client's 'token_endpoint_auth_method' value to accept 'client_secret_post'.`,
|
||||
},
|
||||
{
|
||||
name: "different client used between authorize/authcode calls and the call to token exchange",
|
||||
@@ -1257,21 +1274,71 @@ func TestTokenEndpointTokenExchange(t *testing.T) { // tests for grant_type "urn
|
||||
modifyRequestHeaders: func(r *http.Request) {
|
||||
r.SetBasicAuth(dynamicClientID, testutil.PlaintextPassword1) // use dynamic client for token exchange
|
||||
},
|
||||
requestedAudience: "some-workload-cluster",
|
||||
wantStatus: http.StatusBadRequest,
|
||||
wantResponseBodyContains: `The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. The OAuth 2.0 Client ID from this request does not match the one from the authorize request.`,
|
||||
requestedAudience: "some-workload-cluster",
|
||||
wantStatus: http.StatusBadRequest,
|
||||
wantErrorType: "invalid_grant",
|
||||
wantErrorDescContains: `The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. The OAuth 2.0 Client ID from this request does not match the one from the authorize request.`,
|
||||
},
|
||||
{
|
||||
name: "valid access token, but deleted from storage",
|
||||
name: "valid access token, but it was already deleted from storage",
|
||||
authcodeExchange: doValidAuthCodeExchange,
|
||||
requestedAudience: "some-workload-cluster",
|
||||
modifyStorage: func(t *testing.T, storage *oidc.KubeStorage, pendingRequest *http.Request) {
|
||||
modifyStorage: func(t *testing.T, storage *oidc.KubeStorage, secrets v1.SecretInterface, pendingRequest *http.Request) {
|
||||
parts := strings.Split(pendingRequest.Form.Get("subject_token"), ".")
|
||||
require.Len(t, parts, 2)
|
||||
require.NoError(t, storage.DeleteAccessTokenSession(context.Background(), parts[1]))
|
||||
},
|
||||
wantStatus: http.StatusUnauthorized,
|
||||
wantResponseBodyContains: `invalid subject_token`,
|
||||
wantStatus: http.StatusUnauthorized,
|
||||
wantErrorType: "request_unauthorized",
|
||||
wantErrorDescContains: `Invalid 'subject_token' parameter value.`,
|
||||
},
|
||||
{
|
||||
name: "valid access token, but it has already expired",
|
||||
authcodeExchange: doValidAuthCodeExchange,
|
||||
requestedAudience: "some-workload-cluster",
|
||||
modifyStorage: func(t *testing.T, storage *oidc.KubeStorage, secrets v1.SecretInterface, pendingRequest *http.Request) {
|
||||
// The fosite storage APIs don't offer a way to update an access token, so we will instead find the underlying
|
||||
// storage Secret and update it in a more manual way. First get the access token's signature.
|
||||
parts := strings.Split(pendingRequest.Form.Get("subject_token"), ".")
|
||||
require.Len(t, parts, 2)
|
||||
// Find the storage Secret for the access token by using its signature to compute the Secret name.
|
||||
accessTokenSignature := parts[1]
|
||||
accessTokenSecretName := getSecretNameFromSignature(t, accessTokenSignature, "access-token") // "access-token" is the storage type used in the Secret's name
|
||||
accessTokenSecret, err := secrets.Get(context.Background(), accessTokenSecretName, metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
// Parse the session from the storage Secret.
|
||||
savedSessionJSON := accessTokenSecret.Data["pinniped-storage-data"]
|
||||
// Declare the appropriate empty struct, similar to how our kubestorage implementation
|
||||
// of GetAccessTokenSession() does when parsing a session from a storage Secret.
|
||||
accessTokenSession := &accesstoken.Session{
|
||||
Request: &fosite.Request{
|
||||
Client: &clientregistry.Client{},
|
||||
Session: &psession.PinnipedSession{},
|
||||
},
|
||||
}
|
||||
// Parse the session JSON and fill the empty struct with its data.
|
||||
err = json.Unmarshal(savedSessionJSON, accessTokenSession)
|
||||
require.NoError(t, err)
|
||||
// Change the access token's expiration time to be one hour ago, so it will be considered already expired.
|
||||
oneHourAgoInUTC := time.Now().UTC().Add(-1 * time.Hour)
|
||||
accessTokenSession.Request.Session.(*psession.PinnipedSession).Fosite.SetExpiresAt(fosite.AccessToken, oneHourAgoInUTC)
|
||||
// Write the updated session back to the access token's storage Secret.
|
||||
updatedSessionJSON, err := json.Marshal(accessTokenSession)
|
||||
require.NoError(t, err)
|
||||
accessTokenSecret.Data["pinniped-storage-data"] = updatedSessionJSON
|
||||
_, err = secrets.Update(context.Background(), accessTokenSecret, metav1.UpdateOptions{})
|
||||
require.NoError(t, err)
|
||||
// Just to be sure that this test setup is valid, confirm that the code above correctly updated the
|
||||
// access token's expiration time by reading it again, this time performing the read using the
|
||||
// kubestorage API instead of the manual/direct approach used above.
|
||||
session, err := storage.GetAccessTokenSession(context.Background(), accessTokenSignature, nil)
|
||||
require.NoError(t, err)
|
||||
expiresAt := session.GetSession().GetExpiresAt(fosite.AccessToken)
|
||||
require.Equal(t, oneHourAgoInUTC, expiresAt)
|
||||
},
|
||||
wantStatus: http.StatusUnauthorized,
|
||||
wantErrorType: "invalid_token",
|
||||
wantErrorDescContains: `Token expired. Access token expired at `,
|
||||
},
|
||||
{
|
||||
name: "access token missing pinniped:request-audience scope",
|
||||
@@ -1289,9 +1356,10 @@ func TestTokenEndpointTokenExchange(t *testing.T) { // tests for grant_type "urn
|
||||
wantGroups: goodGroups,
|
||||
},
|
||||
},
|
||||
requestedAudience: "some-workload-cluster",
|
||||
wantStatus: http.StatusForbidden,
|
||||
wantResponseBodyContains: `missing the 'pinniped:request-audience' scope`,
|
||||
requestedAudience: "some-workload-cluster",
|
||||
wantStatus: http.StatusForbidden,
|
||||
wantErrorType: "access_denied",
|
||||
wantErrorDescContains: `Missing the 'pinniped:request-audience' scope.`,
|
||||
},
|
||||
{
|
||||
name: "access token missing openid scope",
|
||||
@@ -1309,9 +1377,10 @@ func TestTokenEndpointTokenExchange(t *testing.T) { // tests for grant_type "urn
|
||||
wantGroups: goodGroups,
|
||||
},
|
||||
},
|
||||
requestedAudience: "some-workload-cluster",
|
||||
wantStatus: http.StatusForbidden,
|
||||
wantResponseBodyContains: `missing the 'openid' scope`,
|
||||
requestedAudience: "some-workload-cluster",
|
||||
wantStatus: http.StatusForbidden,
|
||||
wantErrorType: "access_denied",
|
||||
wantErrorDescContains: `Missing the 'openid' scope.`,
|
||||
},
|
||||
{
|
||||
name: "token minting failure",
|
||||
@@ -1323,9 +1392,10 @@ func TestTokenEndpointTokenExchange(t *testing.T) { // tests for grant_type "urn
|
||||
makeOathHelper: makeOauthHelperWithJWTKeyThatWorksOnlyOnce,
|
||||
want: successfulAuthCodeExchange,
|
||||
},
|
||||
requestedAudience: "some-workload-cluster",
|
||||
wantStatus: http.StatusServiceUnavailable,
|
||||
wantResponseBodyContains: `The authorization server is currently unable to handle the request`,
|
||||
requestedAudience: "some-workload-cluster",
|
||||
wantStatus: http.StatusServiceUnavailable,
|
||||
wantErrorType: "temporarily_unavailable",
|
||||
wantErrorDescContains: `The authorization server is currently unable to handle the request`,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
@@ -1341,13 +1411,13 @@ func TestTokenEndpointTokenExchange(t *testing.T) { // tests for grant_type "urn
|
||||
|
||||
request := happyTokenExchangeRequest(test.requestedAudience, parsedAuthcodeExchangeResponseBody["access_token"].(string))
|
||||
if test.modifyStorage != nil {
|
||||
test.modifyStorage(t, storage, request)
|
||||
test.modifyStorage(t, storage, secrets, request)
|
||||
}
|
||||
if test.modifyRequestParams != nil {
|
||||
test.modifyRequestParams(t, request.Form)
|
||||
}
|
||||
|
||||
req := httptest.NewRequest("POST", "/path/shouldn't/matter", body(request.Form).ReadCloser())
|
||||
req := httptest.NewRequest("POST", "/token/exchange/path/shouldn't/matter", body(request.Form).ReadCloser())
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
rsp = httptest.NewRecorder()
|
||||
|
||||
@@ -1369,12 +1439,23 @@ func TestTokenEndpointTokenExchange(t *testing.T) { // tests for grant_type "urn
|
||||
|
||||
require.Equal(t, test.wantStatus, rsp.Code)
|
||||
testutil.RequireEqualContentType(t, rsp.Header().Get("Content-Type"), "application/json")
|
||||
if test.wantResponseBodyContains != "" {
|
||||
require.Contains(t, rsp.Body.String(), test.wantResponseBodyContains)
|
||||
}
|
||||
|
||||
// The remaining assertions apply only to the happy path.
|
||||
var parsedResponseBody map[string]interface{}
|
||||
require.NoError(t, json.Unmarshal(rsp.Body.Bytes(), &parsedResponseBody))
|
||||
|
||||
if rsp.Code != http.StatusOK {
|
||||
// All error responses should have two JSON keys.
|
||||
require.Len(t, parsedResponseBody, 2)
|
||||
|
||||
errorType := parsedResponseBody["error"]
|
||||
require.NotEmpty(t, errorType)
|
||||
require.Equal(t, test.wantErrorType, errorType)
|
||||
|
||||
errorDesc := parsedResponseBody["error_description"]
|
||||
require.NotEmpty(t, errorDesc)
|
||||
require.Contains(t, errorDesc, test.wantErrorDescContains)
|
||||
|
||||
// The remaining assertions apply only to the happy path.
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1384,15 +1465,12 @@ func TestTokenEndpointTokenExchange(t *testing.T) { // tests for grant_type "urn
|
||||
err = firstIDTokenDecoded.UnsafeClaimsWithoutVerification(&claimsOfFirstIDToken)
|
||||
require.NoError(t, err)
|
||||
|
||||
var responseBody map[string]interface{}
|
||||
require.NoError(t, json.Unmarshal(rsp.Body.Bytes(), &responseBody))
|
||||
|
||||
require.Contains(t, responseBody, "access_token")
|
||||
require.Equal(t, "N_A", responseBody["token_type"])
|
||||
require.Equal(t, "urn:ietf:params:oauth:token-type:jwt", responseBody["issued_token_type"])
|
||||
require.Contains(t, parsedResponseBody, "access_token")
|
||||
require.Equal(t, "N_A", parsedResponseBody["token_type"])
|
||||
require.Equal(t, "urn:ietf:params:oauth:token-type:jwt", parsedResponseBody["issued_token_type"])
|
||||
|
||||
// Parse the returned token.
|
||||
parsedJWT, err := jose.ParseSigned(responseBody["access_token"].(string))
|
||||
parsedJWT, err := jose.ParseSigned(parsedResponseBody["access_token"].(string))
|
||||
require.NoError(t, err)
|
||||
var tokenClaims map[string]interface{}
|
||||
require.NoError(t, json.Unmarshal(parsedJWT.UnsafePayloadWithoutVerification(), &tokenClaims))
|
||||
@@ -1674,7 +1752,7 @@ func TestRefreshGrant(t *testing.T) {
|
||||
kubeResources func(t *testing.T, supervisorClient *supervisorfake.Clientset, kubeClient *fake.Clientset)
|
||||
authcodeExchange authcodeExchangeInputs
|
||||
refreshRequest refreshRequestInputs
|
||||
modifyRefreshTokenStorage func(t *testing.T, oauthStore *oidc.KubeStorage, refreshToken string)
|
||||
modifyRefreshTokenStorage func(t *testing.T, oauthStore *oidc.KubeStorage, secrets v1.SecretInterface, refreshToken string)
|
||||
}{
|
||||
{
|
||||
name: "happy path refresh grant with openid scope granted (id token returned)",
|
||||
@@ -2447,6 +2525,55 @@ func TestRefreshGrant(t *testing.T) {
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "when a valid refresh token is sent in the refresh request, but the token has already expired",
|
||||
idps: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(upstreamOIDCIdentityProviderBuilder().Build()),
|
||||
authcodeExchange: happyAuthcodeExchangeInputsForOIDCUpstream,
|
||||
modifyRefreshTokenStorage: func(t *testing.T, oauthStore *oidc.KubeStorage, secrets v1.SecretInterface, refreshToken string) {
|
||||
// The fosite storage APIs don't offer a way to update a refresh token, so we will instead find the underlying
|
||||
// storage Secret and update it in a more manual way. First get the refresh token's signature.
|
||||
refreshTokenSignature := getFositeDataSignature(t, refreshToken)
|
||||
// Find the storage Secret for the refresh token by using its signature to compute the Secret name.
|
||||
refreshTokenSecretName := getSecretNameFromSignature(t, refreshTokenSignature, "refresh-token") // "refresh-token" is the storage type used in the Secret's name
|
||||
refreshTokenSecret, err := secrets.Get(context.Background(), refreshTokenSecretName, metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
// Parse the session from the storage Secret.
|
||||
savedSessionJSON := refreshTokenSecret.Data["pinniped-storage-data"]
|
||||
// Declare the appropriate empty struct, similar to how our kubestorage implementation
|
||||
// of GetRefreshTokenSession() does when parsing a session from a storage Secret.
|
||||
refreshTokenSession := &refreshtoken.Session{
|
||||
Request: &fosite.Request{
|
||||
Client: &clientregistry.Client{},
|
||||
Session: &psession.PinnipedSession{},
|
||||
},
|
||||
}
|
||||
// Parse the session JSON and fill the empty struct with its data.
|
||||
err = json.Unmarshal(savedSessionJSON, refreshTokenSession)
|
||||
require.NoError(t, err)
|
||||
// Change the refresh token's expiration time to be one hour ago, so it will be considered already expired.
|
||||
oneHourAgoInUTC := time.Now().UTC().Add(-1 * time.Hour)
|
||||
refreshTokenSession.Request.Session.(*psession.PinnipedSession).Fosite.SetExpiresAt(fosite.RefreshToken, oneHourAgoInUTC)
|
||||
// Write the updated session back to the refresh token's storage Secret.
|
||||
updatedSessionJSON, err := json.Marshal(refreshTokenSession)
|
||||
require.NoError(t, err)
|
||||
refreshTokenSecret.Data["pinniped-storage-data"] = updatedSessionJSON
|
||||
_, err = secrets.Update(context.Background(), refreshTokenSecret, metav1.UpdateOptions{})
|
||||
require.NoError(t, err)
|
||||
// Just to be sure that this test setup is valid, confirm that the code above correctly updated the
|
||||
// refresh token's expiration time by reading it again, this time performing the read using the
|
||||
// kubestorage API instead of the manual/direct approach used above.
|
||||
session, err := oauthStore.GetRefreshTokenSession(context.Background(), refreshTokenSignature, nil)
|
||||
require.NoError(t, err)
|
||||
expiresAt := session.GetSession().GetExpiresAt(fosite.RefreshToken)
|
||||
require.Equal(t, oneHourAgoInUTC, expiresAt)
|
||||
},
|
||||
refreshRequest: refreshRequestInputs{
|
||||
want: tokenEndpointResponseExpectedValues{
|
||||
wantStatus: http.StatusBadRequest,
|
||||
wantErrorResponseBody: fositeInvalidAuthCodeErrorBody,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "when a bad refresh token is sent in the refresh request",
|
||||
idps: oidctestutil.NewUpstreamIDPListerBuilder().WithOIDC(upstreamOIDCIdentityProviderBuilder().Build()),
|
||||
@@ -3354,7 +3481,7 @@ func TestRefreshGrant(t *testing.T) {
|
||||
URL: ldapUpstreamURL,
|
||||
}),
|
||||
authcodeExchange: happyAuthcodeExchangeInputsForLDAPUpstream,
|
||||
modifyRefreshTokenStorage: func(t *testing.T, oauthStore *oidc.KubeStorage, refreshToken string) {
|
||||
modifyRefreshTokenStorage: func(t *testing.T, oauthStore *oidc.KubeStorage, secrets v1.SecretInterface, refreshToken string) {
|
||||
refreshTokenSignature := getFositeDataSignature(t, refreshToken)
|
||||
firstRequester, err := oauthStore.GetRefreshTokenSession(context.Background(), refreshTokenSignature, nil)
|
||||
require.NoError(t, err)
|
||||
@@ -3391,7 +3518,7 @@ func TestRefreshGrant(t *testing.T) {
|
||||
happyLDAPCustomSessionData,
|
||||
),
|
||||
},
|
||||
modifyRefreshTokenStorage: func(t *testing.T, oauthStore *oidc.KubeStorage, refreshToken string) {
|
||||
modifyRefreshTokenStorage: func(t *testing.T, oauthStore *oidc.KubeStorage, secrets v1.SecretInterface, refreshToken string) {
|
||||
refreshTokenSignature := getFositeDataSignature(t, refreshToken)
|
||||
firstRequester, err := oauthStore.GetRefreshTokenSession(context.Background(), refreshTokenSignature, nil)
|
||||
require.NoError(t, err)
|
||||
@@ -3428,7 +3555,7 @@ func TestRefreshGrant(t *testing.T) {
|
||||
happyLDAPCustomSessionData,
|
||||
),
|
||||
},
|
||||
modifyRefreshTokenStorage: func(t *testing.T, oauthStore *oidc.KubeStorage, refreshToken string) {
|
||||
modifyRefreshTokenStorage: func(t *testing.T, oauthStore *oidc.KubeStorage, secrets v1.SecretInterface, refreshToken string) {
|
||||
refreshTokenSignature := getFositeDataSignature(t, refreshToken)
|
||||
firstRequester, err := oauthStore.GetRefreshTokenSession(context.Background(), refreshTokenSignature, nil)
|
||||
require.NoError(t, err)
|
||||
@@ -3505,7 +3632,7 @@ func TestRefreshGrant(t *testing.T) {
|
||||
URL: ldapUpstreamURL,
|
||||
}),
|
||||
authcodeExchange: happyAuthcodeExchangeInputsForLDAPUpstream,
|
||||
modifyRefreshTokenStorage: func(t *testing.T, oauthStore *oidc.KubeStorage, refreshToken string) {
|
||||
modifyRefreshTokenStorage: func(t *testing.T, oauthStore *oidc.KubeStorage, secrets v1.SecretInterface, refreshToken string) {
|
||||
refreshTokenSignature := getFositeDataSignature(t, refreshToken)
|
||||
firstRequester, err := oauthStore.GetRefreshTokenSession(context.Background(), refreshTokenSignature, nil)
|
||||
require.NoError(t, err)
|
||||
@@ -3560,7 +3687,7 @@ func TestRefreshGrant(t *testing.T) {
|
||||
require.NotEmpty(t, firstRefreshToken)
|
||||
|
||||
if test.modifyRefreshTokenStorage != nil {
|
||||
test.modifyRefreshTokenStorage(t, oauthStore, firstRefreshToken)
|
||||
test.modifyRefreshTokenStorage(t, oauthStore, secrets, firstRefreshToken)
|
||||
}
|
||||
|
||||
reqContextWarningRecorder := &TestWarningRecorder{}
|
||||
@@ -3899,7 +4026,7 @@ func (b body) WithPKCE(verifier string) body {
|
||||
}
|
||||
|
||||
func (b body) ReadCloser() io.ReadCloser {
|
||||
return ioutil.NopCloser(strings.NewReader(url.Values(b).Encode()))
|
||||
return io.NopCloser(strings.NewReader(url.Values(b).Encode()))
|
||||
}
|
||||
|
||||
func (b body) with(param, value string) body {
|
||||
@@ -4573,3 +4700,14 @@ func (t *TestWarningRecorder) AddWarning(agent, text string) {
|
||||
Text: text,
|
||||
})
|
||||
}
|
||||
|
||||
func getSecretNameFromSignature(t *testing.T, signature string, typeLabel string) string {
|
||||
t.Helper()
|
||||
// try to decode base64 signatures to prevent double encoding of binary data
|
||||
signatureBytes, err := base64.RawURLEncoding.DecodeString(signature)
|
||||
require.NoError(t, err)
|
||||
// lower case base32 encoding insures that our secret name is valid per ValidateSecretName in k/k
|
||||
var b32 = base32.StdEncoding.WithPadding(base32.NoPadding)
|
||||
signatureAsValidName := strings.ToLower(b32.EncodeToString(signatureBytes))
|
||||
return fmt.Sprintf("pinniped-storage-%s-%s", typeLabel, signatureAsValidName)
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
tokenTypeAccessToken = "urn:ietf:params:oauth:token-type:access_token" //nolint: gosec
|
||||
tokenTypeJWT = "urn:ietf:params:oauth:token-type:jwt" //nolint: gosec
|
||||
tokenTypeAccessToken = "urn:ietf:params:oauth:token-type:access_token" //nolint:gosec
|
||||
tokenTypeJWT = "urn:ietf:params:oauth:token-type:jwt" //nolint:gosec
|
||||
)
|
||||
|
||||
type stsParams struct {
|
||||
@@ -83,10 +83,10 @@ func (t *TokenExchangeHandler) PopulateTokenEndpointResponse(ctx context.Context
|
||||
|
||||
// Require that the incoming access token has the pinniped:request-audience and OpenID scopes.
|
||||
if !originalRequester.GetGrantedScopes().Has(oidcapi.ScopeRequestAudience) {
|
||||
return errors.WithStack(fosite.ErrAccessDenied.WithHintf("missing the %q scope", oidcapi.ScopeRequestAudience))
|
||||
return errors.WithStack(fosite.ErrAccessDenied.WithHintf("Missing the %q scope.", oidcapi.ScopeRequestAudience))
|
||||
}
|
||||
if !originalRequester.GetGrantedScopes().Has(oidcapi.ScopeOpenID) {
|
||||
return errors.WithStack(fosite.ErrAccessDenied.WithHintf("missing the %q scope", oidcapi.ScopeOpenID))
|
||||
return errors.WithStack(fosite.ErrAccessDenied.WithHintf("Missing the %q scope.", oidcapi.ScopeOpenID))
|
||||
}
|
||||
|
||||
// Check that the stored session meets the minimum requirements for token exchange.
|
||||
@@ -135,19 +135,19 @@ func (t *TokenExchangeHandler) validateParams(params url.Values) (*stsParams, er
|
||||
// Validate some required parameters.
|
||||
result.requestedAudience = params.Get("audience")
|
||||
if result.requestedAudience == "" {
|
||||
return nil, fosite.ErrInvalidRequest.WithHint("missing audience parameter")
|
||||
return nil, fosite.ErrInvalidRequest.WithHint("Missing 'audience' parameter.")
|
||||
}
|
||||
result.subjectAccessToken = params.Get("subject_token")
|
||||
if result.subjectAccessToken == "" {
|
||||
return nil, fosite.ErrInvalidRequest.WithHint("missing subject_token parameter")
|
||||
return nil, fosite.ErrInvalidRequest.WithHint("Missing 'subject_token' parameter.")
|
||||
}
|
||||
|
||||
// Validate some parameters with hardcoded values we support.
|
||||
if params.Get("subject_token_type") != tokenTypeAccessToken {
|
||||
return nil, fosite.ErrInvalidRequest.WithHintf("unsupported subject_token_type parameter value, must be %q", tokenTypeAccessToken)
|
||||
return nil, fosite.ErrInvalidRequest.WithHintf("Unsupported 'subject_token_type' parameter value, must be %q.", tokenTypeAccessToken)
|
||||
}
|
||||
if params.Get("requested_token_type") != tokenTypeJWT {
|
||||
return nil, fosite.ErrInvalidRequest.WithHintf("unsupported requested_token_type parameter value, must be %q", tokenTypeJWT)
|
||||
return nil, fosite.ErrInvalidRequest.WithHintf("Unsupported 'requested_token_type' parameter value, must be %q.", tokenTypeJWT)
|
||||
}
|
||||
|
||||
// Validate that none of these unsupported parameters were sent. These are optional and we do not currently support them.
|
||||
@@ -158,7 +158,7 @@ func (t *TokenExchangeHandler) validateParams(params url.Values) (*stsParams, er
|
||||
"actor_token_type",
|
||||
} {
|
||||
if params.Get(param) != "" {
|
||||
return nil, fosite.ErrInvalidRequest.WithHintf("unsupported parameter %s", param)
|
||||
return nil, fosite.ErrInvalidRequest.WithHintf("Unsupported parameter %q.", param)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,13 +184,16 @@ func (t *TokenExchangeHandler) validateParams(params url.Values) (*stsParams, er
|
||||
}
|
||||
|
||||
func (t *TokenExchangeHandler) validateAccessToken(ctx context.Context, requester fosite.AccessRequester, accessToken string) (fosite.Requester, error) {
|
||||
if err := t.accessTokenStrategy.ValidateAccessToken(ctx, requester, accessToken); err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
// Look up the access token's stored session data.
|
||||
signature := t.accessTokenStrategy.AccessTokenSignature(accessToken)
|
||||
originalRequester, err := t.accessTokenStorage.GetAccessTokenSession(ctx, signature, requester.GetSession())
|
||||
if err != nil {
|
||||
return nil, fosite.ErrRequestUnauthorized.WithWrap(err).WithHint("invalid subject_token")
|
||||
// The access token was not found, or there was some other error while reading it.
|
||||
return nil, fosite.ErrRequestUnauthorized.WithWrap(err).WithHint("Invalid 'subject_token' parameter value.")
|
||||
}
|
||||
// Validate the access token using its stored session data, which includes its expiration time.
|
||||
if err := t.accessTokenStrategy.ValidateAccessToken(ctx, originalRequester, accessToken); err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
return originalRequester, nil
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package ownerref
|
||||
@@ -64,7 +64,7 @@ func New(refObj kubeclient.Object) kubeclient.Middleware {
|
||||
})
|
||||
}
|
||||
|
||||
//nolint: gochecknoglobals
|
||||
//nolint:gochecknoglobals
|
||||
var namespaceGVK = corev1.SchemeGroupVersion.WithKind("Namespace")
|
||||
|
||||
func isNamespace(obj kubeclient.Object) bool {
|
||||
|
||||
@@ -88,7 +88,7 @@ func ValidateAndSetLogLevelAndFormatGlobally(ctx context.Context, spec LogSpec)
|
||||
|
||||
setGlobalLoggers(log, flush)
|
||||
|
||||
// nolint: exhaustive // the switch above is exhaustive for format already
|
||||
//nolint:exhaustive // the switch above is exhaustive for format already
|
||||
switch spec.Format {
|
||||
case FormatCLI:
|
||||
return nil // do not spawn go routines on the CLI to allow the CLI to call this more than once
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package plog
|
||||
@@ -136,7 +136,7 @@ func TestFormat(t *testing.T) {
|
||||
`go.pinniped.dev/internal/plog.TestFormat
|
||||
%s/config_test.go:%d
|
||||
testing.tRunner
|
||||
%s/src/testing/testing.go:1439`,
|
||||
%s/src/testing/testing.go:1446`,
|
||||
wd, startLogLine+2+13+14+11+12+24, runtime.GOROOT(),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
// nolint: gochecknoglobals
|
||||
//nolint:gochecknoglobals
|
||||
var (
|
||||
// note that these globals have no locks on purpose - they are expected to be set at init and then again after config parsing.
|
||||
globalLevel zap.AtomicLevel
|
||||
@@ -26,7 +26,7 @@ var (
|
||||
sinkMap sync.Map
|
||||
)
|
||||
|
||||
// nolint: gochecknoinits
|
||||
//nolint:gochecknoinits
|
||||
func init() {
|
||||
// make sure we always have a functional global logger
|
||||
globalLevel = zap.NewAtomicLevelAt(0) // log at the 0 verbosity level to start with, i.e. the "always" logs
|
||||
|
||||
@@ -44,6 +44,8 @@ func (*REST) New() runtime.Object {
|
||||
return &clientsecretapi.OIDCClientSecretRequest{}
|
||||
}
|
||||
|
||||
func (*REST) Destroy() {}
|
||||
|
||||
func (*REST) NewList() runtime.Object {
|
||||
return &clientsecretapi.OIDCClientSecretRequestList{}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package credentialrequest provides REST functionality for the CredentialRequest resource.
|
||||
@@ -59,6 +59,8 @@ func (*REST) New() runtime.Object {
|
||||
return &loginapi.TokenCredentialRequest{}
|
||||
}
|
||||
|
||||
func (*REST) Destroy() {}
|
||||
|
||||
func (*REST) NewList() runtime.Object {
|
||||
return &loginapi.TokenCredentialRequestList{}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ func TestCreate(t *testing.T) {
|
||||
it.Before(func() {
|
||||
r = require.New(t)
|
||||
ctrl = gomock.NewController(t)
|
||||
logger = testutil.NewTranscriptLogger(t) // nolint: staticcheck // old test with lots of log statements
|
||||
logger = testutil.NewTranscriptLogger(t) //nolint:staticcheck // old test with lots of log statements
|
||||
klog.SetLogger(logr.New(logger)) // this is unfortunately a global logger, so can't run these tests in parallel :(
|
||||
})
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package whoamirequest
|
||||
@@ -45,6 +45,8 @@ func (*REST) New() runtime.Object {
|
||||
return &identityapi.WhoAmIRequest{}
|
||||
}
|
||||
|
||||
func (*REST) Destroy() {}
|
||||
|
||||
func (*REST) NewList() runtime.Object {
|
||||
return &identityapi.WhoAmIRequestList{}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ func (c completedConfig) New() (*PinnipedServer, error) {
|
||||
GenericAPIServer: genericServer,
|
||||
}
|
||||
|
||||
var errs []error //nolint: prealloc
|
||||
var errs []error //nolint:prealloc
|
||||
for _, f := range []func() (schema.GroupVersionResource, rest.Storage){
|
||||
func() (schema.GroupVersionResource, rest.Storage) {
|
||||
clientSecretReqGVR := c.ExtraConfig.ClientSecretSupervisorGroupVersion.WithResource("oidcclientsecretrequests")
|
||||
|
||||
@@ -77,8 +77,9 @@ func startServer(ctx context.Context, shutdown *sync.WaitGroup, l net.Listener,
|
||||
handler = withBootstrapPaths(handler, "/healthz") // only health checks are allowed for bootstrap connections
|
||||
|
||||
server := http.Server{
|
||||
Handler: handler,
|
||||
ConnContext: withBootstrapConnCtx,
|
||||
Handler: handler,
|
||||
ConnContext: withBootstrapConnCtx,
|
||||
ReadHeaderTimeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
shutdown.Add(1)
|
||||
@@ -288,7 +289,7 @@ func prepareControllers(
|
||||
pinnipedClient,
|
||||
pinnipedInformers.IDP().V1alpha1().OIDCIdentityProviders(),
|
||||
secretInformer,
|
||||
plog.Logr(), // nolint: staticcheck // old controller with lots of log statements
|
||||
plog.Logr(), //nolint:staticcheck // old controller with lots of log statements
|
||||
controllerlib.WithInformer,
|
||||
),
|
||||
singletonWorker).
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package fakekubeapi contains a *very* simple httptest.Server that can be used to stand in for
|
||||
// a real Kube API server in tests.
|
||||
//
|
||||
// Usage:
|
||||
// func TestSomething(t *testing.T) {
|
||||
// resources := map[string]kubeclient.Object{
|
||||
// // store preexisting resources here
|
||||
// "/api/v1/namespaces/default/pods/some-pod-name": &corev1.Pod{...},
|
||||
// }
|
||||
// server, restConfig := fakekubeapi.Start(t, resources)
|
||||
// defer server.Close()
|
||||
// client := kubeclient.New(kubeclient.WithConfig(restConfig))
|
||||
// // do stuff with client...
|
||||
// }
|
||||
/*
|
||||
Package fakekubeapi contains a *very* simple httptest.Server that can be used to stand in for
|
||||
a real Kube API server in tests.
|
||||
|
||||
Usage:
|
||||
|
||||
func TestSomething(t *testing.T) {
|
||||
resources := map[string]kubeclient.Object{
|
||||
// store preexisting resources here
|
||||
"/api/v1/namespaces/default/pods/some-pod-name": &corev1.Pod{...},
|
||||
}
|
||||
server, restConfig := fakekubeapi.Start(t, resources)
|
||||
defer server.Close()
|
||||
client := kubeclient.New(kubeclient.WithConfig(restConfig))
|
||||
// do stuff with client...
|
||||
}
|
||||
*/
|
||||
package fakekubeapi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -104,13 +107,13 @@ func decodeObj(r *http.Request) (runtime.Object, error) {
|
||||
return nil, httperr.Wrap(http.StatusUnsupportedMediaType, "could not parse mime type from content-type header", err)
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return nil, httperr.Wrap(http.StatusInternalServerError, "read body", err)
|
||||
}
|
||||
|
||||
var obj runtime.Object
|
||||
var errs []error //nolint: prealloc
|
||||
var errs []error //nolint:prealloc
|
||||
codecsThatWeUseInOurCode := []runtime.NegotiatedSerializer{
|
||||
kubescheme.Codecs,
|
||||
aggregatorclientscheme.Codecs,
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@@ -23,7 +22,7 @@ func (e *ErrorWriter) Write([]byte) (int, error) { return 0, e.ReturnError }
|
||||
|
||||
func WriteStringToTempFile(t *testing.T, filename string, fileBody string) *os.File {
|
||||
t.Helper()
|
||||
f, err := ioutil.TempFile("", filename)
|
||||
f, err := os.CreateTemp("", filename)
|
||||
require.NoError(t, err)
|
||||
deferMe := func() {
|
||||
err := os.Remove(f.Name())
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//go:build !go1.14
|
||||
// +build !go1.14
|
||||
|
||||
package testutil
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//nolint:goimports // not an import
|
||||
//go:build go1.14
|
||||
// +build go1.14
|
||||
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"io/ioutil" //nolint:staticcheck // ioutil is deprecated, but this file is for go1.14
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package testutil
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@@ -33,8 +34,9 @@ func TLSTestServerWithCert(t *testing.T, handler http.HandlerFunc, certificate *
|
||||
c.Certificates = []tls.Certificate{*certificate}
|
||||
|
||||
server := http.Server{
|
||||
TLSConfig: c,
|
||||
Handler: handler,
|
||||
TLSConfig: c,
|
||||
Handler: handler,
|
||||
ReadHeaderTimeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
|
||||
@@ -74,16 +74,16 @@ func TestProviderConfig(t *testing.T) {
|
||||
// Test JWTs generated with https://smallstep.com/docs/cli/crypto/jwt/:
|
||||
|
||||
// step crypto keypair key.pub key.priv --kty RSA --no-password --insecure --force && echo '{"at_hash": "invalid-at-hash"}' | step crypto jwt sign --key key.priv --aud test-client-id --sub test-user --subtle --kid="test-kid" --jti="test-jti"
|
||||
invalidAccessTokenHashIDToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2lkIiwidHlwIjoiSldUIn0.eyJhdF9oYXNoIjoiaW52YWxpZC1hdC1oYXNoIiwiYXVkIjoidGVzdC1jbGllbnQtaWQiLCJpYXQiOjE2MDIyODM3OTEsImp0aSI6InRlc3QtanRpIiwibmJmIjoxNjAyMjgzNzkxLCJzdWIiOiJ0ZXN0LXVzZXIifQ.jryXr4jiwcf79wBLaHpjdclEYHoUFGhvTu95QyA6Hnk9NQ0x1vsWYurtj7a8uKydNPryC_HNZi9QTAE_tRIJjycseog3695-5y4B4EZlqL-a94rdOtffuF2O_lnPbKvoja9EKNrp0kLBCftFRHhLAEwuP0N9E5padZwPpIGK0yE_JqljnYgCySvzsQu7tasR38yaULny13h3mtp2WRHPG5DrLyuBuF8Z01hSgRi5hGcVpgzTwBgV5-eMaSUCUo-ZDkqUsLQI6dVlaikCSKYZRb53HeexH0tB_R9PJJHY7mIr-rS76kkQEx9pLuVnheIH9Oc6zbdYWg-zWMijopA8Pg" //nolint: gosec
|
||||
invalidAccessTokenHashIDToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2lkIiwidHlwIjoiSldUIn0.eyJhdF9oYXNoIjoiaW52YWxpZC1hdC1oYXNoIiwiYXVkIjoidGVzdC1jbGllbnQtaWQiLCJpYXQiOjE2MDIyODM3OTEsImp0aSI6InRlc3QtanRpIiwibmJmIjoxNjAyMjgzNzkxLCJzdWIiOiJ0ZXN0LXVzZXIifQ.jryXr4jiwcf79wBLaHpjdclEYHoUFGhvTu95QyA6Hnk9NQ0x1vsWYurtj7a8uKydNPryC_HNZi9QTAE_tRIJjycseog3695-5y4B4EZlqL-a94rdOtffuF2O_lnPbKvoja9EKNrp0kLBCftFRHhLAEwuP0N9E5padZwPpIGK0yE_JqljnYgCySvzsQu7tasR38yaULny13h3mtp2WRHPG5DrLyuBuF8Z01hSgRi5hGcVpgzTwBgV5-eMaSUCUo-ZDkqUsLQI6dVlaikCSKYZRb53HeexH0tB_R9PJJHY7mIr-rS76kkQEx9pLuVnheIH9Oc6zbdYWg-zWMijopA8Pg" //nolint:gosec
|
||||
|
||||
// step crypto keypair key.pub key.priv --kty RSA --no-password --insecure --force && echo '{"nonce": "invalid-nonce"}' | step crypto jwt sign --key key.priv --aud test-client-id --sub test-user --subtle --kid="test-kid" --jti="test-jti"
|
||||
invalidNonceIDToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2lkIiwidHlwIjoiSldUIn0.eyJhdWQiOiJ0ZXN0LWNsaWVudC1pZCIsImlhdCI6MTYwMjI4Mzc0MSwianRpIjoidGVzdC1qdGkiLCJuYmYiOjE2MDIyODM3NDEsIm5vbmNlIjoiaW52YWxpZC1ub25jZSIsInN1YiI6InRlc3QtdXNlciJ9.PRpq-7j5djaIAkraL-8t8ad9Xm4hM8RW67gyD1VIe0BecWeBFxsTuh3SZVKM9zmcwTgjudsyn8kQOwipDa49IN4PV8FcJA_uUJZi2wiqGJUSTG2K5I89doV_7e0RM1ZYIDDW1G2heKJNW7MbKkX7iEPr7u4MyEzswcPcupbyDA-CQFeL95vgwawoqa6yO94ympTbozqiNfj6Xyw_nHtThQnstjWsJZ9s2mUgppZezZv4HZYTQ7c3e_bzwhWgCzh2CSDJn9_Ra_n_4GcVkpHbsHTP35dFsnf0vactPx6CAu6A1-Apk-BruCktpZ3B4Ercf1UnUOHdGqzQKJtqvB03xQ" //nolint: gosec
|
||||
invalidNonceIDToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2lkIiwidHlwIjoiSldUIn0.eyJhdWQiOiJ0ZXN0LWNsaWVudC1pZCIsImlhdCI6MTYwMjI4Mzc0MSwianRpIjoidGVzdC1qdGkiLCJuYmYiOjE2MDIyODM3NDEsIm5vbmNlIjoiaW52YWxpZC1ub25jZSIsInN1YiI6InRlc3QtdXNlciJ9.PRpq-7j5djaIAkraL-8t8ad9Xm4hM8RW67gyD1VIe0BecWeBFxsTuh3SZVKM9zmcwTgjudsyn8kQOwipDa49IN4PV8FcJA_uUJZi2wiqGJUSTG2K5I89doV_7e0RM1ZYIDDW1G2heKJNW7MbKkX7iEPr7u4MyEzswcPcupbyDA-CQFeL95vgwawoqa6yO94ympTbozqiNfj6Xyw_nHtThQnstjWsJZ9s2mUgppZezZv4HZYTQ7c3e_bzwhWgCzh2CSDJn9_Ra_n_4GcVkpHbsHTP35dFsnf0vactPx6CAu6A1-Apk-BruCktpZ3B4Ercf1UnUOHdGqzQKJtqvB03xQ" //nolint:gosec
|
||||
|
||||
// step crypto keypair key.pub key.priv --kty RSA --no-password --insecure --force && echo '{"foo": "bar", "bat": "baz"}' | step crypto jwt sign --key key.priv --aud test-client-id --sub '' --subtle --kid="test-kid" --jti="test-jti"
|
||||
invalidSubClaim = "eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2lkIiwidHlwIjoiSldUIn0.eyJhdWQiOiJ0ZXN0LWNsaWVudC1pZCIsImJhdCI6ImJheiIsImZvbyI6ImJhciIsImlhdCI6MTYxMDIxOTY5MCwianRpIjoidGVzdC1qdGkiLCJuYmYiOjE2MTAyMTk2OTB9.CXgUarh9A8QByF_ddw0W1Cldl_n1qmry2cZh9U0Avi5sl7hb1y22MadDLQslvnx0NKx6EdbwI-El7QxDy0SzwomJomFL7WNd5gGk-Ilq9O_emaHekbpphZ5kxyudsAGUYGxrg1zysv1k5JPhnLnOUMcE7wa0uPLDWnrlAMzqHvnbjI3lakZ8v4-dfAKUIUGi3ycwuAh9BdpydwAsSNOpGBM55-O8911dqVfZKiFNNUeHYE1qlnbhCz7_ykLrljao0nRBbEf9FXGolCdhIaglt0LtaZvll9T9StIbSpcRaBGuRm8toTezmhmHjU-iCc0jGeVKsp8eTyOuJllqDSS-uw"
|
||||
|
||||
// step crypto keypair key.pub key.priv --kty RSA --no-password --insecure --force && echo '{"foo": "bar", "bat": "baz"}' | step crypto jwt sign --key key.priv --aud test-client-id --sub test-user --subtle --kid="test-kid" --jti="test-jti"
|
||||
validIDToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2lkIiwidHlwIjoiSldUIn0.eyJhdWQiOiJ0ZXN0LWNsaWVudC1pZCIsImJhdCI6ImJheiIsImZvbyI6ImJhciIsImlhdCI6MTYwNjc2ODU5MywianRpIjoidGVzdC1qdGkiLCJuYmYiOjE2MDY3Njg1OTMsInN1YiI6InRlc3QtdXNlciJ9.DuqVZ7pGhHqKz7gNr4j2W1s1N8YrSltktH4wW19L4oD1OE2-O72jAnNj5xdjilsa8l7h9ox-5sMF0Tkh3BdRlHQK9dEtNm9tW-JreUnWJ3LCqUs-LZp4NG7edvq2sH_1Bn7O2_NQV51s8Pl04F60CndjQ4NM-6WkqDQTKyY6vJXU7idvM-6TM2HJZK-Na88cOJ9KIK37tL5DhcbsHVF47Dq8uPZ0KbjNQjJLAIi_1GeQBgc6yJhDUwRY4Xu6S0dtTHA6xTI8oSXoamt4bkViEHfJBp97LZQiNz8mku5pVc0aNwP1p4hMHxRHhLXrJjbh-Hx4YFjxtOnIq9t1mHlD4A" //nolint: gosec
|
||||
validIDToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6InRlc3Qta2lkIiwidHlwIjoiSldUIn0.eyJhdWQiOiJ0ZXN0LWNsaWVudC1pZCIsImJhdCI6ImJheiIsImZvbyI6ImJhciIsImlhdCI6MTYwNjc2ODU5MywianRpIjoidGVzdC1qdGkiLCJuYmYiOjE2MDY3Njg1OTMsInN1YiI6InRlc3QtdXNlciJ9.DuqVZ7pGhHqKz7gNr4j2W1s1N8YrSltktH4wW19L4oD1OE2-O72jAnNj5xdjilsa8l7h9ox-5sMF0Tkh3BdRlHQK9dEtNm9tW-JreUnWJ3LCqUs-LZp4NG7edvq2sH_1Bn7O2_NQV51s8Pl04F60CndjQ4NM-6WkqDQTKyY6vJXU7idvM-6TM2HJZK-Na88cOJ9KIK37tL5DhcbsHVF47Dq8uPZ0KbjNQjJLAIi_1GeQBgc6yJhDUwRY4Xu6S0dtTHA6xTI8oSXoamt4bkViEHfJBp97LZQiNz8mku5pVc0aNwP1p4hMHxRHhLXrJjbh-Hx4YFjxtOnIq9t1mHlD4A" //nolint:gosec
|
||||
)
|
||||
|
||||
t.Run("PasswordCredentialsGrantAndValidateTokens", func(t *testing.T) {
|
||||
@@ -699,7 +699,7 @@ func TestProviderConfig(t *testing.T) {
|
||||
require.Equal(t, tt.wantNumRequests, numRequests,
|
||||
"did not make expected number of requests to revocation endpoint")
|
||||
|
||||
if tt.wantErr != "" || tt.wantErrRegexp != "" { // nolint:nestif
|
||||
if tt.wantErr != "" || tt.wantErrRegexp != "" { //nolint:nestif
|
||||
if tt.wantErr != "" {
|
||||
require.EqualError(t, err, tt.wantErr)
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user