Merge branch 'main' into fix-redis-sentinel-docs

This commit is contained in:
Yaron Schneider 2025-07-25 09:26:33 -07:00 committed by GitHub
commit fc5c863174
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 364 additions and 49 deletions

View File

@ -12,7 +12,7 @@ require (
)
require (
github.com/dapr/kit v0.15.3-0.20250710140356-9d4f384c5763 // indirect
github.com/dapr/kit v0.15.3-0.20250717140748-8b780b4d81c5 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 // indirect

View File

@ -1,6 +1,6 @@
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/dapr/kit v0.15.3-0.20250710140356-9d4f384c5763 h1:9H0+baON+6+x4A/nQ2Lj+1JNk8MSlNv6sik6zfxdvsE=
github.com/dapr/kit v0.15.3-0.20250710140356-9d4f384c5763/go.mod h1:40ZWs5P6xfYf7O59XgwqZkIyDldTIXlhTQhGop8QoSM=
github.com/dapr/kit v0.15.3-0.20250717140748-8b780b4d81c5 h1:Q26gmPxs6WnnBYoudOlznPHsmrbTawcYEpHg4VoB7v8=
github.com/dapr/kit v0.15.3-0.20250717140748-8b780b4d81c5/go.mod h1:40ZWs5P6xfYf7O59XgwqZkIyDldTIXlhTQhGop8QoSM=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=

13
go.mod
View File

@ -65,7 +65,7 @@ require (
github.com/cloudwego/kitex-examples v0.1.1
github.com/cyphar/filepath-securejoin v0.2.4
github.com/dancannon/gorethink v4.0.0+incompatible
github.com/dapr/kit v0.15.3-0.20250710140356-9d4f384c5763
github.com/dapr/kit v0.15.3-0.20250717140748-8b780b4d81c5
github.com/didip/tollbooth/v7 v7.0.1
github.com/eclipse/paho.mqtt.golang v1.4.3
github.com/fasthttp-contrib/sessions v0.0.0-20160905201309-74f6ac73d5d5
@ -284,7 +284,7 @@ require (
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/flatbuffers v25.2.10+incompatible // indirect
github.com/google/generative-ai-go v0.15.1 // indirect
github.com/google/gnostic-models v0.6.9 // indirect
@ -292,6 +292,7 @@ require (
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
github.com/gorilla/css v1.0.1 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect
@ -362,7 +363,7 @@ require (
github.com/panjf2000/ants/v2 v2.8.1 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pierrec/lz4 v2.6.0+incompatible // indirect
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
github.com/pierrec/lz4/v4 v4.1.22 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect
@ -430,15 +431,15 @@ require (
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/arch v0.10.0 // indirect
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/sync v0.15.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/term v0.32.0 // indirect
golang.org/x/text v0.26.0 // indirect
golang.org/x/time v0.11.0 // indirect
golang.org/x/tools v0.33.0 // indirect
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 // indirect
google.golang.org/genproto v0.0.0-20250512202823-5a2f75b736a9 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250512202823-5a2f75b736a9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/grpc/examples v0.0.0-20230224211313-3775f633ce20 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect

35
go.sum
View File

@ -40,8 +40,8 @@ cloud.google.com/go/datastore v1.20.0/go.mod h1:uFo3e+aEpRfHgtp5pp0+6M0o147KoPaY
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8=
cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE=
cloud.google.com/go/kms v1.21.1 h1:r1Auo+jlfJSf8B7mUnVw5K0fI7jWyoUy65bV53VjKyk=
cloud.google.com/go/kms v1.21.1/go.mod h1:s0wCyByc9LjTdCjG88toVs70U9W+cc6RKFc8zAqX7nE=
cloud.google.com/go/kms v1.21.2 h1:c/PRUSMNQ8zXrc1sdAUnsenWWaNXN+PzTXfXOcSFdoE=
cloud.google.com/go/kms v1.21.2/go.mod h1:8wkMtHV/9Z8mLXEXr1GK7xPSBdi6knuLXIhqjuWcI6w=
cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc=
cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA=
cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE=
@ -63,8 +63,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.50.0 h1:3TbVkzTooBvnZsk7WaAQfOsNrdoM8QHusXA1cpk6QJs=
cloud.google.com/go/storage v1.50.0/go.mod h1:l7XeiD//vx5lfqE3RavfmU9yvk5Pp0Zhcv482poyafY=
cloud.google.com/go/trace v1.11.3 h1:c+I4YFjxRQjvAhRmSsmjpASUKq88chOX854ied0K/pE=
cloud.google.com/go/trace v1.11.3/go.mod h1:pt7zCYiDSQjC9Y2oqCsh9jF4GStB/hmjrYLsxRR27q8=
cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4=
cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI=
cloud.google.com/go/vertexai v0.12.0 h1:zTadEo/CtsoyRXNx3uGCncoWAP1H2HakGqwznt+iMo8=
cloud.google.com/go/vertexai v0.12.0/go.mod h1:8u+d0TsvBfAAd2x5R6GMgbYhsLgo3J7lmP4bR8g2ig8=
contrib.go.opencensus.io/exporter/prometheus v0.4.1/go.mod h1:t9wvfitlUjGXG2IXAZsuFq26mDGid/JwCEXp+gTG/9U=
@ -530,8 +530,8 @@ github.com/dancannon/gorethink v4.0.0+incompatible h1:KFV7Gha3AuqT+gr0B/eKvGhbjm
github.com/dancannon/gorethink v4.0.0+incompatible/go.mod h1:BLvkat9KmZc1efyYwhz3WnybhRZtgF1K929FD8z1avU=
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
github.com/dapr/kit v0.15.3-0.20250710140356-9d4f384c5763 h1:9H0+baON+6+x4A/nQ2Lj+1JNk8MSlNv6sik6zfxdvsE=
github.com/dapr/kit v0.15.3-0.20250710140356-9d4f384c5763/go.mod h1:40ZWs5P6xfYf7O59XgwqZkIyDldTIXlhTQhGop8QoSM=
github.com/dapr/kit v0.15.3-0.20250717140748-8b780b4d81c5 h1:Q26gmPxs6WnnBYoudOlznPHsmrbTawcYEpHg4VoB7v8=
github.com/dapr/kit v0.15.3-0.20250717140748-8b780b4d81c5/go.mod h1:40ZWs5P6xfYf7O59XgwqZkIyDldTIXlhTQhGop8QoSM=
github.com/dave/jennifer v1.4.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -848,8 +848,8 @@ github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP
github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b/go.mod h1:Z4GIJBJO3Wa4gD4vbwQxXXZ+WHmW6E9ixmNrwvs0iZs=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
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/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q=
github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
@ -917,8 +917,8 @@ github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
@ -1439,8 +1439,9 @@ github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4 v2.6.0+incompatible h1:Ix9yFKn1nSPBLFl/yZknTp8TU5G4Ps0JDmguYK6iH1A=
github.com/pierrec/lz4 v2.6.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=
github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
@ -1946,8 +1947,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@ -2412,10 +2413,10 @@ google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQ
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20211104193956-4c6863e31247/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE=
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE=
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 h1:vPV0tzlsK6EzEDHNNH5sa7Hs9bd7iXR7B1tSiPepkV0=
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:pKLAc5OolXC3ViWGI62vvC0n10CpwAtRcTNCFwTKBEw=
google.golang.org/genproto v0.0.0-20250512202823-5a2f75b736a9 h1:0DnDgelxbooHLt0nyiPeCP0zrH/RL+UG558i1oNU1xE=
google.golang.org/genproto v0.0.0-20250512202823-5a2f75b736a9/go.mod h1:IuQRZAKkz+Mhos3ZZ0+hcGaTmLuuTuGw344uzwztGl8=
google.golang.org/genproto/googleapis/api v0.0.0-20250512202823-5a2f75b736a9 h1:WvBuA5rjZx9SNIzgcU53OohgZy6lKSus++uY4xLaWKc=
google.golang.org/genproto/googleapis/api v0.0.0-20250512202823-5a2f75b736a9/go.mod h1:W3S/3np0/dPWsWLi1h/UymYctGXaGBM2StwzD0y140U=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=

View File

@ -18,6 +18,7 @@ import (
"net/http"
"net/url"
"reflect"
"regexp"
"strings"
"github.com/fasthttp-contrib/sessions"
@ -42,6 +43,9 @@ type oAuth2MiddlewareMetadata struct {
AuthHeaderName string `json:"authHeaderName" mapstructure:"authHeaderName"`
RedirectURL string `json:"redirectURL" mapstructure:"redirectURL"`
ForceHTTPS string `json:"forceHTTPS" mapstructure:"forceHTTPS"`
PathFilter string `json:"pathFilter" mapstructure:"pathFilter"`
pathFilterRegex *regexp.Regexp
}
// NewOAuth2Middleware returns a new oAuth2 middleware.
@ -84,6 +88,15 @@ func (m *Middleware) GetHandler(ctx context.Context, metadata middleware.Metadat
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if meta.pathFilterRegex != nil {
matched := meta.pathFilterRegex.MatchString(r.URL.Path)
if !matched {
m.logger.Debugf("PathFilter %s didn't match %s! Skipping!", meta.PathFilter, r.URL.Path)
next.ServeHTTP(w, r)
return
}
}
session := sessions.Start(w, r)
if session.GetString(meta.AuthHeaderName) != "" {
@ -153,6 +166,15 @@ func (m *Middleware) getNativeMetadata(metadata middleware.Metadata) (*oAuth2Mid
if err != nil {
return nil, err
}
if middlewareMetadata.PathFilter != "" {
rx, err := regexp.Compile(middlewareMetadata.PathFilter)
if err != nil {
return nil, err
}
middlewareMetadata.pathFilterRegex = rx
}
return &middlewareMetadata, nil
}

View File

@ -61,3 +61,52 @@ func TestOAuth2CreatesAuthorizationHeaderWhenInSessionState(t *testing.T) {
assert.Equal(t, "Bearer abcd", r.Header.Get("someHeader"))
}
func TestOAuth2CreatesAuthorizationHeaderGetNativeMetadata(t *testing.T) {
var metadata middleware.Metadata
metadata.Properties = map[string]string{
"clientID": "testId",
"clientSecret": "testSecret",
"scopes": "ascope",
"authURL": "https://idp:9999",
"tokenURL": "https://idp:9999",
"redirectUrl": "https://localhost:9999",
"authHeaderName": "someHeader",
}
log := logger.NewLogger("oauth2.test")
oauth2Middleware, ok := NewOAuth2Middleware(log).(*Middleware)
require.True(t, ok)
tc := []struct {
name string
pathFilter string
wantErr bool
}{
{name: "empty pathFilter", pathFilter: "", wantErr: false},
{name: "wildcard pathFilter", pathFilter: ".*", wantErr: false},
{name: "api path pathFilter", pathFilter: "/api/v1/users", wantErr: false},
{name: "debug endpoint pathFilter", pathFilter: "^/debug/?$", wantErr: false},
{name: "user id pathFilter", pathFilter: "^/user/[0-9]+$", wantErr: false},
{name: "invalid wildcard pathFilter", pathFilter: "*invalid", wantErr: true},
{name: "unclosed parenthesis pathFilter", pathFilter: "invalid(", wantErr: true},
{name: "unopened parenthesis pathFilter", pathFilter: "invalid)", wantErr: true},
}
for _, tt := range tc {
t.Run(tt.name, func(t *testing.T) {
metadata.Properties["pathFilter"] = tt.pathFilter
nativeMetadata, err := oauth2Middleware.getNativeMetadata(metadata)
if tt.wantErr {
require.Error(t, err)
} else {
require.NoError(t, err)
if tt.pathFilter != "" {
require.NotNil(t, nativeMetadata.pathFilterRegex)
} else {
require.Nil(t, nativeMetadata.pathFilterRegex)
}
}
})
}
}

View File

@ -21,6 +21,7 @@ import (
"net/http"
"net/url"
"reflect"
"regexp"
"strings"
"time"
@ -43,6 +44,9 @@ type oAuth2ClientCredentialsMiddlewareMetadata struct {
HeaderName string `json:"headerName" mapstructure:"headerName"`
EndpointParamsQuery string `json:"endpointParamsQuery,omitempty" mapstructure:"endpointParamsQuery"`
AuthStyle int `json:"authStyle" mapstructure:"authStyle"`
PathFilter string `json:"pathFilter" mapstructure:"pathFilter"`
pathFilterRegex *regexp.Regexp
}
// TokenProviderInterface provides a common interface to Mock the Token retrieval in unit tests.
@ -69,7 +73,7 @@ type Middleware struct {
tokenProvider TokenProviderInterface
}
// GetHandler retruns the HTTP handler provided by the middleware.
// GetHandler returns the HTTP handler provided by the middleware.
func (m *Middleware) GetHandler(_ context.Context, metadata middleware.Metadata) (func(next http.Handler) http.Handler, error) {
meta, err := m.getNativeMetadata(metadata)
if err != nil {
@ -98,27 +102,38 @@ func (m *Middleware) GetHandler(_ context.Context, metadata middleware.Metadata)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var headerValue string
// Check if valid token is in the cache
cachedToken, found := m.tokenCache.Get(cacheKey)
if !found {
m.log.Debugf("Cached token not found, try get one")
token, err := m.tokenProvider.GetToken(r.Context(), conf)
if err != nil {
m.log.Errorf("Error acquiring token: %s", err)
if meta.pathFilterRegex != nil {
matched := meta.pathFilterRegex.MatchString(r.URL.Path)
if !matched {
m.log.Debugf("PathFilter %s didn't match %s! Skipping!", meta.PathFilter, r.URL.Path)
next.ServeHTTP(w, r)
return
}
}
tokenExpirationDuration := time.Until(token.Expiry)
m.log.Debugf("Token expires at %s (%s from now)", token.Expiry, tokenExpirationDuration)
headerValue = token.Type() + " " + token.AccessToken
m.tokenCache.Set(cacheKey, headerValue, tokenExpirationDuration)
} else {
// Check if valid token is in the cache
cachedToken, found := m.tokenCache.Get(cacheKey)
if found {
m.log.Debugf("Cached token found for key %s", cacheKey)
headerValue = cachedToken.(string)
r.Header.Add(meta.HeaderName, headerValue)
next.ServeHTTP(w, r)
return
}
m.log.Infof("Cached token not found, attempting to retrieve a new one")
token, err := m.tokenProvider.GetToken(r.Context(), conf)
if err != nil {
m.log.Errorf("Error acquiring token: %s", err)
return
}
tokenExpirationDuration := time.Until(token.Expiry)
m.log.Infof("Token expires at %s (%s from now)", token.Expiry, tokenExpirationDuration)
headerValue = token.Type() + " " + token.AccessToken
m.tokenCache.Set(cacheKey, headerValue, tokenExpirationDuration)
r.Header.Add(meta.HeaderName, headerValue)
next.ServeHTTP(w, r)
})
@ -142,6 +157,14 @@ func (m *Middleware) getNativeMetadata(metadata middleware.Metadata) (*oAuth2Cli
m.checkMetadataValueExists(&errorString, &middlewareMetadata.Scopes, "scopes")
m.checkMetadataValueExists(&errorString, &middlewareMetadata.TokenURL, "tokenURL")
if middlewareMetadata.PathFilter != "" {
rx, err := regexp.Compile(middlewareMetadata.PathFilter)
if err != nil {
errorString += "Parameter 'pathFilter' is not a valid regex: " + err.Error() + ". "
}
middlewareMetadata.pathFilterRegex = rx
}
// Value-check AuthStyle
if middlewareMetadata.AuthStyle < 0 || middlewareMetadata.AuthStyle > 2 {
errorString += fmt.Sprintf("Parameter 'authStyle' can only have the values 0,1,2. Received: '%d'. ", middlewareMetadata.AuthStyle)

View File

@ -0,0 +1,113 @@
/*
Copyright 2025 The Dapr Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package oauth2clientcredentials
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"
"golang.org/x/oauth2"
"github.com/dapr/components-contrib/middleware"
mock "github.com/dapr/components-contrib/middleware/http/oauth2clientcredentials/mocks"
"github.com/dapr/kit/logger"
)
func BenchmarkTestOAuth2ClientCredentialsGetHandler(b *testing.B) {
mockCtrl := gomock.NewController(b)
defer mockCtrl.Finish()
mockTokenProvider := mock.NewMockTokenProviderInterface(mockCtrl)
gomock.InOrder(
mockTokenProvider.
EXPECT().
GetToken(gomock.Any()).
Return(&oauth2.Token{
AccessToken: "abcd",
TokenType: "Bearer",
Expiry: time.Now().Add(1 * time.Minute),
}, nil).
Times(1),
)
var metadata middleware.Metadata
metadata.Properties = map[string]string{
"clientID": "testId",
"clientSecret": "testSecret",
"scopes": "ascope",
"tokenURL": "https://localhost:9999",
"headerName": "authorization",
"authStyle": "1",
}
log := logger.NewLogger("oauth2clientcredentials.test")
oauth2clientcredentialsMiddleware, ok := NewOAuth2ClientCredentialsMiddleware(log).(*Middleware)
require.True(b, ok)
oauth2clientcredentialsMiddleware.SetTokenProvider(mockTokenProvider)
handler, err := oauth2clientcredentialsMiddleware.GetHandler(b.Context(), metadata)
require.NoError(b, err)
for i := range b.N {
url := fmt.Sprintf("http://dapr.io/api/v1/users/%d", i)
r := httptest.NewRequest(http.MethodGet, url, nil)
w := httptest.NewRecorder()
handler(http.HandlerFunc(mockedRequestHandler)).ServeHTTP(w, r)
}
}
func BenchmarkTestOAuth2ClientCredentialsGetHandlerWithPathFilter(b *testing.B) {
mockCtrl := gomock.NewController(b)
defer mockCtrl.Finish()
mockTokenProvider := mock.NewMockTokenProviderInterface(mockCtrl)
gomock.InOrder(
mockTokenProvider.
EXPECT().
GetToken(gomock.Any()).
Return(&oauth2.Token{
AccessToken: "abcd",
TokenType: "Bearer",
Expiry: time.Now().Add(1 * time.Minute),
}, nil).
Times(1),
)
var metadata middleware.Metadata
metadata.Properties = map[string]string{
"clientID": "testId",
"clientSecret": "testSecret",
"scopes": "ascope",
"tokenURL": "https://localhost:9999",
"headerName": "authorization",
"authStyle": "1",
"pathFilter": "/api/v1/users/.*",
}
log := logger.NewLogger("oauth2clientcredentials.test")
oauth2clientcredentialsMiddleware, ok := NewOAuth2ClientCredentialsMiddleware(log).(*Middleware)
require.True(b, ok)
oauth2clientcredentialsMiddleware.SetTokenProvider(mockTokenProvider)
handler, err := oauth2clientcredentialsMiddleware.GetHandler(b.Context(), metadata)
require.NoError(b, err)
for i := range b.N {
url := fmt.Sprintf("http://dapr.io/api/v1/users/%d", i)
r := httptest.NewRequest(http.MethodGet, url, nil)
w := httptest.NewRecorder()
handler(http.HandlerFunc(mockedRequestHandler)).ServeHTTP(w, r)
}
}

View File

@ -24,6 +24,7 @@ import (
"github.com/stretchr/testify/require"
oauth2 "golang.org/x/oauth2"
"github.com/dapr/components-contrib/metadata"
"github.com/dapr/components-contrib/middleware"
mock "github.com/dapr/components-contrib/middleware/http/oauth2clientcredentials/mocks"
"github.com/dapr/kit/logger"
@ -107,7 +108,8 @@ func TestOAuth2ClientCredentialsToken(t *testing.T) {
// Initialize middleware component and inject mocked TokenProvider
log := logger.NewLogger("oauth2clientcredentials.test")
oauth2clientcredentialsMiddleware, _ := NewOAuth2ClientCredentialsMiddleware(log).(*Middleware)
oauth2clientcredentialsMiddleware, ok := NewOAuth2ClientCredentialsMiddleware(log).(*Middleware)
require.True(t, ok)
oauth2clientcredentialsMiddleware.SetTokenProvider(mockTokenProvider)
handler, err := oauth2clientcredentialsMiddleware.GetHandler(t.Context(), metadata)
require.NoError(t, err)
@ -167,7 +169,8 @@ func TestOAuth2ClientCredentialsCache(t *testing.T) {
// Initialize middleware component and inject mocked TokenProvider
log := logger.NewLogger("oauth2clientcredentials.test")
oauth2clientcredentialsMiddleware, _ := NewOAuth2ClientCredentialsMiddleware(log).(*Middleware)
oauth2clientcredentialsMiddleware, ok := NewOAuth2ClientCredentialsMiddleware(log).(*Middleware)
require.True(t, ok)
oauth2clientcredentialsMiddleware.SetTokenProvider(mockTokenProvider)
handler, err := oauth2clientcredentialsMiddleware.GetHandler(t.Context(), metadata)
require.NoError(t, err)
@ -199,3 +202,103 @@ func TestOAuth2ClientCredentialsCache(t *testing.T) {
// Assertion
assert.Equal(t, "MAC def", r.Header.Get("someHeader"))
}
func TestOAuth2ClientCredentialsPathFilterGetNativeMetadata(t *testing.T) {
log := logger.NewLogger("oauth2clientcredentials.test")
m, ok := NewOAuth2ClientCredentialsMiddleware(log).(*Middleware)
require.True(t, ok)
baseMiddlewareMetadata := middleware.Metadata{
Base: metadata.Base{
Properties: map[string]string{
"clientID": "testId",
"clientSecret": "testSecret",
"scopes": "ascope",
"tokenURL": "https://localhost:9999",
"headerName": "someHeader",
"authStyle": "1",
},
},
}
tc := []struct {
name string
pathFilter string
wantErr bool
}{
{name: "empty pathFilter", pathFilter: "", wantErr: false},
{name: "wildcard pathFilter", pathFilter: ".*", wantErr: false},
{name: "api path pathFilter", pathFilter: "/api/v1/users", wantErr: false},
{name: "debug endpoint pathFilter", pathFilter: "^/debug/?$", wantErr: false},
{name: "user id pathFilter", pathFilter: "^/user/[0-9]+$", wantErr: false},
{name: "invalid wildcard pathFilter", pathFilter: "*invalid", wantErr: true},
{name: "unclosed parenthesis pathFilter", pathFilter: "invalid(", wantErr: true},
{name: "unopened parenthesis pathFilter", pathFilter: "invalid)", wantErr: true},
}
for _, tt := range tc {
t.Run(tt.name, func(t *testing.T) {
baseMiddlewareMetadata.Properties["pathFilter"] = tt.pathFilter
_, err := m.getNativeMetadata(baseMiddlewareMetadata)
if tt.wantErr {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
}
}
func TestOAuth2ClientCredentialsPathFilterGetHandler(t *testing.T) {
// Setup
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
// Mock mockTokenProvider
mockTokenProvider := mock.NewMockTokenProviderInterface(mockCtrl)
gomock.InOrder(
// First call returning abc and Bearer, expires within 1 second
mockTokenProvider.
EXPECT().
GetToken(gomock.Any()).
Return(&oauth2.Token{
AccessToken: "abcd",
TokenType: "Bearer",
Expiry: time.Now().In(time.UTC).Add(1 * time.Second),
}, nil).
Times(1),
)
var metadata middleware.Metadata
metadata.Properties = map[string]string{
"clientID": "testId",
"clientSecret": "testSecret",
"scopes": "ascope",
"tokenURL": "https://localhost:9999",
"headerName": "authorization",
"authStyle": "1",
"pathFilter": "/api/v1/users/.*",
}
log := logger.NewLogger("oauth2clientcredentials.test")
oauth2clientcredentialsMiddleware, ok := NewOAuth2ClientCredentialsMiddleware(log).(*Middleware)
require.True(t, ok)
oauth2clientcredentialsMiddleware.SetTokenProvider(mockTokenProvider)
handler, err := oauth2clientcredentialsMiddleware.GetHandler(t.Context(), metadata)
require.NoError(t, err)
// pathFilter should match
r := httptest.NewRequest(http.MethodGet, "http://dapr.io/api/v1/users/123", nil)
w := httptest.NewRecorder()
handler(http.HandlerFunc(mockedRequestHandler)).ServeHTTP(w, r)
assert.Equal(t, "Bearer abcd", r.Header.Get("authorization"))
// pathFilter should not match
r = httptest.NewRequest(http.MethodGet, "http://dapr.io/api/v1/tokens/123", nil)
w = httptest.NewRecorder()
handler(http.HandlerFunc(mockedRequestHandler)).ServeHTTP(w, r)
assert.Equal(t, "", r.Header.Get("authorization"))
}

View File

@ -18,7 +18,7 @@ require (
github.com/cloudwego/kitex v0.5.0
github.com/cloudwego/kitex-examples v0.1.1
github.com/dapr/components-contrib v1.15.2
github.com/dapr/dapr v1.15.4-0.20250522161419-bbf2e482dc39
github.com/dapr/dapr v1.16.0-rc.1.0.20250723233324-b5569ff8862e
github.com/dapr/go-sdk v1.10.0-rc-1.0.20240507160435-33180dd89a46
github.com/dapr/kit v0.15.3-0.20250717140748-8b780b4d81c5
github.com/eclipse/paho.mqtt.golang v1.4.3
@ -150,6 +150,7 @@ require (
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/gage-technologies/mistral-go v1.1.0 // indirect
github.com/go-chi/cors v1.2.1 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-jose/go-jose/v4 v4.0.5 // indirect

View File

@ -434,8 +434,8 @@ github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53E
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
github.com/dapr/dapr v1.15.4-0.20250522161419-bbf2e482dc39 h1:QHNFYrWjgFHmoyqcvlhOLNfmnblYplL1Fw0wHinPFHQ=
github.com/dapr/dapr v1.15.4-0.20250522161419-bbf2e482dc39/go.mod h1:9tlP+FA6WHcDPZMvuwKZQGaL/18yL8lZ9lgyU3G9LvA=
github.com/dapr/dapr v1.16.0-rc.1.0.20250723233324-b5569ff8862e h1:PQjnQkSuU7OtiFmmqIRUxvpb14A9va4ZbGh7oXgStbY=
github.com/dapr/dapr v1.16.0-rc.1.0.20250723233324-b5569ff8862e/go.mod h1:3R5TgtcMOyNx2/kzam2YwgbYpFAj+uPGufRYCTczGnk=
github.com/dapr/durabletask-go v0.7.3-0.20250711135247-7a35af6fe0e5 h1:l8oBGwcfCwqvSYDZwla0A2fhENmXFc1Wk4lR0VEq+is=
github.com/dapr/durabletask-go v0.7.3-0.20250711135247-7a35af6fe0e5/go.mod h1:0Ts4rXp74JyG19gDWPcwNo5V6NBZzhARzHF5XynmA7Q=
github.com/dapr/go-sdk v1.10.0-rc-1.0.20240507160435-33180dd89a46 h1:0/WKEqAfTGnFAiFrqMpIEBMkCHaAqt5H9efU0hyKiG4=
@ -551,6 +551,8 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/gage-technologies/mistral-go v1.1.0 h1:POv1wM9jA/9OBXGV2YdPi9Y/h09+MjCbUF+9hRYlVUI=
github.com/gage-technologies/mistral-go v1.1.0/go.mod h1:tF++Xt7U975GcLlzhrjSQb8l/x+PrriO9QEdsgm9l28=
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=

View File

@ -4,7 +4,7 @@ go 1.24.4
require (
github.com/dapr/components-contrib v1.10.6-0.20230403162214-9ee9d56cb7ea
github.com/dapr/kit v0.15.3-0.20250710140356-9d4f384c5763
github.com/dapr/kit v0.15.3-0.20250717140748-8b780b4d81c5
)
require (

View File

@ -4,8 +4,8 @@ github.com/cloudevents/sdk-go/binding/format/protobuf/v2 v2.14.0 h1:dEopBSOSjB5f
github.com/cloudevents/sdk-go/binding/format/protobuf/v2 v2.14.0/go.mod h1:qDSbb0fgIfFNjZrNTPtS5MOMScAGyQtn1KlSvoOdqYw=
github.com/cloudevents/sdk-go/v2 v2.15.2 h1:54+I5xQEnI73RBhWHxbI1XJcqOFOVJN85vb41+8mHUc=
github.com/cloudevents/sdk-go/v2 v2.15.2/go.mod h1:lL7kSWAE/V8VI4Wh0jbL2v/jvqsm6tjmaQBSvxcv4uE=
github.com/dapr/kit v0.15.3-0.20250710140356-9d4f384c5763 h1:9H0+baON+6+x4A/nQ2Lj+1JNk8MSlNv6sik6zfxdvsE=
github.com/dapr/kit v0.15.3-0.20250710140356-9d4f384c5763/go.mod h1:40ZWs5P6xfYf7O59XgwqZkIyDldTIXlhTQhGop8QoSM=
github.com/dapr/kit v0.15.3-0.20250717140748-8b780b4d81c5 h1:Q26gmPxs6WnnBYoudOlznPHsmrbTawcYEpHg4VoB7v8=
github.com/dapr/kit v0.15.3-0.20250717140748-8b780b4d81c5/go.mod h1:40ZWs5P6xfYf7O59XgwqZkIyDldTIXlhTQhGop8QoSM=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=