diff --git a/examples/go.mod b/examples/go.mod index 4a306d4c5e..0611413934 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -6,8 +6,8 @@ require ( github.com/opentdf/platform/protocol/go v0.0.0-00010101000000-000000000000 github.com/opentdf/platform/sdk v0.0.0-00010101000000-000000000000 github.com/spf13/cobra v1.8.0 - google.golang.org/grpc v1.61.0 - google.golang.org/protobuf v1.32.0 + google.golang.org/grpc v1.62.1 + google.golang.org/protobuf v1.33.0 ) replace ( @@ -16,33 +16,32 @@ replace ( ) require ( - buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.32.0-20231115204500-e097f827e652.1 // indirect + buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.33.0-20240221180331-f05a6f4403ce.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/lestrrat-go/blackmagic v1.0.2 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect - github.com/lestrrat-go/httprc v1.0.4 // indirect + github.com/lestrrat-go/httprc v1.0.5 // indirect github.com/lestrrat-go/iter v1.0.2 // indirect - github.com/lestrrat-go/jwx/v2 v2.0.19 // indirect + github.com/lestrrat-go/jwx/v2 v2.0.21 // indirect github.com/lestrrat-go/option v1.0.1 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/crypto v0.18.0 // indirect - golang.org/x/net v0.20.0 // indirect - golang.org/x/oauth2 v0.16.0 // indirect - golang.org/x/sys v0.16.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/net v0.22.0 // indirect + golang.org/x/oauth2 v0.18.0 // indirect + golang.org/x/sys v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20240125205218-1f4bbc51befe // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/examples/go.sum b/examples/go.sum index ffdcb42799..bd2f578e2d 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -1,17 +1,17 @@ -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.32.0-20231115204500-e097f827e652.1 h1:u0olL4yf2p7Tl5jfsAK5keaFi+JFJuv1CDHrbiXkxkk= -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.32.0-20231115204500-e097f827e652.1/go.mod h1:tiTMKD8j6Pd/D2WzREoweufjzaJKHZg35f/VGcZ2v3I= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.33.0-20240221180331-f05a6f4403ce.1 h1:0nWhrRcnkgw1kwJ7xibIO8bqfOA7pBzBjGCDBxIHch8= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.33.0-20240221180331-f05a6f4403ce.1/go.mod h1:Tgn5bgL220vkFOI0KPStlcClPeOJzAv4uT+V8JXGUnw= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8= -github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= +github.com/Microsoft/hcsshim v0.12.0 h1:rbICA+XZFwrBef2Odk++0LjFvClNCJGRK+fsrP254Ts= +github.com/Microsoft/hcsshim v0.12.0/go.mod h1:RZV12pcHCXQ42XnlQ3pz6FZfmrC1C+R4gaOHhRNML1g= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/containerd/containerd v1.7.12 h1:+KQsnv4VnzyxWcfO9mlxxELaoztsDEjOuCMPAuPqgU0= -github.com/containerd/containerd v1.7.12/go.mod h1:/5OMpE1p0ylxtEUGY8kuCYkDRzJm9NO1TFMWjUpdevk= +github.com/containerd/containerd v1.7.14 h1:H/XLzbnGuenZEGK+v0RkwTdv2u1QFAruMe5N0GNPJwA= +github.com/containerd/containerd v1.7.14/go.mod h1:YMC9Qt5yzNqXx/fO4j/5yYVIHXSRrlB3H7sxkUTvspg= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= @@ -19,8 +19,8 @@ github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHf github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -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= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= @@ -31,8 +31,8 @@ github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= -github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -47,8 +47,8 @@ github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOW github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -58,8 +58,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0Q github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= -github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= +github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -68,12 +68,12 @@ github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= -github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8= -github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= +github.com/lestrrat-go/httprc v1.0.5 h1:bsTfiH8xaKOJPrg1R+E3iE/AWZr/x0Phj9PBTG/OLUk= +github.com/lestrrat-go/httprc v1.0.5/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= -github.com/lestrrat-go/jwx/v2 v2.0.19 h1:ekv1qEZE6BVct89QA+pRF6+4pCpfVrOnEJnTnT4RXoY= -github.com/lestrrat-go/jwx/v2 v2.0.19/go.mod h1:l3im3coce1lL2cDeAjqmaR+Awx+X8Ih+2k8BuHNJ4CU= +github.com/lestrrat-go/jwx/v2 v2.0.21 h1:jAPKupy4uHgrHFEdjVjNkUgoBKtVDgrQPB/h55FHrR0= +github.com/lestrrat-go/jwx/v2 v2.0.21/go.mod h1:09mLW8zto6bWL9GbwnqAli+ArLf+5M33QLQPDggkUWM= github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= @@ -92,12 +92,13 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= -github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= @@ -119,8 +120,8 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/testcontainers/testcontainers-go v0.28.0 h1:1HLm9qm+J5VikzFDYhOd+Zw12NtOl+8drH2E8nTY1r8= github.com/testcontainers/testcontainers-go v0.28.0/go.mod h1:COlDpUXbwW3owtpMkEB1zo9gwb1CoKVKlyrVPejF4AU= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= @@ -130,30 +131,30 @@ github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9f github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0/go.mod h1:62CPTSry9QZtOaSsE3tOzhx6LzDhHnXJ6xHeMNNiM6Q= -go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= -go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= -go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= -go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= -go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= -go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= +go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea h1:vLCWI/yYrdEHyN2JzIzPO3aaQJHQdp89IZBA/+azVC4= -golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= -golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= +golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -161,8 +162,8 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -174,24 +175,22 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg= -golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= +golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= +golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/genproto v0.0.0-20240125205218-1f4bbc51befe h1:USL2DhxfgRchafRvt/wYyyQNzwgL7ZiURcozOE/Pkvo= -google.golang.org/genproto v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= -google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014 h1:x9PwdEgd11LgK+orcck69WVRo7DezSO4VUMPI4xpc8A= -google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014/go.mod h1:rbHMSEDyoYX62nRVLOCc4Qt1HbsdytAYoVwgjiOhF3I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe h1:bQnxqljG/wqi4NTXu2+DJ3n7APcEA882QZ1JvhQAq9o= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= -google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= -google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= +google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7 h1:oqta3O3AnlWbmIE3bFnWbu4bRxZjfbWCp0cKSuZh01E= +google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7/go.mod h1:VQW3tUculP/D4B+xVCo+VgSq8As6wA9ZjHl//pmk+6s= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7 h1:8EeVk1VKMD+GD/neyEHGmz7pFblqPjHoi+PGQIlLx2s= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= +google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/go.mod b/go.mod index 6c724a9212..d16934b8a1 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,6 @@ require ( github.com/lestrrat-go/jwx/v2 v2.0.21 github.com/miekg/pkcs11 v1.1.1 github.com/open-policy-agent/opa v0.62.1 - github.com/opentdf/backend-go v0.1.17 github.com/opentdf/platform/protocol/go v0.0.0-00010101000000-000000000000 github.com/opentdf/platform/sdk v0.0.0-00010101000000-000000000000 github.com/pressly/goose/v3 v3.19.1 diff --git a/go.sum b/go.sum index 6afaedf11b..790537e0ea 100644 --- a/go.sum +++ b/go.sum @@ -258,8 +258,6 @@ github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQ github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opencontainers/runc v1.1.12 h1:BOIssBaW1La0/qbNZHXOOa71dZfZEQOzW7dqQf3phss= github.com/opencontainers/runc v1.1.12/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8= -github.com/opentdf/backend-go v0.1.17 h1:RA/9Mj7OKycnvOrkIZTJCsSyROCcioXzzqMUlnCxP48= -github.com/opentdf/backend-go v0.1.17/go.mod h1:4RAZ3K19YOJkpxSh4jl6vtjOUkK3H/zqA32LeBf3CDQ= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4= diff --git a/internal/auth/authn_test.go b/internal/auth/authn_test.go deleted file mode 100644 index 31366d7be5..0000000000 --- a/internal/auth/authn_test.go +++ /dev/null @@ -1,298 +0,0 @@ -package auth - -import ( - "context" - "crypto/rand" - "crypto/rsa" - "encoding/json" - "fmt" - "log/slog" - "net/http" - "net/http/httptest" - "testing" - "time" - - "github.com/lestrrat-go/jwx/v2/jwa" - "github.com/lestrrat-go/jwx/v2/jwk" - "github.com/lestrrat-go/jwx/v2/jws" - "github.com/lestrrat-go/jwx/v2/jwt" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" -) - -type AuthSuite struct { - suite.Suite - server *httptest.Server - key jwk.Key - auth *authentication -} - -func (s *AuthSuite) SetupTest() { - privKey, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - slog.Error("failed to generate RSA private key", slog.String("error", err.Error())) - return - } - - pubKeyJWK, err := jwk.FromRaw(privKey.PublicKey) - if err != nil { - slog.Error("failed to create jwk.Key from RSA public key", slog.String("error", err.Error())) - return - } - pubKeyJWK.Set(jws.KeyIDKey, "test") - pubKeyJWK.Set(jwk.AlgorithmKey, jwa.RS256) - - // Create a new set with rsa public key - set := jwk.NewSet() - if err := set.AddKey(pubKeyJWK); err != nil { - slog.Error("failed to add RSA public key to jwk.Set", slog.String("error", err.Error())) - return - } - - key, err := jwk.FromRaw(privKey) - if err != nil { - slog.Error("failed to create jwk.Key from RSA private key", slog.String("error", err.Error())) - return - } - key.Set(jws.KeyIDKey, "test") - - s.key = key - - s.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - if r.URL.Path == "/.well-known/openid-configuration" { - w.Write([]byte(fmt.Sprintf(`{"jwks_uri": "%s/jwks"}`, s.server.URL))) - return - } - if r.URL.Path == "/jwks" { - json.NewEncoder(w).Encode(set) - return - } - })) - - auth, err := NewAuthenticator(AuthNConfig{ - Issuer: s.server.URL, - Audience: "test", - Clients: []string{"client1", "client2", "client3"}, - }) - - assert.Nil(s.T(), err) - - s.auth = auth -} - -func (s *AuthSuite) TearDownTest() { - s.server.Close() -} - -func TestAuthSuite(t *testing.T) { - suite.Run(t, new(AuthSuite)) -} - -func (s *AuthSuite) Test_CheckToken_When_JWT_Expired_Expect_Error() { - tok := jwt.New() - tok.Set(jwt.ExpirationKey, time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC)) - - signedTok, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, s.key)) - - assert.NotNil(s.T(), signedTok) - assert.Nil(s.T(), err) - - err = checkToken(context.Background(), []string{fmt.Sprintf("Bearer %s", string(signedTok))}, *s.auth) - assert.NotNil(s.T(), err) - assert.Equal(s.T(), "\"exp\" not satisfied", err.Error()) -} - -func (s *AuthSuite) Test_VerifyTokenHandler_When_Authorization_Header_Missing_Expect_Error() { - handler := s.auth.VerifyTokenHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) - rec := httptest.NewRecorder() - handler.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/", nil)) - assert.Equal(s.T(), http.StatusUnauthorized, rec.Code) - assert.Equal(s.T(), "missing authorization header\n", rec.Body.String()) -} - -func (s *AuthSuite) Test_VerifyTokenInterceptor_When_Authorization_Header_Missing_Expect_Error() { - md := metadata.New(map[string]string{}) - ctx := metadata.NewIncomingContext(context.Background(), md) - _, err := s.auth.VerifyTokenInterceptor(ctx, "test", &grpc.UnaryServerInfo{ - FullMethod: "/test", - }, nil) - assert.NotNil(s.T(), err) - assert.ErrorIs(s.T(), err, status.Error(codes.Unauthenticated, "missing authorization header")) -} - -func (s *AuthSuite) Test_CheckToken_When_Authorization_Header_Invalid_Expect_Error() { - err := checkToken(context.Background(), []string{"BPOP "}, *s.auth) - assert.NotNil(s.T(), err) - assert.Equal(s.T(), "not of type bearer or dpop", err.Error()) -} - -func (s *AuthSuite) Test_CheckToken_When_Missing_Issuer_Expect_Error() { - tok := jwt.New() - tok.Set(jwt.ExpirationKey, time.Now().Add(time.Hour)) - - signedTok, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, s.key)) - - assert.NotNil(s.T(), signedTok) - assert.Nil(s.T(), err) - - err = checkToken(context.Background(), []string{fmt.Sprintf("Bearer %s", string(signedTok))}, *s.auth) - assert.NotNil(s.T(), err) - assert.Equal(s.T(), "missing issuer", err.Error()) -} - -func (s *AuthSuite) Test_CheckToken_When_Invalid_Issuer_Value_Expect_Error() { - tok := jwt.New() - tok.Set(jwt.ExpirationKey, time.Now().Add(time.Hour)) - tok.Set("iss", "invalid") - - signedTok, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, s.key)) - - assert.NotNil(s.T(), signedTok) - assert.Nil(s.T(), err) - - err = checkToken(context.Background(), []string{fmt.Sprintf("Bearer %s", string(signedTok))}, *s.auth) - assert.NotNil(s.T(), err) - assert.Equal(s.T(), "invalid issuer", err.Error()) -} - -func (s *AuthSuite) Test_CheckToken_When_Invalid_Issuer_INT_Value_Expect_Error() { - tok := jwt.New() - tok.Set(jwt.ExpirationKey, time.Now().Add(time.Hour)) - tok.Set("iss", 1) - - signedTok, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, s.key)) - - assert.NotNil(s.T(), signedTok) - assert.Nil(s.T(), err) - - err = checkToken(context.Background(), []string{fmt.Sprintf("Bearer %s", string(signedTok))}, *s.auth) - assert.NotNil(s.T(), err) - assert.Equal(s.T(), "missing issuer", err.Error()) -} - -func (s *AuthSuite) Test_CheckToken_When_Audience_Missing_Expect_Error() { - tok := jwt.New() - tok.Set(jwt.ExpirationKey, time.Now().Add(time.Hour)) - tok.Set("iss", s.server.URL) - signedTok, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, s.key)) - - assert.NotNil(s.T(), signedTok) - assert.Nil(s.T(), err) - - err = checkToken(context.Background(), []string{fmt.Sprintf("Bearer %s", string(signedTok))}, *s.auth) - assert.NotNil(s.T(), err) - assert.Equal(s.T(), "claim \"aud\" not found", err.Error()) -} - -func (s *AuthSuite) Test_CheckToken_When_Audience_Invalid_Expect_Error() { - tok := jwt.New() - tok.Set(jwt.ExpirationKey, time.Now().Add(time.Hour)) - tok.Set("iss", s.server.URL) - tok.Set("aud", "invalid") - signedTok, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, s.key)) - - assert.NotNil(s.T(), signedTok) - assert.Nil(s.T(), err) - - err = checkToken(context.Background(), []string{fmt.Sprintf("Bearer %s", string(signedTok))}, *s.auth) - assert.NotNil(s.T(), err) - assert.Equal(s.T(), "\"aud\" not satisfied", err.Error()) -} - -func (s *AuthSuite) Test_CheckToken_When_ClientID_Missing_Expect_Error() { - tok := jwt.New() - tok.Set(jwt.ExpirationKey, time.Now().Add(time.Hour)) - tok.Set("iss", s.server.URL) - tok.Set("aud", "test") - signedTok, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, s.key)) - - assert.NotNil(s.T(), signedTok) - assert.Nil(s.T(), err) - - err = checkToken(context.Background(), []string{fmt.Sprintf("Bearer %s", string(signedTok))}, *s.auth) - assert.NotNil(s.T(), err) - assert.Equal(s.T(), "client id required", err.Error()) -} - -func (s *AuthSuite) Test_CheckToken_When_ClientID_Invalid_Expect_Error() { - tok := jwt.New() - tok.Set(jwt.ExpirationKey, time.Now().Add(time.Hour)) - tok.Set("iss", s.server.URL) - tok.Set("aud", "test") - tok.Set("client_id", "invalid") - signedTok, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, s.key)) - - assert.NotNil(s.T(), signedTok) - assert.Nil(s.T(), err) - - err = checkToken(context.Background(), []string{fmt.Sprintf("Bearer %s", string(signedTok))}, *s.auth) - assert.NotNil(s.T(), err) - assert.Equal(s.T(), "invalid client id", err.Error()) -} - -func (s *AuthSuite) Test_CheckToken_When_CID_Invalid_Expect_Error() { - tok := jwt.New() - tok.Set(jwt.ExpirationKey, time.Now().Add(time.Hour)) - tok.Set("iss", s.server.URL) - tok.Set("aud", "test") - tok.Set("cid", "invalid") - signedTok, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, s.key)) - - assert.NotNil(s.T(), signedTok) - assert.Nil(s.T(), err) - - err = checkToken(context.Background(), []string{fmt.Sprintf("Bearer %s", string(signedTok))}, *s.auth) - assert.NotNil(s.T(), err) - assert.Equal(s.T(), "invalid client id", err.Error()) -} - -func (s *AuthSuite) Test_CheckToken_When_CID_Invalid_INT_Expect_Error() { - tok := jwt.New() - tok.Set(jwt.ExpirationKey, time.Now().Add(time.Hour)) - tok.Set("iss", s.server.URL) - tok.Set("aud", "test") - tok.Set("cid", 1) - signedTok, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, s.key)) - - assert.NotNil(s.T(), signedTok) - assert.Nil(s.T(), err) - - err = checkToken(context.Background(), []string{fmt.Sprintf("Bearer %s", string(signedTok))}, *s.auth) - assert.NotNil(s.T(), err) - assert.Equal(s.T(), "invalid client id", err.Error()) -} - -func (s *AuthSuite) Test_CheckToken_When_Valid_Expect_No_Error() { - tok := jwt.New() - tok.Set(jwt.ExpirationKey, time.Now().Add(time.Hour)) - tok.Set("iss", s.server.URL) - tok.Set("aud", "test") - tok.Set("client_id", "client1") - signedTok, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, s.key)) - - assert.NotNil(s.T(), signedTok) - assert.Nil(s.T(), err) - - err = checkToken(context.Background(), []string{fmt.Sprintf("Bearer %s", string(signedTok))}, *s.auth) - assert.Nil(s.T(), err) -} - -func (s *AuthSuite) Test_CheckToken_When_Valid_CID_Expect_No_Error() { - tok := jwt.New() - tok.Set(jwt.ExpirationKey, time.Now().Add(time.Hour)) - tok.Set("iss", s.server.URL) - tok.Set("aud", "test") - tok.Set("cid", "client2") - signedTok, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, s.key)) - - assert.NotNil(s.T(), signedTok) - assert.Nil(s.T(), err) - - err = checkToken(context.Background(), []string{fmt.Sprintf("DPoP %s", string(signedTok))}, *s.auth) - assert.Nil(s.T(), err) -} diff --git a/internal/server/server.go b/internal/server/server.go index 1f28b48aab..cbe202dc52 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -19,7 +19,7 @@ import ( "github.com/go-chi/cors" protovalidate_middleware "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/protovalidate" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" - "github.com/opentdf/platform/internal/auth" + "github.com/opentdf/platform/sdk/auth" "github.com/valyala/fasthttp/fasthttputil" "google.golang.org/grpc" "google.golang.org/grpc/credentials" diff --git a/pkg/server/start_test.go b/pkg/server/start_test.go index 6e3e8ff40f..574873fcce 100644 --- a/pkg/server/start_test.go +++ b/pkg/server/start_test.go @@ -8,10 +8,10 @@ import ( "time" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" - "github.com/opentdf/platform/internal/auth" "github.com/opentdf/platform/internal/config" "github.com/opentdf/platform/internal/server" "github.com/opentdf/platform/pkg/serviceregistry" + "github.com/opentdf/platform/sdk/auth" "github.com/stretchr/testify/assert" "golang.org/x/exp/slog" "google.golang.org/grpc" diff --git a/sdk/auth/access_token_source.go b/sdk/auth/access_token_source.go new file mode 100644 index 0000000000..dd72dc6a03 --- /dev/null +++ b/sdk/auth/access_token_source.go @@ -0,0 +1,15 @@ +package auth + +import "github.com/lestrrat-go/jwx/v2/jwk" + +type AccessToken string + +type AccessTokenSource interface { + AccessToken() (AccessToken, error) + // probably better to use `crypto.AsymDecryption` here than roll our own since this should be + // more closely linked to what happens in KAS in terms of crypto params + DecryptWithDPoPKey(data []byte) ([]byte, error) + MakeToken(func(jwk.Key) ([]byte, error)) ([]byte, error) + DPoPPublicKeyPEM() string + RefreshAccessToken() error +} diff --git a/internal/auth/authn.go b/sdk/auth/authn.go similarity index 52% rename from internal/auth/authn.go rename to sdk/auth/authn.go index e579b6c2cf..72c33ed349 100644 --- a/internal/auth/authn.go +++ b/sdk/auth/authn.go @@ -2,6 +2,9 @@ package auth import ( "context" + "crypto" + "crypto/sha256" + "encoding/base64" "fmt" "log/slog" "net/http" @@ -9,7 +12,9 @@ import ( "strings" "time" + "github.com/lestrrat-go/jwx/v2/jwa" "github.com/lestrrat-go/jwx/v2/jwk" + "github.com/lestrrat-go/jwx/v2/jws" "github.com/lestrrat-go/jwx/v2/jwt" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -28,10 +33,22 @@ var ( "/healthz", "/.well-known/opentdf-configuration", } + // only asymmetric algorithms and no 'none' + allowedSignatureAlgorithms = map[jwa.SignatureAlgorithm]bool{ + jwa.RS256: true, + jwa.RS384: true, + jwa.RS512: true, + jwa.ES256: true, + jwa.ES384: true, + jwa.ES512: true, + jwa.PS256: true, + jwa.PS384: true, + jwa.PS512: true, + } ) // Authentication holds a jwks cache and information about the openid configuration -type authentication struct { +type Authentication struct { // cache holds the jwks cache cache *jwk.Cache // openidConfigurations holds the openid configuration for each issuer @@ -39,8 +56,8 @@ type authentication struct { } // Creates new authN which is used to verify tokens for a set of given issuers -func NewAuthenticator(cfg AuthNConfig) (*authentication, error) { - a := &authentication{} +func NewAuthenticator(cfg AuthNConfig) (*Authentication, error) { + a := &Authentication{} a.oidcConfigurations = make(map[string]AuthNConfig) ctx := context.Background() @@ -72,8 +89,14 @@ func NewAuthenticator(cfg AuthNConfig) (*authentication, error) { return a, nil } +type dpopInfo struct { + headers []string + path string + method string +} + // verifyTokenHandler is a http handler that verifies the token -func (a authentication) VerifyTokenHandler(handler http.Handler) http.Handler { +func (a Authentication) VerifyTokenHandler(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if slices.Contains(allowedHTTPEndpoints[:], r.URL.Path) { handler.ServeHTTP(w, r) @@ -85,7 +108,11 @@ func (a authentication) VerifyTokenHandler(handler http.Handler) http.Handler { http.Error(w, "missing authorization header", http.StatusUnauthorized) return } - err := checkToken(r.Context(), header, a) + err := a.checkToken(r.Context(), header, dpopInfo{ + headers: r.Header["Dpop"], + path: r.URL.Path, + method: r.Method, + }) if err != nil { slog.WarnContext(r.Context(), "failed to validate token", slog.String("error", err.Error())) http.Error(w, "unauthenticated", http.StatusUnauthorized) @@ -97,7 +124,7 @@ func (a authentication) VerifyTokenHandler(handler http.Handler) http.Handler { } // verifyTokenInterceptor is a grpc interceptor that verifies the token in the metadata -func (a authentication) VerifyTokenInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { +func (a Authentication) VerifyTokenInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { // Allow health checks to pass through if slices.Contains(allowedGRPCEndpoints[:], info.FullMethod) { return handler(ctx, req) @@ -117,7 +144,15 @@ func (a authentication) VerifyTokenInterceptor(ctx context.Context, req any, inf return nil, status.Error(codes.Unauthenticated, "missing authorization header") } - err := checkToken(ctx, header, a) + err := a.checkToken( + ctx, + header, + dpopInfo{ + headers: md["dpop"], + path: info.FullMethod, + method: "POST", + }, + ) if err != nil { slog.Warn("failed to validate token", slog.String("error", err.Error())) return nil, status.Errorf(codes.Unauthenticated, "unauthenticated") @@ -127,7 +162,7 @@ func (a authentication) VerifyTokenInterceptor(ctx context.Context, req any, inf } // checkToken is a helper function to verify the token. -func checkToken(ctx context.Context, authHeader []string, auth authentication) error { +func (a Authentication) checkToken(ctx context.Context, authHeader []string, dpopInfo dpopInfo) error { var ( tokenRaw string tokenType string @@ -145,12 +180,6 @@ func checkToken(ctx context.Context, authHeader []string, auth authentication) e return fmt.Errorf("not of type bearer or dpop") } - // Future work is to validate DPoP proof if token type is DPoP - //nolint:staticcheck - if tokenType == "DPoP" { - // Implement in the future here or as separate interceptor - } - // We have to get iss from the token first to verify the signature unverifiedToken, err := jwt.Parse([]byte(tokenRaw), jwt.WithVerify(false)) if err != nil { @@ -158,47 +187,170 @@ func checkToken(ctx context.Context, authHeader []string, auth authentication) e } // Get issuer from unverified token - issuer, exists := unverifiedToken.Get("iss") - if !exists { + issuer := unverifiedToken.Issuer() + if issuer == "" { return fmt.Errorf("missing issuer") } // Get the openid configuration for the issuer // Because we get an interface we need to cast it to a string // and jwx expects it as a string so we should never hit this error if the token is valid - issuerStr, ok := issuer.(string) - if !ok { - return fmt.Errorf("invalid issuer") - } - oidc, exists := auth.oidcConfigurations[issuerStr] + oidc, exists := a.oidcConfigurations[issuer] if !exists { return fmt.Errorf("invalid issuer") } // Get key set from cache that matches the jwks_uri - keySet, err := auth.cache.Get(ctx, oidc.JwksURI) + keySet, err := a.cache.Get(ctx, oidc.JwksURI) if err != nil { return fmt.Errorf("failed to get jwks from cache") } // Now we verify the token signature - _, err = jwt.Parse([]byte(tokenRaw), + accessToken, err := jwt.Parse([]byte(tokenRaw), jwt.WithKeySet(keySet), jwt.WithValidate(true), - jwt.WithIssuer(issuerStr), + jwt.WithIssuer(issuer), jwt.WithAudience(oidc.Audience), - jwt.WithValidator(jwt.ValidatorFunc(auth.claimsValidator)), + jwt.WithValidator(jwt.ValidatorFunc(a.claimsValidator)), ) + if err != nil { return err } + if tokenType == "Bearer" { + if _, ok := accessToken.Get("cnf"); !ok { + return nil + } + slog.Info("presented token with `cnf` claim as a bearer token. validating as DPoP") + } + + return validateDPoP(accessToken, tokenRaw, dpopInfo) +} + +func validateDPoP(accessToken jwt.Token, acessTokenRaw string, dpopInfo dpopInfo) error { + if len(dpopInfo.headers) != 1 { + return fmt.Errorf("got %d dpop headers, should have 1", len(dpopInfo.headers)) + } + dpopHeader := dpopInfo.headers[0] + + cnf, ok := accessToken.Get("cnf") + if !ok { + return fmt.Errorf("missing `cnf` claim in access token") + } + + cnfDict, ok := cnf.(map[string]interface{}) + if !ok { + return fmt.Errorf("got `cnf` in an invalid format") + } + + jktI, ok := cnfDict["jkt"] + if !ok { + return fmt.Errorf("missing `jkt` field in `cnf` claim. only thumbprint JWK confirmation is supported") + } + + jkt, ok := jktI.(string) + if !ok { + return fmt.Errorf("invalid `jkt` field in `cnf` claim: %v. the value must be a JWK thumbprint", jkt) + } + + dpop, err := jws.Parse([]byte(dpopHeader)) + if err != nil { + slog.Error("error parsing JWT: %w", err) + return fmt.Errorf("invalid DPoP JWT") + } + if len(dpop.Signatures()) != 1 { + return fmt.Errorf("expected one signature on DPoP JWT, got %d", len(dpop.Signatures())) + } + sig := dpop.Signatures()[0] + protectedHeaders := sig.ProtectedHeaders() + if protectedHeaders.Type() != "dpop+jwt" { + return fmt.Errorf("invalid typ on DPoP JWT: %v", protectedHeaders.Type()) + } + + if _, exists := allowedSignatureAlgorithms[protectedHeaders.Algorithm()]; !exists { + return fmt.Errorf("unsupported algorithm specified: %v", protectedHeaders.Algorithm()) + } + + dpopKey := protectedHeaders.JWK() + if dpopKey == nil { + return fmt.Errorf("JWK missing in DPoP JWT") + } + + isPrivate, err := jwk.IsPrivateKey(dpopKey) + if err != nil { + slog.Error("error checking if key is private", err) + return fmt.Errorf("invalid DPoP key specified") + } + + if isPrivate { + return fmt.Errorf("cannot use a private key for DPoP") + } + + thumbprint, err := dpopKey.Thumbprint(crypto.SHA256) + if err != nil { + slog.Error("error computing thumbprint for key", err) + return fmt.Errorf("couldn't compute thumbprint for key in `jwk` in DPoP JWT") + } + + if base64.URLEncoding.EncodeToString(thumbprint) != jkt { + return fmt.Errorf("the `jkt` from the DPoP JWT didn't match the thumbprint from the access token") + } + + // at this point we have the right key because its thumbprint matches the `jkt` claim + // in the validated access token + dpopToken, err := jwt.Parse([]byte(dpopHeader), jwt.WithKey(protectedHeaders.Algorithm(), dpopKey)) + + if err != nil { + slog.Error("error validating DPoP JWT", err) + return fmt.Errorf("failed to verify signature on DPoP JWT") + } + + issuedAt := dpopToken.IssuedAt() + if issuedAt.IsZero() { + return fmt.Errorf("missing `iat` claim in the DPoP JWT") + } + + if issuedAt.Add(time.Hour).Before(time.Now()) { + return fmt.Errorf("the DPoP JWT has expired") + } + + htm, ok := dpopToken.Get("htm") + if !ok { + return fmt.Errorf("`htm` claim missing in DPoP JWT") + } + + if htm != dpopInfo.method { + return fmt.Errorf("incorrect `htm` claim in DPoP JWT") + } + + htu, ok := dpopToken.Get("htu") + if !ok { + return fmt.Errorf("`htu` claim missing in DPoP JWT") + } + + if htu != dpopInfo.path { + return fmt.Errorf("incorrect `htu` claim in DPoP JWT") + } + + ath, ok := dpopToken.Get("ath") + if !ok { + return fmt.Errorf("missing `ath` claim in DPoP JWT") + } + + h := sha256.New() + h.Write([]byte(acessTokenRaw)) + if ath != base64.URLEncoding.EncodeToString(h.Sum(nil)) { + return fmt.Errorf("incorrect `ath` claim in DPoP JWT") + } + return nil } // claimsValidator is a custom validator to check extra claims in the token. // right now it only checks for client_id -func (a authentication) claimsValidator(ctx context.Context, token jwt.Token) jwt.ValidationError { +func (a Authentication) claimsValidator(_ context.Context, token jwt.Token) jwt.ValidationError { var ( clientID string ) diff --git a/sdk/auth/authn_test.go b/sdk/auth/authn_test.go new file mode 100644 index 0000000000..61a6b7b1d4 --- /dev/null +++ b/sdk/auth/authn_test.go @@ -0,0 +1,490 @@ +package auth + +import ( + "context" + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "log/slog" + "net" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwk" + "github.com/lestrrat-go/jwx/v2/jws" + "github.com/lestrrat-go/jwx/v2/jwt" + "github.com/opentdf/platform/protocol/go/kas" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/grpc/test/bufconn" +) + +type AuthSuite struct { + suite.Suite + server *httptest.Server + key jwk.Key + auth *Authentication +} + +type FakeAccessTokenSource struct { + dpopKey jwk.Key + accessToken string +} + +func (fake FakeAccessTokenSource) AccessToken() (AccessToken, error) { + return AccessToken(fake.accessToken), nil +} +func (fake FakeAccessTokenSource) DecryptWithDPoPKey(_ []byte) ([]byte, error) { + return nil, nil +} +func (fake FakeAccessTokenSource) MakeToken(tokenMaker func(jwk.Key) ([]byte, error)) ([]byte, error) { + return tokenMaker(fake.dpopKey) +} +func (fake FakeAccessTokenSource) DPoPPublicKeyPEM() string { + return "this is the PEM" +} +func (fake FakeAccessTokenSource) RefreshAccessToken() error { + return errors.New("can't refresh this one") +} + +func must(err error) { + if err != nil { + panic(err) + } +} + +func (s *AuthSuite) SetupTest() { + privKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + slog.Error("failed to generate RSA private key", slog.String("error", err.Error())) + panic(err) + } + + pubKeyJWK, err := jwk.FromRaw(privKey.PublicKey) + if err != nil { + slog.Error("failed to create jwk.Key from RSA public key", slog.String("error", err.Error())) + panic(err) + } + must(pubKeyJWK.Set(jws.KeyIDKey, "test")) + must(pubKeyJWK.Set(jwk.AlgorithmKey, jwa.RS256)) + + // Create a new set with rsa public key + set := jwk.NewSet() + must(set.AddKey(pubKeyJWK)) + + key, err := jwk.FromRaw(privKey) + must(err) + must(key.Set(jws.KeyIDKey, "test")) + + s.key = key + + s.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + if r.URL.Path == "/.well-known/openid-configuration" { + _, err := w.Write([]byte(fmt.Sprintf(`{"jwks_uri": "%s/jwks"}`, s.server.URL))) + if err != nil { + panic(err) + } + return + } + if r.URL.Path == "/jwks" { + err := json.NewEncoder(w).Encode(set) + if err != nil { + panic(err) + } + } + })) + + auth, err := NewAuthenticator(AuthNConfig{ + Issuer: s.server.URL, + Audience: "test", + Clients: []string{"client1", "client2", "client3"}, + }) + + s.Require().NoError(err) + + s.auth = auth +} + +func (s *AuthSuite) TearDownTest() { + s.server.Close() +} + +func TestAuthSuite(t *testing.T) { + suite.Run(t, new(AuthSuite)) +} + +func (s *AuthSuite) Test_CheckToken_When_JWT_Expired_Expect_Error() { + tok := jwt.New() + s.Require().NoError(tok.Set(jwt.ExpirationKey, time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC))) + + signedTok, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, s.key)) + + s.NotNil(signedTok) + s.Require().NoError(err) + + err = s.auth.checkToken(context.Background(), []string{fmt.Sprintf("Bearer %s", string(signedTok))}, dpopInfo{}) + s.Require().Error(err) + s.Equal("\"exp\" not satisfied", err.Error()) +} + +func (s *AuthSuite) Test_VerifyTokenHandler_When_Authorization_Header_Missing_Expect_Error() { + handler := s.auth.VerifyTokenHandler(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {})) + rec := httptest.NewRecorder() + handler.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/", nil)) + s.Equal(http.StatusUnauthorized, rec.Code) + s.Equal("missing authorization header\n", rec.Body.String()) +} + +func (s *AuthSuite) Test_VerifyTokenInterceptor_When_Authorization_Header_Missing_Expect_Error() { + md := metadata.New(map[string]string{}) + ctx := metadata.NewIncomingContext(context.Background(), md) + _, err := s.auth.VerifyTokenInterceptor(ctx, "test", &grpc.UnaryServerInfo{ + FullMethod: "/test", + }, nil) + s.Require().Error(err) + s.ErrorIs(err, status.Error(codes.Unauthenticated, "missing authorization header")) +} + +func (s *AuthSuite) Test_CheckToken_When_Authorization_Header_Invalid_Expect_Error() { + err := s.auth.checkToken(context.Background(), []string{"BPOP "}, dpopInfo{}) + s.Require().Error(err) + s.Equal("not of type bearer or dpop", err.Error()) +} + +func (s *AuthSuite) Test_CheckToken_When_Missing_Issuer_Expect_Error() { + tok := jwt.New() + s.Require().NoError(tok.Set(jwt.ExpirationKey, time.Now().Add(time.Hour))) + + signedTok, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, s.key)) + + s.NotNil(signedTok) + s.Require().NoError(err) + + err = s.auth.checkToken(context.Background(), []string{fmt.Sprintf("Bearer %s", string(signedTok))}, dpopInfo{}) + s.Require().Error(err) + s.Equal("missing issuer", err.Error()) +} + +func (s *AuthSuite) Test_CheckToken_When_Invalid_Issuer_Value_Expect_Error() { + tok := jwt.New() + s.Require().NoError(tok.Set(jwt.ExpirationKey, time.Now().Add(time.Hour))) + s.Require().NoError(tok.Set("iss", "invalid")) + + signedTok, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, s.key)) + + s.NotNil(signedTok) + s.Require().NoError(err) + + err = s.auth.checkToken(context.Background(), []string{fmt.Sprintf("Bearer %s", string(signedTok))}, dpopInfo{}) + s.Require().Error(err) + s.Equal("invalid issuer", err.Error()) +} + +func (s *AuthSuite) Test_CheckToken_When_Audience_Missing_Expect_Error() { + tok := jwt.New() + s.Require().NoError(tok.Set(jwt.ExpirationKey, time.Now().Add(time.Hour))) + s.Require().NoError(tok.Set("iss", s.server.URL)) + signedTok, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, s.key)) + + s.NotNil(signedTok) + s.Require().NoError(err) + + err = s.auth.checkToken(context.Background(), []string{fmt.Sprintf("Bearer %s", string(signedTok))}, dpopInfo{}) + s.Require().Error(err) + s.Equal("claim \"aud\" not found", err.Error()) +} + +func (s *AuthSuite) Test_CheckToken_When_Audience_Invalid_Expect_Error() { + tok := jwt.New() + s.Require().NoError(tok.Set(jwt.ExpirationKey, time.Now().Add(time.Hour))) + s.Require().NoError(tok.Set("iss", s.server.URL)) + s.Require().NoError(tok.Set("aud", "invalid")) + signedTok, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, s.key)) + + s.NotNil(signedTok) + s.Require().NoError(err) + + err = s.auth.checkToken(context.Background(), []string{fmt.Sprintf("Bearer %s", string(signedTok))}, dpopInfo{}) + s.Require().Error(err) + s.Equal("\"aud\" not satisfied", err.Error()) +} + +func (s *AuthSuite) Test_CheckToken_When_ClientID_Missing_Expect_Error() { + tok := jwt.New() + s.Require().NoError(tok.Set(jwt.ExpirationKey, time.Now().Add(time.Hour))) + s.Require().NoError(tok.Set("iss", s.server.URL)) + s.Require().NoError(tok.Set("aud", "test")) + signedTok, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, s.key)) + + s.NotNil(signedTok) + s.Require().NoError(err) + + err = s.auth.checkToken(context.Background(), []string{fmt.Sprintf("Bearer %s", string(signedTok))}, dpopInfo{}) + s.Require().Error(err) + s.Equal("client id required", err.Error()) +} + +func (s *AuthSuite) Test_CheckToken_When_ClientID_Invalid_Expect_Error() { + tok := jwt.New() + s.Require().NoError(tok.Set(jwt.ExpirationKey, time.Now().Add(time.Hour))) + s.Require().NoError(tok.Set("iss", s.server.URL)) + s.Require().NoError(tok.Set("aud", "test")) + s.Require().NoError(tok.Set("client_id", "invalid")) + signedTok, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, s.key)) + + s.NotNil(signedTok) + s.Require().NoError(err) + + err = s.auth.checkToken(context.Background(), []string{fmt.Sprintf("Bearer %s", string(signedTok))}, dpopInfo{}) + s.Require().Error(err) + s.Equal("invalid client id", err.Error()) +} + +func (s *AuthSuite) Test_CheckToken_When_CID_Invalid_Expect_Error() { + tok := jwt.New() + s.Require().NoError(tok.Set(jwt.ExpirationKey, time.Now().Add(time.Hour))) + s.Require().NoError(tok.Set("iss", s.server.URL)) + s.Require().NoError(tok.Set("aud", "test")) + s.Require().NoError(tok.Set("cid", "invalid")) + signedTok, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, s.key)) + + s.NotNil(signedTok) + s.Require().NoError(err) + + err = s.auth.checkToken(context.Background(), []string{fmt.Sprintf("Bearer %s", string(signedTok))}, dpopInfo{}) + s.Require().Error(err) + s.Equal("invalid client id", err.Error()) +} + +func (s *AuthSuite) Test_CheckToken_When_CID_Invalid_INT_Expect_Error() { + tok := jwt.New() + s.Require().NoError(tok.Set(jwt.ExpirationKey, time.Now().Add(time.Hour))) + s.Require().NoError(tok.Set("iss", s.server.URL)) + s.Require().NoError(tok.Set("aud", "test")) + s.Require().NoError(tok.Set("cid", 1)) + signedTok, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, s.key)) + + s.NotNil(signedTok) + s.Require().NoError(err) + + err = s.auth.checkToken(context.Background(), []string{fmt.Sprintf("Bearer %s", string(signedTok))}, dpopInfo{}) + s.Require().Error(err) + s.Equal("invalid client id", err.Error()) +} + +func (s *AuthSuite) Test_CheckToken_When_Valid_Expect_No_Error() { + tok := jwt.New() + s.Require().NoError(tok.Set(jwt.ExpirationKey, time.Now().Add(time.Hour))) + s.Require().NoError(tok.Set("iss", s.server.URL)) + s.Require().NoError(tok.Set("aud", "test")) + s.Require().NoError(tok.Set("client_id", "client1")) + signedTok, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, s.key)) + + s.NotNil(signedTok) + s.Require().NoError(err) + + err = s.auth.checkToken(context.Background(), []string{fmt.Sprintf("Bearer %s", string(signedTok))}, dpopInfo{}) + s.Require().NoError(err) +} + +type dpopTestCase struct { + key jwk.Key + actualSigningKey jwk.Key + accessToken []byte + alg jwa.SignatureAlgorithm + typ string + htm string + htu string + ath string + iat time.Time + errorMesssage string +} + +func (s *AuthSuite) TestInvalid_DPoP_Cases() { + dpopRaw, err := rsa.GenerateKey(rand.Reader, 2048) + s.Require().NoError(err) + dpopKey, err := jwk.FromRaw(dpopRaw) + s.Require().NoError(err) + s.Require().NoError(dpopKey.Set(jwk.AlgorithmKey, jwa.RS256)) + + tok := jwt.New() + s.Require().NoError(tok.Set(jwt.ExpirationKey, time.Now().Add(time.Hour))) + s.Require().NoError(tok.Set("iss", s.server.URL)) + s.Require().NoError(tok.Set("aud", "test")) + s.Require().NoError(tok.Set("cid", "client2")) + dpopPublic, err := dpopKey.PublicKey() + s.Require().NoError(err) + thumbprint, err := dpopKey.Thumbprint(crypto.SHA256) + s.Require().NoError(err) + cnf := map[string]string{"jkt": base64.URLEncoding.EncodeToString(thumbprint)} + s.Require().NoError(tok.Set("cnf", cnf)) + signedTok, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, s.key)) + + s.NotNil(signedTok) + s.Require().NoError(err) + + otherKeyRaw, err := rsa.GenerateKey(rand.Reader, 2048) + s.Require().NoError(err) + otherKey, err := jwk.FromRaw(otherKeyRaw) + s.Require().NoError(err) + otherKeyPublic, err := otherKey.PublicKey() + s.Require().NoError(err) + s.Require().NoError(otherKey.Set(jwk.AlgorithmKey, jwa.RS256)) + + tokenWithNoCNF := jwt.New() + s.Require().NoError(tokenWithNoCNF.Set(jwt.ExpirationKey, time.Now().Add(time.Hour))) + s.Require().NoError(tokenWithNoCNF.Set("iss", s.server.URL)) + s.Require().NoError(tokenWithNoCNF.Set("aud", "test")) + s.Require().NoError(tokenWithNoCNF.Set("cid", "client2")) + signedTokWithNoCNF, err := jwt.Sign(tokenWithNoCNF, jwt.WithKey(jwa.RS256, s.key)) + s.NotNil(signedTokWithNoCNF) + s.Require().NoError(err) + + testCases := []dpopTestCase{ + {dpopPublic, dpopKey, signedTok, jwa.RS256, "dpop+jwt", "POST", "/a/path", "", time.Now().Add(time.Hour * -100), "the DPoP JWT has expired"}, + {dpopKey, dpopKey, signedTok, jwa.RS256, "dpop+jwt", "POST", "/a/path", "", time.Now(), "cannot use a private key for DPoP"}, + {dpopPublic, dpopKey, signedTok, jwa.RS256, "a weird type", "POST", "/a/path", "", time.Now(), "invalid typ on DPoP JWT: a weird type"}, + {dpopPublic, otherKey, signedTok, jwa.RS256, "dpop+jwt", "POST", "/a/path", "", time.Now(), "failed to verify signature on DPoP JWT"}, + {dpopPublic, dpopKey, signedTok, jwa.RS256, "dpop+jwt", "POST", "/a/different/path", "", time.Now(), "incorrect `htu` claim in DPoP JWT"}, + {dpopPublic, dpopKey, signedTok, jwa.RS256, "dpop+jwt", "POSTERS", "/a/path", "", time.Now(), "incorrect `htm` claim in DPoP JWT"}, + {dpopPublic, dpopKey, signedTok, jwa.RS256, "dpop+jwt", "POST", "/a/path", "bad ath", time.Now(), "incorrect `ath` claim in DPoP JWT"}, + {otherKeyPublic, dpopKey, signedTok, jwa.RS256, "dpop+jwt", "POST", "/a/path", "", time.Now(), + "the `jkt` from the DPoP JWT didn't match the thumbprint from the access token"}, + {dpopPublic, dpopKey, signedTokWithNoCNF, jwa.RS256, "dpop+jwt", "POST", "/a/path", "", time.Now(), + "missing `cnf` claim in access token"}, + } + + for _, testCase := range testCases { + dpopToken := makeDPoPToken(s.T(), testCase) + err = s.auth.checkToken( + context.Background(), + []string{fmt.Sprintf("DPoP %s", string(testCase.accessToken))}, + dpopInfo{ + headers: []string{dpopToken}, + path: "/a/path", + method: "POST", + }, + ) + + s.Require().Error(err) + s.Equal(testCase.errorMesssage, err.Error()) + } +} + +func (s *AuthSuite) TestDPoPEndToEnd_GRPC() { + dpopKeyRaw, err := rsa.GenerateKey(rand.Reader, 2048) + s.Require().NoError(err) + dpopKey, err := jwk.FromRaw(dpopKeyRaw) + s.Require().NoError(err) + s.Require().NoError(dpopKey.Set(jwk.AlgorithmKey, jwa.RS256)) + + tok := jwt.New() + s.Require().NoError(tok.Set(jwt.ExpirationKey, time.Now().Add(time.Hour))) + s.Require().NoError(tok.Set("iss", s.server.URL)) + s.Require().NoError(tok.Set("aud", "test")) + s.Require().NoError(tok.Set("cid", "client2")) + s.Require().NoError(err) + thumbprint, err := dpopKey.Thumbprint(crypto.SHA256) + s.Require().NoError(err) + cnf := map[string]string{"jkt": base64.URLEncoding.EncodeToString(thumbprint)} + s.Require().NoError(tok.Set("cnf", cnf)) + signedTok, err := jwt.Sign(tok, jwt.WithKey(jwa.RS256, s.key)) + s.Require().NoError(err) + + buffer := 1024 * 1024 + listener := bufconn.Listen(buffer) + + server := grpc.NewServer(grpc.UnaryInterceptor(s.auth.VerifyTokenInterceptor)) + defer server.Stop() + + kas.RegisterAccessServiceServer(server, &FakeAccessServiceServer{}) + go func() { + err := server.Serve(listener) + if err != nil { + panic(err) + } + }() + + addingInterceptor := NewTokenAddingInterceptor(&FakeTokenSource{ + key: dpopKey, + accessToken: string(signedTok), + }) + + conn, _ := grpc.DialContext(context.Background(), "", grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return listener.Dial() + }), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock(), grpc.WithUnaryInterceptor(addingInterceptor.AddCredentials)) + + client := kas.NewAccessServiceClient(conn) + + _, err = client.LegacyPublicKey(context.Background(), &kas.LegacyPublicKeyRequest{}) + s.Require().NoError(err) +} + +func makeDPoPToken(t *testing.T, tc dpopTestCase) string { + jtiBytes := make([]byte, JTILength) + _, err := rand.Read(jtiBytes) + if err != nil { + t.Fatalf("error creating jti for dpop jwt: %v", err) + } + + headers := jws.NewHeaders() + err = headers.Set(jws.JWKKey, tc.key) + if err != nil { + t.Fatalf("error setting the key on the DPoP token: %v", err) + } + err = headers.Set(jws.TypeKey, tc.typ) + if err != nil { + t.Fatalf("error setting the type on the DPoP token: %v", err) + } + err = headers.Set(jws.AlgorithmKey, tc.alg) + if err != nil { + t.Fatalf("error setting the algorithm on the DPoP token: %v", err) + } + + var ath string + if tc.ath == "" { + h := sha256.New() + h.Write(tc.accessToken) + ath = base64.URLEncoding.EncodeToString(h.Sum(nil)) + } else { + ath = tc.ath + } + + b := jwt.NewBuilder(). + Claim("htu", tc.htu). + Claim("htm", tc.htm). + Claim("ath", ath). + Claim("jti", base64.StdEncoding.EncodeToString(jtiBytes)) + + if tc.iat.IsZero() { + b = b.IssuedAt(time.Now()) + } else { + b = b.IssuedAt(tc.iat) + } + + dpopTok, err := b.Build() + + if err != nil { + t.Fatalf("error creating dpop jwt: %v", err) + } + + signedToken, err := jwt.Sign(dpopTok, jwt.WithKey(tc.actualSigningKey.Algorithm(), tc.actualSigningKey, jws.WithProtectedHeaders(headers))) + if err != nil { + t.Fatalf("error signing dpop jwt: %v", err) + return "" + } + return string(signedToken) +} diff --git a/internal/auth/config.go b/sdk/auth/config.go similarity index 100% rename from internal/auth/config.go rename to sdk/auth/config.go diff --git a/internal/auth/discovery.go b/sdk/auth/discovery.go similarity index 97% rename from internal/auth/discovery.go rename to sdk/auth/discovery.go index c1dcdf8ca2..a2867900b7 100644 --- a/internal/auth/discovery.go +++ b/sdk/auth/discovery.go @@ -37,7 +37,7 @@ func DiscoverOIDCConfiguration(ctx context.Context, issuer string) (*OIDCConfigu return nil, err } - if resp.StatusCode != 200 { + if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("failed to discover idp: %s", resp.Status) } defer resp.Body.Close() diff --git a/sdk/auth/token_adding_interceptor.go b/sdk/auth/token_adding_interceptor.go new file mode 100644 index 0000000000..a79d0a81da --- /dev/null +++ b/sdk/auth/token_adding_interceptor.go @@ -0,0 +1,115 @@ +package auth + +import ( + "context" + "crypto/rand" + "crypto/sha256" + "encoding/base64" + "fmt" + "log/slog" + "time" + + "github.com/lestrrat-go/jwx/v2/jwk" + "github.com/lestrrat-go/jwx/v2/jws" + "github.com/lestrrat-go/jwx/v2/jwt" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" +) + +const ( + JTILength = 14 + JWTExpirationMinutes = 10 +) + +func NewTokenAddingInterceptor(t AccessTokenSource) TokenAddingInterceptor { + return TokenAddingInterceptor{tokenSource: t} +} + +type TokenAddingInterceptor struct { + tokenSource AccessTokenSource +} + +func (i TokenAddingInterceptor) AddCredentials(ctx context.Context, + method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { + newMetadata := make([]string, 0) + accessToken, err := i.tokenSource.AccessToken() + if err == nil { + newMetadata = append(newMetadata, "Authorization", fmt.Sprintf("DPoP %s", accessToken)) + } else { + slog.Error("error getting access token: %w. request will be unauthenticated", err) + return invoker(ctx, method, req, reply, cc, opts...) + } + + dpopTok, err := i.getDPoPToken(method, string(accessToken)) + if err == nil { + newMetadata = append(newMetadata, "DPoP", dpopTok) + } else { + slog.Error("error adding dpop token to outgoing request. Request will not have DPoP token", err) + } + + newCtx := metadata.AppendToOutgoingContext(ctx, newMetadata...) + + err = invoker(newCtx, method, req, reply, cc, opts...) + + // this is the error from the RPC service. we can determine when the current token is no longer valid + // by inspecting this error + return err +} + +func (i TokenAddingInterceptor) getDPoPToken(path, accessToken string) (string, error) { + tok, err := i.tokenSource.MakeToken(func(key jwk.Key) ([]byte, error) { + jtiBytes := make([]byte, JTILength) + _, err := rand.Read(jtiBytes) + if err != nil { + return nil, fmt.Errorf("error creating jti for dpop jwt: %w", err) + } + + publicKey, err := key.PublicKey() + if err != nil { + return nil, fmt.Errorf("error getting public key from DPoP key: %w", err) + } + + headers := jws.NewHeaders() + err = headers.Set(jws.JWKKey, publicKey) + if err != nil { + return nil, fmt.Errorf("error setting the key on the DPoP token: %w", err) + } + err = headers.Set(jws.TypeKey, "dpop+jwt") + if err != nil { + return nil, fmt.Errorf("error setting the type on the DPoP token: %w", err) + } + err = headers.Set(jws.AlgorithmKey, key.Algorithm()) + if err != nil { + return nil, fmt.Errorf("error setting the algorithm on the DPoP token: %w", err) + } + + h := sha256.New() + h.Write([]byte(accessToken)) + ath := h.Sum(nil) + + dpopTok, err := jwt.NewBuilder(). + Claim("htu", path). + Claim("htm", "POST"). + Claim("ath", base64.URLEncoding.EncodeToString(ath)). + Claim("jti", base64.URLEncoding.EncodeToString(jtiBytes)). + IssuedAt(time.Now()). + Build() + + if err != nil { + return nil, fmt.Errorf("error creating dpop jwt: %w", err) + } + + signedToken, err := jwt.Sign(dpopTok, jwt.WithKey(key.Algorithm(), key, jws.WithProtectedHeaders(headers))) + if err != nil { + return nil, fmt.Errorf("error signing dpop jwt: %w", err) + } + + return signedToken, nil + }) + + if err != nil { + return "", fmt.Errorf("error creating DPoP token in interceptor: %w", err) + } + + return string(tok), nil +} diff --git a/sdk/auth/token_adding_interceptor_test.go b/sdk/auth/token_adding_interceptor_test.go new file mode 100644 index 0000000000..1790fb848c --- /dev/null +++ b/sdk/auth/token_adding_interceptor_test.go @@ -0,0 +1,199 @@ +package auth + +import ( + "context" + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "encoding/base64" + "errors" + "net" + "slices" + "testing" + + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwk" + "github.com/lestrrat-go/jwx/v2/jws" + "github.com/lestrrat-go/jwx/v2/jwt" + "github.com/opentdf/platform/protocol/go/kas" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/grpc/test/bufconn" + "google.golang.org/protobuf/types/known/wrapperspb" +) + +func TestAddingTokensToOutgoingRequest(t *testing.T) { + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatalf("error generating key: %v", err) + } + + key, err := jwk.FromRaw(privateKey) + if err != nil { + t.Fatalf("error getting raw key") + } + + err = key.Set(jwk.AlgorithmKey, jwa.RS256) + if err != nil { + t.Fatalf("error setting the algorithm on the JWK") + } + ts := FakeTokenSource{ + key: key, + accessToken: "thisisafakeaccesstoken", + } + server := FakeAccessServiceServer{} + oo := NewTokenAddingInterceptor(&ts) + + client, stop := runServer(context.Background(), &server, oo) + defer stop() + + _, err = client.Info(context.Background(), &kas.InfoRequest{}) + if err != nil { + t.Fatalf("error making call: %v", err) + } + + if len(server.accessToken) != 1 || server.accessToken[0] != "DPoP thisisafakeaccesstoken" { + t.Fatalf("got incorrect access token: %v", server.accessToken) + } + + if len(server.dpopToken) != 1 { + t.Fatalf("Got incorrect dpop token headers: %v", server.dpopToken) + } + + dpopToken := server.dpopToken[0] + + alg, ok := key.Algorithm().(jwa.SignatureAlgorithm) + if !ok { + t.Fatalf("got a bad signing algorithm") + } + + _, err = jws.Verify([]byte(dpopToken), jws.WithKey(alg, key)) + if err != nil { + t.Fatalf("error verifying signature: %v", err) + } + + parsedSignature, _ := jws.Parse([]byte(dpopToken)) + + if len(parsedSignature.Signatures()) == 0 { + t.Fatalf("didn't get signature from jwt") + } + + sig := parsedSignature.Signatures()[0] + tokenKey, ok := sig.ProtectedHeaders().Get("jwk") + if !ok { + t.Fatalf("didn't get error getting key from token") + } + + tp, _ := tokenKey.(jwk.Key).Thumbprint(crypto.SHA256) + ktp, _ := key.Thumbprint(crypto.SHA256) + if !slices.Equal(tp, ktp) { + t.Fatalf("got the wrong key from the token") + } + + parsedToken, _ := jwt.Parse([]byte(dpopToken), jwt.WithVerify(false)) + + if method, _ := parsedToken.Get("htm"); method != "POST" { + t.Fatalf("we got a bad method: %v", method) + } + + if path, _ := parsedToken.Get("htu"); path != "/kas.AccessService/Info" { + t.Fatalf("we got a bad method: %v", path) + } + + h := sha256.New() + h.Write([]byte("thisisafakeaccesstoken")) + expectedHash := base64.URLEncoding.EncodeToString(h.Sum(nil)) + + if ath, _ := parsedToken.Get("ath"); ath != expectedHash { + t.Fatalf("got invalid ath claim in token: %v", ath) + } +} + +func Test_InvalidCredentials_StillSendMessage(t *testing.T) { + ts := FakeTokenSource{key: nil} + server := FakeAccessServiceServer{} + oo := NewTokenAddingInterceptor(&ts) + + client, stop := runServer(context.Background(), &server, oo) + defer stop() + + _, err := client.Info(context.Background(), &kas.InfoRequest{}) + + if err != nil { + t.Fatalf("got an error when sending the message") + } +} + +type FakeAccessServiceServer struct { + accessToken []string + dpopToken []string + kas.UnimplementedAccessServiceServer +} + +func (f *FakeAccessServiceServer) Info(ctx context.Context, _ *kas.InfoRequest) (*kas.InfoResponse, error) { + if md, ok := metadata.FromIncomingContext(ctx); ok { + f.accessToken = md.Get("authorization") + f.dpopToken = md.Get("dpop") + } + + return &kas.InfoResponse{}, nil +} +func (f *FakeAccessServiceServer) PublicKey(context.Context, *kas.PublicKeyRequest) (*kas.PublicKeyResponse, error) { + return &kas.PublicKeyResponse{}, status.Error(codes.Unauthenticated, "no public key for you") +} +func (f *FakeAccessServiceServer) LegacyPublicKey(context.Context, *kas.LegacyPublicKeyRequest) (*wrapperspb.StringValue, error) { + return &wrapperspb.StringValue{}, nil +} +func (f *FakeAccessServiceServer) Rewrap(context.Context, *kas.RewrapRequest) (*kas.RewrapResponse, error) { + return &kas.RewrapResponse{}, nil +} + +type FakeTokenSource struct { + key jwk.Key + accessToken string +} + +func (fts *FakeTokenSource) AccessToken() (AccessToken, error) { + return AccessToken(fts.accessToken), nil +} +func (*FakeTokenSource) DecryptWithDPoPKey([]byte) ([]byte, error) { + return nil, nil +} +func (fts *FakeTokenSource) MakeToken(f func(jwk.Key) ([]byte, error)) ([]byte, error) { + if fts.key == nil { + return nil, errors.New("no such key") + } + return f(fts.key) +} +func (*FakeTokenSource) DPoPPublicKeyPEM() string { + return "" +} +func (*FakeTokenSource) RefreshAccessToken() error { + return nil +} + +func runServer(ctx context.Context, //nolint:ireturn // this is pretty concrete + f *FakeAccessServiceServer, oo TokenAddingInterceptor) (kas.AccessServiceClient, func()) { + buffer := 1024 * 1024 + listener := bufconn.Listen(buffer) + + s := grpc.NewServer() + kas.RegisterAccessServiceServer(s, f) + go func() { + if err := s.Serve(listener); err != nil { + panic(err) + } + }() + + conn, _ := grpc.DialContext(ctx, "", grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return listener.Dial() + }), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock(), grpc.WithUnaryInterceptor(oo.AddCredentials)) + + client := kas.NewAccessServiceClient(conn) + + return client, s.Stop +} diff --git a/sdk/go.mod b/sdk/go.mod index e81c609212..ff9b0fba15 100644 --- a/sdk/go.mod +++ b/sdk/go.mod @@ -6,44 +6,45 @@ require ( github.com/docker/go-connections v0.5.0 github.com/golang-jwt/jwt/v4 v4.5.0 github.com/google/uuid v1.6.0 - github.com/lestrrat-go/jwx/v2 v2.0.19 + github.com/lestrrat-go/jwx/v2 v2.0.21 github.com/opentdf/platform/protocol/go v0.0.0-00010101000000-000000000000 - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.9.0 github.com/testcontainers/testcontainers-go v0.28.0 - golang.org/x/oauth2 v0.16.0 - google.golang.org/grpc v1.61.0 + golang.org/x/oauth2 v0.18.0 + google.golang.org/grpc v1.62.1 + google.golang.org/protobuf v1.33.0 ) replace github.com/opentdf/platform/protocol/go => ../protocol/go require ( - buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.32.0-20231115204500-e097f827e652.1 // indirect + buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.33.0-20240221180331-f05a6f4403ce.1 // indirect dario.cat/mergo v1.0.0 // indirect - github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/Microsoft/hcsshim v0.11.4 // indirect + github.com/Microsoft/hcsshim v0.12.0 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect - github.com/containerd/containerd v1.7.12 // indirect + github.com/containerd/containerd v1.7.14 // indirect github.com/containerd/log v0.1.0 // indirect github.com/cpuguy83/dockercfg v0.3.1 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/distribution/reference v0.5.0 // indirect github.com/docker/docker v25.0.2+incompatible // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/felixge/httpsnoop v1.0.3 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect - github.com/klauspost/compress v1.16.0 // indirect + github.com/klauspost/compress v1.17.7 // indirect github.com/kr/text v0.2.0 // indirect github.com/lestrrat-go/blackmagic v1.0.2 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect - github.com/lestrrat-go/httprc v1.0.4 // indirect + github.com/lestrrat-go/httprc v1.0.5 // indirect github.com/lestrrat-go/iter v1.0.2 // indirect github.com/lestrrat-go/option v1.0.1 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect @@ -54,9 +55,9 @@ require ( github.com/moby/term v0.5.0 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0-rc5 // indirect + github.com/opencontainers/image-spec v1.1.0 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/shirou/gopsutil/v3 v3.23.12 // indirect @@ -65,21 +66,19 @@ require ( github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 // indirect - go.opentelemetry.io/otel v1.21.0 // indirect - go.opentelemetry.io/otel/metric v1.21.0 // indirect - go.opentelemetry.io/otel/trace v1.21.0 // indirect - golang.org/x/crypto v0.18.0 // indirect - golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea // indirect - golang.org/x/mod v0.11.0 // indirect - golang.org/x/net v0.20.0 // indirect - golang.org/x/sys v0.16.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect + go.opentelemetry.io/otel v1.24.0 // indirect + go.opentelemetry.io/otel/metric v1.24.0 // indirect + go.opentelemetry.io/otel/trace v1.24.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect + golang.org/x/mod v0.15.0 // indirect + golang.org/x/net v0.22.0 // indirect + golang.org/x/sys v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.10.0 // indirect + golang.org/x/tools v0.18.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20240125205218-1f4bbc51befe // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe // indirect - google.golang.org/protobuf v1.32.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/sdk/go.sum b/sdk/go.sum index fea905e53e..d3a04a6cdf 100644 --- a/sdk/go.sum +++ b/sdk/go.sum @@ -1,19 +1,19 @@ -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.32.0-20231115204500-e097f827e652.1 h1:u0olL4yf2p7Tl5jfsAK5keaFi+JFJuv1CDHrbiXkxkk= -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.32.0-20231115204500-e097f827e652.1/go.mod h1:tiTMKD8j6Pd/D2WzREoweufjzaJKHZg35f/VGcZ2v3I= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.33.0-20240221180331-f05a6f4403ce.1 h1:0nWhrRcnkgw1kwJ7xibIO8bqfOA7pBzBjGCDBxIHch8= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.33.0-20240221180331-f05a6f4403ce.1/go.mod h1:Tgn5bgL220vkFOI0KPStlcClPeOJzAv4uT+V8JXGUnw= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8= -github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= +github.com/Microsoft/hcsshim v0.12.0 h1:rbICA+XZFwrBef2Odk++0LjFvClNCJGRK+fsrP254Ts= +github.com/Microsoft/hcsshim v0.12.0/go.mod h1:RZV12pcHCXQ42XnlQ3pz6FZfmrC1C+R4gaOHhRNML1g= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/containerd/containerd v1.7.12 h1:+KQsnv4VnzyxWcfO9mlxxELaoztsDEjOuCMPAuPqgU0= -github.com/containerd/containerd v1.7.12/go.mod h1:/5OMpE1p0ylxtEUGY8kuCYkDRzJm9NO1TFMWjUpdevk= +github.com/containerd/containerd v1.7.14 h1:H/XLzbnGuenZEGK+v0RkwTdv2u1QFAruMe5N0GNPJwA= +github.com/containerd/containerd v1.7.14/go.mod h1:YMC9Qt5yzNqXx/fO4j/5yYVIHXSRrlB3H7sxkUTvspg= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= @@ -22,8 +22,9 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 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= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= @@ -34,8 +35,8 @@ github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= -github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -51,8 +52,8 @@ github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOW github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -64,8 +65,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0Q github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= -github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= +github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -74,12 +75,12 @@ github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= -github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8= -github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= +github.com/lestrrat-go/httprc v1.0.5 h1:bsTfiH8xaKOJPrg1R+E3iE/AWZr/x0Phj9PBTG/OLUk= +github.com/lestrrat-go/httprc v1.0.5/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= -github.com/lestrrat-go/jwx/v2 v2.0.19 h1:ekv1qEZE6BVct89QA+pRF6+4pCpfVrOnEJnTnT4RXoY= -github.com/lestrrat-go/jwx/v2 v2.0.19/go.mod h1:l3im3coce1lL2cDeAjqmaR+Awx+X8Ih+2k8BuHNJ4CU= +github.com/lestrrat-go/jwx/v2 v2.0.21 h1:jAPKupy4uHgrHFEdjVjNkUgoBKtVDgrQPB/h55FHrR0= +github.com/lestrrat-go/jwx/v2 v2.0.21/go.mod h1:09mLW8zto6bWL9GbwnqAli+ArLf+5M33QLQPDggkUWM= github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= @@ -98,12 +99,13 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= -github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= @@ -125,8 +127,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/testcontainers/testcontainers-go v0.28.0 h1:1HLm9qm+J5VikzFDYhOd+Zw12NtOl+8drH2E8nTY1r8= github.com/testcontainers/testcontainers-go v0.28.0/go.mod h1:COlDpUXbwW3owtpMkEB1zo9gwb1CoKVKlyrVPejF4AU= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= @@ -138,51 +141,51 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0/go.mod h1:62CPTSry9QZtOaSsE3tOzhx6LzDhHnXJ6xHeMNNiM6Q= -go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= -go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= -go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= -go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= +go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= +go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= -go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= -go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= +go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea h1:vLCWI/yYrdEHyN2JzIzPO3aaQJHQdp89IZBA/+azVC4= -golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/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.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= -golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= +golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -197,8 +200,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -214,26 +217,24 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg= -golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= +golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= +golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/genproto v0.0.0-20240125205218-1f4bbc51befe h1:USL2DhxfgRchafRvt/wYyyQNzwgL7ZiURcozOE/Pkvo= -google.golang.org/genproto v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= -google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014 h1:x9PwdEgd11LgK+orcck69WVRo7DezSO4VUMPI4xpc8A= -google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014/go.mod h1:rbHMSEDyoYX62nRVLOCc4Qt1HbsdytAYoVwgjiOhF3I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe h1:bQnxqljG/wqi4NTXu2+DJ3n7APcEA882QZ1JvhQAq9o= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= -google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= -google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= +google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7 h1:oqta3O3AnlWbmIE3bFnWbu4bRxZjfbWCp0cKSuZh01E= +google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7/go.mod h1:VQW3tUculP/D4B+xVCo+VgSq8As6wA9ZjHl//pmk+6s= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7 h1:8EeVk1VKMD+GD/neyEHGmz7pFblqPjHoi+PGQIlLx2s= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= +google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/sdk/idp_access_token_source.go b/sdk/idp_access_token_source.go index e56ec62220..30c398deba 100644 --- a/sdk/idp_access_token_source.go +++ b/sdk/idp_access_token_source.go @@ -12,6 +12,7 @@ import ( "github.com/lestrrat-go/jwx/v2/jwa" "github.com/lestrrat-go/jwx/v2/jwk" + "github.com/opentdf/platform/sdk/auth" "github.com/opentdf/platform/sdk/internal/crypto" "github.com/opentdf/platform/sdk/internal/oauth" "golang.org/x/oauth2" @@ -75,7 +76,7 @@ func getNewDPoPKey() (string, jwk.Key, *crypto.AsymDecryption, error) { //nolint /* Credentials that allow us to connect to an IDP and obtain an access token that is bound -to a DPOP key +to a DPoP key */ type IDPAccessTokenSource struct { credentials oauth.ClientCredentials @@ -115,19 +116,19 @@ func NewIDPAccessTokenSource( } // use a pointer receiver so that the token state is shared -func (t *IDPAccessTokenSource) AccessToken() (AccessToken, error) { +func (t *IDPAccessTokenSource) AccessToken() (auth.AccessToken, error) { if t.token == nil { err := t.RefreshAccessToken() if err != nil { - return AccessToken(""), err + return auth.AccessToken(""), err } } - return AccessToken(t.token.AccessToken), nil + return auth.AccessToken(t.token.AccessToken), nil } -func (t *IDPAccessTokenSource) AsymDecryption() crypto.AsymDecryption { - return t.asymDecryption +func (t *IDPAccessTokenSource) DecryptWithDPoPKey(data []byte) ([]byte, error) { + return t.asymDecryption.Decrypt(data) } func (t *IDPAccessTokenSource) RefreshAccessToken() error { @@ -147,6 +148,6 @@ func (t *IDPAccessTokenSource) MakeToken(tokenMaker func(jwk.Key) ([]byte, error return tokenMaker(t.dpopKey) } -func (t *IDPAccessTokenSource) DPOPPublicKeyPEM() string { +func (t *IDPAccessTokenSource) DPoPPublicKeyPEM() string { return t.dpopPEM } diff --git a/sdk/internal/oauth/oauth_test.go b/sdk/internal/oauth/oauth_test.go index 1d495f73a3..614aa0615a 100644 --- a/sdk/internal/oauth/oauth_test.go +++ b/sdk/internal/oauth/oauth_test.go @@ -102,7 +102,7 @@ func TestClientSecretNoNonce(t *testing.T) { require.NoError(t, r.ParseForm()) validateBasicAuth(r, t) - extractDpopToken(r, t) + extractDPoPToken(r, t) tok, err := jwt.NewBuilder(). Issuer("example.org/fake"). @@ -161,7 +161,7 @@ func TestClientSecretWithNonce(t *testing.T) { } // get the key we used to sign the DPoP token from the header - clientTok := extractDpopToken(r, t) + clientTok := extractDPoPToken(r, t) if nonce, ok := clientTok.Get("nonce"); ok { if nonce.(string) != "dfdffdfddf" { @@ -246,7 +246,7 @@ func TestSignedJWTWithNonce(t *testing.T) { } // get the key we used to sign the DPoP token from the header - clientTok := extractDpopToken(r, t) + clientTok := extractDPoPToken(r, t) if nonce, ok := clientTok.Get("nonce"); ok { if nonce.(string) != "dfdffdfddf" { @@ -344,7 +344,7 @@ func validateBasicAuth(r *http.Request, t *testing.T) { } } -func extractDpopToken(r *http.Request, t *testing.T) jwt.Token { +func extractDPoPToken(r *http.Request, t *testing.T) jwt.Token { dpop := r.Header.Get("dpop") jwsMessage, err := jws.ParseString(dpop) if err != nil { diff --git a/sdk/kas_client.go b/sdk/kas_client.go index d9d61db2a5..86ca168ae7 100644 --- a/sdk/kas_client.go +++ b/sdk/kas_client.go @@ -10,7 +10,7 @@ import ( "github.com/lestrrat-go/jwx/v2/jwk" "github.com/lestrrat-go/jwx/v2/jwt" kas "github.com/opentdf/platform/protocol/go/kas" - "github.com/opentdf/platform/sdk/internal/crypto" + "github.com/opentdf/platform/sdk/auth" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -21,22 +21,10 @@ const ( ) type KASClient struct { - accessTokenSource AccessTokenSource + accessTokenSource auth.AccessTokenSource dialOptions []grpc.DialOption } -type AccessToken string - -type AccessTokenSource interface { - AccessToken() (AccessToken, error) - // probably better to use `crypto.AsymDecryption` here than roll our own since this should be - // more closely linked to what happens in KAS in terms of crypto params - AsymDecryption() crypto.AsymDecryption - MakeToken(func(jwk.Key) ([]byte, error)) ([]byte, error) - DPOPPublicKeyPEM() string - RefreshAccessToken() error -} - // once the backend moves over we should use the same type that the golang backend uses here type rewrapRequestBody struct { KeyAccess KeyAccess `json:"keyAccess"` @@ -94,7 +82,7 @@ func (k *KASClient) unwrap(keyAccess KeyAccess, policy string) ([]byte, error) { } } - key, err := k.accessTokenSource.AsymDecryption().Decrypt(response.EntityWrappedKey) + key, err := k.accessTokenSource.DecryptWithDPoPKey(response.GetEntityWrappedKey()) if err != nil { return nil, fmt.Errorf("error decrypting payload from KAS: %w", err) } @@ -122,7 +110,7 @@ func (k *KASClient) getRewrapRequest(keyAccess KeyAccess, policy string) (*kas.R requestBody := rewrapRequestBody{ Policy: policy, KeyAccess: keyAccess, - ClientPublicKey: k.accessTokenSource.DPOPPublicKeyPEM(), + ClientPublicKey: k.accessTokenSource.DPoPPublicKeyPEM(), } requestBodyJSON, err := json.Marshal(requestBody) if err != nil { @@ -142,7 +130,7 @@ func (k *KASClient) getRewrapRequest(keyAccess KeyAccess, policy string) (*kas.R signedToken, err := k.accessTokenSource.MakeToken(func(key jwk.Key) ([]byte, error) { signed, err := jwt.Sign(tok, jwt.WithKey(key.Algorithm(), key)) if err != nil { - return nil, fmt.Errorf("error signing DPOP token: %w", err) + return nil, fmt.Errorf("error signing DPoP token: %w", err) } return signed, nil diff --git a/sdk/kas_client_test.go b/sdk/kas_client_test.go index 3803b45e80..70ee748daf 100644 --- a/sdk/kas_client_test.go +++ b/sdk/kas_client_test.go @@ -8,25 +8,26 @@ import ( "github.com/lestrrat-go/jwx/v2/jwa" "github.com/lestrrat-go/jwx/v2/jwk" "github.com/lestrrat-go/jwx/v2/jwt" + "github.com/opentdf/platform/sdk/auth" "github.com/opentdf/platform/sdk/internal/crypto" ) type FakeAccessTokenSource struct { - dPOPKey jwk.Key + dpopKey jwk.Key asymDecryption crypto.AsymDecryption accessToken string } -func (fake FakeAccessTokenSource) AccessToken() (AccessToken, error) { - return AccessToken(fake.accessToken), nil +func (fake FakeAccessTokenSource) AccessToken() (auth.AccessToken, error) { + return auth.AccessToken(fake.accessToken), nil } -func (fake FakeAccessTokenSource) AsymDecryption() crypto.AsymDecryption { - return fake.asymDecryption +func (fake FakeAccessTokenSource) DecryptWithDPoPKey(encrypted []byte) ([]byte, error) { + return fake.asymDecryption.Decrypt(encrypted) } func (fake FakeAccessTokenSource) MakeToken(tokenMaker func(jwk.Key) ([]byte, error)) ([]byte, error) { - return tokenMaker(fake.dPOPKey) + return tokenMaker(fake.dpopKey) } -func (fake FakeAccessTokenSource) DPOPPublicKeyPEM() string { +func (fake FakeAccessTokenSource) DPoPPublicKeyPEM() string { return "this is the PEM" } func (fake FakeAccessTokenSource) RefreshAccessToken() error { @@ -47,7 +48,7 @@ func getTokenSource(t *testing.T) FakeAccessTokenSource { } return FakeAccessTokenSource{ - dPOPKey: dpopJWK, + dpopKey: dpopJWK, asymDecryption: decryption, accessToken: "thisistheaccesstoken", } @@ -74,9 +75,9 @@ func TestCreatingRequest(t *testing.T) { t.Fatalf("didn't produce a signed request token") } - pubKey, _ := tokenSource.dPOPKey.PublicKey() + pubKey, _ := tokenSource.dpopKey.PublicKey() - tok, err := jwt.ParseString(req.SignedRequestToken, jwt.WithKey(tokenSource.dPOPKey.Algorithm(), pubKey)) + tok, err := jwt.ParseString(req.GetSignedRequestToken(), jwt.WithKey(tokenSource.dpopKey.Algorithm(), pubKey)) if err != nil { t.Fatalf("couldn't parse signed token: %v", err) } @@ -93,10 +94,10 @@ func TestCreatingRequest(t *testing.T) { t.Fatalf("error unmarshaling request body: %v", err) } - if requestBody["clientPublicKey"].(string) != "this is the PEM" { + if requestBody["clientPublicKey"] != "this is the PEM" { t.Fatalf("incorrect public key included") } - if requestBody["policy"].(string) != "a policy" { + if requestBody["policy"] != "a policy" { t.Fatalf("incorrect policy") } diff --git a/sdk/sdk.go b/sdk/sdk.go index ff37f74ab1..ce5d05d8ab 100644 --- a/sdk/sdk.go +++ b/sdk/sdk.go @@ -3,7 +3,7 @@ package sdk import ( "crypto/tls" "errors" - "fmt" + "log/slog" "github.com/opentdf/platform/protocol/go/authorization" "github.com/opentdf/platform/protocol/go/kasregistry" @@ -11,6 +11,7 @@ import ( "github.com/opentdf/platform/protocol/go/policy/namespaces" "github.com/opentdf/platform/protocol/go/policy/resourcemapping" "github.com/opentdf/platform/protocol/go/policy/subjectmapping" + "github.com/opentdf/platform/sdk/auth" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) @@ -52,13 +53,20 @@ func New(platformEndpoint string, opts ...Option) (*SDK, error) { opt(cfg) } + // once we change KAS to use standard DPoP we can put this all in the `build()` method + dialOptions := append([]grpc.DialOption{}, cfg.build()...) + accessTokenSource, err := buildIDPTokenSource(cfg) + if err != nil { + return nil, err + } + if accessTokenSource != nil { + interceptor := auth.NewTokenAddingInterceptor(accessTokenSource) + dialOptions = append(dialOptions, grpc.WithUnaryInterceptor(interceptor.AddCredentials)) + } + var unwrapper Unwrapper if cfg.authConfig == nil { - uw, err := buildKASClient(cfg) - if err != nil { - return nil, err - } - unwrapper = &uw + unwrapper = &KASClient{dialOptions: dialOptions, accessTokenSource: accessTokenSource} } else { unwrapper = cfg.authConfig } @@ -71,7 +79,7 @@ func New(platformEndpoint string, opts ...Option) (*SDK, error) { if platformEndpoint != "" { var err error - defaultConn, err = grpc.Dial(platformEndpoint, cfg.build()...) + defaultConn, err = grpc.Dial(platformEndpoint, dialOptions...) if err != nil { return nil, errors.Join(ErrGrpcDialFailed, err) } @@ -101,19 +109,20 @@ func New(platformEndpoint string, opts ...Option) (*SDK, error) { }, nil } -func buildKASClient(c *config) (KASClient, error) { +func buildIDPTokenSource(c *config) (*IDPAccessTokenSource, error) { if (c.clientCredentials.ClientId == "") != (c.clientCredentials.ClientAuth == nil) { - return KASClient{}, + return nil, errors.New("if specifying client credentials must specify both client id and authentication secret") } if (c.clientCredentials.ClientId == "") != (c.tokenEndpoint == "") { - return KASClient{}, errors.New("either both or neither of client credentials and token endpoint must be specified") + return nil, errors.New("either both or neither of client credentials and token endpoint must be specified") } // at this point we have either both client credentials and a token endpoint or none of the above. if we don't have // any just return a KAS client that can only get public keys if c.clientCredentials.ClientId == "" { - return KASClient{dialOptions: c.build()}, nil + slog.Info("no client credentials provided. GRPC requests to KAS and services will not be authenticated.") + return nil, nil //nolint:nilnil // not having credentials is not an error } ts, err := NewIDPAccessTokenSource( @@ -122,16 +131,7 @@ func buildKASClient(c *config) (KASClient, error) { c.scopes, ) - if err != nil { - return KASClient{}, fmt.Errorf("error configuring IDP access: %w", err) - } - - kasClient := KASClient{ - accessTokenSource: &ts, - dialOptions: c.build(), - } - - return kasClient, nil + return &ts, err } // Close closes the underlying grpc.ClientConn. diff --git a/services/kas/archive/manifest/object.go b/services/kas/archive/manifest/object.go deleted file mode 100644 index 8076521b9b..0000000000 --- a/services/kas/archive/manifest/object.go +++ /dev/null @@ -1,17 +0,0 @@ -package manifest - -import "github.com/opentdf/backend-go/pkg/tdf3" - -type Object struct { - EncryptionInformation tdf3.EncryptionInformation `json:"encryptionInformation"` - Payload Payload `json:"payload"` - SchemaVersion string `json:"schemaVersion,omitempty"` -} - -type Payload struct { - IsEncrypted bool `json:"isEncrypted"` - MimeType string `json:"mimeType"` - Protocol string `json:"protocol"` - Type string `json:"type"` - URL string `json:"url"` -} diff --git a/services/kas/archive/manifest/testdata/manifest.json b/services/kas/archive/manifest/testdata/manifest.json deleted file mode 100644 index 5871532dee..0000000000 --- a/services/kas/archive/manifest/testdata/manifest.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "encryptionInformation": { - "integrityInformation": { - "encryptedSegmentSizeDefault": 2097180, - "rootSignature": { - "alg": "HS256", - "sig": "ZTViOTE5ZmQ2MGZmMjUyN2QzMGM2YmRlN2NiNThiYjg2NTMxYTg5NTRkZjY3MmJjNjNlY2Y5M2VmNTM5NDc1Zg==" - }, - "segmentHashAlg": "GMAC", - "segmentSizeDefault": 2097152, - "segments": [ - { - "encryptedSegmentSize": 2341, - "hash": "YmE1Njk3YzEzMGU0MGM2YzAyZmYwOWU2NTVjODM3YjY=", - "segmentSize": 2097152 - } - ] - }, - "keyAccess": [ - { - "protocol": "kas", - "type": "remote", - "url": "https://api-develop01.develop.virtru.com/kas" - } - ], - "method": { - "algorithm": "AES-256-GCM", - "isStreamable": true, - "iv": "2leWAXhcKPFFh29B" - }, - "policy": "eyJ1dWlkIjoiOTU2ZDhhOWUtOTg4ZC00YWFiLTg3NTQtZjJkMjUzYWIzYjcwIn0=", - "type": "split" - }, - "payload": { - "isEncrypted": true, - "mimeType": "application/octet-stream", - "protocol": "zip", - "type": "reference", - "url": "0.payload" - } -} \ No newline at end of file diff --git a/services/kas/archive/manifest/validator.go b/services/kas/archive/manifest/validator.go deleted file mode 100644 index 48599d66b9..0000000000 --- a/services/kas/archive/manifest/validator.go +++ /dev/null @@ -1,37 +0,0 @@ -package manifest - -import ( - "encoding/json" - "errors" - "log" -) - -const ( - ErrInvalidJson = Error("manifest json invalid") - ErrUnmarshal = Error("manifest unmarshal") -) - -// Valid validates a manifest.json -// well-formed JSON -// validate against spec version -// validate all values in manifest -// check integrity of signatures, JWT -// validate URLs can be found -func Valid(m []byte) error { - if !json.Valid(m) { - return ErrInvalidJson - } - var manifest Object - err := json.Unmarshal(m, &manifest) - if err != nil { - return errors.Join(ErrUnmarshal, err) - } - log.Println(manifest) - return nil -} - -type Error string - -func (e Error) Error() string { - return string(e) -} diff --git a/services/kas/archive/manifest/validator_test.go b/services/kas/archive/manifest/validator_test.go deleted file mode 100644 index 7a8baf7102..0000000000 --- a/services/kas/archive/manifest/validator_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package manifest - -import ( - "os" - "testing" -) - -func TestValid(t *testing.T) { - f, err := os.ReadFile("testdata/manifest.json") - if err != nil { - t.Fatal(err) - } - err = Valid(f) - if err != nil { - t.Fatal(err) - } - err = Valid([]byte("invalid-json")) - if err == nil { - t.Fail() - } - t.Log(err) - t.Log("Valid") -} - -func TestValidFailure(t *testing.T) { - var mockJson = []byte(`[ - {"Wrong": "Platypus", "Fields": "Monotremata"}, - {"Wrong": "Quoll", "Fields": "Dasyuromorphia"} - ]`) - - err := Valid(mockJson) - - if err == nil { - t.Errorf("Error expected, but got %v", err) - } -} diff --git a/services/kas/archive/testdata/envoy.yaml.tdf b/services/kas/archive/testdata/envoy.yaml.tdf deleted file mode 100644 index d8071e25f6..0000000000 Binary files a/services/kas/archive/testdata/envoy.yaml.tdf and /dev/null differ diff --git a/services/kas/archive/validator.go b/services/kas/archive/validator.go deleted file mode 100644 index ab977efd66..0000000000 --- a/services/kas/archive/validator.go +++ /dev/null @@ -1,58 +0,0 @@ -package archive - -import ( - "archive/zip" - "bytes" - "errors" - "io" - "log" - "strings" -) - -const ( - ErrCopy = Error("archive error copy") - ErrZipFormat = Error("archive zip error") - ErrManifestRead = Error("archive manifest error") -) - -// TODO add validate function to be used by CLI and web -// find file -// check zip -// check contents -// check payload is correct -// check integrity versus manifest - -// Valid reports errors if r is an invalid TDF3 archive. -func Valid(r io.Reader) error { - buff := bytes.NewBuffer([]byte{}) - size, err := io.Copy(buff, r) - if err != nil { - return errors.Join(ErrCopy, err) - } - reader := bytes.NewReader(buff.Bytes()) - zipReader, err := zip.NewReader(reader, size) - if err != nil { - return errors.Join(ErrZipFormat, err) - } - for _, f := range zipReader.File { - if strings.Contains(f.Name, "manifest") { - rc, err := f.Open() - if err != nil { - return errors.Join(ErrManifestRead, err) - } - manifest, err := io.ReadAll(rc) - if err != nil { - return errors.Join(ErrManifestRead, err) - } - _ = rc.Close() - log.Println(manifest) - } - } - return nil -} - -type Error string - -func (e Error) Error() string { - return string(e) -} diff --git a/services/kas/archive/validator_test.go b/services/kas/archive/validator_test.go deleted file mode 100644 index 49720bb170..0000000000 --- a/services/kas/archive/validator_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package archive - -import ( - "bytes" - "os" - "strings" - "testing" -) - -func TestValid(t *testing.T) { - f, err := os.ReadFile("testdata/envoy.yaml.tdf") - if err != nil { - t.Fatal(err) - } - err = Valid(bytes.NewReader(f)) - if err != nil { - t.Fatal(err) - } - r := strings.NewReader("hello world") - err = Valid(r) - if err == nil { - t.Fail() - } - t.Log(err) - t.Log("Valid") -}