Update all deps to latest where possible, bump Kube deps to v0.23.1

Highlights from this dep bump:

1. Made a copy of the v0.4.0 github.com/go-logr/stdr implementation
   for use in tests.  We must bump this dep as Kube code uses a
   newer version now.  We would have to rewrite hundreds of test log
   assertions without this copy.
2. Use github.com/felixge/httpsnoop to undo the changes made by
   ory/fosite#636 for CLI based login flows.  This is required for
   backwards compatibility with older versions of our CLI.  A
   separate change after this will update the CLI to be more
   flexible (it is purposefully not part of this change to confirm
   that we did not break anything).  For all browser login flows, we
   now redirect using http.StatusSeeOther instead of http.StatusFound.
3. Drop plog.RemoveKlogGlobalFlags as klog no longer mutates global
   process flags
4. Only bump github.com/ory/x to v0.0.297 instead of the latest
   v0.0.321 because v0.0.298+ pulls in a newer version of
   go.opentelemetry.io/otel/semconv which breaks k8s.io/apiserver.
   We should update k8s.io/apiserver to use the newer code.
5. Migrate all code from k8s.io/apimachinery/pkg/util/clock to
   k8s.io/utils/clock and k8s.io/utils/clock/testing
6. Delete testutil.NewDeleteOptionsRecorder and migrate to the new
   kubetesting.NewDeleteActionWithOptions
7. Updated ExpectedAuthorizeCodeSessionJSONFromFuzzing caused by
   fosite's new rotated_secrets OAuth client field.  This new field
   is currently not relevant to us as we have no private clients.

Signed-off-by: Monis Khan <mok@vmware.com>
This commit is contained in:
Monis Khan 2021-12-10 17:22:36 -05:00
parent 69d5951296
commit 9599ffcfb9
No known key found for this signature in database
GPG Key ID: 52C90ADA01B269B8
46 changed files with 1562 additions and 643 deletions

View File

