Merge pull request #3152 from vmware/fix/dependecy-bump

Bump dependencies and two small bug fixes
This commit is contained in:
Ryan Richard
2026-07-02 16:31:43 -07:00
committed by GitHub
34 changed files with 524 additions and 202 deletions
+2 -2
View File
@@ -3,8 +3,8 @@
# Copyright 2020-2026 the Pinniped contributors. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
ARG BUILD_IMAGE=golang:1.26.3@sha256:2981696eed011d747340d7252620932677929cce7d2d539602f56a8d7e9b660b
ARG BASE_IMAGE=gcr.io/distroless/static:nonroot@sha256:e3f945647ffb95b5839c07038d64f9811adf17308b9121d8a2b87b6a22a80a39
ARG BUILD_IMAGE=golang:1.26.4@sha256:f96cc555eb8db430159a3aa6797cd5bae561945b7b0fe7d0e284c63a3b291609
ARG BASE_IMAGE=gcr.io/distroless/static:nonroot@sha256:963fa6c544fe5ce420f1f54fb88b6fb01479f054c8056d0f74cc2c6000df5240
# Prepare to cross-compile by always running the build stage in the build platform, not the target platform.
FROM --platform=$BUILDPLATFORM $BUILD_IMAGE AS build-env
+26 -26
View File
@@ -6,11 +6,11 @@ require (
github.com/MakeNowJust/heredoc/v2 v2.0.1
github.com/chromedp/cdproto v0.0.0-20260321001828-e3e3800016bc
github.com/chromedp/chromedp v0.15.1
github.com/coreos/go-oidc/v3 v3.18.0
github.com/coreos/go-oidc/v3 v3.19.0
github.com/coreos/go-semver v0.3.1
github.com/creack/pty v1.1.24
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
github.com/felixge/httpsnoop v1.0.4
github.com/felixge/httpsnoop v1.1.0
github.com/go-jose/go-jose/v3 v3.0.5
github.com/go-jose/go-jose/v4 v4.1.4
github.com/go-ldap/ldap/v3 v3.4.13
@@ -18,9 +18,9 @@ require (
github.com/go-logr/stdr v1.2.2
github.com/go-logr/zapr v1.3.0
github.com/gofrs/flock v0.13.0
github.com/google/cel-go v0.28.0
github.com/google/cel-go v0.28.1
github.com/google/go-cmp v0.7.0
github.com/google/go-github/v86 v86.0.0
github.com/google/go-github/v87 v87.0.0
github.com/google/gofuzz v1.2.0
github.com/google/uuid v1.6.0
github.com/gorilla/securecookie v1.1.2
@@ -37,24 +37,24 @@ require (
github.com/tdewolff/minify/v2 v2.24.13
go.uber.org/mock v0.6.0
go.uber.org/zap v1.28.0
golang.org/x/crypto v0.51.0
golang.org/x/net v0.54.0
golang.org/x/crypto v0.53.0
golang.org/x/net v0.56.0
golang.org/x/oauth2 v0.36.0
golang.org/x/sync v0.20.0
golang.org/x/term v0.43.0
golang.org/x/text v0.37.0
k8s.io/api v0.36.0
k8s.io/apiextensions-apiserver v0.36.0
k8s.io/apimachinery v0.36.0
k8s.io/apiserver v0.36.0
k8s.io/client-go v0.36.0
k8s.io/component-base v0.36.0
golang.org/x/sync v0.21.0
golang.org/x/term v0.44.0
golang.org/x/text v0.38.0
k8s.io/api v0.36.2
k8s.io/apiextensions-apiserver v0.36.2
k8s.io/apimachinery v0.36.2
k8s.io/apiserver v0.36.2
k8s.io/client-go v0.36.2
k8s.io/component-base v0.36.2
k8s.io/gengo v0.0.0-20260408192533-25e2208e0dc3
k8s.io/klog/v2 v2.140.0
k8s.io/kube-aggregator v0.36.0
k8s.io/kube-openapi v0.0.0-20260509192518-b540ad9def2b
k8s.io/streaming v0.36.0
k8s.io/utils v0.0.0-20260507154919-ff6756f316d2
k8s.io/kube-aggregator v0.36.2
k8s.io/kube-openapi v0.0.0-20260624041617-8f3fa4921821
k8s.io/streaming v0.36.2
k8s.io/utils v0.0.0-20260626114624-be93311217bd
sigs.k8s.io/yaml v1.6.0
)
@@ -153,24 +153,24 @@ require (
go.opentelemetry.io/contrib/propagators/b3 v1.32.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.32.0 // indirect
go.opentelemetry.io/contrib/samplers/jaegerremote v0.26.0 // indirect
go.opentelemetry.io/otel v1.43.0 // indirect
go.opentelemetry.io/otel v1.44.0 // indirect
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 // indirect
go.opentelemetry.io/otel/exporters/zipkin v1.32.0 // indirect
go.opentelemetry.io/otel/metric v1.43.0 // indirect
go.opentelemetry.io/otel/metric v1.44.0 // indirect
go.opentelemetry.io/otel/sdk v1.43.0 // indirect
go.opentelemetry.io/otel/trace v1.43.0 // indirect
go.opentelemetry.io/otel/trace v1.44.0 // indirect
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect
golang.org/x/mod v0.35.0 // indirect
golang.org/x/sys v0.44.0 // indirect
golang.org/x/mod v0.36.0 // indirect
golang.org/x/sys v0.46.0 // indirect
golang.org/x/time v0.14.0 // indirect
golang.org/x/tools v0.44.0 // indirect
golang.org/x/tools v0.45.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/grpc v1.80.0 // indirect
@@ -181,7 +181,7 @@ require (
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/kms v0.36.0 // indirect
k8s.io/kms v0.36.2 // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
+54 -54
View File
@@ -81,8 +81,8 @@ github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnht
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/coreos/go-oidc v2.5.0+incompatible h1:6W0vGJR3Tu0r0PwfmjOrRZSlfxeEln8dsejt3ZWIvwo=
github.com/coreos/go-oidc v2.5.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/coreos/go-oidc/v3 v3.18.0 h1:V9orjXynvu5wiC9SemFTWnG4F45v403aIcjWo0d41+A=
github.com/coreos/go-oidc/v3 v3.18.0/go.mod h1:DYCf24+ncYi+XkIH97GY1+dqoRlbaSI26KVTCI9SrY4=
github.com/coreos/go-oidc/v3 v3.19.0 h1:F/xyOi3x1UnG1U27YVnM1N6bHiL1K2upi6U/0qr8r+I=
github.com/coreos/go-oidc/v3 v3.19.0/go.mod h1:DYCf24+ncYi+XkIH97GY1+dqoRlbaSI26KVTCI9SrY4=
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
@@ -120,8 +120,8 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/felixge/httpsnoop v1.1.0 h1:3YtUj32ZZkqZtt3sZZsClsymw/QDuVfpNhoA31zeORc=
github.com/felixge/httpsnoop v1.1.0/go.mod h1:Zqxgdd+1Rkcz8euOqdr7lqgCRJztwr5hp9vDSi5UZCE=
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
@@ -253,8 +253,8 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cel-go v0.28.0 h1:KjSWstCpz/MN5t4a8gnGJNIYUsJRpdi/r97xWDphIQc=
github.com/google/cel-go v0.28.0/go.mod h1:X0bD6iVNR8pkROSOoHVdgTkzmRcosof7WQqCD6wcMc8=
github.com/google/cel-go v0.28.1 h1:YWIwi77J4xIsYUwAF/iIuS6haffzIHS8yWI8glSbLWM=
github.com/google/cel-go v0.28.1/go.mod h1:X0bD6iVNR8pkROSOoHVdgTkzmRcosof7WQqCD6wcMc8=
github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -272,8 +272,8 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-github/v73 v73.0.0 h1:aR+Utnh+Y4mMkS+2qLQwcQ/cF9mOTpdwnzlaw//rG24=
github.com/google/go-github/v73 v73.0.0/go.mod h1:fa6w8+/V+edSU0muqdhCVY7Beh1M8F1IlQPZIANKIYw=
github.com/google/go-github/v86 v86.0.0 h1:S/6aANJhwRm8EQmGKVML3j41yq0h2BsTP8FnDkO7kcA=
github.com/google/go-github/v86 v86.0.0/go.mod h1:zKv1l4SwDXNFMGByi2FWkq71KwSXqj/eQRZuqtmcot8=
github.com/google/go-github/v87 v87.0.0 h1:9Ck3dcOxWJyfsN8tzdah4YvmqB/7ZsstMglv/PkOsl0=
github.com/google/go-github/v87 v87.0.0/go.mod h1:hGUoT5pwm/ck5uLL+wroSVQfg8mpe+buxllCcGV4VaM=
github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0=
github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -639,8 +639,8 @@ go.opentelemetry.io/contrib/propagators/jaeger v1.32.0 h1:K/fOyTMD6GELKTIJBaJ9k3
go.opentelemetry.io/contrib/propagators/jaeger v1.32.0/go.mod h1:ISE6hda//MTWvtngG7p4et3OCngsrTVfl7c6DjN17f8=
go.opentelemetry.io/contrib/samplers/jaegerremote v0.26.0 h1:/SKXyZLAnuj981HVc8G5ZylYK3qD2W6AYR6cJx5kIHw=
go.opentelemetry.io/contrib/samplers/jaegerremote v0.26.0/go.mod h1:cOEzME0M2OKeHB45lJiOKfvUCdg/r75mf7YS5w0tbmE=
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4=
go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k=
@@ -651,14 +651,14 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJK
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak=
go.opentelemetry.io/otel/exporters/zipkin v1.32.0 h1:6O8HgLHPXtXE9QEKEWkBImL9mEKCGEl+m+OncVO53go=
go.opentelemetry.io/otel/exporters/zipkin v1.32.0/go.mod h1:+MFvorlowjy0iWnsKaNxC1kzczSxe71mw85h4p8yEvg=
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@@ -698,8 +698,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -738,8 +738,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -779,8 +779,8 @@ golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfS
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -805,8 +805,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -860,10 +860,10 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/telemetry v0.0.0-20260409153401-be6f6cb8b1fa h1:efT73AJZfAAUV7SOip6pWGkwJDzIGiKBZGVzHYa+ve4=
golang.org/x/telemetry v0.0.0-20260409153401-be6f6cb8b1fa/go.mod h1:kHjTxDEnAu6/Nl9lDkzjWpR+bmKfxeiRuSDlsMb70gE=
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6 h1:HjU6IWBiAgRIdAJ9/y1rwCn+UELEmwV+VsTLzj/W4sE=
golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6/go.mod h1:Eqhaxk/wZsWEH8CRxLwj6xzEJbz7k1EFGqx7nyCoabE=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -872,8 +872,8 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -885,8 +885,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -950,8 +950,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1087,33 +1087,33 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.36.0 h1:SgqDhZzHdOtMk40xVSvCXkP9ME0H05hPM3p9AB1kL80=
k8s.io/api v0.36.0/go.mod h1:m1LVrGPNYax5NBHdO+QuAedXyuzTt4RryI/qnmNvs34=
k8s.io/apiextensions-apiserver v0.36.0 h1:Wt7E8J+VBCbj4FjiBfDTK/neXDDjyJVJc7xfuOHImZ0=
k8s.io/apiextensions-apiserver v0.36.0/go.mod h1:kGDjH0msuiIB3tgsYRV0kS9GqpMYMUsQ3GHv7TApyug=
k8s.io/apimachinery v0.36.0 h1:jZyPzhd5Z+3h9vJLt0z9XdzW9VzNzWAUw+P1xZ9PXtQ=
k8s.io/apimachinery v0.36.0/go.mod h1:FklypaRJt6n5wUIwWXIP6GJlIpUizTgfo1T/As+Tyxc=
k8s.io/apiserver v0.36.0 h1:Jg5OFAENUACByUCg15CmhZAYrr5ZyJ+jodyA1mHl3YE=
k8s.io/apiserver v0.36.0/go.mod h1:mHvwdHf+qKEm+1/hYm756SV+oREOKSPnsjagOpx6Vho=
k8s.io/client-go v0.36.0 h1:pOYi7C4RHChYjMiHpZSpSbIM6ZxVbRXBy7CuiIwqA3c=
k8s.io/client-go v0.36.0/go.mod h1:ZKKcpwF0aLYfkHFCjillCKaTK/yBkEDHTDXCFY6AS9Y=
k8s.io/component-base v0.36.0 h1:hFjEktssxiJhrK1zfybkH4kJOi8iZuF+mIDCqS5+jRo=
k8s.io/component-base v0.36.0/go.mod h1:JZvIfcNHk+uck+8LhJzhSBtydWXaZNQwX2OdL+Mnwsk=
k8s.io/api v0.36.2 h1:TF6YDLIzKfccK7cq9YpTcGX8TJmEkHVRv78DM51fRYY=
k8s.io/api v0.36.2/go.mod h1:F4LbMO4brjZYh7yFkXWhynSvtB7YauxV4c+HHkNRGNg=
k8s.io/apiextensions-apiserver v0.36.2 h1:3O5gqOj/dt2XWWbpMe+TXWpE9yU6pjM/tXxtHHJT/K4=
k8s.io/apiextensions-apiserver v0.36.2/go.mod h1:cL1tBWe8XSaP1H30iWKGo7hf6iAUUUJPEU70dskmAnA=
k8s.io/apimachinery v0.36.2 h1:0PE/W/WNy1UX61NLbXY5TMbJ6UwLL6E6lAPkYrKFxbQ=
k8s.io/apimachinery v0.36.2/go.mod h1:fvf/HOLXq9RId0rnDIbN1OEBvHXdQbLMM8nu0LcBUf4=
k8s.io/apiserver v0.36.2 h1:6vMnkmHZPeBloNkHUhmZYq7Ylv8WIB8xjyEl+eSt26E=
k8s.io/apiserver v0.36.2/go.mod h1:9PoQ2ikCytrZyZg11mGhLEF5m8Rgsb5FJmYJ4Wvnl1k=
k8s.io/client-go v0.36.2 h1:bfgxmFKc9CgqsgX4xKLAAdmTQlWee7Ob/HlDOrJ5TBI=
k8s.io/client-go v0.36.2/go.mod h1:1vgO4OAlfPnoLcb+Rze2GF5rAr14w8qjrYMoyXJzQj0=
k8s.io/component-base v0.36.2 h1:Z0VH80O7Ng0HDZnZj3WRR3urEGa0kTwmO8CwEwjVK1w=
k8s.io/component-base v0.36.2/go.mod h1:mGfFOA7Gwpdm1VW2cwSQYbiDIlz8GD2WGwH88QSeCyA=
k8s.io/gengo v0.0.0-20260408192533-25e2208e0dc3 h1:rf5GFLj5SnELuHVF2S6RVkUGJzC1KEiQvWFbxmgI2mM=
k8s.io/gengo v0.0.0-20260408192533-25e2208e0dc3/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc=
k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0=
k8s.io/kms v0.36.0 h1:DPy0VDWi6hCgFMgzV5cNuSDrIROMRcJpTZ1GnB+D368=
k8s.io/kms v0.36.0/go.mod h1:g91diTD9h0oJCCHkTb00krlF+Qm5HTnkWLi9Q/TpRoc=
k8s.io/kube-aggregator v0.36.0 h1:yrR+aw41p4/Wur55FCcfozPSBHH70HIs5j+J6ZphExg=
k8s.io/kube-aggregator v0.36.0/go.mod h1:2CkdUvPZjEbKnlhn+wxj6z3yity7H4xsTrFX+M/t1UE=
k8s.io/kube-openapi v0.0.0-20260509192518-b540ad9def2b h1:uMjR3gZY5tZj+VduN5sj0nsvWUxuvoMVU0yQ0xk6yNA=
k8s.io/kube-openapi v0.0.0-20260509192518-b540ad9def2b/go.mod h1:V/QaCUYDa+0QpcHhVVc5l99Uz56wEMEXBSj9oCDkNDY=
k8s.io/streaming v0.36.0 h1:agnTxU+NFulUrtYzXUGKO3ndEa8jKwht1Kwn9nu9x+4=
k8s.io/streaming v0.36.0/go.mod h1:z6fV3D+NVkoeqRMtWwlUZK6U17SY/LqNzOxWL6GyR/s=
k8s.io/utils v0.0.0-20260507154919-ff6756f316d2 h1:wU4tMEhLGgIbLvXQb1cfN+EcM0wf7zC6CPF+C79jroc=
k8s.io/utils v0.0.0-20260507154919-ff6756f316d2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
k8s.io/kms v0.36.2 h1:o0l8zMRecm38k5NyRnO/3+2YA/MR6TMGcc3KQZA76hI=
k8s.io/kms v0.36.2/go.mod h1:g91diTD9h0oJCCHkTb00krlF+Qm5HTnkWLi9Q/TpRoc=
k8s.io/kube-aggregator v0.36.2 h1:zfeH9Fs16oDquNfBZef3M27dGG3QtJ9/TwkWfBlw1lo=
k8s.io/kube-aggregator v0.36.2/go.mod h1:UMrB5DfEhznFTf0bqYW2SV26GDy8HNaxoYakvKVWZ8M=
k8s.io/kube-openapi v0.0.0-20260624041617-8f3fa4921821 h1:m2wZhD5+vJZyCVkTvUHIfaiXc/mdt3Pxyx3vUnGsKzU=
k8s.io/kube-openapi v0.0.0-20260624041617-8f3fa4921821/go.mod h1:V/QaCUYDa+0QpcHhVVc5l99Uz56wEMEXBSj9oCDkNDY=
k8s.io/streaming v0.36.2 h1:NSKthPPg9UFSKsRauVJUVGH2Dvn8fhKmY4qrMkw/p98=
k8s.io/streaming v0.36.2/go.mod h1:z6fV3D+NVkoeqRMtWwlUZK6U17SY/LqNzOxWL6GyR/s=
k8s.io/utils v0.0.0-20260626114624-be93311217bd h1:Ea7fgQ5we8Y9T0OX5o0dAHzQOBRI07D/dEYRaB9ZZEs=
k8s.io/utils v0.0.0-20260626114624-be93311217bd/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+2 -2
View File
@@ -16,8 +16,8 @@
# See https://go.googlesource.com/go/+/dev.boringcrypto/README.boringcrypto.md
# and https://kupczynski.info/posts/fips-golang/ for details.
ARG BUILD_IMAGE=golang:1.26.3@sha256:2981696eed011d747340d7252620932677929cce7d2d539602f56a8d7e9b660b
ARG BASE_IMAGE=gcr.io/distroless/static:nonroot@sha256:e3f945647ffb95b5839c07038d64f9811adf17308b9121d8a2b87b6a22a80a39
ARG BUILD_IMAGE=golang:1.26.4@sha256:f96cc555eb8db430159a3aa6797cd5bae561945b7b0fe7d0e284c63a3b291609
ARG BASE_IMAGE=gcr.io/distroless/static:nonroot@sha256:963fa6c544fe5ce420f1f54fb88b6fb01479f054c8056d0f74cc2c6000df5240
# This is not currently using --platform to prepare to cross-compile because we use gcc below to build
# platform-specific GCO code. This makes multi-arch builds slow due to target platform emulation.
+1 -1
View File
@@ -1,2 +1,2 @@
Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
Copyright 2020-2026 the Pinniped contributors. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
+9 -1
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
# Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
# Copyright 2020-2026 the Pinniped contributors. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
#
@@ -431,6 +431,10 @@ spec:
podSelector:
matchLabels:
app: proxy
# Allowed to talk to the API Server (and other kube-system pods) which Kind puts in this IP range.
- to:
- ipBlock:
cidr: 172.18.0.0/16 # Default internal KIND Docker bridge network
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
@@ -457,6 +461,10 @@ spec:
podSelector:
matchLabels:
app: proxy
# Allowed to talk to the API Server (and other kube-system pods) which Kind puts in this IP range.
- to:
- ipBlock:
cidr: 172.18.0.0/16 # Default internal KIND Docker bridge network
EOF
fi
+7 -7
View File
@@ -596,7 +596,7 @@ func TestTransformer(t *testing.T) {
&UsernameTransformation{Expression: `groups.filter(x, groups.all(x, true))[0]`},
},
ctx: alreadyCancelledContext,
wantEvaluationErr: `identity transformation at index 0: context canceled`,
wantEvaluationErr: `identity transformation at index 0: operation interrupted: context canceled`,
},
{
name: "slow groups transformation expressions are canceled by the cancel context after partial evaluation",
@@ -606,7 +606,7 @@ func TestTransformer(t *testing.T) {
&GroupsTransformation{Expression: `groups.filter(x, groups.all(x, true))`},
},
ctx: alreadyCancelledContext,
wantEvaluationErr: `identity transformation at index 0: context canceled`,
wantEvaluationErr: `identity transformation at index 0: operation interrupted: context canceled`,
},
{
name: "slow policy expressions are canceled by the cancel context after partial evaluation",
@@ -617,7 +617,7 @@ func TestTransformer(t *testing.T) {
&AllowAuthenticationPolicy{Expression: `groups.all(x, groups.all(x, true))`}, // this is the slow one
},
ctx: alreadyCancelledContext,
wantEvaluationErr: `identity transformation at index 1: context canceled`,
wantEvaluationErr: `identity transformation at index 1: operation interrupted: context canceled`,
},
{
name: "slow transformation expressions are canceled and the rest of the expressions do not run",
@@ -630,7 +630,7 @@ func TestTransformer(t *testing.T) {
&UsernameTransformation{Expression: `groups.filter(x, groups.all(x, true))[0]`},
},
ctx: alreadyCancelledContext,
wantEvaluationErr: `identity transformation at index 1: context canceled`,
wantEvaluationErr: `identity transformation at index 1: operation interrupted: context canceled`,
},
{
name: "slow username transformation expressions are canceled after a maximum allowed duration",
@@ -640,7 +640,7 @@ func TestTransformer(t *testing.T) {
// On my laptop, evaluating this expression would take ~20 seconds if we allowed it to evaluate to completion.
&UsernameTransformation{Expression: `groups.filter(x, groups.all(x, true))[0]`},
},
wantEvaluationErr: `identity transformation at index 0: context deadline exceeded`,
wantEvaluationErr: `identity transformation at index 0: operation interrupted: context deadline exceeded`,
},
{
name: "slow groups transformation expressions are canceled after a maximum allowed duration",
@@ -650,7 +650,7 @@ func TestTransformer(t *testing.T) {
// On my laptop, evaluating this expression would take ~20 seconds if we allowed it to evaluate to completion.
&GroupsTransformation{Expression: `groups.filter(x, groups.all(x, true))`},
},
wantEvaluationErr: `identity transformation at index 0: context deadline exceeded`,
wantEvaluationErr: `identity transformation at index 0: operation interrupted: context deadline exceeded`,
},
{
name: "slow policy transformation expressions are canceled after a maximum allowed duration",
@@ -660,7 +660,7 @@ func TestTransformer(t *testing.T) {
// On my laptop, evaluating this expression would take ~20 seconds if we allowed it to evaluate to completion.
&AllowAuthenticationPolicy{Expression: `groups.all(x, groups.all(x, true))`},
},
wantEvaluationErr: `identity transformation at index 0: context deadline exceeded`,
wantEvaluationErr: `identity transformation at index 0: operation interrupted: context deadline exceeded`,
},
{
name: "compile errors are returned by the compile step for a username transform",
@@ -1,4 +1,4 @@
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2021-2026 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Package activedirectoryupstreamwatcher implements a controller which watches ActiveDirectoryIdentityProviders.
@@ -8,7 +8,6 @@ import (
"context"
"encoding/base64"
"fmt"
"regexp"
"strconv"
"strings"
@@ -454,14 +453,29 @@ func groupSAMAccountNameWithDomainSuffix(entry *ldap.Entry) (string, error) {
return sAMAccountName + "@" + domain, nil
}
var domainComponentsRegexp = regexp.MustCompile(",DC=|,dc=")
func getDomainFromDistinguishedName(distinguishedName string) (string, error) {
domainComponents := domainComponentsRegexp.Split(distinguishedName, -1)
if len(domainComponents) == 1 {
return "", fmt.Errorf("did not find domain components in group dn: %s", distinguishedName)
parsedDN, err := ldap.ParseDN(distinguishedName)
if err != nil {
return "", fmt.Errorf("could not parse DN %q: %w", distinguishedName, err)
}
return strings.Join(domainComponents[1:], "."), nil
var dcValues []string
// Iterate through the Relative Distinguished Names (RDNs)
for _, rdn := range parsedDN.RDNs {
// Each RDN can contain multiple attribute-value pairs (usually just one)
for _, attr := range rdn.Attributes {
// Compare case-insensitively since LDAP attributes are case-insensitive
if strings.EqualFold(attr.Type, "dc") {
dcValues = append(dcValues, attr.Value)
}
}
}
if len(dcValues) == 0 {
return "", fmt.Errorf("did not find domain components in group DN %q", distinguishedName)
}
return strings.Join(dcValues, "."), nil
}
//nolint:gochecknoglobals // this needs to be a global variable so that tests can check pointer equality
@@ -2481,14 +2481,35 @@ func TestGroupSAMAccountNameWithDomainSuffix(t *testing.T) {
wantResult: "Mammals@mycompany.example.com",
},
{
name: "no domain components in DN",
name: "unusual CN with DN and valid sAMAccountName",
entry: &ldap.Entry{
DN: "no-domain-components",
// A backslash within a CN escapes the next character, so we want to ignore the letters "DC=" there.
DN: `CN=animals\,DC=should-be-ignored-because-it-is-part-of-cn,OU=Users,OU=pinniped-ad,DC=mycompany,DC=example,DC=com`,
Attributes: []*ldap.EntryAttribute{
ldap.NewEntryAttribute("sAMAccountName", []string{"Mammals"}),
},
},
wantErr: "did not find domain components in group dn: no-domain-components",
wantResult: "Mammals@mycompany.example.com",
},
{
name: "invalid DN",
entry: &ldap.Entry{
DN: "invalid-dn",
Attributes: []*ldap.EntryAttribute{
ldap.NewEntryAttribute("sAMAccountName", []string{"Mammals"}),
},
},
wantErr: `could not parse DN "invalid-dn": DN ended with incomplete type, value pair`,
},
{
name: "no domain components in DN",
entry: &ldap.Entry{
DN: "CN=foo",
Attributes: []*ldap.EntryAttribute{
ldap.NewEntryAttribute("sAMAccountName", []string{"Mammals"}),
},
},
wantErr: `did not find domain components in group DN "CN=foo"`,
},
{
name: "multiple values for sAMAccountName attribute",
@@ -2576,9 +2597,19 @@ func TestGetDomainFromDistinguishedName(t *testing.T) {
wantDomain: "activedirectory.mycompany.example.com",
},
{
name: "no domain components",
name: "unusual CN",
distinguishedName: `CN=animals\,DC=should-be-ignored-because-it-is-part-of-cn,OU=Users,OU=pinniped-ad,dc=activedirectory,DC=mycompany,DC=example,DC=com`,
wantDomain: "activedirectory.mycompany.example.com",
},
{
name: "invalid DN",
distinguishedName: "not-a-dn",
wantErr: "did not find domain components in group dn: not-a-dn",
wantErr: `could not parse DN "not-a-dn": DN ended with incomplete type, value pair`,
},
{
name: "no domain components",
distinguishedName: "CN=foo",
wantErr: `did not find domain components in group DN "CN=foo"`,
},
}
@@ -1,4 +1,4 @@
// Copyright 2022-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2022-2026 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package login
@@ -52,6 +52,8 @@ func NewHandler(
auditLogger plog.AuditLogger,
) http.Handler {
loginHandler := httperr.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
r.Body = http.MaxBytesReader(w, r.Body, 1<<20) // 1 MB limit
if err := auditLogger.AuditRequestParams(r, paramsSafeToLog()); err != nil {
plog.DebugErr("error parsing callback request params", err)
return httperr.New(http.StatusBadRequest, "error parsing request params")
@@ -4,6 +4,7 @@
package login
import (
"io"
"net/http"
"net/http/httptest"
"net/url"
@@ -116,6 +117,8 @@ func TestLoginEndpoint(t *testing.T) {
method string
path string
csrfCookie string
contentType string
formParams url.Values
getHandlerErr error
postHandlerErr error
@@ -350,6 +353,17 @@ func TestLoginEndpoint(t *testing.T) {
wantEncodedState: happyState,
wantDecodedState: expectedHappyDecodedUpstreamStateParam(),
},
{
name: "POST with body too large",
method: http.MethodPost,
path: happyPathWithState,
csrfCookie: happyCSRFCookie,
contentType: "application/x-www-form-urlencoded",
formParams: map[string][]string{"k": {strings.Repeat("v", 1_048_576)}},
wantStatus: http.StatusBadRequest,
wantContentType: htmlContentType,
wantBody: "Bad Request: error parsing request params\n",
},
{
name: "happy GET request with err param which can be set by the real POST handler on redirects back to the GET handler",
method: http.MethodGet,
@@ -461,10 +475,19 @@ func TestLoginEndpoint(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
req := httptest.NewRequestWithContext(t.Context(), test.method, test.path, nil)
var body io.Reader
if test.method == http.MethodPost && len(test.formParams) > 0 {
body = strings.NewReader(test.formParams.Encode())
}
req := httptest.NewRequestWithContext(t.Context(), test.method, test.path, body)
if test.csrfCookie != "" {
req.Header.Set("Cookie", test.csrfCookie)
}
if test.contentType != "" {
req.Header.Set("Content-Type", test.contentType)
}
req, _ = auditid.NewRequestWithAuditID(req, func() string { return "fake-audit-id" })
rsp := httptest.NewRecorder()
@@ -2149,9 +2149,10 @@ func TestRefreshGrant(t *testing.T) {
return &expectedGithubUpstreamRefresh{
performedByUpstreamName: githubUpstreamName,
args: &oidctestutil.GetUserArgs{
Ctx: nil, // this will be filled in with the actual request context by the test below
AccessToken: githubUpstreamAccessToken,
IDPDisplayName: githubUpstreamName,
Ctx: nil, // this will be filled in with the actual request context by the test below
AccessToken: githubUpstreamAccessToken,
IDPDisplayName: githubUpstreamName,
RetryOnUnauthorized: upstreamprovider.DoNotRetryOnUnauthorized,
},
}
}
@@ -1,4 +1,4 @@
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2026 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package resolvedgithub
@@ -108,7 +108,7 @@ func (p *FederationDomainResolvedGitHubIdentityProvider) LoginFromCallback(
)
}
user, err := p.Provider.GetUser(ctx, accessToken, p.GetDisplayName())
user, err := p.Provider.GetUser(ctx, accessToken, p.GetDisplayName(), upstreamprovider.RetryOnUnauthorized)
if errors.As(err, &upstreamprovider.GitHubLoginDeniedError{}) {
// We specifically want errors of type GitHubLoginDeniedError to have a user-displayed message.
@@ -153,7 +153,11 @@ func (p *FederationDomainResolvedGitHubIdentityProvider) UpstreamRefresh(
}
// Get the user's GitHub identity and groups again using the cached access token.
refreshedUserInfo, err := p.Provider.GetUser(ctx, githubSessionData.UpstreamAccessToken, p.GetDisplayName())
refreshedUserInfo, err := p.Provider.GetUser(ctx,
githubSessionData.UpstreamAccessToken,
p.GetDisplayName(),
upstreamprovider.DoNotRetryOnUnauthorized,
)
if err != nil {
return nil, p.refreshErr(err)
}
@@ -153,9 +153,10 @@ func TestLoginFromCallback(t *testing.T) {
},
wantGetUserCall: true,
wantGetUserArgs: &oidctestutil.GetUserArgs{
Ctx: uniqueCtx,
AccessToken: "fake-access-token",
IDPDisplayName: "fake-display-name",
Ctx: uniqueCtx,
AccessToken: "fake-access-token",
IDPDisplayName: "fake-display-name",
RetryOnUnauthorized: upstreamprovider.RetryOnUnauthorized,
},
wantIdentity: &resolvedprovider.Identity{
UpstreamUsername: "fake-username",
@@ -205,9 +206,10 @@ func TestLoginFromCallback(t *testing.T) {
},
wantGetUserCall: true,
wantGetUserArgs: &oidctestutil.GetUserArgs{
Ctx: uniqueCtx,
AccessToken: "fake-access-token",
IDPDisplayName: "fake-display-name",
Ctx: uniqueCtx,
AccessToken: "fake-access-token",
IDPDisplayName: "fake-display-name",
RetryOnUnauthorized: upstreamprovider.RetryOnUnauthorized,
},
wantIdentity: nil,
wantExtras: nil,
@@ -232,9 +234,10 @@ func TestLoginFromCallback(t *testing.T) {
},
wantGetUserCall: true,
wantGetUserArgs: &oidctestutil.GetUserArgs{
Ctx: uniqueCtx,
AccessToken: "fake-access-token",
IDPDisplayName: "fake-display-name",
Ctx: uniqueCtx,
AccessToken: "fake-access-token",
IDPDisplayName: "fake-display-name",
RetryOnUnauthorized: upstreamprovider.RetryOnUnauthorized,
},
wantIdentity: nil,
wantExtras: nil,
@@ -324,9 +327,10 @@ func TestUpstreamRefresh(t *testing.T) {
idpDisplayName: "fake-display-name",
wantGetUserCall: true,
wantGetUserArgs: &oidctestutil.GetUserArgs{
Ctx: uniqueCtx,
AccessToken: "fake-access-token",
IDPDisplayName: "fake-display-name",
Ctx: uniqueCtx,
AccessToken: "fake-access-token",
IDPDisplayName: "fake-display-name",
RetryOnUnauthorized: upstreamprovider.DoNotRetryOnUnauthorized,
},
wantRefreshedIdentity: &resolvedprovider.RefreshedIdentity{
UpstreamUsername: "refreshed-username",
@@ -349,9 +353,10 @@ func TestUpstreamRefresh(t *testing.T) {
idpDisplayName: "fake-display-name",
wantGetUserCall: true,
wantGetUserArgs: &oidctestutil.GetUserArgs{
Ctx: uniqueCtx,
AccessToken: "fake-access-token",
IDPDisplayName: "fake-display-name",
Ctx: uniqueCtx,
AccessToken: "fake-access-token",
IDPDisplayName: "fake-display-name",
RetryOnUnauthorized: upstreamprovider.DoNotRetryOnUnauthorized,
},
wantRefreshedIdentity: nil,
wantWrappedErr: "fake github GetUser error message",
@@ -407,9 +412,10 @@ func TestUpstreamRefresh(t *testing.T) {
idpDisplayName: "fake-display-name",
wantGetUserCall: true,
wantGetUserArgs: &oidctestutil.GetUserArgs{
Ctx: uniqueCtx,
AccessToken: "fake-access-token",
IDPDisplayName: "fake-display-name",
Ctx: uniqueCtx,
AccessToken: "fake-access-token",
IDPDisplayName: "fake-display-name",
RetryOnUnauthorized: upstreamprovider.DoNotRetryOnUnauthorized,
},
wantRefreshedIdentity: nil,
wantWrappedErr: `user's calculated downstream subject at initial login was "https://fake-downstream-subject" ` +
@@ -1,4 +1,4 @@
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2026 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package upstreamprovider
@@ -26,6 +26,18 @@ const (
AccessTokenType RevocableTokenType = "access_token"
)
// UnauthorizedRetryBehavior controls whether GetUser retries after a 401 from the GitHub user API.
type UnauthorizedRetryBehavior bool
const (
// RetryOnUnauthorized retries a bounded number of times after a short delay, since GitHub
// occasionally returns a transient 401 for a token that was just issued.
RetryOnUnauthorized UnauthorizedRetryBehavior = true
// DoNotRetryOnUnauthorized fails immediately on a 401 (e.g. during a refresh, where a 401
// indicates a genuinely revoked or invalid token).
DoNotRetryOnUnauthorized UnauthorizedRetryBehavior = false
)
// LDAPRefreshAttributes contains information about the user from the original login request
// and previous refreshes to be used during an LDAP session refresh.
type LDAPRefreshAttributes struct {
@@ -189,5 +201,8 @@ type UpstreamGithubIdentityProviderI interface {
// GetUser calls the user, orgs, and teams APIs of GitHub using the accessToken.
// It validates any required org memberships. It returns a User or an error.
// The IDP display name is passed to aid in building a suitable downstream subject string.
GetUser(ctx context.Context, accessToken string, idpDisplayName string) (*GitHubUser, error)
// If retryOnUnauthorized is true, a 401 from the user API will be retried a bounded number of
// times after a short delay, to work around GitHub occasionally returning a transient 401 for a
// token that was just issued.
GetUser(ctx context.Context, accessToken string, idpDisplayName string, retryOnUnauthorized UnauthorizedRetryBehavior) (*GitHubUser, error)
}
+59 -8
View File
@@ -11,8 +11,9 @@ import (
"net/url"
"slices"
"strings"
"time"
"github.com/google/go-github/v86/github"
"github.com/google/go-github/v87/github"
"k8s.io/apimachinery/pkg/util/sets"
"go.pinniped.dev/internal/plog"
@@ -22,6 +23,13 @@ import (
const (
emptyUserMeansTheAuthenticatedUser = ""
pageSize = 100
// defaultUnauthorizedRetryDelay and defaultUnauthorizedMaxRetries control how GetUserInfo
// retries when the GitHub user API returns a 401 for a token that was just issued. GitHub
// sometimes returns a transient 401 for a valid, freshly issued token before it has fully
// propagated; hopefully waiting briefly and retrying will resolve it.
defaultUnauthorizedRetryDelay = 1 * time.Second
defaultUnauthorizedMaxRetries = 2
)
type UserInfo struct {
@@ -36,13 +44,18 @@ type TeamInfo struct {
}
type GitHubInterface interface {
GetUserInfo(ctx context.Context) (*UserInfo, error)
GetUserInfo(ctx context.Context, retryOnUnauthorized bool) (*UserInfo, error)
GetOrgMembership(ctx context.Context) ([]string, error)
GetTeamMembership(ctx context.Context, allowedOrganizations *setutil.CaseInsensitiveSet) ([]TeamInfo, error)
}
type githubClient struct {
client *github.Client
// unauthorizedRetryDelay and unauthorizedMaxRetries configure GetUserInfo's retry-on-401
// behavior. They are fields so that unit tests can adjust them.
unauthorizedRetryDelay time.Duration
unauthorizedMaxRetries int
}
var _ GitHubInterface = (*githubClient)(nil)
@@ -71,19 +84,57 @@ func NewGitHubClient(httpClient *http.Client, apiBaseURL, token string) (GitHubI
return nil, fmt.Errorf("%s: token cannot be empty string", errorPrefix)
}
client := github.NewClient(httpClient).WithAuthToken(token)
client.BaseURL = parsedURL
// go-github's WithEnterpriseURLs requires a non-empty upload URL even though
// Pinniped only calls read endpoints (Users.Get, Organizations.List,
// Teams.ListUserTeams), all of which use the base URL. The upload URL is never
// exercised today. It should be updated if an upload-style call is ever used.
client, err := github.NewClient(
github.WithHTTPClient(httpClient),
github.WithAuthToken(token),
github.WithEnterpriseURLs(parsedURL.String(), parsedURL.String()),
)
if err != nil {
return nil, fmt.Errorf("%s: %w", errorPrefix, err)
}
return &githubClient{
client: client,
client: client,
unauthorizedRetryDelay: defaultUnauthorizedRetryDelay,
unauthorizedMaxRetries: defaultUnauthorizedMaxRetries,
}, nil
}
// GetUserInfo returns the "Login" and "ID" attributes of the logged-in user.
func (g *githubClient) GetUserInfo(ctx context.Context) (*UserInfo, error) {
// isUnauthorized returns true only when err is a *github.ErrorResponse for an HTTP 401. GitHub's
// rate-limiting conditions are surfaced by go-github as the distinct *github.RateLimitError and
// *github.AbuseRateLimitError types (HTTP 403/429), so this check can never match those and cannot
// interfere with go-github's rate-limit handling.
func isUnauthorized(err error) bool {
var errResp *github.ErrorResponse
return errors.As(err, &errResp) &&
errResp.Response != nil &&
errResp.Response.StatusCode == http.StatusUnauthorized
}
// GetUserInfo returns the "Login" and "ID" attributes of the logged-in user. If retryOnUnauthorized
// is true and GitHub responds with a 401, the request is retried a bounded number of times after a
// short delay, since GitHub sometimes returns a transient 401 for a token that was just issued.
func (g *githubClient) GetUserInfo(ctx context.Context, retryOnUnauthorized bool) (*UserInfo, error) {
const errorPrefix = "error fetching authenticated user"
user, _, err := g.client.Users.Get(ctx, emptyUserMeansTheAuthenticatedUser)
var user *github.User
var err error
for attempt := 0; ; attempt++ {
user, _, err = g.client.Users.Get(ctx, emptyUserMeansTheAuthenticatedUser)
if err == nil || !retryOnUnauthorized || !isUnauthorized(err) || attempt >= g.unauthorizedMaxRetries {
break
}
plog.Debug("got 401 from GitHub user endpoint", "attempt", attempt+1)
select {
case <-ctx.Done():
return nil, fmt.Errorf("%s: %w", errorPrefix, ctx.Err())
case <-time.After(g.unauthorizedRetryDelay):
}
}
if err != nil {
return nil, fmt.Errorf("%s: %w", errorPrefix, err)
}
+134 -19
View File
@@ -8,8 +8,9 @@ import (
"net/http"
"strings"
"testing"
"time"
"github.com/google/go-github/v86/github"
"github.com/google/go-github/v87/github"
"github.com/migueleliasweb/go-github-mock/src/mock"
"github.com/stretchr/testify/require"
"k8s.io/client-go/util/cert"
@@ -106,8 +107,8 @@ func TestNewGitHubClient(t *testing.T) {
require.NotNil(t, actualI)
actual, ok := actualI.(*githubClient)
require.True(t, ok)
require.NotNil(t, actual.client.BaseURL)
require.Equal(t, test.wantBaseURL, actual.client.BaseURL.String())
require.NotNil(t, actual.client)
require.Equal(t, test.wantBaseURL, actual.client.BaseURL())
// Force the githubClient's httpClient roundTrippers to run and add the Authorization header
@@ -125,12 +126,13 @@ func TestGetUser(t *testing.T) {
t.Parallel()
tests := []struct {
name string
httpClient *http.Client
token string
ctx context.Context
wantErr string
wantUserInfo UserInfo
name string
httpClient *http.Client
token string
ctx context.Context
retryOnUnauthorized bool
wantErr string
wantUserInfo UserInfo
}{
{
name: "happy path",
@@ -227,16 +229,16 @@ func TestGetUser(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
githubClient := &githubClient{
client: github.NewClient(test.httpClient).WithAuthToken(test.token),
}
c, err := github.NewClient(github.WithHTTPClient(test.httpClient), github.WithAuthToken(test.token))
require.NoError(t, err)
githubClient := &githubClient{client: c}
ctx := context.Background()
if test.ctx != nil {
ctx = test.ctx
}
actual, err := githubClient.GetUserInfo(ctx)
actual, err := githubClient.GetUserInfo(ctx, test.retryOnUnauthorized)
if test.wantErr != "" {
rt, ok := test.httpClient.Transport.(*mock.EnforceHostRoundTripper)
require.True(t, ok)
@@ -251,6 +253,115 @@ func TestGetUser(t *testing.T) {
}
}
func TestGetUserInfoRetryOnUnauthorized(t *testing.T) {
t.Parallel()
tests := []struct {
name string
retryOnUnauthorized bool
retryDelay time.Duration // zero means no artificial delay
ctxTimeout time.Duration // zero means context.Background() with no timeout
handlerStatus int
handlerMessage string
succeedOnAttempt int // returns success instead of handlerStatus after this many attempts; 0 means never succeed
wantErrContains string
wantErrIs error
wantUserInfo *UserInfo
wantAttempts int
}{
{
name: "retries once on a 401 then succeeds, when retryOnUnauthorized is true",
retryOnUnauthorized: true,
handlerStatus: http.StatusUnauthorized,
handlerMessage: "bad credentials",
succeedOnAttempt: 2,
wantUserInfo: &UserInfo{Login: "some-username", ID: "12345678"},
wantAttempts: 2,
},
{
name: "exhausts retries and fails when every attempt returns a 401, when retryOnUnauthorized is true",
retryOnUnauthorized: true,
handlerStatus: http.StatusUnauthorized,
handlerMessage: "bad credentials",
wantErrContains: "401 bad credentials",
wantAttempts: 1 + defaultUnauthorizedMaxRetries,
},
{
name: "does not retry a 401 when retryOnUnauthorized is false",
retryOnUnauthorized: false,
handlerStatus: http.StatusUnauthorized,
handlerMessage: "bad credentials",
wantErrContains: "401 bad credentials",
wantAttempts: 1,
},
{
name: "does not retry a non-401 error even when retryOnUnauthorized is true",
retryOnUnauthorized: true,
handlerStatus: http.StatusForbidden,
handlerMessage: "rate limited",
wantErrContains: "403 rate limited",
wantAttempts: 1,
},
{
name: "returns promptly when the context is canceled during the retry wait",
retryOnUnauthorized: true,
retryDelay: time.Hour, // long enough that a real wait would fail the test
ctxTimeout: 50 * time.Millisecond,
handlerStatus: http.StatusUnauthorized,
handlerMessage: "bad credentials",
wantErrIs: context.DeadlineExceeded,
wantAttempts: 1,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
attempts := new(int)
httpClient := mock.NewMockedHTTPClient(
mock.WithRequestMatchHandler(mock.GetUser, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
*attempts++
if *attempts == test.succeedOnAttempt {
_, err := w.Write([]byte(`{"login":"some-username","id":12345678}`))
require.NoError(t, err)
return
}
mock.WriteError(w, test.handlerStatus, test.handlerMessage)
})),
)
c, err := github.NewClient(github.WithHTTPClient(httpClient), github.WithAuthToken("some-token"))
require.NoError(t, err)
githubClient := &githubClient{
client: c,
unauthorizedRetryDelay: test.retryDelay,
unauthorizedMaxRetries: defaultUnauthorizedMaxRetries,
}
ctx := context.Background()
if test.ctxTimeout != 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, test.ctxTimeout)
defer cancel()
}
actual, err := githubClient.GetUserInfo(ctx, test.retryOnUnauthorized)
require.Equal(t, test.wantAttempts, *attempts)
switch {
case test.wantErrContains != "":
require.ErrorContains(t, err, test.wantErrContains)
case test.wantErrIs != nil:
require.ErrorIs(t, err, test.wantErrIs)
default:
require.NoError(t, err)
require.Equal(t, test.wantUserInfo, actual)
}
})
}
}
func TestGetOrgMembership(t *testing.T) {
t.Parallel()
@@ -361,9 +472,9 @@ func TestGetOrgMembership(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
githubClient := &githubClient{
client: github.NewClient(test.httpClient).WithAuthToken(test.token),
}
c, err := github.NewClient(github.WithHTTPClient(test.httpClient), github.WithAuthToken(test.token))
require.NoError(t, err)
githubClient := &githubClient{client: c}
ctx := context.Background()
if test.ctx != nil {
@@ -700,6 +811,7 @@ func TestGetTeamMembership(t *testing.T) {
},
),
),
token: "fake-token",
wantErr: `error fetching team membership for authenticated user: missing the "organization" attribute for a team`,
},
{
@@ -716,6 +828,7 @@ func TestGetTeamMembership(t *testing.T) {
},
),
),
token: "fake-token",
wantErr: `error fetching team membership for authenticated user: missing the organization's "login" attribute for a team`,
},
{
@@ -733,6 +846,7 @@ func TestGetTeamMembership(t *testing.T) {
},
),
),
token: "fake-token",
wantErr: `error fetching team membership for authenticated user: the "name" attribute is missing for a team`,
},
{
@@ -750,6 +864,7 @@ func TestGetTeamMembership(t *testing.T) {
},
),
),
token: "fake-token",
wantErr: `error fetching team membership for authenticated user: the "slug" attribute is missing for a team`,
},
{
@@ -808,9 +923,9 @@ func TestGetTeamMembership(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
githubClient := &githubClient{
client: github.NewClient(test.httpClient).WithAuthToken(test.token),
}
c, err := github.NewClient(github.WithHTTPClient(test.httpClient), github.WithAuthToken(test.token))
require.NoError(t, err)
githubClient := &githubClient{client: c}
ctx := context.Background()
if test.ctx != nil {
@@ -1,4 +1,4 @@
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2026 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
@@ -1,4 +1,4 @@
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2026 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
@@ -1,4 +1,4 @@
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2026 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
@@ -77,16 +77,16 @@ func (mr *MockGitHubInterfaceMockRecorder) GetTeamMembership(ctx, allowedOrganiz
}
// GetUserInfo mocks base method.
func (m *MockGitHubInterface) GetUserInfo(ctx context.Context) (*githubclient.UserInfo, error) {
func (m *MockGitHubInterface) GetUserInfo(ctx context.Context, retryOnUnauthorized bool) (*githubclient.UserInfo, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetUserInfo", ctx)
ret := m.ctrl.Call(m, "GetUserInfo", ctx, retryOnUnauthorized)
ret0, _ := ret[0].(*githubclient.UserInfo)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetUserInfo indicates an expected call of GetUserInfo.
func (mr *MockGitHubInterfaceMockRecorder) GetUserInfo(ctx any) *gomock.Call {
func (mr *MockGitHubInterfaceMockRecorder) GetUserInfo(ctx, retryOnUnauthorized any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserInfo", reflect.TypeOf((*MockGitHubInterface)(nil).GetUserInfo), ctx)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserInfo", reflect.TypeOf((*MockGitHubInterface)(nil).GetUserInfo), ctx, retryOnUnauthorized)
}
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2026 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2026 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
@@ -1,4 +1,4 @@
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2026 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
@@ -1,4 +1,4 @@
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2026 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2026 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
@@ -1,4 +1,4 @@
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2026 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
@@ -1,4 +1,4 @@
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2026 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
@@ -1,4 +1,4 @@
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2026 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
@@ -1,4 +1,4 @@
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2026 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
@@ -1,4 +1,4 @@
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2026 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package oidctestutil
@@ -25,9 +25,10 @@ type ExchangeAuthcodeArgs struct {
// GetUserArgs is used to spy on calls to
// TestUpstreamGitHubIdentityProvider.GetUserFunc().
type GetUserArgs struct {
Ctx context.Context
AccessToken string
IDPDisplayName string
Ctx context.Context
AccessToken string
IDPDisplayName string
RetryOnUnauthorized upstreamprovider.UnauthorizedRetryBehavior
}
type TestUpstreamGitHubIdentityProviderBuilder struct {
@@ -239,15 +240,16 @@ func (u *TestUpstreamGitHubIdentityProvider) ExchangeAuthcodeArgs(call int) *Exc
return u.exchangeAuthcodeArgs[call]
}
func (u *TestUpstreamGitHubIdentityProvider) GetUser(ctx context.Context, accessToken string, idpDisplayName string) (*upstreamprovider.GitHubUser, error) {
func (u *TestUpstreamGitHubIdentityProvider) GetUser(ctx context.Context, accessToken string, idpDisplayName string, retryOnUnauthorized upstreamprovider.UnauthorizedRetryBehavior) (*upstreamprovider.GitHubUser, error) {
if u.getUserArgs == nil {
u.getUserArgs = make([]*GetUserArgs, 0)
}
u.getUserCallCount++
u.getUserArgs = append(u.getUserArgs, &GetUserArgs{
Ctx: ctx,
AccessToken: accessToken,
IDPDisplayName: idpDisplayName,
Ctx: ctx,
AccessToken: accessToken,
IDPDisplayName: idpDisplayName,
RetryOnUnauthorized: retryOnUnauthorized,
})
return u.GetUserFunc(ctx, accessToken)
}
+3 -3
View File
@@ -1,4 +1,4 @@
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
// Copyright 2020-2026 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Package upstreamgithub implements an abstraction of upstream GitHub provider interactions.
@@ -115,7 +115,7 @@ func (p *Provider) ExchangeAuthcode(ctx context.Context, authcode string, redire
// If the user's information meets the AllowedOrganization criteria specified on the GitHubIdentityProvider,
// they will be allowed to log in.
// Note that errors from the githubclient package already have helpful error prefixes, so there is no need for additional prefixes here.
func (p *Provider) GetUser(ctx context.Context, accessToken string, idpDisplayName string) (*upstreamprovider.GitHubUser, error) {
func (p *Provider) GetUser(ctx context.Context, accessToken string, idpDisplayName string, retryOnUnauthorized upstreamprovider.UnauthorizedRetryBehavior) (*upstreamprovider.GitHubUser, error) {
githubClient, err := p.buildGitHubClient(p.c.HttpClient, p.c.APIBaseURL, accessToken)
if err != nil {
return nil, err
@@ -123,7 +123,7 @@ func (p *Provider) GetUser(ctx context.Context, accessToken string, idpDisplayNa
githubUser := upstreamprovider.GitHubUser{}
userInfo, err := githubClient.GetUserInfo(ctx)
userInfo, err := githubClient.GetUserInfo(ctx, bool(retryOnUnauthorized))
if err != nil {
return nil, err
}
+35 -13
View File
@@ -207,6 +207,7 @@ func TestGetUser(t *testing.T) {
tests := []struct {
name string
providerConfig ProviderConfig
retryOnUnauthorized upstreamprovider.UnauthorizedRetryBehavior
buildGitHubClientError error
buildMockResponses func(hubInterface *mockgithubclient.MockGitHubInterface)
wantUser *upstreamprovider.GitHubUser
@@ -221,7 +222,28 @@ func TestGetUser(t *testing.T) {
UsernameAttribute: idpv1alpha1.GitHubUsernameLoginAndID,
},
buildMockResponses: func(mockGitHubInterface *mockgithubclient.MockGitHubInterface) {
mockGitHubInterface.EXPECT().GetUserInfo(someContext).Return(&githubclient.UserInfo{
mockGitHubInterface.EXPECT().GetUserInfo(someContext, false).Return(&githubclient.UserInfo{
Login: "some-github-login",
ID: "some-github-id",
}, nil)
mockGitHubInterface.EXPECT().GetOrgMembership(someContext).Return(nil, nil)
mockGitHubInterface.EXPECT().GetTeamMembership(someContext, gomock.Any()).Return(nil, nil)
},
wantUser: &upstreamprovider.GitHubUser{
Username: "some-github-login:some-github-id",
DownstreamSubject: fmt.Sprintf("https://some-url?idpName=%s&login=some-github-login&id=some-github-id", encodedIDPDisplayName),
},
},
{
name: "happy path with retryOnUnauthorized",
providerConfig: ProviderConfig{
APIBaseURL: "https://some-url",
HttpClient: someHttpClient,
UsernameAttribute: idpv1alpha1.GitHubUsernameLoginAndID,
},
retryOnUnauthorized: upstreamprovider.RetryOnUnauthorized,
buildMockResponses: func(mockGitHubInterface *mockgithubclient.MockGitHubInterface) {
mockGitHubInterface.EXPECT().GetUserInfo(someContext, true).Return(&githubclient.UserInfo{
Login: "some-github-login",
ID: "some-github-id",
}, nil)
@@ -241,7 +263,7 @@ func TestGetUser(t *testing.T) {
UsernameAttribute: idpv1alpha1.GitHubUsernameLogin,
},
buildMockResponses: func(mockGitHubInterface *mockgithubclient.MockGitHubInterface) {
mockGitHubInterface.EXPECT().GetUserInfo(someContext).Return(&githubclient.UserInfo{
mockGitHubInterface.EXPECT().GetUserInfo(someContext, false).Return(&githubclient.UserInfo{
Login: "some-github-login",
ID: "some-github-id",
}, nil)
@@ -261,7 +283,7 @@ func TestGetUser(t *testing.T) {
UsernameAttribute: idpv1alpha1.GitHubUsernameID,
},
buildMockResponses: func(mockGitHubInterface *mockgithubclient.MockGitHubInterface) {
mockGitHubInterface.EXPECT().GetUserInfo(someContext).Return(&githubclient.UserInfo{
mockGitHubInterface.EXPECT().GetUserInfo(someContext, false).Return(&githubclient.UserInfo{
Login: "some-github-login",
ID: "some-github-id",
}, nil)
@@ -282,7 +304,7 @@ func TestGetUser(t *testing.T) {
AllowedOrganizations: setutil.NewCaseInsensitiveSet("ALLOWED-ORG1", "ALLOWED-ORG2"),
},
buildMockResponses: func(mockGitHubInterface *mockgithubclient.MockGitHubInterface) {
mockGitHubInterface.EXPECT().GetUserInfo(someContext).Return(&githubclient.UserInfo{
mockGitHubInterface.EXPECT().GetUserInfo(someContext, false).Return(&githubclient.UserInfo{
Login: "some-github-login",
ID: "some-github-id",
}, nil)
@@ -303,7 +325,7 @@ func TestGetUser(t *testing.T) {
AllowedOrganizations: setutil.NewCaseInsensitiveSet("allowed-org"),
},
buildMockResponses: func(mockGitHubInterface *mockgithubclient.MockGitHubInterface) {
mockGitHubInterface.EXPECT().GetUserInfo(someContext).Return(&githubclient.UserInfo{
mockGitHubInterface.EXPECT().GetUserInfo(someContext, false).Return(&githubclient.UserInfo{
Login: "some-github-login",
ID: "some-github-id",
}, nil)
@@ -321,7 +343,7 @@ func TestGetUser(t *testing.T) {
GroupNameAttribute: idpv1alpha1.GitHubUseTeamNameForGroupName,
},
buildMockResponses: func(mockGitHubInterface *mockgithubclient.MockGitHubInterface) {
mockGitHubInterface.EXPECT().GetUserInfo(someContext).Return(&githubclient.UserInfo{
mockGitHubInterface.EXPECT().GetUserInfo(someContext, false).Return(&githubclient.UserInfo{
Login: "some-github-login",
ID: "some-github-id",
}, nil)
@@ -360,7 +382,7 @@ func TestGetUser(t *testing.T) {
GroupNameAttribute: idpv1alpha1.GitHubUseTeamSlugForGroupName,
},
buildMockResponses: func(mockGitHubInterface *mockgithubclient.MockGitHubInterface) {
mockGitHubInterface.EXPECT().GetUserInfo(someContext).Return(&githubclient.UserInfo{
mockGitHubInterface.EXPECT().GetUserInfo(someContext, false).Return(&githubclient.UserInfo{
Login: "some-github-login",
ID: "some-github-id",
}, nil)
@@ -405,7 +427,7 @@ func TestGetUser(t *testing.T) {
HttpClient: someHttpClient,
},
buildMockResponses: func(mockGitHubInterface *mockgithubclient.MockGitHubInterface) {
mockGitHubInterface.EXPECT().GetUserInfo(someContext).Return(nil, errors.New("error from githubClient.GetUserInfo"))
mockGitHubInterface.EXPECT().GetUserInfo(someContext, false).Return(nil, errors.New("error from githubClient.GetUserInfo"))
},
wantErrMsg: "error from githubClient.GetUserInfo",
},
@@ -417,7 +439,7 @@ func TestGetUser(t *testing.T) {
UsernameAttribute: idpv1alpha1.GitHubUsernameLoginAndID,
},
buildMockResponses: func(mockGitHubInterface *mockgithubclient.MockGitHubInterface) {
mockGitHubInterface.EXPECT().GetUserInfo(someContext).Return(&githubclient.UserInfo{}, nil)
mockGitHubInterface.EXPECT().GetUserInfo(someContext, false).Return(&githubclient.UserInfo{}, nil)
mockGitHubInterface.EXPECT().GetOrgMembership(someContext).Return(nil, errors.New("error from githubClient.GetOrgMembership"))
},
wantErrMsg: "error from githubClient.GetOrgMembership",
@@ -430,7 +452,7 @@ func TestGetUser(t *testing.T) {
UsernameAttribute: idpv1alpha1.GitHubUsernameLoginAndID,
},
buildMockResponses: func(mockGitHubInterface *mockgithubclient.MockGitHubInterface) {
mockGitHubInterface.EXPECT().GetUserInfo(someContext).Return(&githubclient.UserInfo{}, nil)
mockGitHubInterface.EXPECT().GetUserInfo(someContext, false).Return(&githubclient.UserInfo{}, nil)
mockGitHubInterface.EXPECT().GetOrgMembership(someContext).Return(nil, nil)
mockGitHubInterface.EXPECT().GetTeamMembership(someContext, gomock.Any()).Return(nil, errors.New("error from githubClient.GetTeamMembership"))
},
@@ -444,7 +466,7 @@ func TestGetUser(t *testing.T) {
UsernameAttribute: "this-is-not-legal-value-from-the-enum",
},
buildMockResponses: func(mockGitHubInterface *mockgithubclient.MockGitHubInterface) {
mockGitHubInterface.EXPECT().GetUserInfo(someContext).Return(&githubclient.UserInfo{
mockGitHubInterface.EXPECT().GetUserInfo(someContext, false).Return(&githubclient.UserInfo{
Login: "some-github-login",
ID: "some-github-id",
}, nil)
@@ -460,7 +482,7 @@ func TestGetUser(t *testing.T) {
GroupNameAttribute: "this-is-not-legal-value-from-the-enum",
},
buildMockResponses: func(mockGitHubInterface *mockgithubclient.MockGitHubInterface) {
mockGitHubInterface.EXPECT().GetUserInfo(someContext).Return(&githubclient.UserInfo{
mockGitHubInterface.EXPECT().GetUserInfo(someContext, false).Return(&githubclient.UserInfo{
Login: "some-github-login",
ID: "some-github-id",
}, nil)
@@ -498,7 +520,7 @@ func TestGetUser(t *testing.T) {
return mockGitHubInterface, test.buildGitHubClientError
}
actualUser, actualErr := p.GetUser(context.Background(), accessToken, idpDisplayName)
actualUser, actualErr := p.GetUser(context.Background(), accessToken, idpDisplayName, test.retryOnUnauthorized)
switch {
case test.wantErrMsg != "":
+20 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2024-2025 the Pinniped contributors. All Rights Reserved.
// Copyright 2024-2026 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package integration
@@ -14,6 +14,8 @@ import (
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
apiregistrationv1helper "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1/helper"
"sigs.k8s.io/yaml"
"go.pinniped.dev/internal/config/concierge"
@@ -216,6 +218,23 @@ func restartAllPodsOfApp(
requireEventually.Equal(len(newPods), int(originalScale), "wanted pods to return to original scale")
requireEventually.True(allPodsReady(newPods), "wanted all new pods to be ready")
}, 2*time.Minute, 200*time.Millisecond)
// Wait for all the APIServices belonging to this app to become available again.
aggregatedClient := testlib.NewAggregatedClientset(t)
testlib.RequireEventually(t, func(requireEventually *require.Assertions) {
listCtx, listCancel := context.WithTimeout(context.Background(), 1*time.Minute)
defer listCancel()
apiServices, err := aggregatedClient.ApiregistrationV1().APIServices().List(listCtx, metav1.ListOptions{
LabelSelector: "app=" + appName,
})
requireEventually.NoError(err)
for _, apiService := range apiServices.Items {
requireEventually.True(
apiregistrationv1helper.IsAPIServiceConditionTrue(&apiService, apiregistrationv1.Available),
"wanted APIService %q to have condition Available=True", apiService.Name,
)
}
}, 2*time.Minute, 200*time.Millisecond)
}
// Even if the test fails due to the below assertions, still try to scale back to original scale,
+9
View File
@@ -271,6 +271,11 @@ func (b *Browser) SendKeysToFirstMatch(t *testing.T, cssSelector string, runesTo
b.runWithTimeout(t, b.timeout(), chromedp.SendKeys(cssSelector, runesToType, chromedp.NodeVisible, chromedp.NodeEnabled, chromedp.ByQuery))
}
func (b *Browser) ClearFirstMatch(t *testing.T, cssSelector string) {
t.Helper()
b.runWithTimeout(t, b.timeout(), chromedp.Clear(cssSelector, chromedp.NodeVisible, chromedp.NodeEnabled, chromedp.ByQuery))
}
func (b *Browser) ClickFirstMatch(t *testing.T, cssSelector string) string {
t.Helper()
var text string
@@ -449,6 +454,10 @@ func handleGithubOTPLoginPage(t *testing.T, b *Browser, upstream testlib.TestGit
t.Logf("waiting for GitHub MFA page")
b.WaitForVisibleElements(t, otpSelector)
// We may be in a loop where we are going to re-try entering the OTP code again on the same page,
// so first clear out any old OTP code that might be there from a previous attempt.
b.ClearFirstMatch(t, otpSelector)
// Sleep for a bit to make it less likely that we use the same OTP code twice when multiple tests are run in serial.
// GitHub gets upset when the same OTP code gets reused.
// GitHub seems to also get upset when any OTP codes are used often, like when all our GitHub tests run sequentially,