@ -2850,7 +2850,7 @@ func TestGetKubeconfig(t *testing.T) {
}) })
issuerEndpointPtr = &issuerEndpoint issuerEndpointPtr = &issuerEndpoint
testLog := testlogger.New(t) testLog := testlogger.NewLegacy(t) //nolint: staticcheck // old test with lots of log statements
cmd := kubeconfigCommand(kubeconfigDeps{ cmd := kubeconfigCommand(kubeconfigDeps{
getPathToSelf: func() (string, error) { getPathToSelf: func() (string, error) {
if tt.getPathToSelfErr != nil { if tt.getPathToSelfErr != nil {
@ -2876,7 +2876,7 @@ func TestGetKubeconfig(t *testing.T) {
} }
return fake, nil return fake, nil
}, },
log: testLog, log: testLog.Logger,
}) })
require.NotNil(t, cmd) require.NotNil(t, cmd)

View File

@ -358,8 +358,8 @@ func TestLoginOIDCCommand(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
tt := tt tt := tt
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
testLogger := testlogger.New(t) testLogger := testlogger.NewLegacy(t) //nolint: staticcheck // old test with lots of log statements
klog.SetLogger(testLogger) klog.SetLogger(testLogger.Logger)
var ( var (
gotOptions []oidcclient.Option gotOptions []oidcclient.Option
) )

View File

@ -165,8 +165,8 @@ func TestLoginStaticCommand(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
tt := tt tt := tt
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
testLogger := testlogger.New(t) testLogger := testlogger.NewLegacy(t) //nolint: staticcheck // old test with lots of log statements
klog.SetLogger(testLogger) klog.SetLogger(testLogger.Logger)
cmd := staticLoginCommand(staticLoginDeps{ cmd := staticLoginCommand(staticLoginDeps{
lookupEnv: func(s string) (string, bool) { lookupEnv: func(s string) (string, bool) {
v, ok := tt.env[s] v, ok := tt.env[s]

View File

@ -7,8 +7,6 @@ import (
"os" "os"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"go.pinniped.dev/internal/plog"
) )
//nolint: gochecknoglobals //nolint: gochecknoglobals
@ -19,12 +17,6 @@ var rootCmd = &cobra.Command{
SilenceUsage: true, // do not print usage message when commands fail SilenceUsage: true, // do not print usage message when commands fail
} }
//nolint: gochecknoinits
func init() {
// We don't want klog flags showing up in our CLI.
plog.RemoveKlogGlobalFlags()
}
// Execute adds all child commands to the root command and sets flags appropriately. // Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd. // This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() { func Execute() {

136
go.mod
View File

@ -17,153 +17,161 @@ go 1.17
// As long as it does, we probably need to keep this replace directive. // As long as it does, we probably need to keep this replace directive.
replace github.com/oleiade/reflections => github.com/oleiade/reflections v1.0.1 replace github.com/oleiade/reflections => github.com/oleiade/reflections v1.0.1
// bumping github.com/ory/x to higher than v0.0.297 breaks k8s.io/apiserver via go.opentelemetry.io/otel/semconv
// force the use of an old version for now as it seems to allow a newer ory/x without breaking the apiserver lib.
replace (
go.opentelemetry.io/otel => go.opentelemetry.io/otel v0.20.0
go.opentelemetry.io/otel/trace => go.opentelemetry.io/otel/trace v0.20.0
)
require ( require (
github.com/MakeNowJust/heredoc/v2 v2.0.1 github.com/MakeNowJust/heredoc/v2 v2.0.1
github.com/coreos/go-oidc/v3 v3.0.0 github.com/coreos/go-oidc/v3 v3.1.0
github.com/creack/pty v1.1.14 github.com/creack/pty v1.1.17
github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew v1.1.1
github.com/felixge/httpsnoop v1.0.2
github.com/go-ldap/ldap/v3 v3.4.1 github.com/go-ldap/ldap/v3 v3.4.1
github.com/go-logr/logr v0.4.0 github.com/go-logr/logr v1.2.2
github.com/go-logr/stdr v0.4.0 github.com/go-logr/stdr v1.2.2
github.com/gofrs/flock v0.8.1 github.com/gofrs/flock v0.8.1
github.com/golang/mock v1.6.0 github.com/golang/mock v1.6.0
github.com/google/go-cmp v0.5.6 github.com/google/go-cmp v0.5.6
github.com/google/gofuzz v1.2.0 github.com/google/gofuzz v1.2.0
github.com/google/uuid v1.2.0 github.com/google/uuid v1.3.0
github.com/gorilla/securecookie v1.1.1 github.com/gorilla/securecookie v1.1.1
github.com/gorilla/websocket v1.4.2 github.com/gorilla/websocket v1.4.2
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
github.com/ory/fosite v0.40.2 github.com/ory/fosite v0.41.0
github.com/ory/x v0.0.212 github.com/ory/x v0.0.321
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/sclevine/agouti v3.0.0+incompatible github.com/sclevine/agouti v3.0.0+incompatible
github.com/sclevine/spec v1.4.0 github.com/sclevine/spec v1.4.0
github.com/spf13/cobra v1.2.1 github.com/spf13/cobra v1.3.0
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0
github.com/tdewolff/minify/v2 v2.9.21 github.com/tdewolff/minify/v2 v2.9.24
go.uber.org/atomic v1.7.0 go.uber.org/atomic v1.9.0
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 golang.org/x/net v0.0.0-20211216030914-fe4d6282115f
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/term v0.0.0-20210503060354-a79de5458b56 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
golang.org/x/text v0.3.6 golang.org/x/text v0.3.7
gopkg.in/square/go-jose.v2 v2.6.0 gopkg.in/square/go-jose.v2 v2.6.0
k8s.io/api v0.22.2 k8s.io/api v0.23.1
k8s.io/apiextensions-apiserver v0.22.2 k8s.io/apiextensions-apiserver v0.23.1
k8s.io/apimachinery v0.22.2 k8s.io/apimachinery v0.23.1
k8s.io/apiserver v0.22.2 k8s.io/apiserver v0.23.1
k8s.io/client-go v0.22.2 k8s.io/client-go v0.23.1
k8s.io/component-base v0.22.2 k8s.io/component-base v0.23.1
k8s.io/gengo v0.0.0-20210203185629-de9496dff47b k8s.io/gengo v0.0.0-20211129171323-c02415ce4185
k8s.io/klog/v2 v2.10.0 k8s.io/klog/v2 v2.30.0
k8s.io/kube-aggregator v0.22.1 k8s.io/kube-aggregator v0.23.1
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a k8s.io/utils v0.0.0-20211208161948-7d6a63dca704
sigs.k8s.io/yaml v1.2.0 sigs.k8s.io/yaml v1.3.0
) )
require ( require (
cloud.google.com/go v0.81.0 // indirect cloud.google.com/go v0.99.0 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.18 // indirect github.com/Azure/go-autorest/autorest v0.11.18 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.13 // indirect github.com/Azure/go-autorest/autorest/adal v0.9.13 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver v3.5.1+incompatible // indirect github.com/blang/semver v3.5.1+incompatible // indirect
github.com/cespare/xxhash/v2 v2.1.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/coreos/go-oidc v2.1.0+incompatible // indirect github.com/coreos/go-oidc v2.1.0+incompatible // indirect
github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.3.2 // indirect github.com/coreos/go-systemd/v22 v22.3.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/dgraph-io/ristretto v0.1.0 // indirect github.com/dgraph-io/ristretto v0.1.0 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect github.com/dustin/go-humanize v1.0.0 // indirect
github.com/emicklei/go-restful v2.9.5+incompatible // indirect github.com/emicklei/go-restful v2.9.5+incompatible // indirect
github.com/evanphx/json-patch v4.11.0+incompatible // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect
github.com/felixge/httpsnoop v1.0.1 // indirect
github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.1 // indirect github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.5 // indirect github.com/go-openapi/jsonreference v0.19.5 // indirect
github.com/go-openapi/swag v0.19.14 // indirect github.com/go-openapi/swag v0.19.15 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect github.com/golang/glog v1.0.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect github.com/golang/protobuf v1.5.2 // indirect
github.com/googleapis/gnostic v0.5.5 // indirect github.com/googleapis/gnostic v0.5.5 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect
github.com/imdario/mergo v0.3.5 // indirect github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/josharian/intern v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.11 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/magiconair/properties v1.8.5 // indirect github.com/magiconair/properties v1.8.5 // indirect
github.com/mailru/easyjson v0.7.6 // indirect github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/goveralls v0.0.6 // indirect github.com/mattn/goveralls v0.0.11 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/moby/spdystream v0.2.0 // indirect github.com/moby/spdystream v0.2.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/ory/go-acc v0.2.6 // indirect github.com/ory/go-acc v0.2.6 // indirect
github.com/ory/go-convenience v0.1.0 // indirect github.com/ory/go-convenience v0.1.0 // indirect
github.com/ory/viper v1.7.5 // indirect github.com/ory/viper v1.7.5 // indirect
github.com/pborman/uuid v1.2.1 // indirect github.com/pborman/uuid v1.2.1 // indirect
github.com/pelletier/go-toml v1.9.3 // indirect github.com/pelletier/go-toml v1.9.4 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021 // indirect github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021 // indirect
github.com/prometheus/client_golang v1.11.0 // indirect github.com/prometheus/client_golang v1.11.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.26.0 // indirect github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.6.0 // indirect github.com/prometheus/procfs v0.7.3 // indirect
github.com/russross/blackfriday/v2 v2.0.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/spf13/afero v1.6.0 // indirect github.com/spf13/afero v1.6.0 // indirect
github.com/spf13/cast v1.3.2-0.20200723214538-8d17101741c8 // indirect github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/subosito/gotenv v1.2.0 // indirect github.com/subosito/gotenv v1.2.0 // indirect
github.com/tdewolff/parse/v2 v2.5.19 // indirect github.com/tdewolff/parse/v2 v2.5.26 // indirect
go.etcd.io/etcd/api/v3 v3.5.0 // indirect go.etcd.io/etcd/api/v3 v3.5.1 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.0 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.1 // indirect
go.etcd.io/etcd/client/v3 v3.5.0 // indirect go.etcd.io/etcd/client/v3 v3.5.0 // indirect
go.opentelemetry.io/contrib v0.20.0 // indirect go.opentelemetry.io/contrib v0.20.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0 // indirect
go.opentelemetry.io/otel v0.20.0 // indirect go.opentelemetry.io/otel v1.0.1 // indirect
go.opentelemetry.io/otel/exporters/otlp v0.20.0 // indirect go.opentelemetry.io/otel/exporters/otlp v0.20.0 // indirect
go.opentelemetry.io/otel/metric v0.20.0 // indirect go.opentelemetry.io/otel/metric v0.20.0 // indirect
go.opentelemetry.io/otel/sdk v0.20.0 // indirect go.opentelemetry.io/otel/sdk v0.20.0 // indirect
go.opentelemetry.io/otel/sdk/export/metric v0.20.0 // indirect go.opentelemetry.io/otel/sdk/export/metric v0.20.0 // indirect
go.opentelemetry.io/otel/sdk/metric v0.20.0 // indirect go.opentelemetry.io/otel/sdk/metric v0.20.0 // indirect
go.opentelemetry.io/otel/trace v0.20.0 // indirect go.opentelemetry.io/otel/trace v1.0.1 // indirect
go.opentelemetry.io/proto/otlp v0.7.0 // indirect go.opentelemetry.io/proto/otlp v0.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.17.0 // indirect go.uber.org/zap v1.19.0 // indirect
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect golang.org/x/mod v0.5.1 // indirect
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
golang.org/x/tools v0.1.2 // indirect golang.org/x/tools v0.1.8 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/appengine v1.6.7 // indirect google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect
google.golang.org/grpc v1.38.0 // indirect google.golang.org/grpc v1.42.0 // indirect
google.golang.org/protobuf v1.26.0 // indirect google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.62.0 // indirect gopkg.in/ini.v1 v1.66.2 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e // indirect k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.25 // indirect
sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect
) )

1086
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -31,6 +31,7 @@ import (
utilnet "k8s.io/apimachinery/pkg/util/net" utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
auditinternal "k8s.io/apiserver/pkg/apis/audit" auditinternal "k8s.io/apiserver/pkg/apis/audit"
"k8s.io/apiserver/pkg/audit"
"k8s.io/apiserver/pkg/audit/policy" "k8s.io/apiserver/pkg/audit/policy"
"k8s.io/apiserver/pkg/authentication/authenticator" "k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/request/bearertoken" "k8s.io/apiserver/pkg/authentication/request/bearertoken"
@ -211,7 +212,7 @@ func newInternal( //nolint:funlen // yeah, it's kind of long.
} }
// wire up a fake audit backend at the metadata level so we can preserve the original user during nested impersonation // wire up a fake audit backend at the metadata level so we can preserve the original user during nested impersonation
serverConfig.AuditPolicyChecker = policy.FakeChecker(auditinternal.LevelMetadata, nil) serverConfig.AuditPolicyRuleEvaluator = policy.NewFakePolicyRuleEvaluator(auditinternal.LevelMetadata, nil)
serverConfig.AuditBackend = &auditfake.Backend{} serverConfig.AuditBackend = &auditfake.Backend{}
// Probe the API server to figure out if anonymous auth is enabled. // Probe the API server to figure out if anonymous auth is enabled.
@ -511,7 +512,7 @@ func newImpersonationReverseProxyFunc(restConfig *rest.Config) (func(*genericapi
return return
} }
ae := request.AuditEventFrom(r.Context()) ae := audit.AuditEventFrom(r.Context())
if ae == nil { if ae == nil {
plog.Warning("aggregated API server logic did not set audit event but it is always supposed to do so", plog.Warning("aggregated API server logic did not set audit event but it is always supposed to do so",
"url", r.URL.String(), "url", r.URL.String(),

View File

@ -27,6 +27,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/util/httpstream" "k8s.io/apimachinery/pkg/util/httpstream"
auditinternal "k8s.io/apiserver/pkg/apis/audit" auditinternal "k8s.io/apiserver/pkg/apis/audit"
"k8s.io/apiserver/pkg/audit"
"k8s.io/apiserver/pkg/authentication/authenticator" "k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/request/bearertoken" "k8s.io/apiserver/pkg/authentication/request/bearertoken"
"k8s.io/apiserver/pkg/authentication/user" "k8s.io/apiserver/pkg/authentication/user"
@ -712,8 +713,8 @@ func TestImpersonator(t *testing.T) {
http.NotFound(w, r) http.NotFound(w, r)
return return
case "/apis/flowcontrol.apiserver.k8s.io/v1beta1/prioritylevelconfigurations", case "/apis/flowcontrol.apiserver.k8s.io/v1beta2/prioritylevelconfigurations",
"/apis/flowcontrol.apiserver.k8s.io/v1beta1/flowschemas": "/apis/flowcontrol.apiserver.k8s.io/v1beta2/flowschemas":
// ignore requests related to priority and fairness logic // ignore requests related to priority and fairness logic
require.Equal(t, http.MethodGet, r.Method) require.Equal(t, http.MethodGet, r.Method)
http.NotFound(w, r) http.NotFound(w, r)
@ -1125,7 +1126,7 @@ func TestImpersonatorHTTPHandler(t *testing.T) {
Groups: testGroups, Groups: testGroups,
Extra: testExtra, Extra: testExtra,
}, nil, "") }, nil, "")
ctx := request.WithAuditEvent(req.Context(), nil) ctx := audit.WithAuditContext(req.Context(), nil)
req = req.WithContext(ctx) req = req.WithContext(ctx)
return req return req
}(), }(),
@ -1880,7 +1881,7 @@ func newRequest(t *testing.T, h http.Header, userInfo user.Info, event *auditint
if event != nil { if event != nil {
ae = event ae = event
} }
ctx = request.WithAuditEvent(ctx, ae) ctx = audit.WithAuditContext(ctx, &audit.AuditContext{Event: ae})
reqInfo := &request.RequestInfo{ reqInfo := &request.RequestInfo{
IsResourceRequest: false, IsResourceRequest: false,

View File

@ -35,7 +35,6 @@ import (
"go.pinniped.dev/internal/here" "go.pinniped.dev/internal/here"
"go.pinniped.dev/internal/issuer" "go.pinniped.dev/internal/issuer"
"go.pinniped.dev/internal/kubeclient" "go.pinniped.dev/internal/kubeclient"
"go.pinniped.dev/internal/plog"
"go.pinniped.dev/internal/registry/credentialrequest" "go.pinniped.dev/internal/registry/credentialrequest"
) )
@ -96,8 +95,6 @@ func addCommandlineFlagsToCommand(cmd *cobra.Command, app *App) {
"/etc/podinfo", "/etc/podinfo",
"path to Downward API volume mount", "path to Downward API volume mount",
) )
plog.RemoveKlogGlobalFlags()
} }
// Boot the aggregated API server, which will in turn boot the controllers. // Boot the aggregated API server, which will in turn boot the controllers.

View File

@ -251,13 +251,10 @@ func TestExpirerControllerSync(t *testing.T) {
0, 0,
) )
opts := &[]metav1.DeleteOptions{}
trackDeleteClient := testutil.NewDeleteOptionsRecorder(kubeAPIClient, opts)
c := NewCertsExpirerController( c := NewCertsExpirerController(
namespace, namespace,
certsSecretResourceName, certsSecretResourceName,
trackDeleteClient, kubeAPIClient,
kubeInformers.Core().V1().Secrets(), kubeInformers.Core().V1().Secrets(),
controllerlib.WithInformer, controllerlib.WithInformer,
test.renewBefore, test.renewBefore,
@ -281,7 +278,7 @@ func TestExpirerControllerSync(t *testing.T) {
if test.wantDelete { if test.wantDelete {
exActions = append( exActions = append(
exActions, exActions,
kubetesting.NewDeleteAction( kubetesting.NewDeleteActionWithOptions(
schema.GroupVersionResource{ schema.GroupVersionResource{
Group: "", Group: "",
Version: "v1", Version: "v1",
@ -289,18 +286,12 @@ func TestExpirerControllerSync(t *testing.T) {
}, },
namespace, namespace,
name, name,
testutil.NewPreconditions(testUID, testRV),
), ),
) )
} }
acActions := kubeAPIClient.Actions() acActions := kubeAPIClient.Actions()
require.Equal(t, exActions, acActions) require.Equal(t, exActions, acActions)
if test.wantDelete {
require.Len(t, *opts, 1)
require.Equal(t, testutil.NewPreconditions(testUID, testRV), (*opts)[0])
} else {
require.Len(t, *opts, 0)
}
}) })
} }
} }

View File

@ -143,11 +143,11 @@ func TestController(t *testing.T) {
if tt.initialCache != nil { if tt.initialCache != nil {
tt.initialCache(t, cache) tt.initialCache(t, cache)
} }
testLog := testlogger.New(t) testLog := testlogger.NewLegacy(t) //nolint: staticcheck // old test with lots of log statements
webhooks := informers.Authentication().V1alpha1().WebhookAuthenticators() webhooks := informers.Authentication().V1alpha1().WebhookAuthenticators()
jwtAuthenticators := informers.Authentication().V1alpha1().JWTAuthenticators() jwtAuthenticators := informers.Authentication().V1alpha1().JWTAuthenticators()
controller := New(cache, webhooks, jwtAuthenticators, testLog) controller := New(cache, webhooks, jwtAuthenticators, testLog.Logger)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()

View File

@ -318,13 +318,13 @@ func TestController(t *testing.T) {
fakeClient := pinnipedfake.NewSimpleClientset(tt.jwtAuthenticators...) fakeClient := pinnipedfake.NewSimpleClientset(tt.jwtAuthenticators...)
informers := pinnipedinformers.NewSharedInformerFactory(fakeClient, 0) informers := pinnipedinformers.NewSharedInformerFactory(fakeClient, 0)
cache := authncache.New() cache := authncache.New()
testLog := testlogger.New(t) testLog := testlogger.NewLegacy(t) //nolint: staticcheck // old test with lots of log statements
if tt.cache != nil { if tt.cache != nil {
tt.cache(t, cache, tt.wantClose) tt.cache(t, cache, tt.wantClose)
} }
controller := New(cache, informers.Authentication().V1alpha1().JWTAuthenticators(), testLog) controller := New(cache, informers.Authentication().V1alpha1().JWTAuthenticators(), testLog.Logger)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()

View File

@ -88,9 +88,9 @@ func TestController(t *testing.T) {
fakeClient := pinnipedfake.NewSimpleClientset(tt.webhooks...) fakeClient := pinnipedfake.NewSimpleClientset(tt.webhooks...)
informers := pinnipedinformers.NewSharedInformerFactory(fakeClient, 0) informers := pinnipedinformers.NewSharedInformerFactory(fakeClient, 0)
cache := authncache.New() cache := authncache.New()
testLog := testlogger.New(t) testLog := testlogger.NewLegacy(t) //nolint: staticcheck // old test with lots of log statements
controller := New(cache, informers.Authentication().V1alpha1().WebhookAuthenticators(), testLog) controller := New(cache, informers.Authentication().V1alpha1().WebhookAuthenticators(), testLog.Logger)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()

View File

@ -21,7 +21,6 @@ import (
"k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/equality"
k8serrors "k8s.io/apimachinery/pkg/api/errors" k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/clock"
"k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/errors"
utilerrors "k8s.io/apimachinery/pkg/util/errors" utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
@ -31,6 +30,7 @@ import (
corev1informers "k8s.io/client-go/informers/core/v1" corev1informers "k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/utils/clock"
"go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1" "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
pinnipedclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned" pinnipedclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned"

View File

@ -29,12 +29,11 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/clock"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
kubeinformers "k8s.io/client-go/informers" kubeinformers "k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
kubernetesfake "k8s.io/client-go/kubernetes/fake" kubernetesfake "k8s.io/client-go/kubernetes/fake"
coretesting "k8s.io/client-go/testing" coretesting "k8s.io/client-go/testing"
clocktesting "k8s.io/utils/clock/testing"
"go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1" "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
pinnipedfake "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned/fake" pinnipedfake "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned/fake"
@ -95,7 +94,7 @@ func TestImpersonatorConfigControllerOptions(t *testing.T) {
nil, nil,
caSignerName, caSignerName,
nil, nil,
testLog, testLog.Logger,
) )
credIssuerInformerFilter = observableWithInformerOption.GetFilterForInformer(credIssuerInformer) credIssuerInformerFilter = observableWithInformerOption.GetFilterForInformer(credIssuerInformer)
servicesInformerFilter = observableWithInformerOption.GetFilterForInformer(servicesInformer) servicesInformerFilter = observableWithInformerOption.GetFilterForInformer(servicesInformer)
@ -270,8 +269,6 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
var subject controllerlib.Controller var subject controllerlib.Controller
var kubeAPIClient *kubernetesfake.Clientset var kubeAPIClient *kubernetesfake.Clientset
var deleteOptions *[]metav1.DeleteOptions
var deleteOptionsRecorder kubernetes.Interface
var pinnipedAPIClient *pinnipedfake.Clientset var pinnipedAPIClient *pinnipedfake.Clientset
var pinnipedInformerClient *pinnipedfake.Clientset var pinnipedInformerClient *pinnipedfake.Clientset
var pinnipedInformers pinnipedinformers.SharedInformerFactory var pinnipedInformers pinnipedinformers.SharedInformerFactory
@ -550,7 +547,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
subject = NewImpersonatorConfigController( subject = NewImpersonatorConfigController(
installedInNamespace, installedInNamespace,
credentialIssuerResourceName, credentialIssuerResourceName,
deleteOptionsRecorder, kubeAPIClient,
pinnipedAPIClient, pinnipedAPIClient,
pinnipedInformers.Config().V1alpha1().CredentialIssuers(), pinnipedInformers.Config().V1alpha1().CredentialIssuers(),
kubeInformers.Core().V1().Services(), kubeInformers.Core().V1().Services(),
@ -562,11 +559,11 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
tlsSecretName, tlsSecretName,
caSecretName, caSecretName,
labels, labels,
clock.NewFakeClock(frozenNow), clocktesting.NewFakeClock(frozenNow),
impersonatorFunc, impersonatorFunc,
caSignerName, caSignerName,
signingCertProvider, signingCertProvider,
testLog, testLog.Logger,
) )
controllerlib.TestWrap(t, subject, func(syncer controllerlib.Syncer) controllerlib.Syncer { controllerlib.TestWrap(t, subject, func(syncer controllerlib.Syncer) controllerlib.Syncer {
tlsServingCertDynamicCertProvider = syncer.(*impersonatorConfigController).tlsServingCertDynamicCertProvider tlsServingCertDynamicCertProvider = syncer.(*impersonatorConfigController).tlsServingCertDynamicCertProvider
@ -1032,10 +1029,7 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
r.Equal("secrets", deleteAction.GetResource().Resource) r.Equal("secrets", deleteAction.GetResource().Resource)
// validate that we set delete preconditions correctly // validate that we set delete preconditions correctly
r.NotEmpty(*deleteOptions) r.Equal(testutil.NewPreconditions("uid-1234", "rv-5678"), deleteAction.GetDeleteOptions())
for _, opt := range *deleteOptions {
r.Equal(testutil.NewPreconditions("uid-1234", "rv-5678"), opt)
}
} }
var requireCASecretWasCreated = func(action coretesting.Action) []byte { var requireCASecretWasCreated = func(action coretesting.Action) []byte {
@ -1114,8 +1108,6 @@ func TestImpersonatorConfigControllerSync(t *testing.T) {
kubeinformers.WithNamespace(installedInNamespace), kubeinformers.WithNamespace(installedInNamespace),
) )
kubeAPIClient = kubernetesfake.NewSimpleClientset() kubeAPIClient = kubernetesfake.NewSimpleClientset()
deleteOptions = &[]metav1.DeleteOptions{}
deleteOptionsRecorder = testutil.NewDeleteOptionsRecorder(kubeAPIClient, deleteOptions)
pinnipedAPIClient = pinnipedfake.NewSimpleClientset() pinnipedAPIClient = pinnipedfake.NewSimpleClientset()
frozenNow = time.Date(2021, time.March, 2, 7, 42, 0, 0, time.Local) frozenNow = time.Date(2021, time.March, 2, 7, 42, 0, 0, time.Local)
signingCertProvider = dynamiccert.NewCA(name) signingCertProvider = dynamiccert.NewCA(name)

View File

@ -23,13 +23,13 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/cache" "k8s.io/apimachinery/pkg/util/cache"
"k8s.io/apimachinery/pkg/util/clock"
utilerrors "k8s.io/apimachinery/pkg/util/errors" utilerrors "k8s.io/apimachinery/pkg/util/errors"
appsv1informers "k8s.io/client-go/informers/apps/v1" appsv1informers "k8s.io/client-go/informers/apps/v1"
corev1informers "k8s.io/client-go/informers/core/v1" corev1informers "k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/klog/v2/klogr" "k8s.io/klog/v2/klogr"
"k8s.io/utils/clock"
"k8s.io/utils/pointer" "k8s.io/utils/pointer"
configv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1" configv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"

View File

@ -20,11 +20,11 @@ import (
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/cache" "k8s.io/apimachinery/pkg/util/cache"
"k8s.io/apimachinery/pkg/util/clock"
"k8s.io/client-go/informers" "k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
kubefake "k8s.io/client-go/kubernetes/fake" kubefake "k8s.io/client-go/kubernetes/fake"
coretesting "k8s.io/client-go/testing" coretesting "k8s.io/client-go/testing"
clocktesting "k8s.io/utils/clock/testing"
"k8s.io/utils/pointer" "k8s.io/utils/pointer"
configv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1" configv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
@ -1027,17 +1027,14 @@ func TestAgentController(t *testing.T) {
tt.addKubeReactions(kubeClientset) tt.addKubeReactions(kubeClientset)
} }
actualDeleteActionOpts := &[]metav1.DeleteOptions{}
trackDeleteKubeClient := testutil.NewDeleteOptionsRecorder(kubeClientset, actualDeleteActionOpts)
kubeInformers := informers.NewSharedInformerFactory(kubeClientset, 0) kubeInformers := informers.NewSharedInformerFactory(kubeClientset, 0)
log := testlogger.New(t) log := testlogger.NewLegacy(t) //nolint: staticcheck // old test with lots of log statements
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
defer ctrl.Finish() defer ctrl.Finish()
mockExecutor := mocks.NewMockPodCommandExecutor(ctrl) mockExecutor := mocks.NewMockPodCommandExecutor(ctrl)
mockDynamicCert := mocks.NewMockDynamicCertPrivate(ctrl) mockDynamicCert := mocks.NewMockDynamicCertPrivate(ctrl)
fakeClock := clock.NewFakeClock(now) fakeClock := clocktesting.NewFakeClock(now)
execCache := cache.NewExpiringWithClock(fakeClock) execCache := cache.NewExpiringWithClock(fakeClock)
if tt.mocks != nil { if tt.mocks != nil {
tt.mocks(t, mockExecutor.EXPECT(), mockDynamicCert.EXPECT(), execCache) tt.mocks(t, mockExecutor.EXPECT(), mockDynamicCert.EXPECT(), execCache)
@ -1059,7 +1056,7 @@ func TestAgentController(t *testing.T) {
}, },
DiscoveryURLOverride: tt.discoveryURLOverride, DiscoveryURLOverride: tt.discoveryURLOverride,
}, },
&kubeclient.Client{Kubernetes: trackDeleteKubeClient, PinnipedConcierge: conciergeClientset}, &kubeclient.Client{Kubernetes: kubeClientset, PinnipedConcierge: conciergeClientset},
kubeInformers.Core().V1().Pods(), kubeInformers.Core().V1().Pods(),
kubeInformers.Apps().V1().Deployments(), kubeInformers.Apps().V1().Deployments(),
kubeInformers.Core().V1().Pods(), kubeInformers.Core().V1().Pods(),
@ -1069,13 +1066,13 @@ func TestAgentController(t *testing.T) {
mockDynamicCert, mockDynamicCert,
fakeClock, fakeClock,
execCache, execCache,
log, log.Logger,
) )
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
errorMessages := runControllerUntilQuiet(ctx, t, controller, hasDeploymentSynced(trackDeleteKubeClient, kubeInformers), kubeInformers, conciergeInformers) errorMessages := runControllerUntilQuiet(ctx, t, controller, hasDeploymentSynced(kubeClientset, kubeInformers), kubeInformers, conciergeInformers)
actualErrors := deduplicate(errorMessages) actualErrors := deduplicate(errorMessages)
assert.Subsetf(t, actualErrors, tt.wantDistinctErrors, "required error(s) were not found in the actual errors") assert.Subsetf(t, actualErrors, tt.wantDistinctErrors, "required error(s) were not found in the actual errors")
@ -1088,16 +1085,20 @@ func TestAgentController(t *testing.T) {
// Assert on all actions that happened to deployments. // Assert on all actions that happened to deployments.
var actualDeploymentActionVerbs []string var actualDeploymentActionVerbs []string
var actualDeleteActionOpts []metav1.DeleteOptions
for _, a := range kubeClientset.Actions() { for _, a := range kubeClientset.Actions() {
if a.GetResource().Resource == "deployments" && a.GetVerb() != "get" { // ignore gets caused by hasDeploymentSynced if a.GetResource().Resource == "deployments" && a.GetVerb() != "get" { // ignore gets caused by hasDeploymentSynced
actualDeploymentActionVerbs = append(actualDeploymentActionVerbs, a.GetVerb()) actualDeploymentActionVerbs = append(actualDeploymentActionVerbs, a.GetVerb())
if deleteAction, ok := a.(coretesting.DeleteAction); ok {
actualDeleteActionOpts = append(actualDeleteActionOpts, deleteAction.GetDeleteOptions())
}
} }
} }
if tt.wantDeploymentActionVerbs != nil { if tt.wantDeploymentActionVerbs != nil {
assert.Equal(t, tt.wantDeploymentActionVerbs, actualDeploymentActionVerbs) assert.Equal(t, tt.wantDeploymentActionVerbs, actualDeploymentActionVerbs)
} }
if tt.wantDeploymentDeleteActionOpts != nil { if tt.wantDeploymentDeleteActionOpts != nil {
assert.Equal(t, tt.wantDeploymentDeleteActionOpts, *actualDeleteActionOpts) assert.Equal(t, tt.wantDeploymentDeleteActionOpts, actualDeleteActionOpts)
} }
// Assert that the agent deployment is in the expected final state. // Assert that the agent deployment is in the expected final state.

View File

@ -58,12 +58,10 @@ func TestLegacyPodCleanerController(t *testing.T) {
wantDistinctErrors []string wantDistinctErrors []string
wantDistinctLogs []string wantDistinctLogs []string
wantActions []coretesting.Action wantActions []coretesting.Action
wantDeleteOptions []metav1.DeleteOptions
}{ }{
{ {
name: "no pods", name: "no pods",
wantActions: []coretesting.Action{}, wantActions: []coretesting.Action{},
wantDeleteOptions: []metav1.DeleteOptions{},
}, },
{ {
name: "mix of pods", name: "mix of pods",
@ -78,12 +76,9 @@ func TestLegacyPodCleanerController(t *testing.T) {
}, },
wantActions: []coretesting.Action{ // the first delete triggers the informer again, but the second invocation triggers a Not Found wantActions: []coretesting.Action{ // the first delete triggers the informer again, but the second invocation triggers a Not Found
coretesting.NewGetAction(corev1.Resource("pods").WithVersion("v1"), "concierge", legacyAgentPodWithExtraLabel.Name), coretesting.NewGetAction(corev1.Resource("pods").WithVersion("v1"), "concierge", legacyAgentPodWithExtraLabel.Name),
coretesting.NewDeleteAction(corev1.Resource("pods").WithVersion("v1"), "concierge", legacyAgentPodWithExtraLabel.Name), coretesting.NewDeleteActionWithOptions(corev1.Resource("pods").WithVersion("v1"), "concierge", legacyAgentPodWithExtraLabel.Name, testutil.NewPreconditions("3", "4")),
coretesting.NewGetAction(corev1.Resource("pods").WithVersion("v1"), "concierge", legacyAgentPodWithExtraLabel.Name), coretesting.NewGetAction(corev1.Resource("pods").WithVersion("v1"), "concierge", legacyAgentPodWithExtraLabel.Name),
}, },
wantDeleteOptions: []metav1.DeleteOptions{
testutil.NewPreconditions("3", "4"),
},
}, },
{ {
name: "fail to delete", name: "fail to delete",
@ -102,13 +97,9 @@ func TestLegacyPodCleanerController(t *testing.T) {
}, },
wantActions: []coretesting.Action{ wantActions: []coretesting.Action{
coretesting.NewGetAction(corev1.Resource("pods").WithVersion("v1"), "concierge", legacyAgentPodWithExtraLabel.Name), coretesting.NewGetAction(corev1.Resource("pods").WithVersion("v1"), "concierge", legacyAgentPodWithExtraLabel.Name),
coretesting.NewDeleteAction(corev1.Resource("pods").WithVersion("v1"), "concierge", legacyAgentPodWithExtraLabel.Name), coretesting.NewDeleteActionWithOptions(corev1.Resource("pods").WithVersion("v1"), "concierge", legacyAgentPodWithExtraLabel.Name, testutil.NewPreconditions("3", "4")),
coretesting.NewGetAction(corev1.Resource("pods").WithVersion("v1"), "concierge", legacyAgentPodWithExtraLabel.Name), coretesting.NewGetAction(corev1.Resource("pods").WithVersion("v1"), "concierge", legacyAgentPodWithExtraLabel.Name),
coretesting.NewDeleteAction(corev1.Resource("pods").WithVersion("v1"), "concierge", legacyAgentPodWithExtraLabel.Name), coretesting.NewDeleteActionWithOptions(corev1.Resource("pods").WithVersion("v1"), "concierge", legacyAgentPodWithExtraLabel.Name, testutil.NewPreconditions("3", "4")),
},
wantDeleteOptions: []metav1.DeleteOptions{
testutil.NewPreconditions("3", "4"),
testutil.NewPreconditions("3", "4"),
}, },
}, },
{ {
@ -126,10 +117,7 @@ func TestLegacyPodCleanerController(t *testing.T) {
wantDistinctErrors: []string{""}, wantDistinctErrors: []string{""},
wantActions: []coretesting.Action{ wantActions: []coretesting.Action{
coretesting.NewGetAction(corev1.Resource("pods").WithVersion("v1"), "concierge", legacyAgentPodWithExtraLabel.Name), coretesting.NewGetAction(corev1.Resource("pods").WithVersion("v1"), "concierge", legacyAgentPodWithExtraLabel.Name),
coretesting.NewDeleteAction(corev1.Resource("pods").WithVersion("v1"), "concierge", legacyAgentPodWithExtraLabel.Name), coretesting.NewDeleteActionWithOptions(corev1.Resource("pods").WithVersion("v1"), "concierge", legacyAgentPodWithExtraLabel.Name, testutil.NewPreconditions("3", "4")),
},
wantDeleteOptions: []metav1.DeleteOptions{
testutil.NewPreconditions("3", "4"),
}, },
}, },
{ {
@ -148,7 +136,6 @@ func TestLegacyPodCleanerController(t *testing.T) {
wantActions: []coretesting.Action{ wantActions: []coretesting.Action{
coretesting.NewGetAction(corev1.Resource("pods").WithVersion("v1"), "concierge", legacyAgentPodWithExtraLabel.Name), coretesting.NewGetAction(corev1.Resource("pods").WithVersion("v1"), "concierge", legacyAgentPodWithExtraLabel.Name),
}, },
wantDeleteOptions: []metav1.DeleteOptions{},
}, },
} }
for _, tt := range tests { for _, tt := range tests {
@ -161,19 +148,16 @@ func TestLegacyPodCleanerController(t *testing.T) {
tt.addKubeReactions(kubeClientset) tt.addKubeReactions(kubeClientset)
} }
opts := &[]metav1.DeleteOptions{}
trackDeleteClient := testutil.NewDeleteOptionsRecorder(kubeClientset, opts)
kubeInformers := informers.NewSharedInformerFactory(kubeClientset, 0) kubeInformers := informers.NewSharedInformerFactory(kubeClientset, 0)
log := testlogger.New(t) log := testlogger.NewLegacy(t) //nolint: staticcheck // old test with lots of log statements
controller := NewLegacyPodCleanerController( controller := NewLegacyPodCleanerController(
AgentConfig{ AgentConfig{
Namespace: "concierge", Namespace: "concierge",
Labels: map[string]string{"extralabel": "labelvalue"}, Labels: map[string]string{"extralabel": "labelvalue"},
}, },
&kubeclient.Client{Kubernetes: trackDeleteClient}, &kubeclient.Client{Kubernetes: kubeClientset},
kubeInformers.Core().V1().Pods(), kubeInformers.Core().V1().Pods(),
log, log.Logger,
) )
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
@ -183,7 +167,6 @@ func TestLegacyPodCleanerController(t *testing.T) {
assert.Equal(t, tt.wantDistinctErrors, deduplicate(errorMessages), "unexpected errors") assert.Equal(t, tt.wantDistinctErrors, deduplicate(errorMessages), "unexpected errors")
assert.Equal(t, tt.wantDistinctLogs, deduplicate(log.Lines()), "unexpected logs") assert.Equal(t, tt.wantDistinctLogs, deduplicate(log.Lines()), "unexpected logs")
assert.Equal(t, tt.wantActions, kubeClientset.Actions()[2:], "unexpected actions") assert.Equal(t, tt.wantActions, kubeClientset.Actions()[2:], "unexpected actions")
assert.Equal(t, tt.wantDeleteOptions, *opts, "unexpected delete options")
}) })
} }
} }

View File

@ -11,10 +11,10 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/clock"
"k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/client-go/util/retry" "k8s.io/client-go/util/retry"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/utils/clock"
configv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/config/v1alpha1" configv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/config/v1alpha1"
pinnipedclientset "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned" pinnipedclientset "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned"

View File

@ -20,8 +20,8 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/clock"
coretesting "k8s.io/client-go/testing" coretesting "k8s.io/client-go/testing"
clocktesting "k8s.io/utils/clock/testing"
"go.pinniped.dev/generated/latest/apis/supervisor/config/v1alpha1" "go.pinniped.dev/generated/latest/apis/supervisor/config/v1alpha1"
pinnipedfake "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned/fake" pinnipedfake "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned/fake"
@ -116,7 +116,7 @@ func TestSync(t *testing.T) {
// Set this at the last second to allow for injection of server override. // Set this at the last second to allow for injection of server override.
subject = NewFederationDomainWatcherController( subject = NewFederationDomainWatcherController(
providersSetter, providersSetter,
clock.NewFakeClock(frozenNow), clocktesting.NewFakeClock(frozenNow),
pinnipedAPIClient, pinnipedAPIClient,
federationDomainInformers.Config().V1alpha1().FederationDomains(), federationDomainInformers.Config().V1alpha1().FederationDomains(),
controllerlib.WithInformer, controllerlib.WithInformer,

View File

@ -91,7 +91,7 @@ func TestOIDCUpstreamWatcherControllerFilterSecret(t *testing.T) {
nil, nil,
pinnipedInformers.IDP().V1alpha1().OIDCIdentityProviders(), pinnipedInformers.IDP().V1alpha1().OIDCIdentityProviders(),
secretInformer, secretInformer,
testLog, testLog.Logger,
withInformer.WithInformer, withInformer.WithInformer,
) )
@ -1123,7 +1123,7 @@ oidc: issuer did not match the issuer returned by provider, expected "` + testIs
pinnipedInformers := pinnipedinformers.NewSharedInformerFactory(fakePinnipedClient, 0) pinnipedInformers := pinnipedinformers.NewSharedInformerFactory(fakePinnipedClient, 0)
fakeKubeClient := fake.NewSimpleClientset(tt.inputSecrets...) fakeKubeClient := fake.NewSimpleClientset(tt.inputSecrets...)
kubeInformers := informers.NewSharedInformerFactory(fakeKubeClient, 0) kubeInformers := informers.NewSharedInformerFactory(fakeKubeClient, 0)
testLog := testlogger.New(t) testLog := testlogger.NewLegacy(t) //nolint: staticcheck // old test with lots of log statements
cache := provider.NewDynamicUpstreamIDPProvider() cache := provider.NewDynamicUpstreamIDPProvider()
cache.SetOIDCIdentityProviders([]provider.UpstreamOIDCIdentityProviderI{ cache.SetOIDCIdentityProviders([]provider.UpstreamOIDCIdentityProviderI{
&upstreamoidc.ProviderConfig{Name: "initial-entry"}, &upstreamoidc.ProviderConfig{Name: "initial-entry"},
@ -1134,7 +1134,7 @@ oidc: issuer did not match the issuer returned by provider, expected "` + testIs
fakePinnipedClient, fakePinnipedClient,
pinnipedInformers.IDP().V1alpha1().OIDCIdentityProviders(), pinnipedInformers.IDP().V1alpha1().OIDCIdentityProviders(),
kubeInformers.Core().V1().Secrets(), kubeInformers.Core().V1().Secrets(),
testLog, testLog.Logger,
controllerlib.WithInformer, controllerlib.WithInformer,
) )

View File

@ -13,9 +13,10 @@ import (
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/clock"
corev1informers "k8s.io/client-go/informers/core/v1" corev1informers "k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"k8s.io/utils/clock"
clocktesting "k8s.io/utils/clock/testing"
"k8s.io/utils/strings/slices" "k8s.io/utils/strings/slices"
pinnipedcontroller "go.pinniped.dev/internal/controller" pinnipedcontroller "go.pinniped.dev/internal/controller"
@ -88,7 +89,7 @@ func GarbageCollectorController(
func (c *garbageCollectorController) Sync(ctx controllerlib.Context) error { func (c *garbageCollectorController) Sync(ctx controllerlib.Context) error {
// make sure we have a consistent, static meaning for the current time during the sync loop // make sure we have a consistent, static meaning for the current time during the sync loop
frozenClock := clock.NewFakeClock(c.clock.Now()) frozenClock := clocktesting.NewFakeClock(c.clock.Now())
// The Sync method is triggered upon any change to any Secret, which would make this // The Sync method is triggered upon any change to any Secret, which would make this
// controller too chatty, so it rate limits itself to a more reasonable interval. // controller too chatty, so it rate limits itself to a more reasonable interval.

View File

@ -18,11 +18,11 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/clock"
kubeinformers "k8s.io/client-go/informers" kubeinformers "k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
kubernetesfake "k8s.io/client-go/kubernetes/fake" kubernetesfake "k8s.io/client-go/kubernetes/fake"
kubetesting "k8s.io/client-go/testing" kubetesting "k8s.io/client-go/testing"
"k8s.io/utils/clock"
clocktesting "k8s.io/utils/clock/testing"
"go.pinniped.dev/internal/controllerlib" "go.pinniped.dev/internal/controllerlib"
"go.pinniped.dev/internal/fositestorage/accesstoken" "go.pinniped.dev/internal/fositestorage/accesstoken"
@ -127,13 +127,11 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
subject controllerlib.Controller subject controllerlib.Controller
kubeInformerClient *kubernetesfake.Clientset kubeInformerClient *kubernetesfake.Clientset
kubeClient *kubernetesfake.Clientset kubeClient *kubernetesfake.Clientset
deleteOptions *[]metav1.DeleteOptions
deleteOptionsRecorder kubernetes.Interface
kubeInformers kubeinformers.SharedInformerFactory kubeInformers kubeinformers.SharedInformerFactory
cancelContext context.Context cancelContext context.Context
cancelContextCancelFunc context.CancelFunc cancelContextCancelFunc context.CancelFunc
syncContext *controllerlib.Context syncContext *controllerlib.Context
fakeClock *clock.FakeClock fakeClock *clocktesting.FakeClock
frozenNow time.Time frozenNow time.Time
) )
@ -144,7 +142,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
subject = GarbageCollectorController( subject = GarbageCollectorController(
idpCache, idpCache,
fakeClock, fakeClock,
deleteOptionsRecorder, kubeClient,
kubeInformers.Core().V1().Secrets(), kubeInformers.Core().V1().Secrets(),
controllerlib.WithInformer, controllerlib.WithInformer,
) )
@ -172,11 +170,9 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
kubeInformerClient = kubernetesfake.NewSimpleClientset() kubeInformerClient = kubernetesfake.NewSimpleClientset()
kubeClient = kubernetesfake.NewSimpleClientset() kubeClient = kubernetesfake.NewSimpleClientset()
deleteOptions = &[]metav1.DeleteOptions{}
deleteOptionsRecorder = testutil.NewDeleteOptionsRecorder(kubeClient, deleteOptions)
kubeInformers = kubeinformers.NewSharedInformerFactory(kubeInformerClient, 0) kubeInformers = kubeinformers.NewSharedInformerFactory(kubeInformerClient, 0)
frozenNow = time.Now().UTC() frozenNow = time.Now().UTC()
fakeClock = clock.NewFakeClock(frozenNow) fakeClock = clocktesting.NewFakeClock(frozenNow)
unrelatedSecret := &corev1.Secret{ unrelatedSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
@ -252,18 +248,11 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
r.ElementsMatch( r.ElementsMatch(
[]kubetesting.Action{ []kubetesting.Action{
kubetesting.NewDeleteAction(secretsGVR, installedInNamespace, "first expired secret"), kubetesting.NewDeleteActionWithOptions(secretsGVR, installedInNamespace, "first expired secret", testutil.NewPreconditions("uid-123", "rv-456")),
kubetesting.NewDeleteAction(secretsGVR, installedInNamespace, "second expired secret"), kubetesting.NewDeleteActionWithOptions(secretsGVR, installedInNamespace, "second expired secret", testutil.NewPreconditions("uid-789", "rv-555")),
}, },
kubeClient.Actions(), kubeClient.Actions(),
) )
r.ElementsMatch(
[]metav1.DeleteOptions{
testutil.NewPreconditions("uid-123", "rv-456"),
testutil.NewPreconditions("uid-789", "rv-555"),
},
*deleteOptions,
)
list, err := kubeClient.CoreV1().Secrets(installedInNamespace).List(context.Background(), metav1.ListOptions{}) list, err := kubeClient.CoreV1().Secrets(installedInNamespace).List(context.Background(), metav1.ListOptions{})
r.NoError(err) r.NoError(err)
r.Len(list.Items, 2) r.Len(list.Items, 2)
@ -384,18 +373,11 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
// Both authcode session secrets are deleted. // Both authcode session secrets are deleted.
r.ElementsMatch( r.ElementsMatch(
[]kubetesting.Action{ []kubetesting.Action{
kubetesting.NewDeleteAction(secretsGVR, installedInNamespace, "activeOIDCAuthcodeSession"), kubetesting.NewDeleteActionWithOptions(secretsGVR, installedInNamespace, "activeOIDCAuthcodeSession", testutil.NewPreconditions("uid-123", "rv-123")),
kubetesting.NewDeleteAction(secretsGVR, installedInNamespace, "inactiveOIDCAuthcodeSession"), kubetesting.NewDeleteActionWithOptions(secretsGVR, installedInNamespace, "inactiveOIDCAuthcodeSession", testutil.NewPreconditions("uid-456", "rv-456")),
}, },
kubeClient.Actions(), kubeClient.Actions(),
) )
r.ElementsMatch(
[]metav1.DeleteOptions{
testutil.NewPreconditions("uid-123", "rv-123"),
testutil.NewPreconditions("uid-456", "rv-456"),
},
*deleteOptions,
)
}) })
}) })
@ -460,16 +442,10 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
// The invalid authcode session secrets is still deleted because it is expired. // The invalid authcode session secrets is still deleted because it is expired.
r.ElementsMatch( r.ElementsMatch(
[]kubetesting.Action{ []kubetesting.Action{
kubetesting.NewDeleteAction(secretsGVR, installedInNamespace, "invalidOIDCAuthcodeSession"), kubetesting.NewDeleteActionWithOptions(secretsGVR, installedInNamespace, "invalidOIDCAuthcodeSession", testutil.NewPreconditions("uid-123", "rv-123")),
}, },
kubeClient.Actions(), kubeClient.Actions(),
) )
r.ElementsMatch(
[]metav1.DeleteOptions{
testutil.NewPreconditions("uid-123", "rv-123"),
},
*deleteOptions,
)
}) })
}) })
@ -536,16 +512,10 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
// The authcode session secrets is still deleted because it is expired. // The authcode session secrets is still deleted because it is expired.
r.ElementsMatch( r.ElementsMatch(
[]kubetesting.Action{ []kubetesting.Action{
kubetesting.NewDeleteAction(secretsGVR, installedInNamespace, "wrongProviderNameOIDCAuthcodeSession"), kubetesting.NewDeleteActionWithOptions(secretsGVR, installedInNamespace, "wrongProviderNameOIDCAuthcodeSession", testutil.NewPreconditions("uid-123", "rv-123")),
}, },
kubeClient.Actions(), kubeClient.Actions(),
) )
r.ElementsMatch(
[]metav1.DeleteOptions{
testutil.NewPreconditions("uid-123", "rv-123"),
},
*deleteOptions,
)
}) })
}) })
@ -612,16 +582,10 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
// The authcode session secrets is still deleted because it is expired. // The authcode session secrets is still deleted because it is expired.
r.ElementsMatch( r.ElementsMatch(
[]kubetesting.Action{ []kubetesting.Action{
kubetesting.NewDeleteAction(secretsGVR, installedInNamespace, "wrongProviderNameOIDCAuthcodeSession"), kubetesting.NewDeleteActionWithOptions(secretsGVR, installedInNamespace, "wrongProviderNameOIDCAuthcodeSession", testutil.NewPreconditions("uid-123", "rv-123")),
}, },
kubeClient.Actions(), kubeClient.Actions(),
) )
r.ElementsMatch(
[]metav1.DeleteOptions{
testutil.NewPreconditions("uid-123", "rv-123"),
},
*deleteOptions,
)
}) })
}) })
@ -767,16 +731,10 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
// The authcode session secrets is deleted. // The authcode session secrets is deleted.
r.ElementsMatch( r.ElementsMatch(
[]kubetesting.Action{ []kubetesting.Action{
kubetesting.NewDeleteAction(secretsGVR, installedInNamespace, "activeOIDCAuthcodeSession"), kubetesting.NewDeleteActionWithOptions(secretsGVR, installedInNamespace, "activeOIDCAuthcodeSession", testutil.NewPreconditions("uid-123", "rv-123")),
}, },
kubeClient.Actions(), kubeClient.Actions(),
) )
r.ElementsMatch(
[]metav1.DeleteOptions{
testutil.NewPreconditions("uid-123", "rv-123"),
},
*deleteOptions,
)
}) })
}) })
@ -893,18 +851,11 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
// Both session secrets are deleted. // Both session secrets are deleted.
r.ElementsMatch( r.ElementsMatch(
[]kubetesting.Action{ []kubetesting.Action{
kubetesting.NewDeleteAction(secretsGVR, installedInNamespace, "offlineAccessGrantedOIDCAccessTokenSession"), kubetesting.NewDeleteActionWithOptions(secretsGVR, installedInNamespace, "offlineAccessGrantedOIDCAccessTokenSession", testutil.NewPreconditions("uid-123", "rv-123")),
kubetesting.NewDeleteAction(secretsGVR, installedInNamespace, "offlineAccessNotGrantedOIDCAccessTokenSession"), kubetesting.NewDeleteActionWithOptions(secretsGVR, installedInNamespace, "offlineAccessNotGrantedOIDCAccessTokenSession", testutil.NewPreconditions("uid-456", "rv-456")),
}, },
kubeClient.Actions(), kubeClient.Actions(),
) )
r.ElementsMatch(
[]metav1.DeleteOptions{
testutil.NewPreconditions("uid-123", "rv-123"),
testutil.NewPreconditions("uid-456", "rv-456"),
},
*deleteOptions,
)
}) })
}) })
@ -976,16 +927,10 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
// The secret is deleted. // The secret is deleted.
r.ElementsMatch( r.ElementsMatch(
[]kubetesting.Action{ []kubetesting.Action{
kubetesting.NewDeleteAction(secretsGVR, installedInNamespace, "oidcRefreshSession"), kubetesting.NewDeleteActionWithOptions(secretsGVR, installedInNamespace, "oidcRefreshSession", testutil.NewPreconditions("uid-123", "rv-123")),
}, },
kubeClient.Actions(), kubeClient.Actions(),
) )
r.ElementsMatch(
[]metav1.DeleteOptions{
testutil.NewPreconditions("uid-123", "rv-123"),
},
*deleteOptions,
)
}) })
}) })
@ -996,6 +941,8 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "expired secret", Name: "expired secret",
Namespace: installedInNamespace, Namespace: installedInNamespace,
UID: "uid-747",
ResourceVersion: "rv-609",
Annotations: map[string]string{ Annotations: map[string]string{
"storage.pinniped.dev/garbage-collect-after": frozenNow.Add(20 * time.Second).Format(time.RFC3339), "storage.pinniped.dev/garbage-collect-after": frozenNow.Add(20 * time.Second).Format(time.RFC3339),
}, },
@ -1033,7 +980,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
// It should have deleted the expired secret. // It should have deleted the expired secret.
r.ElementsMatch( r.ElementsMatch(
[]kubetesting.Action{ []kubetesting.Action{
kubetesting.NewDeleteAction(secretsGVR, installedInNamespace, "expired secret"), kubetesting.NewDeleteActionWithOptions(secretsGVR, installedInNamespace, "expired secret", testutil.NewPreconditions("uid-747", "rv-609")),
}, },
kubeClient.Actions(), kubeClient.Actions(),
) )
@ -1061,6 +1008,8 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "expired secret", Name: "expired secret",
Namespace: installedInNamespace, Namespace: installedInNamespace,
UID: "uid-748",
ResourceVersion: "rv-608",
Annotations: map[string]string{ Annotations: map[string]string{
"storage.pinniped.dev/garbage-collect-after": frozenNow.Add(-time.Second).Format(time.RFC3339), "storage.pinniped.dev/garbage-collect-after": frozenNow.Add(-time.Second).Format(time.RFC3339),
}, },
@ -1076,7 +1025,7 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
r.ElementsMatch( r.ElementsMatch(
[]kubetesting.Action{ []kubetesting.Action{
kubetesting.NewDeleteAction(secretsGVR, installedInNamespace, "expired secret"), kubetesting.NewDeleteActionWithOptions(secretsGVR, installedInNamespace, "expired secret", testutil.NewPreconditions("uid-748", "rv-608")),
}, },
kubeClient.Actions(), kubeClient.Actions(),
) )
@ -1093,6 +1042,8 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "erroring secret", Name: "erroring secret",
Namespace: installedInNamespace, Namespace: installedInNamespace,
UID: "uid-111",
ResourceVersion: "rv-222",
Annotations: map[string]string{ Annotations: map[string]string{
"storage.pinniped.dev/garbage-collect-after": frozenNow.Add(-time.Second).Format(time.RFC3339), "storage.pinniped.dev/garbage-collect-after": frozenNow.Add(-time.Second).Format(time.RFC3339),
}, },
@ -1110,6 +1061,8 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "expired secret", Name: "expired secret",
Namespace: installedInNamespace, Namespace: installedInNamespace,
UID: "uid-333",
ResourceVersion: "rv-444",
Annotations: map[string]string{ Annotations: map[string]string{
"storage.pinniped.dev/garbage-collect-after": frozenNow.Add(-time.Second).Format(time.RFC3339), "storage.pinniped.dev/garbage-collect-after": frozenNow.Add(-time.Second).Format(time.RFC3339),
}, },
@ -1125,8 +1078,8 @@ func TestGarbageCollectorControllerSync(t *testing.T) {
r.ElementsMatch( r.ElementsMatch(
[]kubetesting.Action{ []kubetesting.Action{
kubetesting.NewDeleteAction(secretsGVR, installedInNamespace, "erroring secret"), kubetesting.NewDeleteActionWithOptions(secretsGVR, installedInNamespace, "erroring secret", testutil.NewPreconditions("uid-111", "rv-222")),
kubetesting.NewDeleteAction(secretsGVR, installedInNamespace, "expired secret"), kubetesting.NewDeleteActionWithOptions(secretsGVR, installedInNamespace, "expired secret", testutil.NewPreconditions("uid-333", "rv-444")),
}, },
kubeClient.Actions(), kubeClient.Actions(),
) )

View File

@ -9,10 +9,10 @@ import (
"fmt" "fmt"
"time" "time"
"k8s.io/apimachinery/pkg/util/clock"
k8sinformers "k8s.io/client-go/informers" k8sinformers "k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"k8s.io/klog/v2/klogr" "k8s.io/klog/v2/klogr"
"k8s.io/utils/clock"
pinnipedclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned" pinnipedclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned"
pinnipedinformers "go.pinniped.dev/generated/latest/client/concierge/informers/externalversions" pinnipedinformers "go.pinniped.dev/generated/latest/client/concierge/informers/externalversions"

View File

@ -18,9 +18,9 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/clock"
"k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/fake"
coretesting "k8s.io/client-go/testing" coretesting "k8s.io/client-go/testing"
clocktesting "k8s.io/utils/clock/testing"
) )
func TestStorage(t *testing.T) { func TestStorage(t *testing.T) {
@ -62,7 +62,7 @@ func TestStorage(t *testing.T) {
name string name string
resource string resource string
mocks func(*testing.T, mocker) mocks func(*testing.T, mocker)
run func(*testing.T, Storage, *clock.FakeClock) error run func(*testing.T, Storage, *clocktesting.FakeClock) error
wantActions []coretesting.Action wantActions []coretesting.Action
wantSecrets []corev1.Secret wantSecrets []corev1.Secret
wantErr string wantErr string
@ -71,7 +71,7 @@ func TestStorage(t *testing.T) {
name: "get non-existent", name: "get non-existent",
resource: "authcode", resource: "authcode",
mocks: nil, mocks: nil,
run: func(t *testing.T, storage Storage, fakeClock *clock.FakeClock) error { run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
_, err := storage.Get(ctx, "not-exists", nil) _, err := storage.Get(ctx, "not-exists", nil)
return err return err
}, },
@ -85,7 +85,7 @@ func TestStorage(t *testing.T) {
name: "delete non-existent", name: "delete non-existent",
resource: "tokens", resource: "tokens",
mocks: nil, mocks: nil,
run: func(t *testing.T, storage Storage, fakeClock *clock.FakeClock) error { run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
return storage.Delete(ctx, "not-a-token") return storage.Delete(ctx, "not-a-token")
}, },
wantActions: []coretesting.Action{ wantActions: []coretesting.Action{
@ -98,7 +98,7 @@ func TestStorage(t *testing.T) {
name: "delete non-existent by label", name: "delete non-existent by label",
resource: "tokens", resource: "tokens",
mocks: nil, mocks: nil,
run: func(t *testing.T, storage Storage, fakeClock *clock.FakeClock) error { run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
return storage.DeleteByLabel(ctx, "additionalLabel", "matching-value") return storage.DeleteByLabel(ctx, "additionalLabel", "matching-value")
}, },
wantActions: []coretesting.Action{ wantActions: []coretesting.Action{
@ -113,7 +113,7 @@ func TestStorage(t *testing.T) {
name: "create and get", name: "create and get",
resource: "access-tokens", resource: "access-tokens",
mocks: nil, mocks: nil,
run: func(t *testing.T, storage Storage, fakeClock *clock.FakeClock) error { run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
signature := hmac.AuthorizeCodeSignature(authorizationCode1) signature := hmac.AuthorizeCodeSignature(authorizationCode1)
require.NotEmpty(t, signature) require.NotEmpty(t, signature)
require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is
@ -177,7 +177,7 @@ func TestStorage(t *testing.T) {
name: "create multiple, each gets the correct lifetime timestamp", name: "create multiple, each gets the correct lifetime timestamp",
resource: "access-tokens", resource: "access-tokens",
mocks: nil, mocks: nil,
run: func(t *testing.T, storage Storage, fakeClock *clock.FakeClock) error { run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
data := &testJSON{Data: "create1"} data := &testJSON{Data: "create1"}
rv1, err := storage.Create(ctx, "sig1", data, nil) rv1, err := storage.Create(ctx, "sig1", data, nil)
require.Empty(t, rv1) // fake client does not set this require.Empty(t, rv1) // fake client does not set this
@ -272,7 +272,7 @@ func TestStorage(t *testing.T) {
name: "create and get with additional labels", name: "create and get with additional labels",
resource: "access-tokens", resource: "access-tokens",
mocks: nil, mocks: nil,
run: func(t *testing.T, storage Storage, fakeClock *clock.FakeClock) error { run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
signature := hmac.AuthorizeCodeSignature(authorizationCode1) signature := hmac.AuthorizeCodeSignature(authorizationCode1)
require.NotEmpty(t, signature) require.NotEmpty(t, signature)
require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is
@ -360,7 +360,7 @@ func TestStorage(t *testing.T) {
}) })
require.NoError(t, err) require.NoError(t, err)
}, },
run: func(t *testing.T, storage Storage, fakeClock *clock.FakeClock) error { run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
signature := hmac.AuthorizeCodeSignature(authorizationCode2) signature := hmac.AuthorizeCodeSignature(authorizationCode2)
require.NotEmpty(t, signature) require.NotEmpty(t, signature)
require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is
@ -429,7 +429,7 @@ func TestStorage(t *testing.T) {
return false, nil, nil // we mutated the secret in place but we do not "handle" it return false, nil, nil // we mutated the secret in place but we do not "handle" it
}) })
}, },
run: func(t *testing.T, storage Storage, fakeClock *clock.FakeClock) error { run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
signature := hmac.AuthorizeCodeSignature(authorizationCode3) signature := hmac.AuthorizeCodeSignature(authorizationCode3)
require.NotEmpty(t, signature) require.NotEmpty(t, signature)
require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is
@ -521,7 +521,7 @@ func TestStorage(t *testing.T) {
}) })
require.NoError(t, err) require.NoError(t, err)
}, },
run: func(t *testing.T, storage Storage, fakeClock *clock.FakeClock) error { run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
signature := hmac.AuthorizeCodeSignature(authorizationCode2) signature := hmac.AuthorizeCodeSignature(authorizationCode2)
require.NotEmpty(t, signature) require.NotEmpty(t, signature)
require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is
@ -615,7 +615,7 @@ func TestStorage(t *testing.T) {
Type: "storage.pinniped.dev/walruses", Type: "storage.pinniped.dev/walruses",
})) }))
}, },
run: func(t *testing.T, storage Storage, fakeClock *clock.FakeClock) error { run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
return storage.DeleteByLabel(ctx, "additionalLabel", "matching-value") return storage.DeleteByLabel(ctx, "additionalLabel", "matching-value")
}, },
wantActions: []coretesting.Action{ wantActions: []coretesting.Action{
@ -696,7 +696,7 @@ func TestStorage(t *testing.T) {
return true, nil, fmt.Errorf("some delete error") return true, nil, fmt.Errorf("some delete error")
}) })
}, },
run: func(t *testing.T, storage Storage, fakeClock *clock.FakeClock) error { run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
return storage.DeleteByLabel(ctx, "additionalLabel", "matching-value") return storage.DeleteByLabel(ctx, "additionalLabel", "matching-value")
}, },
wantActions: []coretesting.Action{ wantActions: []coretesting.Action{
@ -749,7 +749,7 @@ func TestStorage(t *testing.T) {
return true, nil, fmt.Errorf("some listing error") return true, nil, fmt.Errorf("some listing error")
}) })
}, },
run: func(t *testing.T, storage Storage, fakeClock *clock.FakeClock) error { run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
return storage.DeleteByLabel(ctx, "additionalLabel", "matching-value") return storage.DeleteByLabel(ctx, "additionalLabel", "matching-value")
}, },
wantActions: []coretesting.Action{ wantActions: []coretesting.Action{
@ -783,7 +783,7 @@ func TestStorage(t *testing.T) {
}) })
require.NoError(t, err) require.NoError(t, err)
}, },
run: func(t *testing.T, storage Storage, fakeClock *clock.FakeClock) error { run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
signature := hmac.AuthorizeCodeSignature(authorizationCode3) signature := hmac.AuthorizeCodeSignature(authorizationCode3)
require.NotEmpty(t, signature) require.NotEmpty(t, signature)
require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is
@ -847,7 +847,7 @@ func TestStorage(t *testing.T) {
}) })
require.NoError(t, err) require.NoError(t, err)
}, },
run: func(t *testing.T, storage Storage, fakeClock *clock.FakeClock) error { run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
signature := hmac.AuthorizeCodeSignature(authorizationCode3) signature := hmac.AuthorizeCodeSignature(authorizationCode3)
require.NotEmpty(t, signature) require.NotEmpty(t, signature)
require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is
@ -911,7 +911,7 @@ func TestStorage(t *testing.T) {
}) })
require.NoError(t, err) require.NoError(t, err)
}, },
run: func(t *testing.T, storage Storage, fakeClock *clock.FakeClock) error { run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
signature := hmac.AuthorizeCodeSignature(authorizationCode3) signature := hmac.AuthorizeCodeSignature(authorizationCode3)
require.NotEmpty(t, signature) require.NotEmpty(t, signature)
require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is
@ -975,7 +975,7 @@ func TestStorage(t *testing.T) {
}) })
require.NoError(t, err) require.NoError(t, err)
}, },
run: func(t *testing.T, storage Storage, fakeClock *clock.FakeClock) error { run: func(t *testing.T, storage Storage, fakeClock *clocktesting.FakeClock) error {
signature := hmac.AuthorizeCodeSignature(authorizationCode3) signature := hmac.AuthorizeCodeSignature(authorizationCode3)
require.NotEmpty(t, signature) require.NotEmpty(t, signature)
require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is require.NotEmpty(t, validateSecretName(signature, false)) // signature is not valid secret name as-is
@ -1025,7 +1025,7 @@ func TestStorage(t *testing.T) {
tt.mocks(t, client) tt.mocks(t, client)
} }
secrets := client.CoreV1().Secrets(namespace) secrets := client.CoreV1().Secrets(namespace)
fakeClock := clock.NewFakeClock(fakeNow) fakeClock := clocktesting.NewFakeClock(fakeNow)
storage := New(tt.resource, secrets, fakeClock.Now, lifetime) storage := New(tt.resource, secrets, fakeClock.Now, lifetime)
err := tt.run(t, storage, fakeClock) err := tt.run(t, storage, fakeClock)

View File

@ -16,10 +16,10 @@ import (
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/clock"
"k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/fake"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1" corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
coretesting "k8s.io/client-go/testing" coretesting "k8s.io/client-go/testing"
clocktesting "k8s.io/utils/clock/testing"
"go.pinniped.dev/internal/oidc/clientregistry" "go.pinniped.dev/internal/oidc/clientregistry"
"go.pinniped.dev/internal/psession" "go.pinniped.dev/internal/psession"
@ -276,7 +276,7 @@ func TestCreateWithoutRequesterID(t *testing.T) {
func makeTestSubject() (context.Context, *fake.Clientset, corev1client.SecretInterface, RevocationStorage) { func makeTestSubject() (context.Context, *fake.Clientset, corev1client.SecretInterface, RevocationStorage) {
client := fake.NewSimpleClientset() client := fake.NewSimpleClientset()
secrets := client.CoreV1().Secrets(namespace) secrets := client.CoreV1().Secrets(namespace)
return context.Background(), client, secrets, New(secrets, clock.NewFakeClock(fakeNow).Now, lifetime) return context.Background(), client, secrets, New(secrets, clocktesting.NewFakeClock(fakeNow).Now, lifetime)
} }
func TestReadFromSecret(t *testing.T) { func TestReadFromSecret(t *testing.T) {

View File

@ -196,32 +196,38 @@ const ExpectedAuthorizeCodeSessionJSONFromFuzzing = `{
"client": { "client": {
"id": ":NJ¸Ɣ8(黋馛ÄRɴJa¶z", "id": ":NJ¸Ɣ8(黋馛ÄRɴJa¶z",
"client_secret": "UQ==", "client_secret": "UQ==",
"rotated_secrets": [
"Bno=",
"0j8=",
"1c4="
],
"redirect_uris": [ "redirect_uris": [
"ǖ枭kʍ切厦ȳ箦;¥ʊXĝ奨誷傥祩d", "ʊXĝ",
"Ƿ"
],
"grant_types": [
"祩d",
"zŇZ", "zŇZ",
"優蒼ĊɌț訫DŽǽeʀO2ƚ\u0026N" "優蒼ĊɌț訫DŽǽeʀO2ƚ\u0026N"
], ],
"grant_types": [ "response_types": [
"唐W6ɻ橩斚薛ɑƐ" "唐W6ɻ橩斚薛ɑƐ"
], ],
"response_types": [ "scopes": [
"w", "w",
"ǔŭe[u@阽羂ŷ-Ĵ½輢OÅ濲喾H" "ǔŭe[u@阽羂ŷ-Ĵ½輢OÅ濲喾H"
], ],
"scopes": [ "audience": [
"G螩歐湡ƙı唡ɸğƎ\u0026胢輢Ƈĵƚ" "G螩歐湡ƙı唡ɸğƎ\u0026胢輢Ƈĵƚ"
], ],
"audience": [
"ě"
],
"public": false, "public": false,
"jwks_uri": "o*泞羅ʘ Ⱦķ瀊垰7ã\")", "jwks_uri": "潌țjA9;焋Ēƕ",
"jwks": { "jwks": {
"keys": [ "keys": [
{ {
"kty": "OKP", "kty": "OKP",
"crv": "Ed25519", "crv": "Ed25519",
"x": "nK9xgX_iN7u3u_i8YOO7ZRT_WK028Vd_nhtsUu7Eo6E", "x": "LHMZ29A64WecPQSLotS8hfZ2mae0SR17CtPdnMDP7ZI",
"x5u": { "x5u": {
"Scheme": "", "Scheme": "",
"Opaque": "", "Opaque": "",
@ -238,7 +244,24 @@ const ExpectedAuthorizeCodeSessionJSONFromFuzzing = `{
{ {
"kty": "OKP", "kty": "OKP",
"crv": "Ed25519", "crv": "Ed25519",
"x": "UbbswQgzWhfGCRlwQmMp6fw_HoIoqkIaKT-2XN2fuYU", "x": "1PwKrC4qDe8cabzGTdA0NjuMJhAZAw7Bu7Tj9z2Y4pE",
"x5u": {
"Scheme": "",
"Opaque": "",
"User": null,
"Host": "",
"Path": "",
"RawPath": "",
"ForceQuery": false,
"RawQuery": "",
"Fragment": "",
"RawFragment": ""
}
},
{
"kty": "OKP",
"crv": "Ed25519",
"x": "j4b-Vld5buh_2KIpjjaDRJ8OY7l7d6XAumvDtVTT9BI",
"x5u": { "x5u": {
"Scheme": "", "Scheme": "",
"Opaque": "", "Opaque": "",
@ -254,104 +277,107 @@ const ExpectedAuthorizeCodeSessionJSONFromFuzzing = `{
} }
] ]
}, },
"token_endpoint_auth_method": "ƿʥǟȒ伉\u003cx¹T鼓c吏", "token_endpoint_auth_method": "趀Ȁ;hYGe天蹗ĽǙ澅j翕q骽",
"request_uris": [ "request_uris": [
"Ć捘j]=谅ʑɑɮ$Ól4Ȟ", "Ǐ蛓ȿ,JwwƐ\u003c涵ØƉKĵ",
",Q7钎漡臧n" "Ȟú",
"Q7钎漡臧n栀,i"
], ],
"request_object_signing_alg": "3@¡廜+v,淬Ʋ4Dʧ呩锏緍场", "request_object_signing_alg": "廜+v,淬Ʋ4Dʧ呩锏緍场",
"token_endpoint_auth_signing_alg": "(ưƓǴ罷ǹ~]ea胠" "token_endpoint_auth_signing_alg": "ưƓǴ罷ǹ~]ea胠Ĺĩv絹b垇I"
}, },
"scopes": [ "scopes": [
v絹b垇IŕĩǀŻQ'k頂箨J-a", ǀŻQ'k頂箨J-a",
"啶#昏Q遐*\\髎bŸ" "ɓ啶#昏Q遐*\\髎bŸ1慂U"
], ],
"grantedScopes": [ "grantedScopes": [
"慂UFƼĮǡ鑻Z" "ƼĮǡ鑻Z¥篚h°ʣ£ǖ%\"砬ʍ"
], ],
"form": { "form": {
"褾攚ŝlĆ厦駳骪l拁乖¡J¿Ƈ妔": [ "¡": [
"懧¥ɂĵ~Čyʊ恀c\"NJřðȿ/", "Ła卦牟懧¥ɂĵ",
"裢?霃谥vƘ:ƿ/濔Aʉ\u003c", "ɎǛƍdÚ慂+槰蚪i齥篗裢?霃谥vƘ:",
"ȭ$奍囀Dž悷鵱民撲ʓeŘ嬀j¤" "/濔Aʉ\u003cS獾蔀OƭUǦ"
], ],
"诞": [ "民撲ʓeŘ嬀j¤囡莒汗狲N\u003cCq": [
"狲N\u003cCq罉ZPſĝEK郊©l", "5ȏ樛ȧ.mĔ櫓Ǩ療騃Ǐ}ɟ",
"餚LJ/ɷȑ潠[ĝU噤'pX ", "潠[ĝU噤'",
"Y妶ǵ!ȁu狍ɶȳsčɦƦ诱" "ŁȗɉY妶ǵ!ȁ"
],
"褰ʎɰ癟VĎĢ婄磫绒u妔隤ʑƍš駎竪": [
"鱙翑ȲŻ麤ã桒嘞\\摗Ǘū稖咾鎅ǸÖ"
] ]
}, },
"session": { "session": {
"fosite": { "fosite": {
"Claims": { "Claims": {
"JTI": "u妔隤ʑƍš駎竪0ɔ闏À1", "JTI": "褗6巽ēđų蓼tùZ蛆鬣a\"ÙǞ0觢",
"Issuer": "麤ã桒嘞\\摗Ǘū稖咾鎅ǸÖ绝TF", "Issuer": "j¦鲶H股ƲLŋZ-{",
"Subject": "巽ēđų蓼tùZ蛆鬣a\"ÙǞ0觢Û±", "Subject": "ehpƧ",
"Audience": [ "Audience": [
"H股ƲL", "驜Ŗ~ů崧軒q腟u尿宲!"
"肟v\u0026đehpƧ",
"5^驜Ŗ~ů崧軒q腟u尿"
], ],
"Nonce": "ğ", "Nonce": "ǎ^嫯R忑隯ƗƋ*L\u0026",
"ExpiresAt": "2016-11-22T21:33:58.460521133Z", "ExpiresAt": "1989-06-02T14:40:29.613836765Z",
"IssuedAt": "1990-07-25T23:42:07.055978334Z", "IssuedAt": "2052-03-26T02:39:27.882495556Z",
"RequestedAt": "1971-01-30T00:23:36.377684025Z", "RequestedAt": "2038-04-06T10:46:24.698586972Z",
"AuthTime": "2088-11-09T12:09:14.051840239Z", "AuthTime": "2003-01-05T11:30:18.206004879Z",
"AccessTokenHash": "蕖¤'+ʣȍ瓁U4鞀", "AccessTokenHash": "ğǫ\\aȊ4ț髄Al",
"AuthenticationContextClassReference": "ʏÑęN\u003c_z", "AuthenticationContextClassReference": "曓蓳n匟鯘磹*金爃鶴滱ůĮǐ_c3#",
"AuthenticationMethodsReference": "ț髄A", "AuthenticationMethodsReferences": [
"CodeHash": "4磔_袻vÓG-壧丵礴鋈k蟵pAɂʅ", "装ƹýĸŴB岺Ð嫹Sx镯荫őł疂ư墫"
],
"CodeHash": "\u0026鶡",
"Extra": { "Extra": {
"#\u0026PƢ曰l騌蘙螤\\阏Đ镴Ƥm蔻ǭ\\鿞": 1677215584, "rǓ\\BRë_g\"ʎ啴SƇMǃļū": {
"Y\u0026鶡萷ɵ啜s攦Ɩïdnǔ": { "4撎胬龯,t猟i\u0026\u0026Q@ǤǟǗ": [
",t猟i\u0026\u0026Q@ǤǟǗǪ飘ȱF?Ƈ": { 1239190737
"~劰û橸ɽ銐ƭ?}H": null, ],
"癑勦e骲v0H晦XŘO溪V蔓": { "飘ȱF?Ƈ畋": {
"碼Ǫ": false "劰û橸ɽ銐ƭ?}HƟ玈鳚": null,
"骲v0H晦XŘO溪V蔓Ȍ+~ē埅Ȝ": {
"4Ǟ": false
}
} }
}, },
"钻煐ɨəÅDČ{Ȩʦ4撎": [ "鑳绪": 2738428764
3684968178
]
}
} }
}, },
"Headers": { "Headers": {
"Extra": { "Extra": {
"ĊdŘ鸨EJ毕懴řĬń戹": { "d謺錳4帳ŅǃĊ": 663773398,
"诳DT=3骜Ǹ,": { "Ř鸨EJ": {
"\u003e": { "Ǽǟ迍阊v\"豑觳翢砜": [
"ǰ": false 995342744
}, ],
"ɁOƪ穋嶿鳈恱va": null "ȏl鐉诳DT=3骜Ǹ": {
}, "厷ɁOƪ穋嶿鳈恱va|载ǰɱ汶C]ɲ": null,
"豑觳翢砜Fȏl": [ "荤Ý呐ʣ®DžȪǣǎǔ爣縗ɦü": {
927958776 "H :靥湤庤毩fɤȆʪ融ƆuŤn": true
] }
}, }
"埅ȜʁɁ;Bd謺錳4帳Ņ": 388005986 }
} }
}, },
"ExpiresAt": { "ExpiresAt": {
"C]ɲ'=ĸ闒NȢȰ.醋": "1970-07-19T18:03:29.902062193Z", "韁臯氃妪婝rȤ\"h丬鎒ơ娻}ɼƟ": "1970-04-27T04:31:30.902468229Z"
"fɤȆʪ融ƆuŤn": "2064-01-24T20:34:16.593152073Z",
"爣縗ɦüHêQ仏1ő": "2102-03-17T06:24:40.256846902Z"
}, },
"Username": "韁臯氃妪婝rȤ\"h丬鎒ơ娻}ɼƟ", "Username": "髉龳ǽÙ",
"Subject": "闺髉龳ǽÙ龦O亾EW莛8嘶×" "Subject": "\u0026¥潝邎Ȗ莅ŝǔ盕戙鵮碡ʯiŬŽ"
}, },
"custom": { "custom": {
"providerUID": "鵮碡ʯiŬŽ非Ĝ眧Ĭ葜SŦ餧Ĭ倏4", "providerUID": "Ĝ眧Ĭ",
"providerName": "nŐǛ3", "providerName": "ʼn2ƋŢ觛ǂ焺nŐǛ",
"providerType": "闣ʬ橳(ý綃ʃʚƟ覣k眐4Ĉt", "providerType": "ɥ闣ʬ橳(ý綃ʃʚƟ覣k眐4",
"oidc": { "oidc": {
"upstreamRefreshToken": "嵽痊w©Ź榨Q|ôɵt毇妬" "upstreamRefreshToken": "tC嵽痊w"
}, },
"ldap": { "ldap": {
"userDN": "6鉢緋uƴŤȱʀļÂ?墖\u003cƬb獭潜Ʃ饾", "userDN": "Ź榨Q|ôɵt毇妬\u003e6鉢緋",
"extraRefreshAttributes": { "extraRefreshAttributes": {
"ď逳鞪?3)藵睋邔\u0026Ű惫蜀Ģ¡圔": "墀jMʥ", "ď逳鞪?3)藵睋邔\u0026Ű惫蜀Ģ¡圔": "墀jMʥ",
"齁š%OpKȱ藚ɏ¬Ê蒭堜]ȗ韚ʫ": "鷞aŚB碠k9帴ʘ赱" "ƍ蛊ʚ£:設虝27": "b獭潜Ʃ饾k|鬌R蜚蠣麹概",
"藚ɏ¬Ê蒭堜]ȗ韚ʫ": "鷞aŚB碠k9帴ʘ赱"
} }
}, },
"activedirectory": { "activedirectory": {

View File

@ -28,10 +28,10 @@ import (
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/clock"
"k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/fake"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1" corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
kubetesting "k8s.io/client-go/testing" kubetesting "k8s.io/client-go/testing"
clocktesting "k8s.io/utils/clock/testing"
"go.pinniped.dev/internal/fositestorage" "go.pinniped.dev/internal/fositestorage"
"go.pinniped.dev/internal/oidc/clientregistry" "go.pinniped.dev/internal/oidc/clientregistry"
@ -258,7 +258,7 @@ func TestCreateWithWrongRequesterDataTypes(t *testing.T) {
func makeTestSubject() (context.Context, *fake.Clientset, corev1client.SecretInterface, oauth2.AuthorizeCodeStorage) { func makeTestSubject() (context.Context, *fake.Clientset, corev1client.SecretInterface, oauth2.AuthorizeCodeStorage) {
client := fake.NewSimpleClientset() client := fake.NewSimpleClientset()
secrets := client.CoreV1().Secrets(namespace) secrets := client.CoreV1().Secrets(namespace)
return context.Background(), client, secrets, New(secrets, clock.NewFakeClock(fakeNow).Now, lifetime) return context.Background(), client, secrets, New(secrets, clocktesting.NewFakeClock(fakeNow).Now, lifetime)
} }
// TestFuzzAndJSONNewValidEmptyAuthorizeCodeSession asserts that we can correctly round trip our authorize code session. // TestFuzzAndJSONNewValidEmptyAuthorizeCodeSession asserts that we can correctly round trip our authorize code session.
@ -396,7 +396,7 @@ func TestFuzzAndJSONNewValidEmptyAuthorizeCodeSession(t *testing.T) {
// while the fuzzer will panic if AuthorizeRequest changes in a way that cannot be fuzzed, // while the fuzzer will panic if AuthorizeRequest changes in a way that cannot be fuzzed,
// if it adds a new field that can be fuzzed, this check will fail // if it adds a new field that can be fuzzed, this check will fail
// thus if AuthorizeRequest changes, we will detect it here (though we could possibly miss an omitempty field) // thus if AuthorizeRequest changes, we will detect it here (though we could possibly miss an omitempty field)
require.JSONEq(t, ExpectedAuthorizeCodeSessionJSONFromFuzzing, authorizeCodeSessionJSONFromFuzzing) require.JSONEq(t, ExpectedAuthorizeCodeSessionJSONFromFuzzing, authorizeCodeSessionJSONFromFuzzing, "actual:\n%s", authorizeCodeSessionJSONFromFuzzing)
} }
func TestReadFromSecret(t *testing.T) { func TestReadFromSecret(t *testing.T) {

View File

@ -16,10 +16,10 @@ import (
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/clock"
"k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/fake"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1" corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
coretesting "k8s.io/client-go/testing" coretesting "k8s.io/client-go/testing"
clocktesting "k8s.io/utils/clock/testing"
"go.pinniped.dev/internal/oidc/clientregistry" "go.pinniped.dev/internal/oidc/clientregistry"
"go.pinniped.dev/internal/psession" "go.pinniped.dev/internal/psession"
@ -100,7 +100,7 @@ func TestOpenIdConnectStorage(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, request, newRequest) require.Equal(t, request, newRequest)
err = storage.DeleteOpenIDConnectSession(ctx, "fancy-code.fancy-signature") 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) require.NoError(t, err)
testutil.LogActualJSONFromCreateAction(t, client, 0) // makes it easier to update expected values when needed testutil.LogActualJSONFromCreateAction(t, client, 0) // makes it easier to update expected values when needed
@ -200,5 +200,5 @@ func TestAuthcodeHasNoDot(t *testing.T) {
func makeTestSubject() (context.Context, *fake.Clientset, corev1client.SecretInterface, openid.OpenIDConnectRequestStorage) { func makeTestSubject() (context.Context, *fake.Clientset, corev1client.SecretInterface, openid.OpenIDConnectRequestStorage) {
client := fake.NewSimpleClientset() client := fake.NewSimpleClientset()
secrets := client.CoreV1().Secrets(namespace) secrets := client.CoreV1().Secrets(namespace)
return context.Background(), client, secrets, New(secrets, clock.NewFakeClock(fakeNow).Now, lifetime) return context.Background(), client, secrets, New(secrets, clocktesting.NewFakeClock(fakeNow).Now, lifetime)
} }

View File

@ -16,10 +16,10 @@ import (
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/clock"
"k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/fake"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1" corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
coretesting "k8s.io/client-go/testing" coretesting "k8s.io/client-go/testing"
clocktesting "k8s.io/utils/clock/testing"
"go.pinniped.dev/internal/oidc/clientregistry" "go.pinniped.dev/internal/oidc/clientregistry"
"go.pinniped.dev/internal/psession" "go.pinniped.dev/internal/psession"
@ -199,5 +199,5 @@ func TestCreateWithWrongRequesterDataTypes(t *testing.T) {
func makeTestSubject() (context.Context, *fake.Clientset, corev1client.SecretInterface, pkce.PKCERequestStorage) { func makeTestSubject() (context.Context, *fake.Clientset, corev1client.SecretInterface, pkce.PKCERequestStorage) {
client := fake.NewSimpleClientset() client := fake.NewSimpleClientset()
secrets := client.CoreV1().Secrets(namespace) secrets := client.CoreV1().Secrets(namespace)
return context.Background(), client, secrets, New(secrets, clock.NewFakeClock(fakeNow).Now, lifetime) return context.Background(), client, secrets, New(secrets, clocktesting.NewFakeClock(fakeNow).Now, lifetime)
} }

View File

@ -16,10 +16,10 @@ import (
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/clock"
"k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/fake"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1" corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
coretesting "k8s.io/client-go/testing" coretesting "k8s.io/client-go/testing"
clocktesting "k8s.io/utils/clock/testing"
"go.pinniped.dev/internal/oidc/clientregistry" "go.pinniped.dev/internal/oidc/clientregistry"
"go.pinniped.dev/internal/psession" "go.pinniped.dev/internal/psession"
@ -276,7 +276,7 @@ func TestCreateWithoutRequesterID(t *testing.T) {
func makeTestSubject() (context.Context, *fake.Clientset, corev1client.SecretInterface, RevocationStorage) { func makeTestSubject() (context.Context, *fake.Clientset, corev1client.SecretInterface, RevocationStorage) {
client := fake.NewSimpleClientset() client := fake.NewSimpleClientset()
secrets := client.CoreV1().Secrets(namespace) secrets := client.CoreV1().Secrets(namespace)
return context.Background(), client, secrets, New(secrets, clock.NewFakeClock(fakeNow).Now, lifetime) return context.Background(), client, secrets, New(secrets, clocktesting.NewFakeClock(fakeNow).Now, lifetime)
} }
func TestReadFromSecret(t *testing.T) { func TestReadFromSecret(t *testing.T) {

View File

@ -10,6 +10,7 @@ import (
"time" "time"
coreosoidc "github.com/coreos/go-oidc/v3/oidc" coreosoidc "github.com/coreos/go-oidc/v3/oidc"
"github.com/felixge/httpsnoop"
"github.com/ory/fosite" "github.com/ory/fosite"
"github.com/ory/fosite/handler/openid" "github.com/ory/fosite/handler/openid"
"github.com/ory/fosite/token/jwt" "github.com/ory/fosite/token/jwt"
@ -89,7 +90,7 @@ func handleAuthRequestForLDAPUpstream(
ldapUpstream provider.UpstreamLDAPIdentityProviderI, ldapUpstream provider.UpstreamLDAPIdentityProviderI,
idpType psession.ProviderType, idpType psession.ProviderType,
) error { ) error {
authorizeRequester, created := newAuthorizeRequest(r, w, oauthHelper) authorizeRequester, created := newAuthorizeRequest(r, w, oauthHelper, true)
if !created { if !created {
return nil return nil
} }
@ -106,7 +107,7 @@ func handleAuthRequestForLDAPUpstream(
} }
if !authenticated { if !authenticated {
return writeAuthorizeError(w, oauthHelper, authorizeRequester, return writeAuthorizeError(w, oauthHelper, authorizeRequester,
fosite.ErrAccessDenied.WithHintf("Username/password not accepted by LDAP provider.")) fosite.ErrAccessDenied.WithHintf("Username/password not accepted by LDAP provider."), true)
} }
subject := downstreamSubjectFromUpstreamLDAP(ldapUpstream, authenticateResponse) subject := downstreamSubjectFromUpstreamLDAP(ldapUpstream, authenticateResponse)
@ -143,7 +144,7 @@ func handleAuthRequestForOIDCUpstreamPasswordGrant(
oauthHelper fosite.OAuth2Provider, oauthHelper fosite.OAuth2Provider,
oidcUpstream provider.UpstreamOIDCIdentityProviderI, oidcUpstream provider.UpstreamOIDCIdentityProviderI,
) error { ) error {
authorizeRequester, created := newAuthorizeRequest(r, w, oauthHelper) authorizeRequester, created := newAuthorizeRequest(r, w, oauthHelper, true)
if !created { if !created {
return nil return nil
} }
@ -157,7 +158,7 @@ func handleAuthRequestForOIDCUpstreamPasswordGrant(
// Return a user-friendly error for this case which is entirely within our control. // Return a user-friendly error for this case which is entirely within our control.
return writeAuthorizeError(w, oauthHelper, authorizeRequester, return writeAuthorizeError(w, oauthHelper, authorizeRequester,
fosite.ErrAccessDenied.WithHint( fosite.ErrAccessDenied.WithHint(
"Resource owner password credentials grant is not allowed for this upstream provider according to its configuration.")) "Resource owner password credentials grant is not allowed for this upstream provider according to its configuration."), true)
} }
token, err := oidcUpstream.PasswordCredentialsGrantAndValidateTokens(r.Context(), username, password) token, err := oidcUpstream.PasswordCredentialsGrantAndValidateTokens(r.Context(), username, password)
@ -170,7 +171,7 @@ func handleAuthRequestForOIDCUpstreamPasswordGrant(
// the OIDC spec, so we don't try too hard to read the upstream errors in this case. (E.g. Dex departs from the // the OIDC spec, so we don't try too hard to read the upstream errors in this case. (E.g. Dex departs from the
// spec and returns something other than an "invalid_grant" error for bad resource owner credentials.) // spec and returns something other than an "invalid_grant" error for bad resource owner credentials.)
return writeAuthorizeError(w, oauthHelper, authorizeRequester, return writeAuthorizeError(w, oauthHelper, authorizeRequester,
fosite.ErrAccessDenied.WithDebug(err.Error())) // WithDebug hides the error from the client fosite.ErrAccessDenied.WithDebug(err.Error()), true) // WithDebug hides the error from the client
} }
if token.RefreshToken == nil || token.RefreshToken.Token == "" { if token.RefreshToken == nil || token.RefreshToken.Token == "" {
@ -180,14 +181,14 @@ func handleAuthRequestForOIDCUpstreamPasswordGrant(
"scopes", oidcUpstream.GetScopes()) "scopes", oidcUpstream.GetScopes())
return writeAuthorizeError(w, oauthHelper, authorizeRequester, return writeAuthorizeError(w, oauthHelper, authorizeRequester,
fosite.ErrAccessDenied.WithHint( fosite.ErrAccessDenied.WithHint(
"Refresh token not returned by upstream provider during password grant.")) "Refresh token not returned by upstream provider during password grant."), true)
} }
subject, username, groups, err := downstreamsession.GetDownstreamIdentityFromUpstreamIDToken(oidcUpstream, token.IDToken.Claims) subject, username, groups, err := downstreamsession.GetDownstreamIdentityFromUpstreamIDToken(oidcUpstream, token.IDToken.Claims)
if err != nil { if err != nil {
// Return a user-friendly error for this case which is entirely within our control. // Return a user-friendly error for this case which is entirely within our control.
return writeAuthorizeError(w, oauthHelper, authorizeRequester, return writeAuthorizeError(w, oauthHelper, authorizeRequester,
fosite.ErrAccessDenied.WithHintf("Reason: %s.", err.Error()), fosite.ErrAccessDenied.WithHintf("Reason: %s.", err.Error()), true,
) )
} }
@ -214,7 +215,7 @@ func handleAuthRequestForOIDCUpstreamAuthcodeGrant(
upstreamStateEncoder oidc.Encoder, upstreamStateEncoder oidc.Encoder,
cookieCodec oidc.Codec, cookieCodec oidc.Codec,
) error { ) error {
authorizeRequester, created := newAuthorizeRequest(r, w, oauthHelper) authorizeRequester, created := newAuthorizeRequest(r, w, oauthHelper, false)
if !created { if !created {
return nil return nil
} }
@ -231,7 +232,7 @@ func handleAuthRequestForOIDCUpstreamAuthcodeGrant(
}, },
}) })
if err != nil { if err != nil {
return writeAuthorizeError(w, oauthHelper, authorizeRequester, err) return writeAuthorizeError(w, oauthHelper, authorizeRequester, err, false)
} }
csrfValue, nonceValue, pkceValue, err := generateValues(generateCSRF, generateNonce, generatePKCE) csrfValue, nonceValue, pkceValue, err := generateValues(generateCSRF, generateNonce, generatePKCE)
@ -274,7 +275,7 @@ func handleAuthRequestForOIDCUpstreamAuthcodeGrant(
promptParam := r.Form.Get(promptParamName) promptParam := r.Form.Get(promptParamName)
if promptParam == promptParamNone && oidc.ScopeWasRequested(authorizeRequester, coreosoidc.ScopeOpenID) { if promptParam == promptParamNone && oidc.ScopeWasRequested(authorizeRequester, coreosoidc.ScopeOpenID) {
return writeAuthorizeError(w, oauthHelper, authorizeRequester, fosite.ErrLoginRequired) return writeAuthorizeError(w, oauthHelper, authorizeRequester, fosite.ErrLoginRequired, false)
} }
for key, val := range oidcUpstream.GetAdditionalAuthcodeParams() { for key, val := range oidcUpstream.GetAdditionalAuthcodeParams() {
@ -295,13 +296,13 @@ func handleAuthRequestForOIDCUpstreamAuthcodeGrant(
encodedStateParamValue, encodedStateParamValue,
authCodeOptions..., authCodeOptions...,
), ),
302, http.StatusSeeOther, // match fosite and https://tools.ietf.org/id/draft-ietf-oauth-security-topics-18.html#section-4.11
) )
return nil return nil
} }
func writeAuthorizeError(w http.ResponseWriter, oauthHelper fosite.OAuth2Provider, authorizeRequester fosite.AuthorizeRequester, err error) error { func writeAuthorizeError(w http.ResponseWriter, oauthHelper fosite.OAuth2Provider, authorizeRequester fosite.AuthorizeRequester, err error, isBrowserless bool) error {
if plog.Enabled(plog.LevelTrace) { if plog.Enabled(plog.LevelTrace) {
// When trace level logging is enabled, include the stack trace in the log message. // When trace level logging is enabled, include the stack trace in the log message.
keysAndValues := oidc.FositeErrorForLog(err) keysAndValues := oidc.FositeErrorForLog(err)
@ -314,6 +315,9 @@ func writeAuthorizeError(w http.ResponseWriter, oauthHelper fosite.OAuth2Provide
} else { } else {
plog.Info("authorize response error", oidc.FositeErrorForLog(err)...) plog.Info("authorize response error", oidc.FositeErrorForLog(err)...)
} }
if isBrowserless {
w = rewriteStatusSeeOtherToStatusFoundForBrowserless(w)
}
// Return an error according to OIDC spec 3.1.2.6 (second paragraph). // Return an error according to OIDC spec 3.1.2.6 (second paragraph).
oauthHelper.WriteAuthorizeError(w, authorizeRequester, err) oauthHelper.WriteAuthorizeError(w, authorizeRequester, err)
return nil return nil
@ -333,29 +337,53 @@ func makeDownstreamSessionAndReturnAuthcodeRedirect(
authorizeResponder, err := oauthHelper.NewAuthorizeResponse(r.Context(), authorizeRequester, openIDSession) authorizeResponder, err := oauthHelper.NewAuthorizeResponse(r.Context(), authorizeRequester, openIDSession)
if err != nil { if err != nil {
return writeAuthorizeError(w, oauthHelper, authorizeRequester, err) return writeAuthorizeError(w, oauthHelper, authorizeRequester, err, true)
} }
w = rewriteStatusSeeOtherToStatusFoundForBrowserless(w)
oauthHelper.WriteAuthorizeResponse(w, authorizeRequester, authorizeResponder) oauthHelper.WriteAuthorizeResponse(w, authorizeRequester, authorizeResponder)
return nil return nil
} }
func rewriteStatusSeeOtherToStatusFoundForBrowserless(w http.ResponseWriter) http.ResponseWriter {
// rewrite http.StatusSeeOther to http.StatusFound for backwards compatibility with old pinniped CLIs.
// we can drop this in a few releases once we feel enough time has passed for users to update.
//
// WriteAuthorizeResponse/WriteAuthorizeError calls used to result in http.StatusFound until
// https://github.com/ory/fosite/pull/636 changed it to http.StatusSeeOther to address
// https://tools.ietf.org/id/draft-ietf-oauth-security-topics-18.html#section-4.11
// Safari has the bad behavior in the case of http.StatusFound and not just http.StatusTemporaryRedirect.
//
// in the browserless flows, the OAuth client is the pinniped CLI and it already has access to the user's
// password. Thus there is no security issue with using http.StatusFound vs. http.StatusSeeOther.
return httpsnoop.Wrap(w, httpsnoop.Hooks{
WriteHeader: func(delegate httpsnoop.WriteHeaderFunc) httpsnoop.WriteHeaderFunc {
return func(code int) {
if code == http.StatusSeeOther {
code = http.StatusFound
}
delegate(code)
}
},
})
}
func requireNonEmptyUsernameAndPasswordHeaders(r *http.Request, w http.ResponseWriter, oauthHelper fosite.OAuth2Provider, authorizeRequester fosite.AuthorizeRequester) (string, string, bool) { func requireNonEmptyUsernameAndPasswordHeaders(r *http.Request, w http.ResponseWriter, oauthHelper fosite.OAuth2Provider, authorizeRequester fosite.AuthorizeRequester) (string, string, bool) {
username := r.Header.Get(supervisoroidc.AuthorizeUsernameHeaderName) username := r.Header.Get(supervisoroidc.AuthorizeUsernameHeaderName)
password := r.Header.Get(supervisoroidc.AuthorizePasswordHeaderName) password := r.Header.Get(supervisoroidc.AuthorizePasswordHeaderName)
if username == "" || password == "" { if username == "" || password == "" {
_ = writeAuthorizeError(w, oauthHelper, authorizeRequester, _ = writeAuthorizeError(w, oauthHelper, authorizeRequester,
fosite.ErrAccessDenied.WithHintf("Missing or blank username or password.")) fosite.ErrAccessDenied.WithHintf("Missing or blank username or password."), true)
return "", "", false return "", "", false
} }
return username, password, true return username, password, true
} }
func newAuthorizeRequest(r *http.Request, w http.ResponseWriter, oauthHelper fosite.OAuth2Provider) (fosite.AuthorizeRequester, bool) { func newAuthorizeRequest(r *http.Request, w http.ResponseWriter, oauthHelper fosite.OAuth2Provider, isBrowserless bool) (fosite.AuthorizeRequester, bool) {
authorizeRequester, err := oauthHelper.NewAuthorizeRequest(r.Context(), r) authorizeRequester, err := oauthHelper.NewAuthorizeRequest(r.Context(), r)
if err != nil { if err != nil {
_ = writeAuthorizeError(w, oauthHelper, authorizeRequester, err) _ = writeAuthorizeError(w, oauthHelper, authorizeRequester, err, isBrowserless)
return nil, false return nil, false
} }

View File

@ -533,7 +533,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
cookieEncoder: happyCookieEncoder, cookieEncoder: happyCookieEncoder,
method: http.MethodGet, method: http.MethodGet,
path: happyGetRequestPath, path: happyGetRequestPath,
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantCSRFValueInCookieHeader: happyCSRF, wantCSRFValueInCookieHeader: happyCSRF,
wantLocationHeader: expectedRedirectLocationForUpstreamOIDC(expectedUpstreamStateParam(nil, "", ""), nil), wantLocationHeader: expectedRedirectLocationForUpstreamOIDC(expectedUpstreamStateParam(nil, "", ""), nil),
@ -615,7 +615,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
method: http.MethodGet, method: http.MethodGet,
path: happyGetRequestPath, path: happyGetRequestPath,
csrfCookie: "__Host-pinniped-csrf=" + encodedIncomingCookieCSRFValue + " ", csrfCookie: "__Host-pinniped-csrf=" + encodedIncomingCookieCSRFValue + " ",
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantLocationHeader: expectedRedirectLocationForUpstreamOIDC(expectedUpstreamStateParam(nil, incomingCookieCSRFValue, ""), nil), wantLocationHeader: expectedRedirectLocationForUpstreamOIDC(expectedUpstreamStateParam(nil, incomingCookieCSRFValue, ""), nil),
wantUpstreamStateParamInLocationHeader: true, wantUpstreamStateParamInLocationHeader: true,
@ -633,7 +633,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
path: "/some/path", path: "/some/path",
contentType: "application/x-www-form-urlencoded", contentType: "application/x-www-form-urlencoded",
body: encodeQuery(happyGetRequestQueryMap), body: encodeQuery(happyGetRequestQueryMap),
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantContentType: "", wantContentType: "",
wantBodyString: "", wantBodyString: "",
wantCSRFValueInCookieHeader: happyCSRF, wantCSRFValueInCookieHeader: happyCSRF,
@ -722,7 +722,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
path: modifiedHappyGetRequestPath(map[string]string{"prompt": "login"}), path: modifiedHappyGetRequestPath(map[string]string{"prompt": "login"}),
contentType: "application/x-www-form-urlencoded", contentType: "application/x-www-form-urlencoded",
body: encodeQuery(happyGetRequestQueryMap), body: encodeQuery(happyGetRequestQueryMap),
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantBodyStringWithLocationInHref: true, wantBodyStringWithLocationInHref: true,
wantCSRFValueInCookieHeader: happyCSRF, wantCSRFValueInCookieHeader: happyCSRF,
@ -741,7 +741,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
path: modifiedHappyGetRequestPath(map[string]string{"prompt": "login"}), path: modifiedHappyGetRequestPath(map[string]string{"prompt": "login"}),
contentType: "application/x-www-form-urlencoded", contentType: "application/x-www-form-urlencoded",
body: encodeQuery(happyGetRequestQueryMap), body: encodeQuery(happyGetRequestQueryMap),
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantBodyStringWithLocationInHref: true, wantBodyStringWithLocationInHref: true,
wantCSRFValueInCookieHeader: happyCSRF, wantCSRFValueInCookieHeader: happyCSRF,
@ -760,7 +760,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
path: modifiedHappyGetRequestPath(map[string]string{"prompt": "none"}), path: modifiedHappyGetRequestPath(map[string]string{"prompt": "none"}),
contentType: "application/x-www-form-urlencoded", contentType: "application/x-www-form-urlencoded",
body: encodeQuery(happyGetRequestQueryMap), body: encodeQuery(happyGetRequestQueryMap),
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantContentType: "application/json; charset=utf-8", wantContentType: "application/json; charset=utf-8",
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeLoginRequiredErrorQuery), wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeLoginRequiredErrorQuery),
wantBodyString: "", wantBodyString: "",
@ -776,7 +776,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
method: http.MethodGet, method: http.MethodGet,
path: happyGetRequestPath, path: happyGetRequestPath,
csrfCookie: "__Host-pinniped-csrf=this-value-was-not-signed-by-pinniped", csrfCookie: "__Host-pinniped-csrf=this-value-was-not-signed-by-pinniped",
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantContentType: htmlContentType, wantContentType: htmlContentType,
// Generated a new CSRF cookie and set it in the response. // Generated a new CSRF cookie and set it in the response.
wantCSRFValueInCookieHeader: happyCSRF, wantCSRFValueInCookieHeader: happyCSRF,
@ -796,7 +796,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
path: modifiedHappyGetRequestPath(map[string]string{ path: modifiedHappyGetRequestPath(map[string]string{
"redirect_uri": downstreamRedirectURIWithDifferentPort, // not the same port number that is registered for the client "redirect_uri": downstreamRedirectURIWithDifferentPort, // not the same port number that is registered for the client
}), }),
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantCSRFValueInCookieHeader: happyCSRF, wantCSRFValueInCookieHeader: happyCSRF,
wantLocationHeader: expectedRedirectLocationForUpstreamOIDC(expectedUpstreamStateParam(map[string]string{ wantLocationHeader: expectedRedirectLocationForUpstreamOIDC(expectedUpstreamStateParam(map[string]string{
@ -862,7 +862,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
cookieEncoder: happyCookieEncoder, cookieEncoder: happyCookieEncoder,
method: http.MethodGet, method: http.MethodGet,
path: modifiedHappyGetRequestPath(map[string]string{"scope": "openid offline_access"}), path: modifiedHappyGetRequestPath(map[string]string{"scope": "openid offline_access"}),
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantCSRFValueInCookieHeader: happyCSRF, wantCSRFValueInCookieHeader: happyCSRF,
wantLocationHeader: expectedRedirectLocationForUpstreamOIDC(expectedUpstreamStateParam(map[string]string{ wantLocationHeader: expectedRedirectLocationForUpstreamOIDC(expectedUpstreamStateParam(map[string]string{
@ -1170,7 +1170,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
cookieEncoder: happyCookieEncoder, cookieEncoder: happyCookieEncoder,
method: http.MethodGet, method: http.MethodGet,
path: modifiedHappyGetRequestPath(map[string]string{"response_type": "unsupported"}), path: modifiedHappyGetRequestPath(map[string]string{"response_type": "unsupported"}),
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantContentType: "application/json; charset=utf-8", wantContentType: "application/json; charset=utf-8",
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeUnsupportedResponseTypeErrorQuery), wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeUnsupportedResponseTypeErrorQuery),
wantBodyString: "", wantBodyString: "",
@ -1217,7 +1217,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
cookieEncoder: happyCookieEncoder, cookieEncoder: happyCookieEncoder,
method: http.MethodGet, method: http.MethodGet,
path: modifiedHappyGetRequestPath(map[string]string{"scope": "openid profile email tuna"}), path: modifiedHappyGetRequestPath(map[string]string{"scope": "openid profile email tuna"}),
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantContentType: "application/json; charset=utf-8", wantContentType: "application/json; charset=utf-8",
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeInvalidScopeErrorQuery), wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeInvalidScopeErrorQuery),
wantBodyString: "", wantBodyString: "",
@ -1268,7 +1268,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
cookieEncoder: happyCookieEncoder, cookieEncoder: happyCookieEncoder,
method: http.MethodGet, method: http.MethodGet,
path: modifiedHappyGetRequestPath(map[string]string{"response_type": ""}), path: modifiedHappyGetRequestPath(map[string]string{"response_type": ""}),
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantContentType: "application/json; charset=utf-8", wantContentType: "application/json; charset=utf-8",
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeMissingResponseTypeErrorQuery), wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeMissingResponseTypeErrorQuery),
wantBodyString: "", wantBodyString: "",
@ -1349,7 +1349,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
cookieEncoder: happyCookieEncoder, cookieEncoder: happyCookieEncoder,
method: http.MethodGet, method: http.MethodGet,
path: modifiedHappyGetRequestPath(map[string]string{"code_challenge": ""}), path: modifiedHappyGetRequestPath(map[string]string{"code_challenge": ""}),
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantContentType: "application/json; charset=utf-8", wantContentType: "application/json; charset=utf-8",
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeMissingCodeChallengeErrorQuery), wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeMissingCodeChallengeErrorQuery),
wantBodyString: "", wantBodyString: "",
@ -1391,7 +1391,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
cookieEncoder: happyCookieEncoder, cookieEncoder: happyCookieEncoder,
method: http.MethodGet, method: http.MethodGet,
path: modifiedHappyGetRequestPath(map[string]string{"code_challenge_method": "this-is-not-a-valid-pkce-alg"}), path: modifiedHappyGetRequestPath(map[string]string{"code_challenge_method": "this-is-not-a-valid-pkce-alg"}),
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantContentType: "application/json; charset=utf-8", wantContentType: "application/json; charset=utf-8",
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeInvalidCodeChallengeErrorQuery), wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeInvalidCodeChallengeErrorQuery),
wantBodyString: "", wantBodyString: "",
@ -1433,7 +1433,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
cookieEncoder: happyCookieEncoder, cookieEncoder: happyCookieEncoder,
method: http.MethodGet, method: http.MethodGet,
path: modifiedHappyGetRequestPath(map[string]string{"code_challenge_method": "plain"}), path: modifiedHappyGetRequestPath(map[string]string{"code_challenge_method": "plain"}),
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantContentType: "application/json; charset=utf-8", wantContentType: "application/json; charset=utf-8",
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeMissingCodeChallengeMethodErrorQuery), wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeMissingCodeChallengeMethodErrorQuery),
wantBodyString: "", wantBodyString: "",
@ -1475,7 +1475,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
cookieEncoder: happyCookieEncoder, cookieEncoder: happyCookieEncoder,
method: http.MethodGet, method: http.MethodGet,
path: modifiedHappyGetRequestPath(map[string]string{"code_challenge_method": ""}), path: modifiedHappyGetRequestPath(map[string]string{"code_challenge_method": ""}),
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantContentType: "application/json; charset=utf-8", wantContentType: "application/json; charset=utf-8",
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeMissingCodeChallengeMethodErrorQuery), wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeMissingCodeChallengeMethodErrorQuery),
wantBodyString: "", wantBodyString: "",
@ -1519,7 +1519,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
cookieEncoder: happyCookieEncoder, cookieEncoder: happyCookieEncoder,
method: http.MethodGet, method: http.MethodGet,
path: modifiedHappyGetRequestPath(map[string]string{"prompt": "none login"}), path: modifiedHappyGetRequestPath(map[string]string{"prompt": "none login"}),
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantContentType: "application/json; charset=utf-8", wantContentType: "application/json; charset=utf-8",
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositePromptHasNoneAndOtherValueErrorQuery), wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositePromptHasNoneAndOtherValueErrorQuery),
wantBodyString: "", wantBodyString: "",
@ -1566,7 +1566,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
method: http.MethodGet, method: http.MethodGet,
// The following prompt value is illegal when openid is requested, but note that openid is not requested. // The following prompt value is illegal when openid is requested, but note that openid is not requested.
path: modifiedHappyGetRequestPath(map[string]string{"prompt": "none login", "scope": "email"}), path: modifiedHappyGetRequestPath(map[string]string{"prompt": "none login", "scope": "email"}),
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantContentType: htmlContentType, wantContentType: htmlContentType,
wantCSRFValueInCookieHeader: happyCSRF, wantCSRFValueInCookieHeader: happyCSRF,
wantLocationHeader: expectedRedirectLocationForUpstreamOIDC(expectedUpstreamStateParam( wantLocationHeader: expectedRedirectLocationForUpstreamOIDC(expectedUpstreamStateParam(
@ -2049,7 +2049,7 @@ func TestAuthorizationEndpoint(t *testing.T) {
cookieEncoder: happyCookieEncoder, cookieEncoder: happyCookieEncoder,
method: http.MethodGet, method: http.MethodGet,
path: modifiedHappyGetRequestPath(map[string]string{"state": "short"}), path: modifiedHappyGetRequestPath(map[string]string{"state": "short"}),
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantContentType: "application/json; charset=utf-8", wantContentType: "application/json; charset=utf-8",
wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeInvalidStateErrorQuery), wantLocationHeader: urlWithQuery(downstreamRedirectURI, fositeInvalidStateErrorQuery),
wantBodyString: "", wantBodyString: "",
@ -2308,8 +2308,16 @@ func TestAuthorizationEndpoint(t *testing.T) {
case test.wantBodyJSON != "": case test.wantBodyJSON != "":
require.JSONEq(t, test.wantBodyJSON, rsp.Body.String()) require.JSONEq(t, test.wantBodyJSON, rsp.Body.String())
case test.wantBodyStringWithLocationInHref: case test.wantBodyStringWithLocationInHref:
switch code := rsp.Code; code {
case http.StatusFound:
anchorTagWithLocationHref := fmt.Sprintf("<a href=\"%s\">Found</a>.\n\n", html.EscapeString(actualLocation)) anchorTagWithLocationHref := fmt.Sprintf("<a href=\"%s\">Found</a>.\n\n", html.EscapeString(actualLocation))
require.Equal(t, anchorTagWithLocationHref, rsp.Body.String()) require.Equal(t, anchorTagWithLocationHref, rsp.Body.String())
case http.StatusSeeOther:
anchorTagWithLocationHref := fmt.Sprintf("<a href=\"%s\">See Other</a>.\n\n", html.EscapeString(actualLocation))
require.Equal(t, anchorTagWithLocationHref, rsp.Body.String())
default:
t.Errorf("unexpected response code: %v", code)
}
default: default:
require.Equal(t, test.wantBodyString, rsp.Body.String()) require.Equal(t, test.wantBodyString, rsp.Body.String())
} }

View File

@ -179,7 +179,7 @@ func TestCallbackEndpoint(t *testing.T) {
method: http.MethodGet, method: http.MethodGet,
path: newRequestPath().WithState(happyState).String(), path: newRequestPath().WithState(happyState).String(),
csrfCookie: happyCSRFCookie, csrfCookie: happyCSRFCookie,
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
@ -204,7 +204,7 @@ func TestCallbackEndpoint(t *testing.T) {
method: http.MethodGet, method: http.MethodGet,
path: newRequestPath().WithState(happyState).String(), path: newRequestPath().WithState(happyState).String(),
csrfCookie: happyCSRFCookie, csrfCookie: happyCSRFCookie,
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
@ -229,7 +229,7 @@ func TestCallbackEndpoint(t *testing.T) {
method: http.MethodGet, method: http.MethodGet,
path: newRequestPath().WithState(happyState).String(), path: newRequestPath().WithState(happyState).String(),
csrfCookie: happyCSRFCookie, csrfCookie: happyCSRFCookie,
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
@ -256,7 +256,7 @@ func TestCallbackEndpoint(t *testing.T) {
method: http.MethodGet, method: http.MethodGet,
path: newRequestPath().WithState(happyState).String(), path: newRequestPath().WithState(happyState).String(),
csrfCookie: happyCSRFCookie, csrfCookie: happyCSRFCookie,
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
@ -284,7 +284,7 @@ func TestCallbackEndpoint(t *testing.T) {
method: http.MethodGet, method: http.MethodGet,
path: newRequestPath().WithState(happyState).String(), path: newRequestPath().WithState(happyState).String(),
csrfCookie: happyCSRFCookie, csrfCookie: happyCSRFCookie,
wantStatus: http.StatusFound, // succeed despite `email_verified=false` because we're not using the email claim for anything wantStatus: http.StatusSeeOther, // succeed despite `email_verified=false` because we're not using the email claim for anything
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
@ -372,7 +372,7 @@ func TestCallbackEndpoint(t *testing.T) {
method: http.MethodGet, method: http.MethodGet,
path: newRequestPath().WithState(happyState).String(), path: newRequestPath().WithState(happyState).String(),
csrfCookie: happyCSRFCookie, csrfCookie: happyCSRFCookie,
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
@ -397,7 +397,7 @@ func TestCallbackEndpoint(t *testing.T) {
method: http.MethodGet, method: http.MethodGet,
path: newRequestPath().WithState(happyState).String(), path: newRequestPath().WithState(happyState).String(),
csrfCookie: happyCSRFCookie, csrfCookie: happyCSRFCookie,
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
@ -422,7 +422,7 @@ func TestCallbackEndpoint(t *testing.T) {
method: http.MethodGet, method: http.MethodGet,
path: newRequestPath().WithState(happyState).String(), path: newRequestPath().WithState(happyState).String(),
csrfCookie: happyCSRFCookie, csrfCookie: happyCSRFCookie,
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
@ -575,7 +575,7 @@ func TestCallbackEndpoint(t *testing.T) {
Build(t, happyStateCodec), Build(t, happyStateCodec),
).String(), ).String(),
csrfCookie: happyCSRFCookie, csrfCookie: happyCSRFCookie,
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=&state=` + happyDownstreamState, wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=&state=` + happyDownstreamState,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
@ -601,7 +601,7 @@ func TestCallbackEndpoint(t *testing.T) {
Build(t, happyStateCodec), Build(t, happyStateCodec),
).String(), ).String(),
csrfCookie: happyCSRFCookie, csrfCookie: happyCSRFCookie,
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access&state=` + happyDownstreamState, wantRedirectLocationRegexp: downstreamRedirectURI + `\?code=([^&]+)&scope=openid\+offline_access&state=` + happyDownstreamState,
wantDownstreamIDTokenUsername: oidcUpstreamUsername, wantDownstreamIDTokenUsername: oidcUpstreamUsername,
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,
@ -698,7 +698,7 @@ func TestCallbackEndpoint(t *testing.T) {
method: http.MethodGet, method: http.MethodGet,
path: newRequestPath().WithState(happyState).String(), path: newRequestPath().WithState(happyState).String(),
csrfCookie: happyCSRFCookie, csrfCookie: happyCSRFCookie,
wantStatus: http.StatusFound, wantStatus: http.StatusSeeOther,
wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp, wantRedirectLocationRegexp: happyDownstreamRedirectLocationRegexp,
wantBody: "", wantBody: "",
wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped, wantDownstreamIDTokenSubject: oidcUpstreamIssuer + "?sub=" + oidcUpstreamSubjectQueryEscaped,

View File

@ -114,7 +114,7 @@ func (k KubeStorage) GetOpenIDConnectSession(ctx context.Context, fullAuthcode s
} }
func (k KubeStorage) DeleteOpenIDConnectSession(ctx context.Context, fullAuthcode string) error { func (k KubeStorage) DeleteOpenIDConnectSession(ctx context.Context, fullAuthcode string) error {
return k.oidcStorage.DeleteOpenIDConnectSession(ctx, fullAuthcode) return k.oidcStorage.DeleteOpenIDConnectSession(ctx, fullAuthcode) //nolint: staticcheck // we know this is deprecated and never called. our GC controller cleans these up.
} }
// //

View File

@ -121,7 +121,7 @@ func TestManager(t *testing.T) {
r.False(fallbackHandlerWasCalled) r.False(fallbackHandlerWasCalled)
// Minimal check to ensure that the right endpoint was called // Minimal check to ensure that the right endpoint was called
r.Equal(http.StatusFound, recorder.Code) r.Equal(http.StatusSeeOther, recorder.Code)
actualLocation := recorder.Header().Get("Location") actualLocation := recorder.Header().Get("Location")
r.True( r.True(
strings.HasPrefix(actualLocation, expectedRedirectLocationPrefix), strings.HasPrefix(actualLocation, expectedRedirectLocationPrefix),
@ -160,7 +160,7 @@ func TestManager(t *testing.T) {
// Check just enough of the response to ensure that we wired up the callback endpoint correctly. // Check just enough of the response to ensure that we wired up the callback endpoint correctly.
// The endpoint's own unit tests cover everything else. // The endpoint's own unit tests cover everything else.
r.Equal(http.StatusFound, recorder.Code) r.Equal(http.StatusSeeOther, recorder.Code)
actualLocation := recorder.Header().Get("Location") actualLocation := recorder.Header().Get("Location")
r.True( r.True(
strings.HasPrefix(actualLocation, downstreamRedirectURL), strings.HasPrefix(actualLocation, downstreamRedirectURL),

View File

@ -2971,7 +2971,7 @@ func requireValidStoredRequest(
// At this time, we don't use any of these optional (per the OIDC spec) fields. // At this time, we don't use any of these optional (per the OIDC spec) fields.
require.Empty(t, claims.AuthenticationContextClassReference) require.Empty(t, claims.AuthenticationContextClassReference)
require.Empty(t, claims.AuthenticationMethodsReference) require.Empty(t, claims.AuthenticationMethodsReferences)
require.Empty(t, claims.CodeHash) require.Empty(t, claims.CodeHash)
} }

View File

@ -5,40 +5,10 @@ package plog
import ( import (
"fmt" "fmt"
"sync"
"github.com/spf13/pflag"
"k8s.io/klog/v2" "k8s.io/klog/v2"
) )
//nolint: gochecknoglobals
var removeKlogGlobalFlagsLock sync.Mutex
// RemoveKlogGlobalFlags attempts to "remove" flags that get unconditionally added by importing klog.
func RemoveKlogGlobalFlags() {
// since we mess with global state, we need a lock to synchronize us when called in parallel during tests
removeKlogGlobalFlagsLock.Lock()
defer removeKlogGlobalFlagsLock.Unlock()
// if this function starts to panic, it likely means that klog stopped mucking with global flags
const globalLogFlushFlag = "log-flush-frequency"
if err := pflag.CommandLine.MarkHidden(globalLogFlushFlag); err != nil {
panic(err)
}
if err := pflag.CommandLine.MarkDeprecated(globalLogFlushFlag, "unsupported"); err != nil {
panic(err)
}
if pflag.CommandLine.Changed(globalLogFlushFlag) {
panic("unsupported global klog flag set")
}
}
// KRef is (mostly) copied from klog - it is a standard way to represent a metav1.Object in logs
// when you only have access to the namespace and name of the object.
func KRef(namespace, name string) string {
return fmt.Sprintf("%s/%s", namespace, name)
}
// KObj is (mostly) copied from klog - it is a standard way to represent a metav1.Object in logs. // KObj is (mostly) copied from klog - it is a standard way to represent a metav1.Object in logs.
func KObj(obj klog.KMetadata) string { func KObj(obj klog.KMetadata) string {
return fmt.Sprintf("%s/%s", obj.GetNamespace(), obj.GetName()) return fmt.Sprintf("%s/%s", obj.GetNamespace(), obj.GetName())

View File

@ -10,6 +10,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/go-logr/logr"
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
"github.com/sclevine/spec" "github.com/sclevine/spec"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -71,11 +72,11 @@ func TestCreate(t *testing.T) {
r = require.New(t) r = require.New(t)
ctrl = gomock.NewController(t) ctrl = gomock.NewController(t)
logger = testutil.NewTranscriptLogger(t) logger = testutil.NewTranscriptLogger(t)
klog.SetLogger(logger) // this is unfortunately a global logger, so can't run these tests in parallel :( klog.SetLogger(logr.New(logger)) // this is unfortunately a global logger, so can't run these tests in parallel :(
}) })
it.After(func() { it.After(func() {
klog.SetLogger(nil) klog.ClearLogger()
ctrl.Finish() ctrl.Finish()
}) })

View File

@ -20,7 +20,6 @@ import (
appsv1 "k8s.io/api/apps/v1" appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/clock"
genericapifilters "k8s.io/apiserver/pkg/endpoints/filters" genericapifilters "k8s.io/apiserver/pkg/endpoints/filters"
kubeinformers "k8s.io/client-go/informers" kubeinformers "k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
@ -29,6 +28,7 @@ import (
"k8s.io/component-base/logs" "k8s.io/component-base/logs"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/klog/v2/klogr" "k8s.io/klog/v2/klogr"
"k8s.io/utils/clock"
configv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/config/v1alpha1" configv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/config/v1alpha1"
pinnipedclientset "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned" pinnipedclientset "go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned"
@ -431,7 +431,6 @@ func runSupervisor(podInfo *downward.PodInfo, cfg *supervisor.Config) error {
func main() error { // return an error instead of klog.Fatal to allow defer statements to run func main() error { // return an error instead of klog.Fatal to allow defer statements to run
logs.InitLogs() logs.InitLogs()
defer logs.FlushLogs() defer logs.FlushLogs()
plog.RemoveKlogGlobalFlags() // move this whenever the below code gets refactored to use cobra
klog.Infof("Running %s at %#v", rest.DefaultKubernetesUserAgent(), version.Get()) klog.Infof("Running %s at %#v", rest.DefaultKubernetesUserAgent(), version.Get())
klog.Infof("Command-line arguments were: %s %s %s", os.Args[0], os.Args[1], os.Args[2]) klog.Infof("Command-line arguments were: %s %s %s", os.Args[0], os.Args[1], os.Args[2])

View File

@ -4,87 +4,10 @@
package testutil package testutil
import ( import (
"context"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes"
appsv1client "k8s.io/client-go/kubernetes/typed/apps/v1"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
) )
func NewDeleteOptionsRecorder(client kubernetes.Interface, opts *[]metav1.DeleteOptions) kubernetes.Interface {
return &clientWrapper{
Interface: client,
opts: opts,
}
}
type clientWrapper struct {
kubernetes.Interface
opts *[]metav1.DeleteOptions
}
func (c *clientWrapper) CoreV1() corev1client.CoreV1Interface {
return &coreWrapper{CoreV1Interface: c.Interface.CoreV1(), opts: c.opts}
}
func (c *clientWrapper) AppsV1() appsv1client.AppsV1Interface {
return &appsWrapper{AppsV1Interface: c.Interface.AppsV1(), opts: c.opts}
}
type coreWrapper struct {
corev1client.CoreV1Interface
opts *[]metav1.DeleteOptions
}
func (c *coreWrapper) Pods(namespace string) corev1client.PodInterface {
return &podsWrapper{PodInterface: c.CoreV1Interface.Pods(namespace), opts: c.opts}
}
func (c *coreWrapper) Secrets(namespace string) corev1client.SecretInterface {
return &secretsWrapper{SecretInterface: c.CoreV1Interface.Secrets(namespace), opts: c.opts}
}
type appsWrapper struct {
appsv1client.AppsV1Interface
opts *[]metav1.DeleteOptions
}
func (c *appsWrapper) Deployments(namespace string) appsv1client.DeploymentInterface {
return &deploymentsWrapper{DeploymentInterface: c.AppsV1Interface.Deployments(namespace), opts: c.opts}
}
type podsWrapper struct {
corev1client.PodInterface
opts *[]metav1.DeleteOptions
}
func (s *podsWrapper) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error {
*s.opts = append(*s.opts, opts)
return s.PodInterface.Delete(ctx, name, opts)
}
type secretsWrapper struct {
corev1client.SecretInterface
opts *[]metav1.DeleteOptions
}
func (s *secretsWrapper) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error {
*s.opts = append(*s.opts, opts)
return s.SecretInterface.Delete(ctx, name, opts)
}
type deploymentsWrapper struct {
appsv1client.DeploymentInterface
opts *[]metav1.DeleteOptions
}
func (s *deploymentsWrapper) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error {
*s.opts = append(*s.opts, opts)
return s.DeploymentInterface.Delete(ctx, name, opts)
}
func NewPreconditions(uid types.UID, rv string) metav1.DeleteOptions { func NewPreconditions(uid types.UID, rv string) metav1.DeleteOptions {
return metav1.DeleteOptions{ return metav1.DeleteOptions{
Preconditions: &metav1.Preconditions{ Preconditions: &metav1.Preconditions{

View File

@ -1001,7 +1001,7 @@ func validateAuthcodeStorage(
require.Empty(t, actualClaims.CodeHash) require.Empty(t, actualClaims.CodeHash)
require.Empty(t, actualClaims.AccessTokenHash) require.Empty(t, actualClaims.AccessTokenHash)
require.Empty(t, actualClaims.AuthenticationContextClassReference) require.Empty(t, actualClaims.AuthenticationContextClassReference)
require.Empty(t, actualClaims.AuthenticationMethodsReference) require.Empty(t, actualClaims.AuthenticationMethodsReferences)
// Check that the custom Pinniped session data matches. // Check that the custom Pinniped session data matches.
require.Equal(t, wantCustomSessionData, storedSessionFromAuthcode.Custom) require.Equal(t, wantCustomSessionData, storedSessionFromAuthcode.Custom)

View File

@ -0,0 +1,168 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package testlogger
import (
"bytes"
"encoding/json"
"fmt"
"log"
"runtime"
"sort"
"github.com/go-logr/logr"
"github.com/go-logr/stdr"
)
// newStdLogger returns a logr.Logger that matches the legacy v0.4.0 stdr.New implementation.
// All unnecessary functionality has been stripped out. Avoid using this if possible.
func newStdLogger(std stdr.StdLogger) logr.Logger {
return logr.New(logger{
std: std,
prefix: "",
values: nil,
})
}
type logger struct {
std stdr.StdLogger
prefix string
values []interface{}
}
func (l logger) clone() logger {
out := l
l.values = copySlice(l.values)
return out
}
func copySlice(in []interface{}) []interface{} {
out := make([]interface{}, len(in))
copy(out, in)
return out
}
// Magic string for intermediate frames that we should ignore.
const autogeneratedFrameName = "<autogenerated>"
// Discover how many frames we need to climb to find the caller. This approach
// was suggested by Ian Lance Taylor of the Go team, so it *should* be safe
// enough (famous last words).
func framesToCaller() int {
// 1 is the immediate caller. 3 should be too many.
for i := 1; i < 3; i++ {
_, file, _, _ := runtime.Caller(i + 1) // +1 for this function's frame
if file != autogeneratedFrameName {
return i
}
}
return 1 // something went wrong, this is safe
}
func flatten(kvList ...interface{}) string {
keys := make([]string, 0, len(kvList))
vals := make(map[string]interface{}, len(kvList))
for i := 0; i < len(kvList); i += 2 {
k, ok := kvList[i].(string)
if !ok {
panic(fmt.Sprintf("key is not a string: %s", pretty(kvList[i])))
}
var v interface{}
if i+1 < len(kvList) {
v = kvList[i+1]
}
keys = append(keys, k)
vals[k] = v
}
sort.Strings(keys)
buf := bytes.Buffer{}
for i, k := range keys {
v := vals[k]
if i > 0 {
buf.WriteRune(' ')
}
buf.WriteString(pretty(k))
buf.WriteString("=")
buf.WriteString(pretty(v))
}
return buf.String()
}
func pretty(value interface{}) string {
jb, _ := json.Marshal(value)
return string(jb)
}
func (l logger) Info(level int, msg string, kvList ...interface{}) {
if l.Enabled(level) {
builtin := make([]interface{}, 0, 4)
builtin = append(builtin, "level", level, "msg", msg)
builtinStr := flatten(builtin...)
fixedStr := flatten(l.values...)
userStr := flatten(kvList...)
l.output(framesToCaller(), fmt.Sprintln(l.prefix, builtinStr, fixedStr, userStr))
}
}
func (l logger) Enabled(level int) bool {
return true
}
func (l logger) Error(err error, msg string, kvList ...interface{}) {
builtin := make([]interface{}, 0, 4)
builtin = append(builtin, "msg", msg)
builtinStr := flatten(builtin...)
var loggableErr interface{}
if err != nil {
loggableErr = err.Error()
}
errStr := flatten("error", loggableErr)
fixedStr := flatten(l.values...)
userStr := flatten(kvList...)
l.output(framesToCaller(), fmt.Sprintln(l.prefix, builtinStr, errStr, fixedStr, userStr))
}
func (l logger) output(calldepth int, s string) {
depth := calldepth + 2 // offset for this adapter
// ignore errors - what can we really do about them?
if l.std != nil {
_ = l.std.Output(depth, s)
} else {
_ = log.Output(depth, s)
}
}
func (l logger) V(level int) logr.LogSink {
return l.clone()
}
// WithName returns a new logr.Logger with the specified name appended. stdr
// uses '/' characters to separate name elements. Callers should not pass '/'
// in the provided name string, but this library does not actually enforce that.
func (l logger) WithName(name string) logr.LogSink {
new := l.clone()
if len(l.prefix) > 0 {
new.prefix = l.prefix + "/"
}
new.prefix += name
return new
}
// WithValues returns a new logr.Logger with the specified key-and-values
// saved.
func (l logger) WithValues(kvList ...interface{}) logr.LogSink {
new := l.clone()
new.values = append(new.values, kvList...)
return new
}
func (l logger) WithCallDepth(depth int) logr.LogSink {
return l.clone()
}
var _ logr.LogSink = logger{}
var _ logr.CallDepthLogSink = logger{}
func (l logger) Init(info logr.RuntimeInfo) {}

View File

@ -1,7 +1,7 @@
// Copyright 2020 the Pinniped contributors. All Rights Reserved. // Copyright 2020 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// Package testlogger implements a logr.Logger suitable for writing test assertions. // Package testlogger wraps logr.Logger to allow for writing test assertions.
package testlogger package testlogger
import ( import (
@ -17,20 +17,27 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// Logger implements logr.Logger in a way that captures logs for test assertions. // Logger wraps logr.Logger in a way that captures logs for test assertions.
type Logger struct { type Logger struct {
logr.Logger Logger logr.Logger
t *testing.T t *testing.T
buffer syncBuffer buffer syncBuffer
} }
// New returns a new test Logger. // New returns a new test Logger. Use this for all new tests.
func New(t *testing.T) *Logger { func New(t *testing.T) *Logger {
res := Logger{t: t} res := Logger{t: t}
res.Logger = stdr.New(log.New(&res.buffer, "", 0)) res.Logger = stdr.New(log.New(&res.buffer, "", 0))
return &res return &res
} }
// Deprecated: NewLegacy returns a new test Logger. Use this for old tests if necessary.
func NewLegacy(t *testing.T) *Logger {
res := New(t)
res.Logger = newStdLogger(log.New(&res.buffer, "", 0))
return res
}
// Lines returns the lines written to the test logger. // Lines returns the lines written to the test logger.
func (l *Logger) Lines() []string { func (l *Logger) Lines() []string {
l.t.Helper() l.t.Helper()

View File

@ -17,7 +17,7 @@ type TranscriptLogger struct {
transcript []TranscriptLogMessage transcript []TranscriptLogMessage
} }
var _ logr.Logger = &TranscriptLogger{} var _ logr.LogSink = &TranscriptLogger{}
type TranscriptLogMessage struct { type TranscriptLogMessage struct {
Level string Level string
@ -36,7 +36,7 @@ func (log *TranscriptLogger) Transcript() []TranscriptLogMessage {
return result return result
} }
func (log *TranscriptLogger) Info(msg string, keysAndValues ...interface{}) { func (log *TranscriptLogger) Info(level int, msg string, keysAndValues ...interface{}) {
log.lock.Lock() log.lock.Lock()
defer log.lock.Unlock() defer log.lock.Unlock()
log.transcript = append(log.transcript, TranscriptLogMessage{ log.transcript = append(log.transcript, TranscriptLogMessage{
@ -54,18 +54,20 @@ func (log *TranscriptLogger) Error(_ error, msg string, _ ...interface{}) {
}) })
} }
func (*TranscriptLogger) Enabled() bool { func (log *TranscriptLogger) Enabled(level int) bool {
return true return true
} }
func (log *TranscriptLogger) V(_ int) logr.Logger { func (log *TranscriptLogger) V(_ int) logr.LogSink {
return log return log
} }
func (log *TranscriptLogger) WithName(_ string) logr.Logger { func (log *TranscriptLogger) WithName(_ string) logr.LogSink {
return log return log
} }
func (log *TranscriptLogger) WithValues(_ ...interface{}) logr.Logger { func (log *TranscriptLogger) WithValues(_ ...interface{}) logr.LogSink {
return log return log
} }
func (log *TranscriptLogger) Init(info logr.RuntimeInfo) {}

View File

@ -19,8 +19,6 @@ import (
"testing" "testing"
"time" "time"
"github.com/go-logr/stdr"
"github.com/coreos/go-oidc/v3/oidc" "github.com/coreos/go-oidc/v3/oidc"
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -1571,9 +1569,8 @@ func TestLogin(t *testing.T) { // nolint:gocyclo
for _, tt := range tests { for _, tt := range tests {
tt := tt tt := tt
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
testLogger := testlogger.New(t) testLogger := testlogger.NewLegacy(t) //nolint: staticcheck // old test with lots of log statements
klog.SetLogger(testLogger) klog.SetLogger(testLogger.Logger)
stdr.SetVerbosity(debugLogLevel) // set stdr's global log level to debug so the test logger will send output.
tok, err := Login(tt.issuer, tt.clientID, tok, err := Login(tt.issuer, tt.clientID,
WithContext(context.Background()), WithContext(context.Background()),
@ -1581,7 +1578,7 @@ func TestLogin(t *testing.T) { // nolint:gocyclo
WithScopes([]string{"test-scope"}), WithScopes([]string{"test-scope"}),
WithSkipBrowserOpen(), WithSkipBrowserOpen(),
tt.opt(t), tt.opt(t),
WithLogger(testLogger), WithLogger(testLogger.Logger),
) )
testLogger.Expect(tt.wantLogs) testLogger.Expect(tt.wantLogs)
if tt.wantErr != "" { if tt.wantErr != "" {