forked from TrueCloudLab/distribution
build(deps): bump github.com/Azure/azure-sdk-for-go/sdk/azidentity
Bumps [github.com/Azure/azure-sdk-for-go/sdk/azidentity](https://github.com/Azure/azure-sdk-for-go) from 1.3.0 to 1.6.0. - [Release notes](https://github.com/Azure/azure-sdk-for-go/releases) - [Changelog](https://github.com/Azure/azure-sdk-for-go/blob/main/documentation/release.md) - [Commits](https://github.com/Azure/azure-sdk-for-go/compare/sdk/azcore/v1.3.0...sdk/azcore/v1.6.0) --- updated-dependencies: - dependency-name: github.com/Azure/azure-sdk-for-go/sdk/azidentity dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
parent
e1ec19ae60
commit
050e1a3ee7
239 changed files with 17097 additions and 7428 deletions
27
go.mod
27
go.mod
|
@ -5,8 +5,8 @@ go 1.21.8
|
|||
require (
|
||||
cloud.google.com/go/storage v1.30.1
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20221103172237-443f56ff4ba8
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.0.0
|
||||
github.com/aws/aws-sdk-go v1.48.10
|
||||
github.com/bshuster-repo/logrus-logstash-hook v1.0.0
|
||||
|
@ -15,7 +15,7 @@ require (
|
|||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c
|
||||
github.com/docker/go-metrics v0.0.1
|
||||
github.com/go-jose/go-jose/v4 v4.0.2
|
||||
github.com/google/uuid v1.3.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/handlers v1.5.2
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/hashicorp/golang-lru/arc/v2 v2.0.5
|
||||
|
@ -27,27 +27,29 @@ require (
|
|||
github.com/redis/go-redis/v9 v9.1.0
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/stretchr/testify v1.9.0
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.46.1
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1
|
||||
go.opentelemetry.io/otel v1.21.0
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.21.0
|
||||
go.opentelemetry.io/otel/sdk v1.21.0
|
||||
go.opentelemetry.io/otel/trace v1.21.0
|
||||
golang.org/x/crypto v0.21.0
|
||||
golang.org/x/net v0.23.0
|
||||
golang.org/x/crypto v0.24.0
|
||||
golang.org/x/net v0.26.0
|
||||
golang.org/x/oauth2 v0.11.0
|
||||
google.golang.org/api v0.126.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.110.7 // indirect
|
||||
cloud.google.com/go/compute v1.23.0 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
cloud.google.com/go/iam v1.1.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
|
@ -57,7 +59,6 @@ require (
|
|||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-logr/logr v1.3.0 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/s2a-go v0.1.4 // indirect
|
||||
|
@ -69,7 +70,7 @@ require (
|
|||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.17.0 // indirect; updated to latest
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
|
@ -88,9 +89,9 @@ require (
|
|||
go.opentelemetry.io/otel/metric v1.21.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.21.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
|
||||
golang.org/x/sync v0.3.0
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/sync v0.7.0
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect
|
||||
|
|
60
go.sum
60
go.sum
|
@ -12,16 +12,16 @@ cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/o
|
|||
cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20221103172237-443f56ff4ba8 h1:d+pBUmsteW5tM87xmVXHZ4+LibHRFn40SPAoZJOg2ak=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20221103172237-443f56ff4ba8/go.mod h1:i9fr2JpcEcY/IHEvzCM3qXUZYOQHgR89dt4es1CgMhc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 h1:8kDqDngH+DmVBiCtIjCFTGa7MBnsIOkF9IccInFEbjk=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqbjDYsgN+RzP4q16yV5eM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 h1:U2rTu3Ef+7w9FHKIAXM6ZyqF3UOWJZ12zIm8zECAFfg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 h1:jBQA3cKT4L2rWMpgE7Yt3Hwh2aUj8KXjIGLxjHeYNNo=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0/go.mod h1:4OG6tQ9EOP/MT0NMjDlRzWoVFxfu9rN9B2X+tlSVktg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.0.0 h1:u/LLAOFgsMv7HmNL4Qufg58y+qElGOt5qv0z1mURkRY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.0.0/go.mod h1:2e8rMJtl2+2j+HXbTBwnyGpm5Nou7KhvSfxOq8JpTag=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkMqLPymWEppkm7vgPQY2XsHoEkaMQ0AdZY=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
|
@ -66,8 +66,6 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
|
|||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
|
||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
|
||||
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
|
||||
|
@ -94,8 +92,8 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre
|
|||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
|
||||
github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
|
||||
|
@ -134,8 +132,8 @@ github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3
|
|||
github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc=
|
||||
github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
|
||||
github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4=
|
||||
|
@ -187,8 +185,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
|
|||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
|
||||
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/errors v0.8.0/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=
|
||||
|
@ -219,8 +217,8 @@ github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDO
|
|||
github.com/redis/go-redis/v9 v9.1.0 h1:137FnGdk+EQdCbye1FW+qOEcY5S+SpY9T0NiuqvtfMY=
|
||||
github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
|
@ -241,8 +239,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||
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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
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/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
|
@ -286,8 +284,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
|||
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.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
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/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
|
@ -308,8 +306,8 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY
|
|||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU=
|
||||
|
@ -319,8 +317,8 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
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/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -333,13 +331,13 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||
golang.org/x/sys v0.21.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=
|
||||
|
@ -348,8 +346,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
|
|
221
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/CHANGELOG.md
generated
vendored
221
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/CHANGELOG.md
generated
vendored
|
@ -1,5 +1,226 @@
|
|||
# Release History
|
||||
|
||||
## 1.11.1 (2024-04-02)
|
||||
|
||||
### Bugs Fixed
|
||||
|
||||
* Pollers that use the `Location` header won't consider `http.StatusRequestTimeout` a terminal failure.
|
||||
* `runtime.Poller[T].Result` won't consider non-terminal error responses as terminal.
|
||||
|
||||
## 1.11.0 (2024-04-01)
|
||||
|
||||
### Features Added
|
||||
|
||||
* Added `StatusCodes` to `arm/policy.RegistrationOptions` to allow supporting non-standard HTTP status codes during registration.
|
||||
* Added field `InsecureAllowCredentialWithHTTP` to `azcore.ClientOptions` and dependent authentication pipeline policies.
|
||||
* Added type `MultipartContent` to the `streaming` package to support multipart/form payloads with custom Content-Type and file name.
|
||||
|
||||
### Bugs Fixed
|
||||
|
||||
* `runtime.SetMultipartFormData` won't try to stringify `[]byte` values.
|
||||
* Pollers that use the `Location` header won't consider `http.StatusTooManyRequests` a terminal failure.
|
||||
|
||||
### Other Changes
|
||||
|
||||
* Update dependencies.
|
||||
|
||||
## 1.10.0 (2024-02-29)
|
||||
|
||||
### Features Added
|
||||
|
||||
* Added logging event `log.EventResponseError` that will contain the contents of `ResponseError.Error()` whenever an `azcore.ResponseError` is created.
|
||||
* Added `runtime.NewResponseErrorWithErrorCode` for creating an `azcore.ResponseError` with a caller-supplied error code.
|
||||
* Added type `MatchConditions` for use in conditional requests.
|
||||
|
||||
### Bugs Fixed
|
||||
|
||||
* Fixed a potential race condition between `NullValue` and `IsNullValue`.
|
||||
* `runtime.EncodeQueryParams` will escape semicolons before calling `url.ParseQuery`.
|
||||
|
||||
### Other Changes
|
||||
|
||||
* Update dependencies.
|
||||
|
||||
## 1.9.2 (2024-02-06)
|
||||
|
||||
### Bugs Fixed
|
||||
|
||||
* `runtime.MarshalAsByteArray` and `runtime.MarshalAsJSON` will preserve the preexisting value of the `Content-Type` header.
|
||||
|
||||
### Other Changes
|
||||
|
||||
* Update to latest version of `internal`.
|
||||
|
||||
## 1.9.1 (2023-12-11)
|
||||
|
||||
### Bugs Fixed
|
||||
|
||||
* The `retry-after-ms` and `x-ms-retry-after-ms` headers weren't being checked during retries.
|
||||
|
||||
### Other Changes
|
||||
|
||||
* Update dependencies.
|
||||
|
||||
## 1.9.0 (2023-11-06)
|
||||
|
||||
### Breaking Changes
|
||||
> These changes affect only code written against previous beta versions of `v1.7.0` and `v1.8.0`
|
||||
* The function `NewTokenCredential` has been removed from the `fake` package. Use a literal `&fake.TokenCredential{}` instead.
|
||||
* The field `TracingNamespace` in `runtime.PipelineOptions` has been replaced by `TracingOptions`.
|
||||
|
||||
### Bugs Fixed
|
||||
|
||||
* Fixed an issue that could cause some allowed HTTP header values to not show up in logs.
|
||||
* Include error text instead of error type in traces when the transport returns an error.
|
||||
* Fixed an issue that could cause an HTTP/2 request to hang when the TCP connection becomes unresponsive.
|
||||
* Block key and SAS authentication for non TLS protected endpoints.
|
||||
* Passing a `nil` credential value will no longer cause a panic. Instead, the authentication is skipped.
|
||||
* Calling `Error` on a zero-value `azcore.ResponseError` will no longer panic.
|
||||
* Fixed an issue in `fake.PagerResponder[T]` that would cause a trailing error to be omitted when iterating over pages.
|
||||
* Context values created by `azcore` will no longer flow across disjoint HTTP requests.
|
||||
|
||||
### Other Changes
|
||||
|
||||
* Skip generating trace info for no-op tracers.
|
||||
* The `clientName` paramater in client constructors has been renamed to `moduleName`.
|
||||
|
||||
## 1.9.0-beta.1 (2023-10-05)
|
||||
|
||||
### Other Changes
|
||||
|
||||
* The beta features for tracing and fakes have been reinstated.
|
||||
|
||||
## 1.8.0 (2023-10-05)
|
||||
|
||||
### Features Added
|
||||
|
||||
* This includes the following features from `v1.8.0-beta.N` releases.
|
||||
* Claims and CAE for authentication.
|
||||
* New `messaging` package.
|
||||
* Various helpers in the `runtime` package.
|
||||
* Deprecation of `runtime.With*` funcs and their replacements in the `policy` package.
|
||||
* Added types `KeyCredential` and `SASCredential` to the `azcore` package.
|
||||
* Includes their respective constructor functions.
|
||||
* Added types `KeyCredentialPolicy` and `SASCredentialPolicy` to the `azcore/runtime` package.
|
||||
* Includes their respective constructor functions and options types.
|
||||
|
||||
### Breaking Changes
|
||||
> These changes affect only code written against beta versions of `v1.8.0`
|
||||
* The beta features for tracing and fakes have been omitted for this release.
|
||||
|
||||
### Bugs Fixed
|
||||
|
||||
* Fixed an issue that could cause some ARM RPs to not be automatically registered.
|
||||
* Block bearer token authentication for non TLS protected endpoints.
|
||||
|
||||
### Other Changes
|
||||
|
||||
* Updated dependencies.
|
||||
|
||||
## 1.8.0-beta.3 (2023-09-07)
|
||||
|
||||
### Features Added
|
||||
|
||||
* Added function `FetcherForNextLink` and `FetcherForNextLinkOptions` to the `runtime` package to centralize creation of `Pager[T].Fetcher` from a next link URL.
|
||||
|
||||
### Bugs Fixed
|
||||
|
||||
* Suppress creating spans for nested SDK API calls. The HTTP span will be a child of the outer API span.
|
||||
|
||||
### Other Changes
|
||||
|
||||
* The following functions in the `runtime` package are now exposed from the `policy` package, and the `runtime` versions have been deprecated.
|
||||
* `WithCaptureResponse`
|
||||
* `WithHTTPHeader`
|
||||
* `WithRetryOptions`
|
||||
|
||||
## 1.7.2 (2023-09-06)
|
||||
|
||||
### Bugs Fixed
|
||||
|
||||
* Fix default HTTP transport to work in WASM modules.
|
||||
|
||||
## 1.8.0-beta.2 (2023-08-14)
|
||||
|
||||
### Features Added
|
||||
|
||||
* Added function `SanitizePagerPollerPath` to the `server` package to centralize sanitization and formalize the contract.
|
||||
* Added `TokenRequestOptions.EnableCAE` to indicate whether to request a CAE token.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
> This change affects only code written against beta version `v1.8.0-beta.1`.
|
||||
* `messaging.CloudEvent` deserializes JSON objects as `[]byte`, instead of `json.RawMessage`. See the documentation for CloudEvent.Data for more information.
|
||||
|
||||
> This change affects only code written against beta versions `v1.7.0-beta.2` and `v1.8.0-beta.1`.
|
||||
* Removed parameter from method `Span.End()` and its type `tracing.SpanEndOptions`. This API GA'ed in `v1.2.0` so we cannot change it.
|
||||
|
||||
### Bugs Fixed
|
||||
|
||||
* Propagate any query parameters when constructing a fake poller and/or injecting next links.
|
||||
|
||||
## 1.7.1 (2023-08-14)
|
||||
|
||||
## Bugs Fixed
|
||||
|
||||
* Enable TLS renegotiation in the default transport policy.
|
||||
|
||||
## 1.8.0-beta.1 (2023-07-12)
|
||||
|
||||
### Features Added
|
||||
|
||||
- `messaging/CloudEvent` allows you to serialize/deserialize CloudEvents, as described in the CloudEvents 1.0 specification: [link](https://github.com/cloudevents/spec)
|
||||
|
||||
### Other Changes
|
||||
|
||||
* The beta features for CAE, tracing, and fakes have been reinstated.
|
||||
|
||||
## 1.7.0 (2023-07-12)
|
||||
|
||||
### Features Added
|
||||
* Added method `WithClientName()` to type `azcore.Client` to support shallow cloning of a client with a new name used for tracing.
|
||||
|
||||
### Breaking Changes
|
||||
> These changes affect only code written against beta versions v1.7.0-beta.1 or v1.7.0-beta.2
|
||||
* The beta features for CAE, tracing, and fakes have been omitted for this release.
|
||||
|
||||
## 1.7.0-beta.2 (2023-06-06)
|
||||
|
||||
### Breaking Changes
|
||||
> These changes affect only code written against beta version v1.7.0-beta.1
|
||||
* Method `SpanFromContext()` on type `tracing.Tracer` had the `bool` return value removed.
|
||||
* This includes the field `SpanFromContext` in supporting type `tracing.TracerOptions`.
|
||||
* Method `AddError()` has been removed from type `tracing.Span`.
|
||||
* Method `Span.End()` now requires an argument of type `*tracing.SpanEndOptions`.
|
||||
|
||||
## 1.6.1 (2023-06-06)
|
||||
|
||||
### Bugs Fixed
|
||||
* Fixed an issue in `azcore.NewClient()` and `arm.NewClient()` that could cause an incorrect module name to be used in telemetry.
|
||||
|
||||
### Other Changes
|
||||
* This version contains all bug fixes from `v1.7.0-beta.1`
|
||||
|
||||
## 1.7.0-beta.1 (2023-05-24)
|
||||
|
||||
### Features Added
|
||||
* Restored CAE support for ARM clients.
|
||||
* Added supporting features to enable distributed tracing.
|
||||
* Added func `runtime.StartSpan()` for use by SDKs to start spans.
|
||||
* Added method `WithContext()` to `runtime.Request` to support shallow cloning with a new context.
|
||||
* Added field `TracingNamespace` to `runtime.PipelineOptions`.
|
||||
* Added field `Tracer` to `runtime.NewPollerOptions` and `runtime.NewPollerFromResumeTokenOptions` types.
|
||||
* Added field `SpanFromContext` to `tracing.TracerOptions`.
|
||||
* Added methods `Enabled()`, `SetAttributes()`, and `SpanFromContext()` to `tracing.Tracer`.
|
||||
* Added supporting pipeline policies to include HTTP spans when creating clients.
|
||||
* Added package `fake` to support generated fakes packages in SDKs.
|
||||
* The package contains public surface area exposed by fake servers and supporting APIs intended only for use by the fake server implementations.
|
||||
* Added an internal fake poller implementation.
|
||||
|
||||
### Bugs Fixed
|
||||
* Retry policy always clones the underlying `*http.Request` before invoking the next policy.
|
||||
* Added some non-standard error codes to the list of error codes for unregistered resource providers.
|
||||
|
||||
## 1.6.0 (2023-05-04)
|
||||
|
||||
### Features Added
|
||||
|
|
224
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/resource/resource_identifier.go
generated
vendored
Normal file
224
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/resource/resource_identifier.go
generated
vendored
Normal file
|
@ -0,0 +1,224 @@
|
|||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package resource
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
providersKey = "providers"
|
||||
subscriptionsKey = "subscriptions"
|
||||
resourceGroupsLowerKey = "resourcegroups"
|
||||
locationsKey = "locations"
|
||||
builtInResourceNamespace = "Microsoft.Resources"
|
||||
)
|
||||
|
||||
// RootResourceID defines the tenant as the root parent of all other ResourceID.
|
||||
var RootResourceID = &ResourceID{
|
||||
Parent: nil,
|
||||
ResourceType: TenantResourceType,
|
||||
Name: "",
|
||||
}
|
||||
|
||||
// ResourceID represents a resource ID such as `/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRg`.
|
||||
// Don't create this type directly, use ParseResourceID instead.
|
||||
type ResourceID struct {
|
||||
// Parent is the parent ResourceID of this instance.
|
||||
// Can be nil if there is no parent.
|
||||
Parent *ResourceID
|
||||
|
||||
// SubscriptionID is the subscription ID in this resource ID.
|
||||
// The value can be empty if the resource ID does not contain a subscription ID.
|
||||
SubscriptionID string
|
||||
|
||||
// ResourceGroupName is the resource group name in this resource ID.
|
||||
// The value can be empty if the resource ID does not contain a resource group name.
|
||||
ResourceGroupName string
|
||||
|
||||
// Provider represents the provider name in this resource ID.
|
||||
// This is only valid when the resource ID represents a resource provider.
|
||||
// Example: `/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Insights`
|
||||
Provider string
|
||||
|
||||
// Location is the location in this resource ID.
|
||||
// The value can be empty if the resource ID does not contain a location name.
|
||||
Location string
|
||||
|
||||
// ResourceType represents the type of this resource ID.
|
||||
ResourceType ResourceType
|
||||
|
||||
// Name is the resource name of this resource ID.
|
||||
Name string
|
||||
|
||||
isChild bool
|
||||
stringValue string
|
||||
}
|
||||
|
||||
// ParseResourceID parses a string to an instance of ResourceID
|
||||
func ParseResourceID(id string) (*ResourceID, error) {
|
||||
if len(id) == 0 {
|
||||
return nil, fmt.Errorf("invalid resource ID: id cannot be empty")
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(id, "/") {
|
||||
return nil, fmt.Errorf("invalid resource ID: resource id '%s' must start with '/'", id)
|
||||
}
|
||||
|
||||
parts := splitStringAndOmitEmpty(id, "/")
|
||||
|
||||
if len(parts) < 2 {
|
||||
return nil, fmt.Errorf("invalid resource ID: %s", id)
|
||||
}
|
||||
|
||||
if !strings.EqualFold(parts[0], subscriptionsKey) && !strings.EqualFold(parts[0], providersKey) {
|
||||
return nil, fmt.Errorf("invalid resource ID: %s", id)
|
||||
}
|
||||
|
||||
return appendNext(RootResourceID, parts, id)
|
||||
}
|
||||
|
||||
// String returns the string of the ResourceID
|
||||
func (id *ResourceID) String() string {
|
||||
if len(id.stringValue) > 0 {
|
||||
return id.stringValue
|
||||
}
|
||||
|
||||
if id.Parent == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
builder := strings.Builder{}
|
||||
builder.WriteString(id.Parent.String())
|
||||
|
||||
if id.isChild {
|
||||
builder.WriteString(fmt.Sprintf("/%s", id.ResourceType.lastType()))
|
||||
if len(id.Name) > 0 {
|
||||
builder.WriteString(fmt.Sprintf("/%s", id.Name))
|
||||
}
|
||||
} else {
|
||||
builder.WriteString(fmt.Sprintf("/providers/%s/%s/%s", id.ResourceType.Namespace, id.ResourceType.Type, id.Name))
|
||||
}
|
||||
|
||||
id.stringValue = builder.String()
|
||||
|
||||
return id.stringValue
|
||||
}
|
||||
|
||||
func newResourceID(parent *ResourceID, resourceTypeName string, resourceName string) *ResourceID {
|
||||
id := &ResourceID{}
|
||||
id.init(parent, chooseResourceType(resourceTypeName, parent), resourceName, true)
|
||||
return id
|
||||
}
|
||||
|
||||
func newResourceIDWithResourceType(parent *ResourceID, resourceType ResourceType, resourceName string) *ResourceID {
|
||||
id := &ResourceID{}
|
||||
id.init(parent, resourceType, resourceName, true)
|
||||
return id
|
||||
}
|
||||
|
||||
func newResourceIDWithProvider(parent *ResourceID, providerNamespace, resourceTypeName, resourceName string) *ResourceID {
|
||||
id := &ResourceID{}
|
||||
id.init(parent, NewResourceType(providerNamespace, resourceTypeName), resourceName, false)
|
||||
return id
|
||||
}
|
||||
|
||||
func chooseResourceType(resourceTypeName string, parent *ResourceID) ResourceType {
|
||||
if strings.EqualFold(resourceTypeName, resourceGroupsLowerKey) {
|
||||
return ResourceGroupResourceType
|
||||
} else if strings.EqualFold(resourceTypeName, subscriptionsKey) && parent != nil && parent.ResourceType.String() == TenantResourceType.String() {
|
||||
return SubscriptionResourceType
|
||||
}
|
||||
|
||||
return parent.ResourceType.AppendChild(resourceTypeName)
|
||||
}
|
||||
|
||||
func (id *ResourceID) init(parent *ResourceID, resourceType ResourceType, name string, isChild bool) {
|
||||
if parent != nil {
|
||||
id.Provider = parent.Provider
|
||||
id.SubscriptionID = parent.SubscriptionID
|
||||
id.ResourceGroupName = parent.ResourceGroupName
|
||||
id.Location = parent.Location
|
||||
}
|
||||
|
||||
if resourceType.String() == SubscriptionResourceType.String() {
|
||||
id.SubscriptionID = name
|
||||
}
|
||||
|
||||
if resourceType.lastType() == locationsKey {
|
||||
id.Location = name
|
||||
}
|
||||
|
||||
if resourceType.String() == ResourceGroupResourceType.String() {
|
||||
id.ResourceGroupName = name
|
||||
}
|
||||
|
||||
if resourceType.String() == ProviderResourceType.String() {
|
||||
id.Provider = name
|
||||
}
|
||||
|
||||
if parent == nil {
|
||||
id.Parent = RootResourceID
|
||||
} else {
|
||||
id.Parent = parent
|
||||
}
|
||||
id.isChild = isChild
|
||||
id.ResourceType = resourceType
|
||||
id.Name = name
|
||||
}
|
||||
|
||||
func appendNext(parent *ResourceID, parts []string, id string) (*ResourceID, error) {
|
||||
if len(parts) == 0 {
|
||||
return parent, nil
|
||||
}
|
||||
|
||||
if len(parts) == 1 {
|
||||
// subscriptions and resourceGroups are not valid ids without their names
|
||||
if strings.EqualFold(parts[0], subscriptionsKey) || strings.EqualFold(parts[0], resourceGroupsLowerKey) {
|
||||
return nil, fmt.Errorf("invalid resource ID: %s", id)
|
||||
}
|
||||
|
||||
// resourceGroup must contain either child or provider resource type
|
||||
if parent.ResourceType.String() == ResourceGroupResourceType.String() {
|
||||
return nil, fmt.Errorf("invalid resource ID: %s", id)
|
||||
}
|
||||
|
||||
return newResourceID(parent, parts[0], ""), nil
|
||||
}
|
||||
|
||||
if strings.EqualFold(parts[0], providersKey) && (len(parts) == 2 || strings.EqualFold(parts[2], providersKey)) {
|
||||
//provider resource can only be on a tenant or a subscription parent
|
||||
if parent.ResourceType.String() != SubscriptionResourceType.String() && parent.ResourceType.String() != TenantResourceType.String() {
|
||||
return nil, fmt.Errorf("invalid resource ID: %s", id)
|
||||
}
|
||||
|
||||
return appendNext(newResourceIDWithResourceType(parent, ProviderResourceType, parts[1]), parts[2:], id)
|
||||
}
|
||||
|
||||
if len(parts) > 3 && strings.EqualFold(parts[0], providersKey) {
|
||||
return appendNext(newResourceIDWithProvider(parent, parts[1], parts[2], parts[3]), parts[4:], id)
|
||||
}
|
||||
|
||||
if len(parts) > 1 && !strings.EqualFold(parts[0], providersKey) {
|
||||
return appendNext(newResourceID(parent, parts[0], parts[1]), parts[2:], id)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("invalid resource ID: %s", id)
|
||||
}
|
||||
|
||||
func splitStringAndOmitEmpty(v, sep string) []string {
|
||||
r := make([]string, 0)
|
||||
for _, s := range strings.Split(v, sep) {
|
||||
if len(s) == 0 {
|
||||
continue
|
||||
}
|
||||
r = append(r, s)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
114
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/resource/resource_type.go
generated
vendored
Normal file
114
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/resource/resource_type.go
generated
vendored
Normal file
|
@ -0,0 +1,114 @@
|
|||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package resource
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SubscriptionResourceType is the ResourceType of a subscription
|
||||
var SubscriptionResourceType = NewResourceType(builtInResourceNamespace, "subscriptions")
|
||||
|
||||
// ResourceGroupResourceType is the ResourceType of a resource group
|
||||
var ResourceGroupResourceType = NewResourceType(builtInResourceNamespace, "resourceGroups")
|
||||
|
||||
// TenantResourceType is the ResourceType of a tenant
|
||||
var TenantResourceType = NewResourceType(builtInResourceNamespace, "tenants")
|
||||
|
||||
// ProviderResourceType is the ResourceType of a provider
|
||||
var ProviderResourceType = NewResourceType(builtInResourceNamespace, "providers")
|
||||
|
||||
// ResourceType represents an Azure resource type, e.g. "Microsoft.Network/virtualNetworks/subnets".
|
||||
// Don't create this type directly, use ParseResourceType or NewResourceType instead.
|
||||
type ResourceType struct {
|
||||
// Namespace is the namespace of the resource type.
|
||||
// e.g. "Microsoft.Network" in resource type "Microsoft.Network/virtualNetworks/subnets"
|
||||
Namespace string
|
||||
|
||||
// Type is the full type name of the resource type.
|
||||
// e.g. "virtualNetworks/subnets" in resource type "Microsoft.Network/virtualNetworks/subnets"
|
||||
Type string
|
||||
|
||||
// Types is the slice of all the sub-types of this resource type.
|
||||
// e.g. ["virtualNetworks", "subnets"] in resource type "Microsoft.Network/virtualNetworks/subnets"
|
||||
Types []string
|
||||
|
||||
stringValue string
|
||||
}
|
||||
|
||||
// String returns the string of the ResourceType
|
||||
func (t ResourceType) String() string {
|
||||
return t.stringValue
|
||||
}
|
||||
|
||||
// IsParentOf returns true when the receiver is the parent resource type of the child.
|
||||
func (t ResourceType) IsParentOf(child ResourceType) bool {
|
||||
if !strings.EqualFold(t.Namespace, child.Namespace) {
|
||||
return false
|
||||
}
|
||||
if len(t.Types) >= len(child.Types) {
|
||||
return false
|
||||
}
|
||||
for i := range t.Types {
|
||||
if !strings.EqualFold(t.Types[i], child.Types[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// AppendChild creates an instance of ResourceType using the receiver as the parent with childType appended to it.
|
||||
func (t ResourceType) AppendChild(childType string) ResourceType {
|
||||
return NewResourceType(t.Namespace, fmt.Sprintf("%s/%s", t.Type, childType))
|
||||
}
|
||||
|
||||
// NewResourceType creates an instance of ResourceType using a provider namespace
|
||||
// such as "Microsoft.Network" and type such as "virtualNetworks/subnets".
|
||||
func NewResourceType(providerNamespace, typeName string) ResourceType {
|
||||
return ResourceType{
|
||||
Namespace: providerNamespace,
|
||||
Type: typeName,
|
||||
Types: splitStringAndOmitEmpty(typeName, "/"),
|
||||
stringValue: fmt.Sprintf("%s/%s", providerNamespace, typeName),
|
||||
}
|
||||
}
|
||||
|
||||
// ParseResourceType parses the ResourceType from a resource type string (e.g. Microsoft.Network/virtualNetworks/subsets)
|
||||
// or a resource identifier string.
|
||||
// e.g. /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRg/providers/Microsoft.Network/virtualNetworks/vnet/subnets/mySubnet)
|
||||
func ParseResourceType(resourceIDOrType string) (ResourceType, error) {
|
||||
// split the path into segments
|
||||
parts := splitStringAndOmitEmpty(resourceIDOrType, "/")
|
||||
|
||||
// There must be at least a namespace and type name
|
||||
if len(parts) < 1 {
|
||||
return ResourceType{}, fmt.Errorf("invalid resource ID or type: %s", resourceIDOrType)
|
||||
}
|
||||
|
||||
// if the type is just subscriptions, it is a built-in type in the Microsoft.Resources namespace
|
||||
if len(parts) == 1 {
|
||||
// Simple resource type
|
||||
return NewResourceType(builtInResourceNamespace, parts[0]), nil
|
||||
} else if strings.Contains(parts[0], ".") {
|
||||
// Handle resource types (Microsoft.Compute/virtualMachines, Microsoft.Network/virtualNetworks/subnets)
|
||||
// it is a full type name
|
||||
return NewResourceType(parts[0], strings.Join(parts[1:], "/")), nil
|
||||
} else {
|
||||
// Check if ResourceID
|
||||
id, err := ParseResourceID(resourceIDOrType)
|
||||
if err != nil {
|
||||
return ResourceType{}, err
|
||||
}
|
||||
return NewResourceType(id.ResourceType.Namespace, id.ResourceType.Type), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (t ResourceType) lastType() string {
|
||||
return t.Types[len(t.Types)-1]
|
||||
}
|
108
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/policy/policy.go
generated
vendored
Normal file
108
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/policy/policy.go
generated
vendored
Normal file
|
@ -0,0 +1,108 @@
|
|||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package policy
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
)
|
||||
|
||||
// BearerTokenOptions configures the bearer token policy's behavior.
|
||||
type BearerTokenOptions struct {
|
||||
// AuxiliaryTenants are additional tenant IDs for authenticating cross-tenant requests.
|
||||
// The policy will add a token from each of these tenants to every request. The
|
||||
// authenticating user or service principal must be a guest in these tenants, and the
|
||||
// policy's credential must support multitenant authentication.
|
||||
AuxiliaryTenants []string
|
||||
|
||||
// InsecureAllowCredentialWithHTTP enables authenticated requests over HTTP.
|
||||
// By default, authenticated requests to an HTTP endpoint are rejected by the client.
|
||||
// WARNING: setting this to true will allow sending the authentication key in clear text. Use with caution.
|
||||
InsecureAllowCredentialWithHTTP bool
|
||||
|
||||
// Scopes contains the list of permission scopes required for the token.
|
||||
Scopes []string
|
||||
}
|
||||
|
||||
// RegistrationOptions configures the registration policy's behavior.
|
||||
// All zero-value fields will be initialized with their default values.
|
||||
type RegistrationOptions struct {
|
||||
policy.ClientOptions
|
||||
|
||||
// MaxAttempts is the total number of times to attempt automatic registration
|
||||
// in the event that an attempt fails.
|
||||
// The default value is 3.
|
||||
// Set to a value less than zero to disable the policy.
|
||||
MaxAttempts int
|
||||
|
||||
// PollingDelay is the amount of time to sleep between polling intervals.
|
||||
// The default value is 15 seconds.
|
||||
// A value less than zero means no delay between polling intervals (not recommended).
|
||||
PollingDelay time.Duration
|
||||
|
||||
// PollingDuration is the amount of time to wait before abandoning polling.
|
||||
// The default valule is 5 minutes.
|
||||
// NOTE: Setting this to a small value might cause the policy to prematurely fail.
|
||||
PollingDuration time.Duration
|
||||
|
||||
// StatusCodes contains the slice of custom HTTP status codes to use instead
|
||||
// of the default http.StatusConflict. This should only be set if a service
|
||||
// returns a non-standard HTTP status code when unregistered.
|
||||
StatusCodes []int
|
||||
}
|
||||
|
||||
// ClientOptions contains configuration settings for a client's pipeline.
|
||||
type ClientOptions struct {
|
||||
policy.ClientOptions
|
||||
|
||||
// AuxiliaryTenants are additional tenant IDs for authenticating cross-tenant requests.
|
||||
// The client will add a token from each of these tenants to every request. The
|
||||
// authenticating user or service principal must be a guest in these tenants, and the
|
||||
// client's credential must support multitenant authentication.
|
||||
AuxiliaryTenants []string
|
||||
|
||||
// DisableRPRegistration disables the auto-RP registration policy. Defaults to false.
|
||||
DisableRPRegistration bool
|
||||
}
|
||||
|
||||
// Clone return a deep copy of the current options.
|
||||
func (o *ClientOptions) Clone() *ClientOptions {
|
||||
if o == nil {
|
||||
return nil
|
||||
}
|
||||
copiedOptions := *o
|
||||
copiedOptions.Cloud.Services = copyMap(copiedOptions.Cloud.Services)
|
||||
copiedOptions.Logging.AllowedHeaders = copyArray(copiedOptions.Logging.AllowedHeaders)
|
||||
copiedOptions.Logging.AllowedQueryParams = copyArray(copiedOptions.Logging.AllowedQueryParams)
|
||||
copiedOptions.Retry.StatusCodes = copyArray(copiedOptions.Retry.StatusCodes)
|
||||
copiedOptions.PerRetryPolicies = copyArray(copiedOptions.PerRetryPolicies)
|
||||
copiedOptions.PerCallPolicies = copyArray(copiedOptions.PerCallPolicies)
|
||||
return &copiedOptions
|
||||
}
|
||||
|
||||
// copyMap return a new map with all the key value pair in the src map
|
||||
func copyMap[K comparable, V any](src map[K]V) map[K]V {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
copiedMap := make(map[K]V)
|
||||
for k, v := range src {
|
||||
copiedMap[k] = v
|
||||
}
|
||||
return copiedMap
|
||||
}
|
||||
|
||||
// copyMap return a new array with all the elements in the src array
|
||||
func copyArray[T any](src []T) []T {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
copiedArray := make([]T, len(src))
|
||||
copy(copiedArray, src)
|
||||
return copiedArray
|
||||
}
|
66
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/pipeline.go
generated
vendored
Normal file
66
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/pipeline.go
generated
vendored
Normal file
|
@ -0,0 +1,66 @@
|
|||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
armpolicy "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
|
||||
azpolicy "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
azruntime "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
|
||||
)
|
||||
|
||||
// NewPipeline creates a pipeline from connection options. Policies from ClientOptions are
|
||||
// placed after policies from PipelineOptions. The telemetry policy, when enabled, will
|
||||
// use the specified module and version info.
|
||||
func NewPipeline(module, version string, cred azcore.TokenCredential, plOpts azruntime.PipelineOptions, options *armpolicy.ClientOptions) (azruntime.Pipeline, error) {
|
||||
if options == nil {
|
||||
options = &armpolicy.ClientOptions{}
|
||||
}
|
||||
conf, err := getConfiguration(&options.ClientOptions)
|
||||
if err != nil {
|
||||
return azruntime.Pipeline{}, err
|
||||
}
|
||||
authPolicy := NewBearerTokenPolicy(cred, &armpolicy.BearerTokenOptions{
|
||||
AuxiliaryTenants: options.AuxiliaryTenants,
|
||||
InsecureAllowCredentialWithHTTP: options.InsecureAllowCredentialWithHTTP,
|
||||
Scopes: []string{conf.Audience + "/.default"},
|
||||
})
|
||||
perRetry := make([]azpolicy.Policy, len(plOpts.PerRetry), len(plOpts.PerRetry)+1)
|
||||
copy(perRetry, plOpts.PerRetry)
|
||||
plOpts.PerRetry = append(perRetry, authPolicy, exported.PolicyFunc(httpTraceNamespacePolicy))
|
||||
if !options.DisableRPRegistration {
|
||||
regRPOpts := armpolicy.RegistrationOptions{ClientOptions: options.ClientOptions}
|
||||
regPolicy, err := NewRPRegistrationPolicy(cred, ®RPOpts)
|
||||
if err != nil {
|
||||
return azruntime.Pipeline{}, err
|
||||
}
|
||||
perCall := make([]azpolicy.Policy, len(plOpts.PerCall), len(plOpts.PerCall)+1)
|
||||
copy(perCall, plOpts.PerCall)
|
||||
plOpts.PerCall = append(perCall, regPolicy)
|
||||
}
|
||||
if plOpts.APIVersion.Name == "" {
|
||||
plOpts.APIVersion.Name = "api-version"
|
||||
}
|
||||
return azruntime.NewPipeline(module, version, plOpts, &options.ClientOptions), nil
|
||||
}
|
||||
|
||||
func getConfiguration(o *azpolicy.ClientOptions) (cloud.ServiceConfiguration, error) {
|
||||
c := cloud.AzurePublic
|
||||
if !reflect.ValueOf(o.Cloud).IsZero() {
|
||||
c = o.Cloud
|
||||
}
|
||||
if conf, ok := c.Services[cloud.ResourceManager]; ok && conf.Endpoint != "" && conf.Audience != "" {
|
||||
return conf, nil
|
||||
} else {
|
||||
return conf, errors.New("provided Cloud field is missing Azure Resource Manager configuration")
|
||||
}
|
||||
}
|
146
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/policy_bearer_token.go
generated
vendored
Normal file
146
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/policy_bearer_token.go
generated
vendored
Normal file
|
@ -0,0 +1,146 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
armpolicy "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
|
||||
azpolicy "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
azruntime "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/internal/temporal"
|
||||
)
|
||||
|
||||
const headerAuxiliaryAuthorization = "x-ms-authorization-auxiliary"
|
||||
|
||||
// acquiringResourceState holds data for an auxiliary token request
|
||||
type acquiringResourceState struct {
|
||||
ctx context.Context
|
||||
p *BearerTokenPolicy
|
||||
tenant string
|
||||
}
|
||||
|
||||
// acquireAuxToken acquires a token from an auxiliary tenant. Only one thread/goroutine at a time ever calls this function.
|
||||
func acquireAuxToken(state acquiringResourceState) (newResource azcore.AccessToken, newExpiration time.Time, err error) {
|
||||
tk, err := state.p.cred.GetToken(state.ctx, azpolicy.TokenRequestOptions{
|
||||
EnableCAE: true,
|
||||
Scopes: state.p.scopes,
|
||||
TenantID: state.tenant,
|
||||
})
|
||||
if err != nil {
|
||||
return azcore.AccessToken{}, time.Time{}, err
|
||||
}
|
||||
return tk, tk.ExpiresOn, nil
|
||||
}
|
||||
|
||||
// BearerTokenPolicy authorizes requests with bearer tokens acquired from a TokenCredential.
|
||||
type BearerTokenPolicy struct {
|
||||
auxResources map[string]*temporal.Resource[azcore.AccessToken, acquiringResourceState]
|
||||
btp *azruntime.BearerTokenPolicy
|
||||
cred azcore.TokenCredential
|
||||
scopes []string
|
||||
}
|
||||
|
||||
// NewBearerTokenPolicy creates a policy object that authorizes requests with bearer tokens.
|
||||
// cred: an azcore.TokenCredential implementation such as a credential object from azidentity
|
||||
// opts: optional settings. Pass nil to accept default values; this is the same as passing a zero-value options.
|
||||
func NewBearerTokenPolicy(cred azcore.TokenCredential, opts *armpolicy.BearerTokenOptions) *BearerTokenPolicy {
|
||||
if opts == nil {
|
||||
opts = &armpolicy.BearerTokenOptions{}
|
||||
}
|
||||
p := &BearerTokenPolicy{cred: cred}
|
||||
p.auxResources = make(map[string]*temporal.Resource[azcore.AccessToken, acquiringResourceState], len(opts.AuxiliaryTenants))
|
||||
for _, t := range opts.AuxiliaryTenants {
|
||||
p.auxResources[t] = temporal.NewResource(acquireAuxToken)
|
||||
}
|
||||
p.scopes = make([]string, len(opts.Scopes))
|
||||
copy(p.scopes, opts.Scopes)
|
||||
p.btp = azruntime.NewBearerTokenPolicy(cred, opts.Scopes, &azpolicy.BearerTokenOptions{
|
||||
InsecureAllowCredentialWithHTTP: opts.InsecureAllowCredentialWithHTTP,
|
||||
AuthorizationHandler: azpolicy.AuthorizationHandler{
|
||||
OnChallenge: p.onChallenge,
|
||||
OnRequest: p.onRequest,
|
||||
},
|
||||
})
|
||||
return p
|
||||
}
|
||||
|
||||
func (b *BearerTokenPolicy) onChallenge(req *azpolicy.Request, res *http.Response, authNZ func(azpolicy.TokenRequestOptions) error) error {
|
||||
challenge := res.Header.Get(shared.HeaderWWWAuthenticate)
|
||||
claims, err := parseChallenge(challenge)
|
||||
if err != nil {
|
||||
// the challenge contains claims we can't parse
|
||||
return err
|
||||
} else if claims != "" {
|
||||
// request a new token having the specified claims, send the request again
|
||||
return authNZ(azpolicy.TokenRequestOptions{Claims: claims, EnableCAE: true, Scopes: b.scopes})
|
||||
}
|
||||
// auth challenge didn't include claims, so this is a simple authorization failure
|
||||
return azruntime.NewResponseError(res)
|
||||
}
|
||||
|
||||
// onRequest authorizes requests with one or more bearer tokens
|
||||
func (b *BearerTokenPolicy) onRequest(req *azpolicy.Request, authNZ func(azpolicy.TokenRequestOptions) error) error {
|
||||
// authorize the request with a token for the primary tenant
|
||||
err := authNZ(azpolicy.TokenRequestOptions{EnableCAE: true, Scopes: b.scopes})
|
||||
if err != nil || len(b.auxResources) == 0 {
|
||||
return err
|
||||
}
|
||||
// add tokens for auxiliary tenants
|
||||
as := acquiringResourceState{
|
||||
ctx: req.Raw().Context(),
|
||||
p: b,
|
||||
}
|
||||
auxTokens := make([]string, 0, len(b.auxResources))
|
||||
for tenant, er := range b.auxResources {
|
||||
as.tenant = tenant
|
||||
auxTk, err := er.Get(as)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
auxTokens = append(auxTokens, fmt.Sprintf("%s%s", shared.BearerTokenPrefix, auxTk.Token))
|
||||
}
|
||||
req.Raw().Header.Set(headerAuxiliaryAuthorization, strings.Join(auxTokens, ", "))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do authorizes a request with a bearer token
|
||||
func (b *BearerTokenPolicy) Do(req *azpolicy.Request) (*http.Response, error) {
|
||||
return b.btp.Do(req)
|
||||
}
|
||||
|
||||
// parseChallenge parses claims from an authentication challenge issued by ARM so a client can request a token
|
||||
// that will satisfy conditional access policies. It returns a non-nil error when the given value contains
|
||||
// claims it can't parse. If the value contains no claims, it returns an empty string and a nil error.
|
||||
func parseChallenge(wwwAuthenticate string) (string, error) {
|
||||
claims := ""
|
||||
var err error
|
||||
for _, param := range strings.Split(wwwAuthenticate, ",") {
|
||||
if _, after, found := strings.Cut(param, "claims="); found {
|
||||
if claims != "" {
|
||||
// The header contains multiple challenges, at least two of which specify claims. The specs allow this
|
||||
// but it's unclear what a client should do in this case and there's as yet no concrete example of it.
|
||||
err = fmt.Errorf("found multiple claims challenges in %q", wwwAuthenticate)
|
||||
break
|
||||
}
|
||||
// trim stuff that would get an error from RawURLEncoding; claims may or may not be padded
|
||||
claims = strings.Trim(after, `\"=`)
|
||||
// we don't return this error because it's something unhelpful like "illegal base64 data at input byte 42"
|
||||
if b, decErr := base64.RawURLEncoding.DecodeString(claims); decErr == nil {
|
||||
claims = string(b)
|
||||
} else {
|
||||
err = fmt.Errorf("failed to parse claims from %q", wwwAuthenticate)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return claims, err
|
||||
}
|
322
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/policy_register_rp.go
generated
vendored
Normal file
322
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/policy_register_rp.go
generated
vendored
Normal file
|
@ -0,0 +1,322 @@
|
|||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/resource"
|
||||
armpolicy "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
|
||||
azpolicy "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/internal/log"
|
||||
)
|
||||
|
||||
const (
|
||||
// LogRPRegistration entries contain information specific to the automatic registration of an RP.
|
||||
// Entries of this classification are written IFF the policy needs to take any action.
|
||||
LogRPRegistration log.Event = "RPRegistration"
|
||||
)
|
||||
|
||||
// init sets any default values
|
||||
func setDefaults(r *armpolicy.RegistrationOptions) {
|
||||
if r.MaxAttempts == 0 {
|
||||
r.MaxAttempts = 3
|
||||
} else if r.MaxAttempts < 0 {
|
||||
r.MaxAttempts = 0
|
||||
}
|
||||
if r.PollingDelay == 0 {
|
||||
r.PollingDelay = 15 * time.Second
|
||||
} else if r.PollingDelay < 0 {
|
||||
r.PollingDelay = 0
|
||||
}
|
||||
if r.PollingDuration == 0 {
|
||||
r.PollingDuration = 5 * time.Minute
|
||||
}
|
||||
if len(r.StatusCodes) == 0 {
|
||||
r.StatusCodes = []int{http.StatusConflict}
|
||||
}
|
||||
}
|
||||
|
||||
// NewRPRegistrationPolicy creates a policy object configured using the specified options.
|
||||
// The policy controls whether an unregistered resource provider should automatically be
|
||||
// registered. See https://aka.ms/rps-not-found for more information.
|
||||
func NewRPRegistrationPolicy(cred azcore.TokenCredential, o *armpolicy.RegistrationOptions) (azpolicy.Policy, error) {
|
||||
if o == nil {
|
||||
o = &armpolicy.RegistrationOptions{}
|
||||
}
|
||||
conf, err := getConfiguration(&o.ClientOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authPolicy := NewBearerTokenPolicy(cred, &armpolicy.BearerTokenOptions{Scopes: []string{conf.Audience + "/.default"}})
|
||||
p := &rpRegistrationPolicy{
|
||||
endpoint: conf.Endpoint,
|
||||
pipeline: runtime.NewPipeline(shared.Module, shared.Version, runtime.PipelineOptions{PerRetry: []azpolicy.Policy{authPolicy}}, &o.ClientOptions),
|
||||
options: *o,
|
||||
}
|
||||
// init the copy
|
||||
setDefaults(&p.options)
|
||||
return p, nil
|
||||
}
|
||||
|
||||
type rpRegistrationPolicy struct {
|
||||
endpoint string
|
||||
pipeline runtime.Pipeline
|
||||
options armpolicy.RegistrationOptions
|
||||
}
|
||||
|
||||
func (r *rpRegistrationPolicy) Do(req *azpolicy.Request) (*http.Response, error) {
|
||||
if r.options.MaxAttempts == 0 {
|
||||
// policy is disabled
|
||||
return req.Next()
|
||||
}
|
||||
const registeredState = "Registered"
|
||||
var rp string
|
||||
var resp *http.Response
|
||||
for attempts := 0; attempts < r.options.MaxAttempts; attempts++ {
|
||||
var err error
|
||||
// make the original request
|
||||
resp, err = req.Next()
|
||||
// getting a 409 is the first indication that the RP might need to be registered, check error response
|
||||
if err != nil || !runtime.HasStatusCode(resp, r.options.StatusCodes...) {
|
||||
return resp, err
|
||||
}
|
||||
var reqErr requestError
|
||||
if err = runtime.UnmarshalAsJSON(resp, &reqErr); err != nil {
|
||||
return resp, err
|
||||
}
|
||||
if reqErr.ServiceError == nil {
|
||||
// missing service error info. just return the response
|
||||
// to the caller so its error unmarshalling will kick in
|
||||
return resp, err
|
||||
}
|
||||
if !isUnregisteredRPCode(reqErr.ServiceError.Code) {
|
||||
// not a 409 due to unregistered RP. just return the response
|
||||
// to the caller so its error unmarshalling will kick in
|
||||
return resp, err
|
||||
}
|
||||
res, err := resource.ParseResourceID(req.Raw().URL.Path)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
rp = res.ResourceType.Namespace
|
||||
logRegistrationExit := func(v any) {
|
||||
log.Writef(LogRPRegistration, "END registration for %s: %v", rp, v)
|
||||
}
|
||||
log.Writef(LogRPRegistration, "BEGIN registration for %s", rp)
|
||||
// create client and make the registration request
|
||||
// we use the scheme and host from the original request
|
||||
rpOps := &providersOperations{
|
||||
p: r.pipeline,
|
||||
u: r.endpoint,
|
||||
subID: res.SubscriptionID,
|
||||
}
|
||||
if _, err = rpOps.Register(&shared.ContextWithDeniedValues{Context: req.Raw().Context()}, rp); err != nil {
|
||||
logRegistrationExit(err)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// RP was registered, however we need to wait for the registration to complete
|
||||
pollCtx, pollCancel := context.WithTimeout(&shared.ContextWithDeniedValues{Context: req.Raw().Context()}, r.options.PollingDuration)
|
||||
var lastRegState string
|
||||
for {
|
||||
// get the current registration state
|
||||
getResp, err := rpOps.Get(pollCtx, rp)
|
||||
if err != nil {
|
||||
pollCancel()
|
||||
logRegistrationExit(err)
|
||||
return resp, err
|
||||
}
|
||||
if getResp.Provider.RegistrationState != nil && !strings.EqualFold(*getResp.Provider.RegistrationState, lastRegState) {
|
||||
// registration state has changed, or was updated for the first time
|
||||
lastRegState = *getResp.Provider.RegistrationState
|
||||
log.Writef(LogRPRegistration, "registration state is %s", lastRegState)
|
||||
}
|
||||
if strings.EqualFold(lastRegState, registeredState) {
|
||||
// registration complete
|
||||
pollCancel()
|
||||
logRegistrationExit(lastRegState)
|
||||
break
|
||||
}
|
||||
// wait before trying again
|
||||
select {
|
||||
case <-time.After(r.options.PollingDelay):
|
||||
// continue polling
|
||||
case <-pollCtx.Done():
|
||||
pollCancel()
|
||||
logRegistrationExit(pollCtx.Err())
|
||||
return resp, pollCtx.Err()
|
||||
}
|
||||
}
|
||||
// RP was successfully registered, retry the original request
|
||||
err = req.RewindBody()
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
}
|
||||
// if we get here it means we exceeded the number of attempts
|
||||
return resp, fmt.Errorf("exceeded attempts to register %s", rp)
|
||||
}
|
||||
|
||||
var unregisteredRPCodes = []string{
|
||||
"MissingSubscriptionRegistration",
|
||||
"MissingRegistrationForResourceProvider",
|
||||
"Subscription Not Registered",
|
||||
"SubscriptionNotRegistered",
|
||||
}
|
||||
|
||||
func isUnregisteredRPCode(errorCode string) bool {
|
||||
for _, code := range unregisteredRPCodes {
|
||||
if strings.EqualFold(errorCode, code) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// minimal error definitions to simplify detection
|
||||
type requestError struct {
|
||||
ServiceError *serviceError `json:"error"`
|
||||
}
|
||||
|
||||
type serviceError struct {
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// the following code was copied from module armresources, providers.go and models.go
|
||||
// only the minimum amount of code was copied to get this working and some edits were made.
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
type providersOperations struct {
|
||||
p runtime.Pipeline
|
||||
u string
|
||||
subID string
|
||||
}
|
||||
|
||||
// Get - Gets the specified resource provider.
|
||||
func (client *providersOperations) Get(ctx context.Context, resourceProviderNamespace string) (providerResponse, error) {
|
||||
req, err := client.getCreateRequest(ctx, resourceProviderNamespace)
|
||||
if err != nil {
|
||||
return providerResponse{}, err
|
||||
}
|
||||
resp, err := client.p.Do(req)
|
||||
if err != nil {
|
||||
return providerResponse{}, err
|
||||
}
|
||||
result, err := client.getHandleResponse(resp)
|
||||
if err != nil {
|
||||
return providerResponse{}, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// getCreateRequest creates the Get request.
|
||||
func (client *providersOperations) getCreateRequest(ctx context.Context, resourceProviderNamespace string) (*azpolicy.Request, error) {
|
||||
urlPath := "/subscriptions/{subscriptionId}/providers/{resourceProviderNamespace}"
|
||||
urlPath = strings.ReplaceAll(urlPath, "{resourceProviderNamespace}", url.PathEscape(resourceProviderNamespace))
|
||||
urlPath = strings.ReplaceAll(urlPath, "{subscriptionId}", url.PathEscape(client.subID))
|
||||
req, err := runtime.NewRequest(ctx, http.MethodGet, runtime.JoinPaths(client.u, urlPath))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
query := req.Raw().URL.Query()
|
||||
query.Set("api-version", "2019-05-01")
|
||||
req.Raw().URL.RawQuery = query.Encode()
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// getHandleResponse handles the Get response.
|
||||
func (client *providersOperations) getHandleResponse(resp *http.Response) (providerResponse, error) {
|
||||
if !runtime.HasStatusCode(resp, http.StatusOK) {
|
||||
return providerResponse{}, exported.NewResponseError(resp)
|
||||
}
|
||||
result := providerResponse{RawResponse: resp}
|
||||
err := runtime.UnmarshalAsJSON(resp, &result.Provider)
|
||||
if err != nil {
|
||||
return providerResponse{}, err
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
// Register - Registers a subscription with a resource provider.
|
||||
func (client *providersOperations) Register(ctx context.Context, resourceProviderNamespace string) (providerResponse, error) {
|
||||
req, err := client.registerCreateRequest(ctx, resourceProviderNamespace)
|
||||
if err != nil {
|
||||
return providerResponse{}, err
|
||||
}
|
||||
resp, err := client.p.Do(req)
|
||||
if err != nil {
|
||||
return providerResponse{}, err
|
||||
}
|
||||
result, err := client.registerHandleResponse(resp)
|
||||
if err != nil {
|
||||
return providerResponse{}, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// registerCreateRequest creates the Register request.
|
||||
func (client *providersOperations) registerCreateRequest(ctx context.Context, resourceProviderNamespace string) (*azpolicy.Request, error) {
|
||||
urlPath := "/subscriptions/{subscriptionId}/providers/{resourceProviderNamespace}/register"
|
||||
urlPath = strings.ReplaceAll(urlPath, "{resourceProviderNamespace}", url.PathEscape(resourceProviderNamespace))
|
||||
urlPath = strings.ReplaceAll(urlPath, "{subscriptionId}", url.PathEscape(client.subID))
|
||||
req, err := runtime.NewRequest(ctx, http.MethodPost, runtime.JoinPaths(client.u, urlPath))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
query := req.Raw().URL.Query()
|
||||
query.Set("api-version", "2019-05-01")
|
||||
req.Raw().URL.RawQuery = query.Encode()
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// registerHandleResponse handles the Register response.
|
||||
func (client *providersOperations) registerHandleResponse(resp *http.Response) (providerResponse, error) {
|
||||
if !runtime.HasStatusCode(resp, http.StatusOK) {
|
||||
return providerResponse{}, exported.NewResponseError(resp)
|
||||
}
|
||||
result := providerResponse{RawResponse: resp}
|
||||
err := runtime.UnmarshalAsJSON(resp, &result.Provider)
|
||||
if err != nil {
|
||||
return providerResponse{}, err
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
// ProviderResponse is the response envelope for operations that return a Provider type.
|
||||
type providerResponse struct {
|
||||
// Resource provider information.
|
||||
Provider *provider
|
||||
|
||||
// RawResponse contains the underlying HTTP response.
|
||||
RawResponse *http.Response
|
||||
}
|
||||
|
||||
// Provider - Resource provider information.
|
||||
type provider struct {
|
||||
// The provider ID.
|
||||
ID *string `json:"id,omitempty"`
|
||||
|
||||
// The namespace of the resource provider.
|
||||
Namespace *string `json:"namespace,omitempty"`
|
||||
|
||||
// The registration policy of the resource provider.
|
||||
RegistrationPolicy *string `json:"registrationPolicy,omitempty"`
|
||||
|
||||
// The registration state of the resource provider.
|
||||
RegistrationState *string `json:"registrationState,omitempty"`
|
||||
}
|
30
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/policy_trace_namespace.go
generated
vendored
Normal file
30
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/policy_trace_namespace.go
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/resource"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/tracing"
|
||||
)
|
||||
|
||||
// httpTraceNamespacePolicy is a policy that adds the az.namespace attribute to the current Span
|
||||
func httpTraceNamespacePolicy(req *policy.Request) (resp *http.Response, err error) {
|
||||
rawTracer := req.Raw().Context().Value(shared.CtxWithTracingTracer{})
|
||||
if tracer, ok := rawTracer.(tracing.Tracer); ok && tracer.Enabled() {
|
||||
rt, err := resource.ParseResourceType(req.Raw().URL.Path)
|
||||
if err == nil {
|
||||
// add the namespace attribute to the current span
|
||||
span := tracer.SpanFromContext(req.Raw().Context())
|
||||
span.SetAttributes(tracing.Attribute{Key: shared.TracingNamespaceAttrName, Value: rt.Namespace})
|
||||
}
|
||||
}
|
||||
return req.Next()
|
||||
}
|
24
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/runtime.go
generated
vendored
Normal file
24
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime/runtime.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
//go:build go1.16
|
||||
// +build go1.16
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package runtime
|
||||
|
||||
import "github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
|
||||
|
||||
func init() {
|
||||
cloud.AzureChina.Services[cloud.ResourceManager] = cloud.ServiceConfiguration{
|
||||
Audience: "https://management.core.chinacloudapi.cn",
|
||||
Endpoint: "https://management.chinacloudapi.cn",
|
||||
}
|
||||
cloud.AzureGovernment.Services[cloud.ResourceManager] = cloud.ServiceConfiguration{
|
||||
Audience: "https://management.core.usgovcloudapi.net",
|
||||
Endpoint: "https://management.usgovcloudapi.net",
|
||||
}
|
||||
cloud.AzurePublic.Services[cloud.ResourceManager] = cloud.ServiceConfiguration{
|
||||
Audience: "https://management.core.windows.net/",
|
||||
Endpoint: "https://management.azure.com",
|
||||
}
|
||||
}
|
4
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/ci.yml
generated
vendored
4
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/ci.yml
generated
vendored
|
@ -23,7 +23,7 @@ pr:
|
|||
- sdk/azcore/
|
||||
- eng/
|
||||
|
||||
stages:
|
||||
- template: /eng/pipelines/templates/jobs/archetype-sdk-client.yml
|
||||
extends:
|
||||
template: /eng/pipelines/templates/jobs/archetype-sdk-client.yml
|
||||
parameters:
|
||||
ServiceDirectory: azcore
|
||||
|
|
86
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/core.go
generated
vendored
86
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/core.go
generated
vendored
|
@ -8,6 +8,7 @@ package azcore
|
|||
|
||||
import (
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
|
||||
|
@ -22,14 +23,47 @@ type AccessToken = exported.AccessToken
|
|||
// TokenCredential represents a credential capable of providing an OAuth token.
|
||||
type TokenCredential = exported.TokenCredential
|
||||
|
||||
// KeyCredential contains an authentication key used to authenticate to an Azure service.
|
||||
type KeyCredential = exported.KeyCredential
|
||||
|
||||
// NewKeyCredential creates a new instance of [KeyCredential] with the specified values.
|
||||
// - key is the authentication key
|
||||
func NewKeyCredential(key string) *KeyCredential {
|
||||
return exported.NewKeyCredential(key)
|
||||
}
|
||||
|
||||
// SASCredential contains a shared access signature used to authenticate to an Azure service.
|
||||
type SASCredential = exported.SASCredential
|
||||
|
||||
// NewSASCredential creates a new instance of [SASCredential] with the specified values.
|
||||
// - sas is the shared access signature
|
||||
func NewSASCredential(sas string) *SASCredential {
|
||||
return exported.NewSASCredential(sas)
|
||||
}
|
||||
|
||||
// holds sentinel values used to send nulls
|
||||
var nullables map[reflect.Type]interface{} = map[reflect.Type]interface{}{}
|
||||
var nullables map[reflect.Type]any = map[reflect.Type]any{}
|
||||
var nullablesMu sync.RWMutex
|
||||
|
||||
// NullValue is used to send an explicit 'null' within a request.
|
||||
// This is typically used in JSON-MERGE-PATCH operations to delete a value.
|
||||
func NullValue[T any]() T {
|
||||
t := shared.TypeOfT[T]()
|
||||
|
||||
nullablesMu.RLock()
|
||||
v, found := nullables[t]
|
||||
nullablesMu.RUnlock()
|
||||
|
||||
if found {
|
||||
// return the sentinel object
|
||||
return v.(T)
|
||||
}
|
||||
|
||||
// promote to exclusive lock and check again (double-checked locking pattern)
|
||||
nullablesMu.Lock()
|
||||
defer nullablesMu.Unlock()
|
||||
v, found = nullables[t]
|
||||
|
||||
if !found {
|
||||
var o reflect.Value
|
||||
if k := t.Kind(); k == reflect.Map {
|
||||
|
@ -54,6 +88,9 @@ func NullValue[T any]() T {
|
|||
func IsNullValue[T any](v T) bool {
|
||||
// see if our map has a sentinel object for this *T
|
||||
t := reflect.TypeOf(v)
|
||||
nullablesMu.RLock()
|
||||
defer nullablesMu.RUnlock()
|
||||
|
||||
if o, found := nullables[t]; found {
|
||||
o1 := reflect.ValueOf(o)
|
||||
v1 := reflect.ValueOf(v)
|
||||
|
@ -66,26 +103,28 @@ func IsNullValue[T any](v T) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// ClientOptions contains configuration settings for a client's pipeline.
|
||||
// ClientOptions contains optional settings for a client's pipeline.
|
||||
// Instances can be shared across calls to SDK client constructors when uniform configuration is desired.
|
||||
// Zero-value fields will have their specified default values applied during use.
|
||||
type ClientOptions = policy.ClientOptions
|
||||
|
||||
// Client is a basic HTTP client. It consists of a pipeline and tracing provider.
|
||||
type Client struct {
|
||||
pl runtime.Pipeline
|
||||
tr tracing.Tracer
|
||||
|
||||
// cached on the client to support shallow copying with new values
|
||||
tp tracing.Provider
|
||||
modVer string
|
||||
namespace string
|
||||
}
|
||||
|
||||
// NewClient creates a new Client instance with the provided values.
|
||||
// - clientName - the fully qualified name of the client ("package.Client"); this is used by the tracing provider when creating spans
|
||||
// - moduleVersion - the semantic version of the containing module; used by the telemetry policy
|
||||
// - moduleName - the fully qualified name of the module where the client is defined; used by the telemetry policy and tracing provider.
|
||||
// - moduleVersion - the semantic version of the module; used by the telemetry policy and tracing provider.
|
||||
// - plOpts - pipeline configuration options; can be the zero-value
|
||||
// - options - optional client configurations; pass nil to accept the default values
|
||||
func NewClient(clientName, moduleVersion string, plOpts runtime.PipelineOptions, options *ClientOptions) (*Client, error) {
|
||||
pkg, err := shared.ExtractPackageName(clientName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func NewClient(moduleName, moduleVersion string, plOpts runtime.PipelineOptions, options *ClientOptions) (*Client, error) {
|
||||
if options == nil {
|
||||
options = &ClientOptions{}
|
||||
}
|
||||
|
@ -96,10 +135,20 @@ func NewClient(clientName, moduleVersion string, plOpts runtime.PipelineOptions,
|
|||
}
|
||||
}
|
||||
|
||||
pl := runtime.NewPipeline(pkg, moduleVersion, plOpts, options)
|
||||
pl := runtime.NewPipeline(moduleName, moduleVersion, plOpts, options)
|
||||
|
||||
tr := options.TracingProvider.NewTracer(clientName, moduleVersion)
|
||||
return &Client{pl: pl, tr: tr}, nil
|
||||
tr := options.TracingProvider.NewTracer(moduleName, moduleVersion)
|
||||
if tr.Enabled() && plOpts.Tracing.Namespace != "" {
|
||||
tr.SetAttributes(tracing.Attribute{Key: shared.TracingNamespaceAttrName, Value: plOpts.Tracing.Namespace})
|
||||
}
|
||||
|
||||
return &Client{
|
||||
pl: pl,
|
||||
tr: tr,
|
||||
tp: options.TracingProvider,
|
||||
modVer: moduleVersion,
|
||||
namespace: plOpts.Tracing.Namespace,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Pipeline returns the pipeline for this client.
|
||||
|
@ -111,3 +160,14 @@ func (c *Client) Pipeline() runtime.Pipeline {
|
|||
func (c *Client) Tracer() tracing.Tracer {
|
||||
return c.tr
|
||||
}
|
||||
|
||||
// WithClientName returns a shallow copy of the Client with its tracing client name changed to clientName.
|
||||
// Note that the values for module name and version will be preserved from the source Client.
|
||||
// - clientName - the fully qualified name of the client ("package.Client"); this is used by the tracing provider when creating spans
|
||||
func (c *Client) WithClientName(clientName string) *Client {
|
||||
tr := c.tp.NewTracer(clientName, c.modVer)
|
||||
if tr.Enabled() && c.namespace != "" {
|
||||
tr.SetAttributes(tracing.Attribute{Key: shared.TracingNamespaceAttrName, Value: c.namespace})
|
||||
}
|
||||
return &Client{pl: c.pl, tr: tr, tp: c.tp, modVer: c.modVer, namespace: c.namespace}
|
||||
}
|
||||
|
|
7
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/doc.go
generated
vendored
7
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/doc.go
generated
vendored
|
@ -253,5 +253,12 @@ When resuming a poller, no IO is performed, and zero-value arguments can be used
|
|||
|
||||
Resume tokens are unique per service client and operation. Attempting to resume a poller for LRO BeginB() with a token from LRO
|
||||
BeginA() will result in an error.
|
||||
|
||||
# Fakes
|
||||
|
||||
The fake package contains types used for constructing in-memory fake servers used in unit tests.
|
||||
This allows writing tests to cover various success/error conditions without the need for connecting to a live service.
|
||||
|
||||
Please see https://github.com/Azure/azure-sdk-for-go/tree/main/sdk/samples/fakes for details and examples on how to use fakes.
|
||||
*/
|
||||
package azcore
|
||||
|
|
9
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/etag.go
generated
vendored
9
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/etag.go
generated
vendored
|
@ -46,3 +46,12 @@ func (e ETag) WeakEquals(other ETag) bool {
|
|||
func (e ETag) IsWeak() bool {
|
||||
return len(e) >= 4 && strings.HasPrefix(string(e), "W/\"") && strings.HasSuffix(string(e), "\"")
|
||||
}
|
||||
|
||||
// MatchConditions specifies HTTP options for conditional requests.
|
||||
type MatchConditions struct {
|
||||
// Optionally limit requests to resources that have a matching ETag.
|
||||
IfMatch *ETag
|
||||
|
||||
// Optionally limit requests to resources that do not match the ETag.
|
||||
IfNoneMatch *ETag
|
||||
}
|
||||
|
|
108
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/exported.go
generated
vendored
108
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/exported.go
generated
vendored
|
@ -8,8 +8,11 @@ package exported
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -51,6 +54,17 @@ type AccessToken struct {
|
|||
// TokenRequestOptions contain specific parameter that may be used by credentials types when attempting to get a token.
|
||||
// Exported as policy.TokenRequestOptions.
|
||||
type TokenRequestOptions struct {
|
||||
// Claims are any additional claims required for the token to satisfy a conditional access policy, such as a
|
||||
// service may return in a claims challenge following an authorization failure. If a service returned the
|
||||
// claims value base64 encoded, it must be decoded before setting this field.
|
||||
Claims string
|
||||
|
||||
// EnableCAE indicates whether to enable Continuous Access Evaluation (CAE) for the requested token. When true,
|
||||
// azidentity credentials request CAE tokens for resource APIs supporting CAE. Clients are responsible for
|
||||
// handling CAE challenges. If a client that doesn't handle CAE challenges receives a CAE token, it may end up
|
||||
// in a loop retrying an API call with a token that has been revoked due to CAE.
|
||||
EnableCAE bool
|
||||
|
||||
// Scopes contains the list of permission scopes required for the token.
|
||||
Scopes []string
|
||||
|
||||
|
@ -65,3 +79,97 @@ type TokenCredential interface {
|
|||
// GetToken requests an access token for the specified set of scopes.
|
||||
GetToken(ctx context.Context, options TokenRequestOptions) (AccessToken, error)
|
||||
}
|
||||
|
||||
// DecodeByteArray will base-64 decode the provided string into v.
|
||||
// Exported as runtime.DecodeByteArray()
|
||||
func DecodeByteArray(s string, v *[]byte, format Base64Encoding) error {
|
||||
if len(s) == 0 {
|
||||
return nil
|
||||
}
|
||||
payload := string(s)
|
||||
if payload[0] == '"' {
|
||||
// remove surrounding quotes
|
||||
payload = payload[1 : len(payload)-1]
|
||||
}
|
||||
switch format {
|
||||
case Base64StdFormat:
|
||||
decoded, err := base64.StdEncoding.DecodeString(payload)
|
||||
if err == nil {
|
||||
*v = decoded
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
case Base64URLFormat:
|
||||
// use raw encoding as URL format should not contain any '=' characters
|
||||
decoded, err := base64.RawURLEncoding.DecodeString(payload)
|
||||
if err == nil {
|
||||
*v = decoded
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
default:
|
||||
return fmt.Errorf("unrecognized byte array format: %d", format)
|
||||
}
|
||||
}
|
||||
|
||||
// KeyCredential contains an authentication key used to authenticate to an Azure service.
|
||||
// Exported as azcore.KeyCredential.
|
||||
type KeyCredential struct {
|
||||
cred *keyCredential
|
||||
}
|
||||
|
||||
// NewKeyCredential creates a new instance of [KeyCredential] with the specified values.
|
||||
// - key is the authentication key
|
||||
func NewKeyCredential(key string) *KeyCredential {
|
||||
return &KeyCredential{cred: newKeyCredential(key)}
|
||||
}
|
||||
|
||||
// Update replaces the existing key with the specified value.
|
||||
func (k *KeyCredential) Update(key string) {
|
||||
k.cred.Update(key)
|
||||
}
|
||||
|
||||
// SASCredential contains a shared access signature used to authenticate to an Azure service.
|
||||
// Exported as azcore.SASCredential.
|
||||
type SASCredential struct {
|
||||
cred *keyCredential
|
||||
}
|
||||
|
||||
// NewSASCredential creates a new instance of [SASCredential] with the specified values.
|
||||
// - sas is the shared access signature
|
||||
func NewSASCredential(sas string) *SASCredential {
|
||||
return &SASCredential{cred: newKeyCredential(sas)}
|
||||
}
|
||||
|
||||
// Update replaces the existing shared access signature with the specified value.
|
||||
func (k *SASCredential) Update(sas string) {
|
||||
k.cred.Update(sas)
|
||||
}
|
||||
|
||||
// KeyCredentialGet returns the key for cred.
|
||||
func KeyCredentialGet(cred *KeyCredential) string {
|
||||
return cred.cred.Get()
|
||||
}
|
||||
|
||||
// SASCredentialGet returns the shared access sig for cred.
|
||||
func SASCredentialGet(cred *SASCredential) string {
|
||||
return cred.cred.Get()
|
||||
}
|
||||
|
||||
type keyCredential struct {
|
||||
key atomic.Value // string
|
||||
}
|
||||
|
||||
func newKeyCredential(key string) *keyCredential {
|
||||
keyCred := keyCredential{}
|
||||
keyCred.key.Store(key)
|
||||
return &keyCred
|
||||
}
|
||||
|
||||
func (k *keyCredential) Get() string {
|
||||
return k.key.Load().(string)
|
||||
}
|
||||
|
||||
func (k *keyCredential) Update(key string) {
|
||||
k.key.Store(key)
|
||||
}
|
||||
|
|
20
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/pipeline.go
generated
vendored
20
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/pipeline.go
generated
vendored
|
@ -8,10 +8,7 @@ package exported
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/net/http/httpguts"
|
||||
)
|
||||
|
||||
// Policy represents an extensibility point for the Pipeline that can mutate the specified
|
||||
|
@ -75,23 +72,6 @@ func (p Pipeline) Do(req *Request) (*http.Response, error) {
|
|||
if req == nil {
|
||||
return nil, errors.New("request cannot be nil")
|
||||
}
|
||||
// check copied from Transport.roundTrip()
|
||||
for k, vv := range req.Raw().Header {
|
||||
if !httpguts.ValidHeaderFieldName(k) {
|
||||
if req.Raw().Body != nil {
|
||||
req.Raw().Body.Close()
|
||||
}
|
||||
return nil, fmt.Errorf("invalid header field name %q", k)
|
||||
}
|
||||
for _, v := range vv {
|
||||
if !httpguts.ValidHeaderFieldValue(v) {
|
||||
if req.Raw().Body != nil {
|
||||
req.Raw().Body.Close()
|
||||
}
|
||||
return nil, fmt.Errorf("invalid header field value %q for key %v", v, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
req.policies = p.policies
|
||||
return req.Next()
|
||||
}
|
||||
|
|
129
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/request.go
generated
vendored
129
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/request.go
generated
vendored
|
@ -8,6 +8,7 @@ package exported
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -18,6 +19,28 @@ import (
|
|||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
|
||||
)
|
||||
|
||||
// Base64Encoding is usesd to specify which base-64 encoder/decoder to use when
|
||||
// encoding/decoding a slice of bytes to/from a string.
|
||||
// Exported as runtime.Base64Encoding
|
||||
type Base64Encoding int
|
||||
|
||||
const (
|
||||
// Base64StdFormat uses base64.StdEncoding for encoding and decoding payloads.
|
||||
Base64StdFormat Base64Encoding = 0
|
||||
|
||||
// Base64URLFormat uses base64.RawURLEncoding for encoding and decoding payloads.
|
||||
Base64URLFormat Base64Encoding = 1
|
||||
)
|
||||
|
||||
// EncodeByteArray will base-64 encode the byte slice v.
|
||||
// Exported as runtime.EncodeByteArray()
|
||||
func EncodeByteArray(v []byte, format Base64Encoding) string {
|
||||
if format == Base64URLFormat {
|
||||
return base64.RawURLEncoding.EncodeToString(v)
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(v)
|
||||
}
|
||||
|
||||
// Request is an abstraction over the creation of an HTTP request as it passes through the pipeline.
|
||||
// Don't use this type directly, use NewRequest() instead.
|
||||
// Exported as policy.Request.
|
||||
|
@ -28,15 +51,15 @@ type Request struct {
|
|||
values opValues
|
||||
}
|
||||
|
||||
type opValues map[reflect.Type]interface{}
|
||||
type opValues map[reflect.Type]any
|
||||
|
||||
// Set adds/changes a value
|
||||
func (ov opValues) set(value interface{}) {
|
||||
func (ov opValues) set(value any) {
|
||||
ov[reflect.TypeOf(value)] = value
|
||||
}
|
||||
|
||||
// Get looks for a value set by SetValue first
|
||||
func (ov opValues) get(value interface{}) bool {
|
||||
func (ov opValues) get(value any) bool {
|
||||
v, ok := ov[reflect.ValueOf(value).Elem().Type()]
|
||||
if ok {
|
||||
reflect.ValueOf(value).Elem().Set(reflect.ValueOf(v))
|
||||
|
@ -85,7 +108,7 @@ func (req *Request) Next() (*http.Response, error) {
|
|||
}
|
||||
|
||||
// SetOperationValue adds/changes a mutable key/value associated with a single operation.
|
||||
func (req *Request) SetOperationValue(value interface{}) {
|
||||
func (req *Request) SetOperationValue(value any) {
|
||||
if req.values == nil {
|
||||
req.values = opValues{}
|
||||
}
|
||||
|
@ -93,7 +116,7 @@ func (req *Request) SetOperationValue(value interface{}) {
|
|||
}
|
||||
|
||||
// OperationValue looks for a value set by SetOperationValue().
|
||||
func (req *Request) OperationValue(value interface{}) bool {
|
||||
func (req *Request) OperationValue(value any) bool {
|
||||
if req.values == nil {
|
||||
return false
|
||||
}
|
||||
|
@ -102,9 +125,64 @@ func (req *Request) OperationValue(value interface{}) bool {
|
|||
|
||||
// SetBody sets the specified ReadSeekCloser as the HTTP request body, and sets Content-Type and Content-Length
|
||||
// accordingly. If the ReadSeekCloser is nil or empty, Content-Length won't be set. If contentType is "",
|
||||
// Content-Type won't be set.
|
||||
// Content-Type won't be set, and if it was set, will be deleted.
|
||||
// Use streaming.NopCloser to turn an io.ReadSeeker into an io.ReadSeekCloser.
|
||||
func (req *Request) SetBody(body io.ReadSeekCloser, contentType string) error {
|
||||
// clobber the existing Content-Type to preserve behavior
|
||||
return SetBody(req, body, contentType, true)
|
||||
}
|
||||
|
||||
// RewindBody seeks the request's Body stream back to the beginning so it can be resent when retrying an operation.
|
||||
func (req *Request) RewindBody() error {
|
||||
if req.body != nil {
|
||||
// Reset the stream back to the beginning and restore the body
|
||||
_, err := req.body.Seek(0, io.SeekStart)
|
||||
req.req.Body = req.body
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes the request body.
|
||||
func (req *Request) Close() error {
|
||||
if req.body == nil {
|
||||
return nil
|
||||
}
|
||||
return req.body.Close()
|
||||
}
|
||||
|
||||
// Clone returns a deep copy of the request with its context changed to ctx.
|
||||
func (req *Request) Clone(ctx context.Context) *Request {
|
||||
r2 := *req
|
||||
r2.req = req.req.Clone(ctx)
|
||||
return &r2
|
||||
}
|
||||
|
||||
// WithContext returns a shallow copy of the request with its context changed to ctx.
|
||||
func (req *Request) WithContext(ctx context.Context) *Request {
|
||||
r2 := new(Request)
|
||||
*r2 = *req
|
||||
r2.req = r2.req.WithContext(ctx)
|
||||
return r2
|
||||
}
|
||||
|
||||
// not exported but dependent on Request
|
||||
|
||||
// PolicyFunc is a type that implements the Policy interface.
|
||||
// Use this type when implementing a stateless policy as a first-class function.
|
||||
type PolicyFunc func(*Request) (*http.Response, error)
|
||||
|
||||
// Do implements the Policy interface on policyFunc.
|
||||
func (pf PolicyFunc) Do(req *Request) (*http.Response, error) {
|
||||
return pf(req)
|
||||
}
|
||||
|
||||
// SetBody sets the specified ReadSeekCloser as the HTTP request body, and sets Content-Type and Content-Length accordingly.
|
||||
// - req is the request to modify
|
||||
// - body is the request body; if nil or empty, Content-Length won't be set
|
||||
// - contentType is the value for the Content-Type header; if empty, Content-Type will be deleted
|
||||
// - clobberContentType when true, will overwrite the existing value of Content-Type with contentType
|
||||
func SetBody(req *Request, body io.ReadSeekCloser, contentType string, clobberContentType bool) error {
|
||||
var err error
|
||||
var size int64
|
||||
if body != nil {
|
||||
|
@ -138,45 +216,8 @@ func (req *Request) SetBody(body io.ReadSeekCloser, contentType string) error {
|
|||
if contentType == "" {
|
||||
// Del is a no-op when the header has no value
|
||||
req.req.Header.Del(shared.HeaderContentType)
|
||||
} else {
|
||||
} else if req.req.Header.Get(shared.HeaderContentType) == "" || clobberContentType {
|
||||
req.req.Header.Set(shared.HeaderContentType, contentType)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RewindBody seeks the request's Body stream back to the beginning so it can be resent when retrying an operation.
|
||||
func (req *Request) RewindBody() error {
|
||||
if req.body != nil {
|
||||
// Reset the stream back to the beginning and restore the body
|
||||
_, err := req.body.Seek(0, io.SeekStart)
|
||||
req.req.Body = req.body
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes the request body.
|
||||
func (req *Request) Close() error {
|
||||
if req.body == nil {
|
||||
return nil
|
||||
}
|
||||
return req.body.Close()
|
||||
}
|
||||
|
||||
// Clone returns a deep copy of the request with its context changed to ctx.
|
||||
func (req *Request) Clone(ctx context.Context) *Request {
|
||||
r2 := *req
|
||||
r2.req = req.req.Clone(ctx)
|
||||
return &r2
|
||||
}
|
||||
|
||||
// not exported but dependent on Request
|
||||
|
||||
// PolicyFunc is a type that implements the Policy interface.
|
||||
// Use this type when implementing a stateless policy as a first-class function.
|
||||
type PolicyFunc func(*Request) (*http.Response, error)
|
||||
|
||||
// Do implements the Policy interface on policyFunc.
|
||||
func (pf PolicyFunc) Do(req *Request) (*http.Response, error) {
|
||||
return pf(req)
|
||||
}
|
||||
|
|
87
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/response_error.go
generated
vendored
87
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/response_error.go
generated
vendored
|
@ -13,42 +13,53 @@ import (
|
|||
"net/http"
|
||||
"regexp"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/log"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/internal/exported"
|
||||
)
|
||||
|
||||
// NewResponseError creates a new *ResponseError from the provided HTTP response.
|
||||
// Exported as runtime.NewResponseError().
|
||||
func NewResponseError(resp *http.Response) error {
|
||||
respErr := &ResponseError{
|
||||
StatusCode: resp.StatusCode,
|
||||
RawResponse: resp,
|
||||
}
|
||||
|
||||
// prefer the error code in the response header
|
||||
if ec := resp.Header.Get("x-ms-error-code"); ec != "" {
|
||||
respErr.ErrorCode = ec
|
||||
return respErr
|
||||
if ec := resp.Header.Get(shared.HeaderXMSErrorCode); ec != "" {
|
||||
return NewResponseErrorWithErrorCode(resp, ec)
|
||||
}
|
||||
|
||||
// if we didn't get x-ms-error-code, check in the response body
|
||||
body, err := exported.Payload(resp, nil)
|
||||
if err != nil {
|
||||
// since we're not returning the ResponseError in this
|
||||
// case we also don't want to write it to the log.
|
||||
return err
|
||||
}
|
||||
|
||||
var errorCode string
|
||||
if len(body) > 0 {
|
||||
if code := extractErrorCodeJSON(body); code != "" {
|
||||
respErr.ErrorCode = code
|
||||
} else if code := extractErrorCodeXML(body); code != "" {
|
||||
respErr.ErrorCode = code
|
||||
if fromJSON := extractErrorCodeJSON(body); fromJSON != "" {
|
||||
errorCode = fromJSON
|
||||
} else if fromXML := extractErrorCodeXML(body); fromXML != "" {
|
||||
errorCode = fromXML
|
||||
}
|
||||
}
|
||||
|
||||
return NewResponseErrorWithErrorCode(resp, errorCode)
|
||||
}
|
||||
|
||||
// NewResponseErrorWithErrorCode creates an *azcore.ResponseError from the provided HTTP response and errorCode.
|
||||
// Exported as runtime.NewResponseErrorWithErrorCode().
|
||||
func NewResponseErrorWithErrorCode(resp *http.Response, errorCode string) error {
|
||||
respErr := &ResponseError{
|
||||
ErrorCode: errorCode,
|
||||
StatusCode: resp.StatusCode,
|
||||
RawResponse: resp,
|
||||
}
|
||||
log.Write(log.EventResponseError, respErr.Error())
|
||||
return respErr
|
||||
}
|
||||
|
||||
func extractErrorCodeJSON(body []byte) string {
|
||||
var rawObj map[string]interface{}
|
||||
var rawObj map[string]any
|
||||
if err := json.Unmarshal(body, &rawObj); err != nil {
|
||||
// not a JSON object
|
||||
return ""
|
||||
|
@ -57,7 +68,7 @@ func extractErrorCodeJSON(body []byte) string {
|
|||
// check if this is a wrapped error, i.e. { "error": { ... } }
|
||||
// if so then unwrap it
|
||||
if wrapped, ok := rawObj["error"]; ok {
|
||||
unwrapped, ok := wrapped.(map[string]interface{})
|
||||
unwrapped, ok := wrapped.(map[string]any)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
@ -112,33 +123,45 @@ type ResponseError struct {
|
|||
// Error implements the error interface for type ResponseError.
|
||||
// Note that the message contents are not contractual and can change over time.
|
||||
func (e *ResponseError) Error() string {
|
||||
const separator = "--------------------------------------------------------------------------------"
|
||||
// write the request method and URL with response status code
|
||||
msg := &bytes.Buffer{}
|
||||
fmt.Fprintf(msg, "%s %s://%s%s\n", e.RawResponse.Request.Method, e.RawResponse.Request.URL.Scheme, e.RawResponse.Request.URL.Host, e.RawResponse.Request.URL.Path)
|
||||
fmt.Fprintln(msg, "--------------------------------------------------------------------------------")
|
||||
fmt.Fprintf(msg, "RESPONSE %d: %s\n", e.RawResponse.StatusCode, e.RawResponse.Status)
|
||||
if e.RawResponse != nil {
|
||||
if e.RawResponse.Request != nil {
|
||||
fmt.Fprintf(msg, "%s %s://%s%s\n", e.RawResponse.Request.Method, e.RawResponse.Request.URL.Scheme, e.RawResponse.Request.URL.Host, e.RawResponse.Request.URL.Path)
|
||||
} else {
|
||||
fmt.Fprintln(msg, "Request information not available")
|
||||
}
|
||||
fmt.Fprintln(msg, separator)
|
||||
fmt.Fprintf(msg, "RESPONSE %d: %s\n", e.RawResponse.StatusCode, e.RawResponse.Status)
|
||||
} else {
|
||||
fmt.Fprintln(msg, "Missing RawResponse")
|
||||
fmt.Fprintln(msg, separator)
|
||||
}
|
||||
if e.ErrorCode != "" {
|
||||
fmt.Fprintf(msg, "ERROR CODE: %s\n", e.ErrorCode)
|
||||
} else {
|
||||
fmt.Fprintln(msg, "ERROR CODE UNAVAILABLE")
|
||||
}
|
||||
fmt.Fprintln(msg, "--------------------------------------------------------------------------------")
|
||||
body, err := exported.Payload(e.RawResponse, nil)
|
||||
if err != nil {
|
||||
// this really shouldn't fail at this point as the response
|
||||
// body is already cached (it was read in NewResponseError)
|
||||
fmt.Fprintf(msg, "Error reading response body: %v", err)
|
||||
} else if len(body) > 0 {
|
||||
if err := json.Indent(msg, body, "", " "); err != nil {
|
||||
// failed to pretty-print so just dump it verbatim
|
||||
fmt.Fprint(msg, string(body))
|
||||
if e.RawResponse != nil {
|
||||
fmt.Fprintln(msg, separator)
|
||||
body, err := exported.Payload(e.RawResponse, nil)
|
||||
if err != nil {
|
||||
// this really shouldn't fail at this point as the response
|
||||
// body is already cached (it was read in NewResponseError)
|
||||
fmt.Fprintf(msg, "Error reading response body: %v", err)
|
||||
} else if len(body) > 0 {
|
||||
if err := json.Indent(msg, body, "", " "); err != nil {
|
||||
// failed to pretty-print so just dump it verbatim
|
||||
fmt.Fprint(msg, string(body))
|
||||
}
|
||||
// the standard library doesn't have a pretty-printer for XML
|
||||
fmt.Fprintln(msg)
|
||||
} else {
|
||||
fmt.Fprintln(msg, "Response contained no body")
|
||||
}
|
||||
// the standard library doesn't have a pretty-printer for XML
|
||||
fmt.Fprintln(msg)
|
||||
} else {
|
||||
fmt.Fprintln(msg, "Response contained no body")
|
||||
}
|
||||
fmt.Fprintln(msg, "--------------------------------------------------------------------------------")
|
||||
fmt.Fprintln(msg, separator)
|
||||
|
||||
return msg.String()
|
||||
}
|
||||
|
|
22
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/log/log.go
generated
vendored
22
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/log/log.go
generated
vendored
|
@ -15,24 +15,36 @@ import (
|
|||
type Event = log.Event
|
||||
|
||||
const (
|
||||
EventRequest = azlog.EventRequest
|
||||
EventResponse = azlog.EventResponse
|
||||
EventRetryPolicy = azlog.EventRetryPolicy
|
||||
EventLRO = azlog.EventLRO
|
||||
EventRequest = azlog.EventRequest
|
||||
EventResponse = azlog.EventResponse
|
||||
EventResponseError = azlog.EventResponseError
|
||||
EventRetryPolicy = azlog.EventRetryPolicy
|
||||
EventLRO = azlog.EventLRO
|
||||
)
|
||||
|
||||
// Write invokes the underlying listener with the specified event and message.
|
||||
// If the event shouldn't be logged or there is no listener then Write does nothing.
|
||||
func Write(cls log.Event, msg string) {
|
||||
log.Write(cls, msg)
|
||||
}
|
||||
|
||||
func Writef(cls log.Event, format string, a ...interface{}) {
|
||||
// Writef invokes the underlying listener with the specified event and formatted message.
|
||||
// If the event shouldn't be logged or there is no listener then Writef does nothing.
|
||||
func Writef(cls log.Event, format string, a ...any) {
|
||||
log.Writef(cls, format, a...)
|
||||
}
|
||||
|
||||
// SetListener will set the Logger to write to the specified listener.
|
||||
func SetListener(lst func(Event, string)) {
|
||||
log.SetListener(lst)
|
||||
}
|
||||
|
||||
// Should returns true if the specified log event should be written to the log.
|
||||
// By default all log events will be logged. Call SetEvents() to limit
|
||||
// the log events for logging.
|
||||
// If no listener has been set this will return false.
|
||||
// Calling this method is useful when the message to log is computationally expensive
|
||||
// and you want to avoid the overhead if its log event is not enabled.
|
||||
func Should(cls log.Event) bool {
|
||||
return log.Should(cls)
|
||||
}
|
||||
|
|
2
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/async/async.go
generated
vendored
2
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/async/async.go
generated
vendored
|
@ -27,7 +27,7 @@ func Applicable(resp *http.Response) bool {
|
|||
}
|
||||
|
||||
// CanResume returns true if the token can rehydrate this poller type.
|
||||
func CanResume(token map[string]interface{}) bool {
|
||||
func CanResume(token map[string]any) bool {
|
||||
_, ok := token["asyncURL"]
|
||||
return ok
|
||||
}
|
||||
|
|
2
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/body/body.go
generated
vendored
2
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/body/body.go
generated
vendored
|
@ -29,7 +29,7 @@ func Applicable(resp *http.Response) bool {
|
|||
}
|
||||
|
||||
// CanResume returns true if the token can rehydrate this poller type.
|
||||
func CanResume(token map[string]interface{}) bool {
|
||||
func CanResume(token map[string]any) bool {
|
||||
t, ok := token["type"]
|
||||
if !ok {
|
||||
return false
|
||||
|
|
133
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/fake/fake.go
generated
vendored
Normal file
133
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/fake/fake.go
generated
vendored
Normal file
|
@ -0,0 +1,133 @@
|
|||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package fake
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/log"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/internal/poller"
|
||||
)
|
||||
|
||||
// Applicable returns true if the LRO is a fake.
|
||||
func Applicable(resp *http.Response) bool {
|
||||
return resp.Header.Get(shared.HeaderFakePollerStatus) != ""
|
||||
}
|
||||
|
||||
// CanResume returns true if the token can rehydrate this poller type.
|
||||
func CanResume(token map[string]any) bool {
|
||||
_, ok := token["fakeURL"]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Poller is an LRO poller that uses the Core-Fake-Poller pattern.
|
||||
type Poller[T any] struct {
|
||||
pl exported.Pipeline
|
||||
|
||||
resp *http.Response
|
||||
|
||||
// The API name from CtxAPINameKey
|
||||
APIName string `json:"apiName"`
|
||||
|
||||
// The URL from Core-Fake-Poller header.
|
||||
FakeURL string `json:"fakeURL"`
|
||||
|
||||
// The LRO's current state.
|
||||
FakeStatus string `json:"status"`
|
||||
}
|
||||
|
||||
// lroStatusURLSuffix is the URL path suffix for a faked LRO.
|
||||
const lroStatusURLSuffix = "/get/fake/status"
|
||||
|
||||
// New creates a new Poller from the provided initial response.
|
||||
// Pass nil for response to create an empty Poller for rehydration.
|
||||
func New[T any](pl exported.Pipeline, resp *http.Response) (*Poller[T], error) {
|
||||
if resp == nil {
|
||||
log.Write(log.EventLRO, "Resuming Core-Fake-Poller poller.")
|
||||
return &Poller[T]{pl: pl}, nil
|
||||
}
|
||||
|
||||
log.Write(log.EventLRO, "Using Core-Fake-Poller poller.")
|
||||
fakeStatus := resp.Header.Get(shared.HeaderFakePollerStatus)
|
||||
if fakeStatus == "" {
|
||||
return nil, errors.New("response is missing Fake-Poller-Status header")
|
||||
}
|
||||
|
||||
ctxVal := resp.Request.Context().Value(shared.CtxAPINameKey{})
|
||||
if ctxVal == nil {
|
||||
return nil, errors.New("missing value for CtxAPINameKey")
|
||||
}
|
||||
|
||||
apiName, ok := ctxVal.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected string for CtxAPINameKey, the type was %T", ctxVal)
|
||||
}
|
||||
|
||||
qp := ""
|
||||
if resp.Request.URL.RawQuery != "" {
|
||||
qp = "?" + resp.Request.URL.RawQuery
|
||||
}
|
||||
|
||||
p := &Poller[T]{
|
||||
pl: pl,
|
||||
resp: resp,
|
||||
APIName: apiName,
|
||||
// NOTE: any changes to this path format MUST be reflected in SanitizePollerPath()
|
||||
FakeURL: fmt.Sprintf("%s://%s%s%s%s", resp.Request.URL.Scheme, resp.Request.URL.Host, resp.Request.URL.Path, lroStatusURLSuffix, qp),
|
||||
FakeStatus: fakeStatus,
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// Done returns true if the LRO is in a terminal state.
|
||||
func (p *Poller[T]) Done() bool {
|
||||
return poller.IsTerminalState(p.FakeStatus)
|
||||
}
|
||||
|
||||
// Poll retrieves the current state of the LRO.
|
||||
func (p *Poller[T]) Poll(ctx context.Context) (*http.Response, error) {
|
||||
ctx = context.WithValue(ctx, shared.CtxAPINameKey{}, p.APIName)
|
||||
err := pollers.PollHelper(ctx, p.FakeURL, p.pl, func(resp *http.Response) (string, error) {
|
||||
if !poller.StatusCodeValid(resp) {
|
||||
p.resp = resp
|
||||
return "", exported.NewResponseError(resp)
|
||||
}
|
||||
fakeStatus := resp.Header.Get(shared.HeaderFakePollerStatus)
|
||||
if fakeStatus == "" {
|
||||
return "", errors.New("response is missing Fake-Poller-Status header")
|
||||
}
|
||||
p.resp = resp
|
||||
p.FakeStatus = fakeStatus
|
||||
return p.FakeStatus, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p.resp, nil
|
||||
}
|
||||
|
||||
func (p *Poller[T]) Result(ctx context.Context, out *T) error {
|
||||
if p.resp.StatusCode == http.StatusNoContent {
|
||||
return nil
|
||||
} else if poller.Failed(p.FakeStatus) {
|
||||
return exported.NewResponseError(p.resp)
|
||||
}
|
||||
|
||||
return pollers.ResultHelper(p.resp, poller.Failed(p.FakeStatus), out)
|
||||
}
|
||||
|
||||
// SanitizePollerPath removes any fake-appended suffix from a URL's path.
|
||||
func SanitizePollerPath(path string) string {
|
||||
return strings.TrimSuffix(path, lroStatusURLSuffix)
|
||||
}
|
6
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/loc/loc.go
generated
vendored
6
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/loc/loc.go
generated
vendored
|
@ -28,7 +28,7 @@ func Applicable(resp *http.Response) bool {
|
|||
}
|
||||
|
||||
// CanResume returns true if the token can rehydrate this poller type.
|
||||
func CanResume(token map[string]interface{}) bool {
|
||||
func CanResume(token map[string]any) bool {
|
||||
t, ok := token["type"]
|
||||
if !ok {
|
||||
return false
|
||||
|
@ -103,6 +103,10 @@ func (p *Poller[T]) Poll(ctx context.Context) (*http.Response, error) {
|
|||
} else if resp.StatusCode > 199 && resp.StatusCode < 300 {
|
||||
// any 2xx other than a 202 indicates success
|
||||
p.CurState = poller.StatusSucceeded
|
||||
} else if pollers.IsNonTerminalHTTPStatusCode(resp) {
|
||||
// the request timed out or is being throttled.
|
||||
// DO NOT include this as a terminal failure. preserve
|
||||
// the existing state and return the response.
|
||||
} else {
|
||||
p.CurState = poller.StatusFailed
|
||||
}
|
||||
|
|
2
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/op/op.go
generated
vendored
2
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/op/op.go
generated
vendored
|
@ -25,7 +25,7 @@ func Applicable(resp *http.Response) bool {
|
|||
}
|
||||
|
||||
// CanResume returns true if the token can rehydrate this poller type.
|
||||
func CanResume(token map[string]interface{}) bool {
|
||||
func CanResume(token map[string]any) bool {
|
||||
_, ok := token["oplocURL"]
|
||||
return ok
|
||||
}
|
||||
|
|
15
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/util.go
generated
vendored
15
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/util.go
generated
vendored
|
@ -74,7 +74,7 @@ func ExtractToken(token string) ([]byte, error) {
|
|||
|
||||
// IsTokenValid returns an error if the specified token isn't applicable for generic type T.
|
||||
func IsTokenValid[T any](token string) error {
|
||||
raw := map[string]interface{}{}
|
||||
raw := map[string]any{}
|
||||
if err := json.Unmarshal([]byte(token), &raw); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -185,3 +185,16 @@ func ResultHelper[T any](resp *http.Response, failed bool, out *T) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsNonTerminalHTTPStatusCode returns true if the HTTP status code should be
|
||||
// considered non-terminal thus eligible for retry.
|
||||
func IsNonTerminalHTTPStatusCode(resp *http.Response) bool {
|
||||
return exported.HasStatusCode(resp,
|
||||
http.StatusRequestTimeout, // 408
|
||||
http.StatusTooManyRequests, // 429
|
||||
http.StatusInternalServerError, // 500
|
||||
http.StatusBadGateway, // 502
|
||||
http.StatusServiceUnavailable, // 503
|
||||
http.StatusGatewayTimeout, // 504
|
||||
)
|
||||
}
|
||||
|
|
14
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared/constants.go
generated
vendored
14
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared/constants.go
generated
vendored
|
@ -7,8 +7,9 @@
|
|||
package shared
|
||||
|
||||
const (
|
||||
ContentTypeAppJSON = "application/json"
|
||||
ContentTypeAppXML = "application/xml"
|
||||
ContentTypeAppJSON = "application/json"
|
||||
ContentTypeAppXML = "application/xml"
|
||||
ContentTypeTextPlain = "text/plain"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -17,20 +18,27 @@ const (
|
|||
HeaderAzureAsync = "Azure-AsyncOperation"
|
||||
HeaderContentLength = "Content-Length"
|
||||
HeaderContentType = "Content-Type"
|
||||
HeaderFakePollerStatus = "Fake-Poller-Status"
|
||||
HeaderLocation = "Location"
|
||||
HeaderOperationLocation = "Operation-Location"
|
||||
HeaderRetryAfter = "Retry-After"
|
||||
HeaderRetryAfterMS = "Retry-After-Ms"
|
||||
HeaderUserAgent = "User-Agent"
|
||||
HeaderWWWAuthenticate = "WWW-Authenticate"
|
||||
HeaderXMSClientRequestID = "x-ms-client-request-id"
|
||||
HeaderXMSRequestID = "x-ms-request-id"
|
||||
HeaderXMSErrorCode = "x-ms-error-code"
|
||||
HeaderXMSRetryAfterMS = "x-ms-retry-after-ms"
|
||||
)
|
||||
|
||||
const BearerTokenPrefix = "Bearer "
|
||||
|
||||
const TracingNamespaceAttrName = "az.namespace"
|
||||
|
||||
const (
|
||||
// Module is the name of the calling module used in telemetry data.
|
||||
Module = "azcore"
|
||||
|
||||
// Version is the semantic version (see http://semver.org) of this module.
|
||||
Version = "v1.6.0"
|
||||
Version = "v1.11.1"
|
||||
)
|
||||
|
|
103
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared/shared.go
generated
vendored
103
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared/shared.go
generated
vendored
|
@ -13,18 +13,26 @@ import (
|
|||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NOTE: when adding a new context key type, it likely needs to be
|
||||
// added to the deny-list of key types in ContextWithDeniedValues
|
||||
|
||||
// CtxWithHTTPHeaderKey is used as a context key for adding/retrieving http.Header.
|
||||
type CtxWithHTTPHeaderKey struct{}
|
||||
|
||||
// CtxWithRetryOptionsKey is used as a context key for adding/retrieving RetryOptions.
|
||||
type CtxWithRetryOptionsKey struct{}
|
||||
|
||||
// CtxIncludeResponseKey is used as a context key for retrieving the raw response.
|
||||
type CtxIncludeResponseKey struct{}
|
||||
// CtxWithCaptureResponse is used as a context key for retrieving the raw response.
|
||||
type CtxWithCaptureResponse struct{}
|
||||
|
||||
// CtxWithTracingTracer is used as a context key for adding/retrieving tracing.Tracer.
|
||||
type CtxWithTracingTracer struct{}
|
||||
|
||||
// CtxAPINameKey is used as a context key for adding/retrieving the API name.
|
||||
type CtxAPINameKey struct{}
|
||||
|
||||
// Delay waits for the duration to elapse or the context to be cancelled.
|
||||
func Delay(ctx context.Context, delay time.Duration) error {
|
||||
|
@ -36,22 +44,64 @@ func Delay(ctx context.Context, delay time.Duration) error {
|
|||
}
|
||||
}
|
||||
|
||||
// RetryAfter returns non-zero if the response contains a Retry-After header value.
|
||||
// RetryAfter returns non-zero if the response contains one of the headers with a "retry after" value.
|
||||
// Headers are checked in the following order: retry-after-ms, x-ms-retry-after-ms, retry-after
|
||||
func RetryAfter(resp *http.Response) time.Duration {
|
||||
if resp == nil {
|
||||
return 0
|
||||
}
|
||||
ra := resp.Header.Get(HeaderRetryAfter)
|
||||
if ra == "" {
|
||||
return 0
|
||||
|
||||
type retryData struct {
|
||||
header string
|
||||
units time.Duration
|
||||
|
||||
// custom is used when the regular algorithm failed and is optional.
|
||||
// the returned duration is used verbatim (units is not applied).
|
||||
custom func(string) time.Duration
|
||||
}
|
||||
// retry-after values are expressed in either number of
|
||||
// seconds or an HTTP-date indicating when to try again
|
||||
if retryAfter, _ := strconv.Atoi(ra); retryAfter > 0 {
|
||||
return time.Duration(retryAfter) * time.Second
|
||||
} else if t, err := time.Parse(time.RFC1123, ra); err == nil {
|
||||
return time.Until(t)
|
||||
|
||||
nop := func(string) time.Duration { return 0 }
|
||||
|
||||
// the headers are listed in order of preference
|
||||
retries := []retryData{
|
||||
{
|
||||
header: HeaderRetryAfterMS,
|
||||
units: time.Millisecond,
|
||||
custom: nop,
|
||||
},
|
||||
{
|
||||
header: HeaderXMSRetryAfterMS,
|
||||
units: time.Millisecond,
|
||||
custom: nop,
|
||||
},
|
||||
{
|
||||
header: HeaderRetryAfter,
|
||||
units: time.Second,
|
||||
|
||||
// retry-after values are expressed in either number of
|
||||
// seconds or an HTTP-date indicating when to try again
|
||||
custom: func(ra string) time.Duration {
|
||||
t, err := time.Parse(time.RFC1123, ra)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return time.Until(t)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, retry := range retries {
|
||||
v := resp.Header.Get(retry.header)
|
||||
if v == "" {
|
||||
continue
|
||||
}
|
||||
if retryAfter, _ := strconv.Atoi(v); retryAfter > 0 {
|
||||
return time.Duration(retryAfter) * retry.units
|
||||
} else if d := retry.custom(v); d > 0 {
|
||||
return d
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
@ -79,14 +129,21 @@ func ValidateModVer(moduleVersion string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// ExtractPackageName returns "package" from "package.Client".
|
||||
// If clientName is malformed, an error is returned.
|
||||
func ExtractPackageName(clientName string) (string, error) {
|
||||
pkg, client, ok := strings.Cut(clientName, ".")
|
||||
if !ok {
|
||||
return "", fmt.Errorf("missing . in clientName %s", clientName)
|
||||
} else if pkg == "" || client == "" {
|
||||
return "", fmt.Errorf("malformed clientName %s", clientName)
|
||||
}
|
||||
return pkg, nil
|
||||
// ContextWithDeniedValues wraps an existing [context.Context], denying access to certain context values.
|
||||
// Pipeline policies that create new requests to be sent down their own pipeline MUST wrap the caller's
|
||||
// context with an instance of this type. This is to prevent context values from flowing across disjoint
|
||||
// requests which can have unintended side-effects.
|
||||
type ContextWithDeniedValues struct {
|
||||
context.Context
|
||||
}
|
||||
|
||||
// Value implements part of the [context.Context] interface.
|
||||
// It acts as a deny-list for certain context keys.
|
||||
func (c *ContextWithDeniedValues) Value(key any) any {
|
||||
switch key.(type) {
|
||||
case CtxAPINameKey, CtxWithCaptureResponse, CtxWithHTTPHeaderKey, CtxWithRetryOptionsKey, CtxWithTracingTracer:
|
||||
return nil
|
||||
default:
|
||||
return c.Context.Value(key)
|
||||
}
|
||||
}
|
||||
|
|
5
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/log/log.go
generated
vendored
5
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/log/log.go
generated
vendored
|
@ -23,6 +23,11 @@ const (
|
|||
// This includes information like the HTTP status code, headers, and request URL.
|
||||
EventResponse Event = "Response"
|
||||
|
||||
// EventResponseError entries contain information about HTTP responses that returned
|
||||
// an *azcore.ResponseError (i.e. responses with a non 2xx HTTP status code).
|
||||
// This includes the contents of ResponseError.Error().
|
||||
EventResponseError Event = "ResponseError"
|
||||
|
||||
// EventRetryPolicy entries contain information specific to the retry policy in use.
|
||||
EventRetryPolicy Event = "Retry"
|
||||
|
||||
|
|
37
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/policy/policy.go
generated
vendored
37
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/policy/policy.go
generated
vendored
|
@ -7,11 +7,13 @@
|
|||
package policy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/tracing"
|
||||
)
|
||||
|
||||
|
@ -27,14 +29,21 @@ type Transporter = exported.Transporter
|
|||
type Request = exported.Request
|
||||
|
||||
// ClientOptions contains optional settings for a client's pipeline.
|
||||
// All zero-value fields will be initialized with default values.
|
||||
// Instances can be shared across calls to SDK client constructors when uniform configuration is desired.
|
||||
// Zero-value fields will have their specified default values applied during use.
|
||||
type ClientOptions struct {
|
||||
// APIVersion overrides the default version requested of the service. Set with caution as this package version has not been tested with arbitrary service versions.
|
||||
// APIVersion overrides the default version requested of the service.
|
||||
// Set with caution as this package version has not been tested with arbitrary service versions.
|
||||
APIVersion string
|
||||
|
||||
// Cloud specifies a cloud for the client. The default is Azure Public Cloud.
|
||||
Cloud cloud.Configuration
|
||||
|
||||
// InsecureAllowCredentialWithHTTP enables authenticated requests over HTTP.
|
||||
// By default, authenticated requests to an HTTP endpoint are rejected by the client.
|
||||
// WARNING: setting this to true will allow sending the credential in clear text. Use with caution.
|
||||
InsecureAllowCredentialWithHTTP bool
|
||||
|
||||
// Logging configures the built-in logging policy.
|
||||
Logging LogOptions
|
||||
|
||||
|
@ -143,6 +152,11 @@ type BearerTokenOptions struct {
|
|||
// When this field isn't set, the policy follows its default behavior of authorizing every request with a bearer token from
|
||||
// its given credential.
|
||||
AuthorizationHandler AuthorizationHandler
|
||||
|
||||
// InsecureAllowCredentialWithHTTP enables authenticated requests over HTTP.
|
||||
// By default, authenticated requests to an HTTP endpoint are rejected by the client.
|
||||
// WARNING: setting this to true will allow sending the bearer token in clear text. Use with caution.
|
||||
InsecureAllowCredentialWithHTTP bool
|
||||
}
|
||||
|
||||
// AuthorizationHandler allows SDK developers to insert custom logic that runs when BearerTokenPolicy must authorize a request.
|
||||
|
@ -162,3 +176,22 @@ type AuthorizationHandler struct {
|
|||
// the policy will return any 401 response to the client.
|
||||
OnChallenge func(*Request, *http.Response, func(TokenRequestOptions) error) error
|
||||
}
|
||||
|
||||
// WithCaptureResponse applies the HTTP response retrieval annotation to the parent context.
|
||||
// The resp parameter will contain the HTTP response after the request has completed.
|
||||
func WithCaptureResponse(parent context.Context, resp **http.Response) context.Context {
|
||||
return context.WithValue(parent, shared.CtxWithCaptureResponse{}, resp)
|
||||
}
|
||||
|
||||
// WithHTTPHeader adds the specified http.Header to the parent context.
|
||||
// Use this to specify custom HTTP headers at the API-call level.
|
||||
// Any overlapping headers will have their values replaced with the values specified here.
|
||||
func WithHTTPHeader(parent context.Context, header http.Header) context.Context {
|
||||
return context.WithValue(parent, shared.CtxWithHTTPHeaderKey{}, header)
|
||||
}
|
||||
|
||||
// WithRetryOptions adds the specified RetryOptions to the parent context.
|
||||
// Use this to specify custom RetryOptions at the API-call level.
|
||||
func WithRetryOptions(parent context.Context, options RetryOptions) context.Context {
|
||||
return context.WithValue(parent, shared.CtxWithRetryOptionsKey{}, options)
|
||||
}
|
||||
|
|
8
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/errors.go
generated
vendored
8
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/errors.go
generated
vendored
|
@ -14,6 +14,14 @@ import (
|
|||
|
||||
// NewResponseError creates an *azcore.ResponseError from the provided HTTP response.
|
||||
// Call this when a service request returns a non-successful status code.
|
||||
// The error code will be extracted from the *http.Response, either from the x-ms-error-code
|
||||
// header (preferred) or attempted to be parsed from the response body.
|
||||
func NewResponseError(resp *http.Response) error {
|
||||
return exported.NewResponseError(resp)
|
||||
}
|
||||
|
||||
// NewResponseErrorWithErrorCode creates an *azcore.ResponseError from the provided HTTP response and errorCode.
|
||||
// Use this variant when the error code is in a non-standard location.
|
||||
func NewResponseErrorWithErrorCode(resp *http.Response, errorCode string) error {
|
||||
return exported.NewResponseErrorWithErrorCode(resp, errorCode)
|
||||
}
|
||||
|
|
59
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/pager.go
generated
vendored
59
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/pager.go
generated
vendored
|
@ -10,6 +10,12 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/tracing"
|
||||
)
|
||||
|
||||
// PagingHandler contains the required data for constructing a Pager.
|
||||
|
@ -20,12 +26,16 @@ type PagingHandler[T any] struct {
|
|||
|
||||
// Fetcher fetches the first and subsequent pages.
|
||||
Fetcher func(context.Context, *T) (T, error)
|
||||
|
||||
// Tracer contains the Tracer from the client that's creating the Pager.
|
||||
Tracer tracing.Tracer
|
||||
}
|
||||
|
||||
// Pager provides operations for iterating over paged responses.
|
||||
type Pager[T any] struct {
|
||||
current *T
|
||||
handler PagingHandler[T]
|
||||
tracer tracing.Tracer
|
||||
firstPage bool
|
||||
}
|
||||
|
||||
|
@ -34,6 +44,7 @@ type Pager[T any] struct {
|
|||
func NewPager[T any](handler PagingHandler[T]) *Pager[T] {
|
||||
return &Pager[T]{
|
||||
handler: handler,
|
||||
tracer: handler.Tracer,
|
||||
firstPage: true,
|
||||
}
|
||||
}
|
||||
|
@ -48,8 +59,6 @@ func (p *Pager[T]) More() bool {
|
|||
|
||||
// NextPage advances the pager to the next page.
|
||||
func (p *Pager[T]) NextPage(ctx context.Context) (T, error) {
|
||||
var resp T
|
||||
var err error
|
||||
if p.current != nil {
|
||||
if p.firstPage {
|
||||
// we get here if it's an LRO-pager, we already have the first page
|
||||
|
@ -58,12 +67,16 @@ func (p *Pager[T]) NextPage(ctx context.Context) (T, error) {
|
|||
} else if !p.handler.More(*p.current) {
|
||||
return *new(T), errors.New("no more pages")
|
||||
}
|
||||
resp, err = p.handler.Fetcher(ctx, p.current)
|
||||
} else {
|
||||
// non-LRO case, first page
|
||||
p.firstPage = false
|
||||
resp, err = p.handler.Fetcher(ctx, nil)
|
||||
}
|
||||
|
||||
var err error
|
||||
ctx, endSpan := StartSpan(ctx, fmt.Sprintf("%s.NextPage", shortenTypeName(reflect.TypeOf(*p).Name())), p.tracer, nil)
|
||||
defer func() { endSpan(err) }()
|
||||
|
||||
resp, err := p.handler.Fetcher(ctx, p.current)
|
||||
if err != nil {
|
||||
return *new(T), err
|
||||
}
|
||||
|
@ -75,3 +88,41 @@ func (p *Pager[T]) NextPage(ctx context.Context) (T, error) {
|
|||
func (p *Pager[T]) UnmarshalJSON(data []byte) error {
|
||||
return json.Unmarshal(data, &p.current)
|
||||
}
|
||||
|
||||
// FetcherForNextLinkOptions contains the optional values for [FetcherForNextLink].
|
||||
type FetcherForNextLinkOptions struct {
|
||||
// NextReq is the func to be called when requesting subsequent pages.
|
||||
// Used for paged operations that have a custom next link operation.
|
||||
NextReq func(context.Context, string) (*policy.Request, error)
|
||||
}
|
||||
|
||||
// FetcherForNextLink is a helper containing boilerplate code to simplify creating a PagingHandler[T].Fetcher from a next link URL.
|
||||
// - ctx is the [context.Context] controlling the lifetime of the HTTP operation
|
||||
// - pl is the [Pipeline] used to dispatch the HTTP request
|
||||
// - nextLink is the URL used to fetch the next page. the empty string indicates the first page is to be requested
|
||||
// - firstReq is the func to be called when creating the request for the first page
|
||||
// - options contains any optional parameters, pass nil to accept the default values
|
||||
func FetcherForNextLink(ctx context.Context, pl Pipeline, nextLink string, firstReq func(context.Context) (*policy.Request, error), options *FetcherForNextLinkOptions) (*http.Response, error) {
|
||||
var req *policy.Request
|
||||
var err error
|
||||
if nextLink == "" {
|
||||
req, err = firstReq(ctx)
|
||||
} else if nextLink, err = EncodeQueryParams(nextLink); err == nil {
|
||||
if options != nil && options.NextReq != nil {
|
||||
req, err = options.NextReq(ctx, nextLink)
|
||||
} else {
|
||||
req, err = NewRequest(ctx, http.MethodGet, nextLink)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := pl.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !HasStatusCode(resp, http.StatusOK) {
|
||||
return nil, NewResponseError(resp)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
|
36
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/pipeline.go
generated
vendored
36
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/pipeline.go
generated
vendored
|
@ -13,9 +13,35 @@ import (
|
|||
|
||||
// PipelineOptions contains Pipeline options for SDK developers
|
||||
type PipelineOptions struct {
|
||||
AllowedHeaders, AllowedQueryParameters []string
|
||||
APIVersion APIVersionOptions
|
||||
PerCall, PerRetry []policy.Policy
|
||||
// AllowedHeaders is the slice of headers to log with their values intact.
|
||||
// All headers not in the slice will have their values REDACTED.
|
||||
// Applies to request and response headers.
|
||||
AllowedHeaders []string
|
||||
|
||||
// AllowedQueryParameters is the slice of query parameters to log with their values intact.
|
||||
// All query parameters not in the slice will have their values REDACTED.
|
||||
AllowedQueryParameters []string
|
||||
|
||||
// APIVersion overrides the default version requested of the service.
|
||||
// Set with caution as this package version has not been tested with arbitrary service versions.
|
||||
APIVersion APIVersionOptions
|
||||
|
||||
// PerCall contains custom policies to inject into the pipeline.
|
||||
// Each policy is executed once per request.
|
||||
PerCall []policy.Policy
|
||||
|
||||
// PerRetry contains custom policies to inject into the pipeline.
|
||||
// Each policy is executed once per request, and for each retry of that request.
|
||||
PerRetry []policy.Policy
|
||||
|
||||
// Tracing contains options used to configure distributed tracing.
|
||||
Tracing TracingOptions
|
||||
}
|
||||
|
||||
// TracingOptions contains tracing options for SDK developers.
|
||||
type TracingOptions struct {
|
||||
// Namespace contains the value to use for the az.namespace span attribute.
|
||||
Namespace string
|
||||
}
|
||||
|
||||
// Pipeline represents a primitive for sending HTTP requests and receiving responses.
|
||||
|
@ -56,8 +82,10 @@ func NewPipeline(module, version string, plOpts PipelineOptions, options *policy
|
|||
policies = append(policies, NewRetryPolicy(&cp.Retry))
|
||||
policies = append(policies, plOpts.PerRetry...)
|
||||
policies = append(policies, cp.PerRetryPolicies...)
|
||||
policies = append(policies, exported.PolicyFunc(httpHeaderPolicy))
|
||||
policies = append(policies, newHTTPTracePolicy(cp.Logging.AllowedQueryParams))
|
||||
policies = append(policies, NewLogPolicy(&cp.Logging))
|
||||
policies = append(policies, exported.PolicyFunc(httpHeaderPolicy), exported.PolicyFunc(bodyDownloadPolicy))
|
||||
policies = append(policies, exported.PolicyFunc(bodyDownloadPolicy))
|
||||
transport := cp.Transport
|
||||
if transport == nil {
|
||||
transport = defaultHTTPClient
|
||||
|
|
41
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_bearer_token.go
generated
vendored
41
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_bearer_token.go
generated
vendored
|
@ -6,6 +6,7 @@ package runtime
|
|||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
|
||||
|
@ -23,6 +24,7 @@ type BearerTokenPolicy struct {
|
|||
authzHandler policy.AuthorizationHandler
|
||||
cred exported.TokenCredential
|
||||
scopes []string
|
||||
allowHTTP bool
|
||||
}
|
||||
|
||||
type acquiringResourceState struct {
|
||||
|
@ -34,7 +36,7 @@ type acquiringResourceState struct {
|
|||
// acquire acquires or updates the resource; only one
|
||||
// thread/goroutine at a time ever calls this function
|
||||
func acquire(state acquiringResourceState) (newResource exported.AccessToken, newExpiration time.Time, err error) {
|
||||
tk, err := state.p.cred.GetToken(state.req.Raw().Context(), state.tro)
|
||||
tk, err := state.p.cred.GetToken(&shared.ContextWithDeniedValues{Context: state.req.Raw().Context()}, state.tro)
|
||||
if err != nil {
|
||||
return exported.AccessToken{}, time.Time{}, err
|
||||
}
|
||||
|
@ -54,6 +56,7 @@ func NewBearerTokenPolicy(cred exported.TokenCredential, scopes []string, opts *
|
|||
cred: cred,
|
||||
scopes: scopes,
|
||||
mainResource: temporal.NewResource(acquire),
|
||||
allowHTTP: opts.InsecureAllowCredentialWithHTTP,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,6 +75,17 @@ func (b *BearerTokenPolicy) authenticateAndAuthorize(req *policy.Request) func(p
|
|||
|
||||
// Do authorizes a request with a bearer token
|
||||
func (b *BearerTokenPolicy) Do(req *policy.Request) (*http.Response, error) {
|
||||
// skip adding the authorization header if no TokenCredential was provided.
|
||||
// this prevents a panic that might be hard to diagnose and allows testing
|
||||
// against http endpoints that don't require authentication.
|
||||
if b.cred == nil {
|
||||
return req.Next()
|
||||
}
|
||||
|
||||
if err := checkHTTPSForAuth(req, b.allowHTTP); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var err error
|
||||
if b.authzHandler.OnRequest != nil {
|
||||
err = b.authzHandler.OnRequest(req, b.authenticateAndAuthorize(req))
|
||||
|
@ -79,7 +93,7 @@ func (b *BearerTokenPolicy) Do(req *policy.Request) (*http.Response, error) {
|
|||
err = b.authenticateAndAuthorize(req)(policy.TokenRequestOptions{Scopes: b.scopes})
|
||||
}
|
||||
if err != nil {
|
||||
return nil, ensureNonRetriable(err)
|
||||
return nil, errorinfo.NonRetriableError(err)
|
||||
}
|
||||
|
||||
res, err := req.Next()
|
||||
|
@ -95,22 +109,15 @@ func (b *BearerTokenPolicy) Do(req *policy.Request) (*http.Response, error) {
|
|||
}
|
||||
}
|
||||
}
|
||||
return res, ensureNonRetriable(err)
|
||||
}
|
||||
|
||||
func ensureNonRetriable(err error) error {
|
||||
var nre errorinfo.NonRetriable
|
||||
if err != nil && !errors.As(err, &nre) {
|
||||
err = btpError{err}
|
||||
if err != nil {
|
||||
err = errorinfo.NonRetriableError(err)
|
||||
}
|
||||
return err
|
||||
return res, err
|
||||
}
|
||||
|
||||
// btpError is a wrapper that ensures RetryPolicy doesn't retry requests BearerTokenPolicy couldn't authorize
|
||||
type btpError struct {
|
||||
error
|
||||
func checkHTTPSForAuth(req *policy.Request, allowHTTP bool) error {
|
||||
if strings.ToLower(req.Raw().URL.Scheme) != "https" && !allowHTTP {
|
||||
return errorinfo.NonRetriableError(errors.New("authenticated requests are not permitted for non TLS protected (https) endpoints"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (btpError) NonRetriable() {}
|
||||
|
||||
var _ errorinfo.NonRetriable = (*btpError)(nil)
|
||||
|
|
3
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_http_header.go
generated
vendored
3
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_http_header.go
generated
vendored
|
@ -34,6 +34,7 @@ func httpHeaderPolicy(req *policy.Request) (*http.Response, error) {
|
|||
// WithHTTPHeader adds the specified http.Header to the parent context.
|
||||
// Use this to specify custom HTTP headers at the API-call level.
|
||||
// Any overlapping headers will have their values replaced with the values specified here.
|
||||
// Deprecated: use [policy.WithHTTPHeader] instead.
|
||||
func WithHTTPHeader(parent context.Context, header http.Header) context.Context {
|
||||
return context.WithValue(parent, shared.CtxWithHTTPHeaderKey{}, header)
|
||||
return policy.WithHTTPHeader(parent, header)
|
||||
}
|
||||
|
|
143
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_http_trace.go
generated
vendored
Normal file
143
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_http_trace.go
generated
vendored
Normal file
|
@ -0,0 +1,143 @@
|
|||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/tracing"
|
||||
)
|
||||
|
||||
const (
|
||||
attrHTTPMethod = "http.method"
|
||||
attrHTTPURL = "http.url"
|
||||
attrHTTPUserAgent = "http.user_agent"
|
||||
attrHTTPStatusCode = "http.status_code"
|
||||
|
||||
attrAZClientReqID = "az.client_request_id"
|
||||
attrAZServiceReqID = "az.service_request_id"
|
||||
|
||||
attrNetPeerName = "net.peer.name"
|
||||
)
|
||||
|
||||
// newHTTPTracePolicy creates a new instance of the httpTracePolicy.
|
||||
// - allowedQueryParams contains the user-specified query parameters that don't need to be redacted from the trace
|
||||
func newHTTPTracePolicy(allowedQueryParams []string) exported.Policy {
|
||||
return &httpTracePolicy{allowedQP: getAllowedQueryParams(allowedQueryParams)}
|
||||
}
|
||||
|
||||
// httpTracePolicy is a policy that creates a trace for the HTTP request and its response
|
||||
type httpTracePolicy struct {
|
||||
allowedQP map[string]struct{}
|
||||
}
|
||||
|
||||
// Do implements the pipeline.Policy interfaces for the httpTracePolicy type.
|
||||
func (h *httpTracePolicy) Do(req *policy.Request) (resp *http.Response, err error) {
|
||||
rawTracer := req.Raw().Context().Value(shared.CtxWithTracingTracer{})
|
||||
if tracer, ok := rawTracer.(tracing.Tracer); ok && tracer.Enabled() {
|
||||
attributes := []tracing.Attribute{
|
||||
{Key: attrHTTPMethod, Value: req.Raw().Method},
|
||||
{Key: attrHTTPURL, Value: getSanitizedURL(*req.Raw().URL, h.allowedQP)},
|
||||
{Key: attrNetPeerName, Value: req.Raw().URL.Host},
|
||||
}
|
||||
|
||||
if ua := req.Raw().Header.Get(shared.HeaderUserAgent); ua != "" {
|
||||
attributes = append(attributes, tracing.Attribute{Key: attrHTTPUserAgent, Value: ua})
|
||||
}
|
||||
if reqID := req.Raw().Header.Get(shared.HeaderXMSClientRequestID); reqID != "" {
|
||||
attributes = append(attributes, tracing.Attribute{Key: attrAZClientReqID, Value: reqID})
|
||||
}
|
||||
|
||||
ctx := req.Raw().Context()
|
||||
ctx, span := tracer.Start(ctx, "HTTP "+req.Raw().Method, &tracing.SpanOptions{
|
||||
Kind: tracing.SpanKindClient,
|
||||
Attributes: attributes,
|
||||
})
|
||||
|
||||
defer func() {
|
||||
if resp != nil {
|
||||
span.SetAttributes(tracing.Attribute{Key: attrHTTPStatusCode, Value: resp.StatusCode})
|
||||
if resp.StatusCode > 399 {
|
||||
span.SetStatus(tracing.SpanStatusError, resp.Status)
|
||||
}
|
||||
if reqID := resp.Header.Get(shared.HeaderXMSRequestID); reqID != "" {
|
||||
span.SetAttributes(tracing.Attribute{Key: attrAZServiceReqID, Value: reqID})
|
||||
}
|
||||
} else if err != nil {
|
||||
var urlErr *url.Error
|
||||
if errors.As(err, &urlErr) {
|
||||
// calling *url.Error.Error() will include the unsanitized URL
|
||||
// which we don't want. in addition, we already have the HTTP verb
|
||||
// and sanitized URL in the trace so we aren't losing any info
|
||||
err = urlErr.Err
|
||||
}
|
||||
span.SetStatus(tracing.SpanStatusError, err.Error())
|
||||
}
|
||||
span.End()
|
||||
}()
|
||||
|
||||
req = req.WithContext(ctx)
|
||||
}
|
||||
resp, err = req.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// StartSpanOptions contains the optional values for StartSpan.
|
||||
type StartSpanOptions struct {
|
||||
// for future expansion
|
||||
}
|
||||
|
||||
// StartSpan starts a new tracing span.
|
||||
// You must call the returned func to terminate the span. Pass the applicable error
|
||||
// if the span will exit with an error condition.
|
||||
// - ctx is the parent context of the newly created context
|
||||
// - name is the name of the span. this is typically the fully qualified name of an API ("Client.Method")
|
||||
// - tracer is the client's Tracer for creating spans
|
||||
// - options contains optional values. pass nil to accept any default values
|
||||
func StartSpan(ctx context.Context, name string, tracer tracing.Tracer, options *StartSpanOptions) (context.Context, func(error)) {
|
||||
if !tracer.Enabled() {
|
||||
return ctx, func(err error) {}
|
||||
}
|
||||
|
||||
// we MUST propagate the active tracer before returning so that the trace policy can access it
|
||||
ctx = context.WithValue(ctx, shared.CtxWithTracingTracer{}, tracer)
|
||||
|
||||
const newSpanKind = tracing.SpanKindInternal
|
||||
if activeSpan := ctx.Value(ctxActiveSpan{}); activeSpan != nil {
|
||||
// per the design guidelines, if a SDK method Foo() calls SDK method Bar(),
|
||||
// then the span for Bar() must be suppressed. however, if Bar() makes a REST
|
||||
// call, then Bar's HTTP span must be a child of Foo's span.
|
||||
// however, there is an exception to this rule. if the SDK method Foo() is a
|
||||
// messaging producer/consumer, and it takes a callback that's a SDK method
|
||||
// Bar(), then the span for Bar() must _not_ be suppressed.
|
||||
if kind := activeSpan.(tracing.SpanKind); kind == tracing.SpanKindClient || kind == tracing.SpanKindInternal {
|
||||
return ctx, func(err error) {}
|
||||
}
|
||||
}
|
||||
ctx, span := tracer.Start(ctx, name, &tracing.SpanOptions{
|
||||
Kind: newSpanKind,
|
||||
})
|
||||
ctx = context.WithValue(ctx, ctxActiveSpan{}, newSpanKind)
|
||||
return ctx, func(err error) {
|
||||
if err != nil {
|
||||
errType := strings.Replace(fmt.Sprintf("%T", err), "*exported.", "*azcore.", 1)
|
||||
span.SetStatus(tracing.SpanStatusError, fmt.Sprintf("%s:\n%s", errType, err.Error()))
|
||||
}
|
||||
span.End()
|
||||
}
|
||||
}
|
||||
|
||||
// ctxActiveSpan is used as a context key for indicating a SDK client span is in progress.
|
||||
type ctxActiveSpan struct{}
|
|
@ -20,7 +20,7 @@ func includeResponsePolicy(req *policy.Request) (*http.Response, error) {
|
|||
if resp == nil {
|
||||
return resp, err
|
||||
}
|
||||
if httpOutRaw := req.Raw().Context().Value(shared.CtxIncludeResponseKey{}); httpOutRaw != nil {
|
||||
if httpOutRaw := req.Raw().Context().Value(shared.CtxWithCaptureResponse{}); httpOutRaw != nil {
|
||||
httpOut := httpOutRaw.(**http.Response)
|
||||
*httpOut = resp
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ func includeResponsePolicy(req *policy.Request) (*http.Response, error) {
|
|||
|
||||
// WithCaptureResponse applies the HTTP response retrieval annotation to the parent context.
|
||||
// The resp parameter will contain the HTTP response after the request has completed.
|
||||
// Deprecated: use [policy.WithCaptureResponse] instead.
|
||||
func WithCaptureResponse(parent context.Context, resp **http.Response) context.Context {
|
||||
return context.WithValue(parent, shared.CtxIncludeResponseKey{}, resp)
|
||||
return policy.WithCaptureResponse(parent, resp)
|
||||
}
|
||||
|
|
64
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_key_credential.go
generated
vendored
Normal file
64
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_key_credential.go
generated
vendored
Normal file
|
@ -0,0 +1,64 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
)
|
||||
|
||||
// KeyCredentialPolicy authorizes requests with a [azcore.KeyCredential].
|
||||
type KeyCredentialPolicy struct {
|
||||
cred *exported.KeyCredential
|
||||
header string
|
||||
prefix string
|
||||
allowHTTP bool
|
||||
}
|
||||
|
||||
// KeyCredentialPolicyOptions contains the optional values configuring [KeyCredentialPolicy].
|
||||
type KeyCredentialPolicyOptions struct {
|
||||
// InsecureAllowCredentialWithHTTP enables authenticated requests over HTTP.
|
||||
// By default, authenticated requests to an HTTP endpoint are rejected by the client.
|
||||
// WARNING: setting this to true will allow sending the authentication key in clear text. Use with caution.
|
||||
InsecureAllowCredentialWithHTTP bool
|
||||
|
||||
// Prefix is used if the key requires a prefix before it's inserted into the HTTP request.
|
||||
Prefix string
|
||||
}
|
||||
|
||||
// NewKeyCredentialPolicy creates a new instance of [KeyCredentialPolicy].
|
||||
// - cred is the [azcore.KeyCredential] used to authenticate with the service
|
||||
// - header is the name of the HTTP request header in which the key is placed
|
||||
// - options contains optional configuration, pass nil to accept the default values
|
||||
func NewKeyCredentialPolicy(cred *exported.KeyCredential, header string, options *KeyCredentialPolicyOptions) *KeyCredentialPolicy {
|
||||
if options == nil {
|
||||
options = &KeyCredentialPolicyOptions{}
|
||||
}
|
||||
return &KeyCredentialPolicy{
|
||||
cred: cred,
|
||||
header: header,
|
||||
prefix: options.Prefix,
|
||||
allowHTTP: options.InsecureAllowCredentialWithHTTP,
|
||||
}
|
||||
}
|
||||
|
||||
// Do implementes the Do method on the [policy.Polilcy] interface.
|
||||
func (k *KeyCredentialPolicy) Do(req *policy.Request) (*http.Response, error) {
|
||||
// skip adding the authorization header if no KeyCredential was provided.
|
||||
// this prevents a panic that might be hard to diagnose and allows testing
|
||||
// against http endpoints that don't require authentication.
|
||||
if k.cred != nil {
|
||||
if err := checkHTTPSForAuth(req, k.allowHTTP); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
val := exported.KeyCredentialGet(k.cred)
|
||||
if k.prefix != "" {
|
||||
val = k.prefix + val
|
||||
}
|
||||
req.Raw().Header.Add(k.header, val)
|
||||
}
|
||||
return req.Next()
|
||||
}
|
3
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_logging.go
generated
vendored
3
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_logging.go
generated
vendored
|
@ -191,7 +191,8 @@ func (p *logPolicy) writeHeader(b *bytes.Buffer, header http.Header) {
|
|||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
value := header.Get(k)
|
||||
// don't use Get() as it will canonicalize k which might cause a mismatch
|
||||
value := header[k][0]
|
||||
// redact all header values not in the allow-list
|
||||
if _, ok := p.allowedHeaders[strings.ToLower(k)]; !ok {
|
||||
value = redactedValue
|
||||
|
|
16
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_retry.go
generated
vendored
16
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_retry.go
generated
vendored
|
@ -59,15 +59,7 @@ func setDefaults(o *policy.RetryOptions) {
|
|||
}
|
||||
|
||||
func calcDelay(o policy.RetryOptions, try int32) time.Duration { // try is >=1; never 0
|
||||
pow := func(number int64, exponent int32) int64 { // pow is nested helper function
|
||||
var result int64 = 1
|
||||
for n := int32(0); n < exponent; n++ {
|
||||
result *= number
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
delay := time.Duration(pow(2, try)-1) * o.RetryDelay
|
||||
delay := time.Duration((1<<try)-1) * o.RetryDelay
|
||||
|
||||
// Introduce some jitter: [0.0, 1.0) / 2 = [0.0, 0.5) + 0.8 = [0.8, 1.3)
|
||||
delay = time.Duration(delay.Seconds() * (rand.Float64()/2 + 0.8) * float64(time.Second)) // NOTE: We want math/rand; not crypto/rand
|
||||
|
@ -125,7 +117,8 @@ func (p *retryPolicy) Do(req *policy.Request) (resp *http.Response, err error) {
|
|||
}
|
||||
|
||||
if options.TryTimeout == 0 {
|
||||
resp, err = req.Next()
|
||||
clone := req.Clone(req.Raw().Context())
|
||||
resp, err = clone.Next()
|
||||
} else {
|
||||
// Set the per-try time for this particular retry operation and then Do the operation.
|
||||
tryCtx, tryCancel := context.WithTimeout(req.Raw().Context(), options.TryTimeout)
|
||||
|
@ -208,8 +201,9 @@ func (p *retryPolicy) Do(req *policy.Request) (resp *http.Response, err error) {
|
|||
|
||||
// WithRetryOptions adds the specified RetryOptions to the parent context.
|
||||
// Use this to specify custom RetryOptions at the API-call level.
|
||||
// Deprecated: use [policy.WithRetryOptions] instead.
|
||||
func WithRetryOptions(parent context.Context, options policy.RetryOptions) context.Context {
|
||||
return context.WithValue(parent, shared.CtxWithRetryOptionsKey{}, options)
|
||||
return policy.WithRetryOptions(parent, options)
|
||||
}
|
||||
|
||||
// ********** The following type/methods implement the retryableRequestBody (a ReadSeekCloser)
|
||||
|
|
55
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_sas_credential.go
generated
vendored
Normal file
55
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_sas_credential.go
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
)
|
||||
|
||||
// SASCredentialPolicy authorizes requests with a [azcore.SASCredential].
|
||||
type SASCredentialPolicy struct {
|
||||
cred *exported.SASCredential
|
||||
header string
|
||||
allowHTTP bool
|
||||
}
|
||||
|
||||
// SASCredentialPolicyOptions contains the optional values configuring [SASCredentialPolicy].
|
||||
type SASCredentialPolicyOptions struct {
|
||||
// InsecureAllowCredentialWithHTTP enables authenticated requests over HTTP.
|
||||
// By default, authenticated requests to an HTTP endpoint are rejected by the client.
|
||||
// WARNING: setting this to true will allow sending the authentication key in clear text. Use with caution.
|
||||
InsecureAllowCredentialWithHTTP bool
|
||||
}
|
||||
|
||||
// NewSASCredentialPolicy creates a new instance of [SASCredentialPolicy].
|
||||
// - cred is the [azcore.SASCredential] used to authenticate with the service
|
||||
// - header is the name of the HTTP request header in which the shared access signature is placed
|
||||
// - options contains optional configuration, pass nil to accept the default values
|
||||
func NewSASCredentialPolicy(cred *exported.SASCredential, header string, options *SASCredentialPolicyOptions) *SASCredentialPolicy {
|
||||
if options == nil {
|
||||
options = &SASCredentialPolicyOptions{}
|
||||
}
|
||||
return &SASCredentialPolicy{
|
||||
cred: cred,
|
||||
header: header,
|
||||
allowHTTP: options.InsecureAllowCredentialWithHTTP,
|
||||
}
|
||||
}
|
||||
|
||||
// Do implementes the Do method on the [policy.Polilcy] interface.
|
||||
func (k *SASCredentialPolicy) Do(req *policy.Request) (*http.Response, error) {
|
||||
// skip adding the authorization header if no SASCredential was provided.
|
||||
// this prevents a panic that might be hard to diagnose and allows testing
|
||||
// against http endpoints that don't require authentication.
|
||||
if k.cred != nil {
|
||||
if err := checkHTTPSForAuth(req, k.allowHTTP); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Raw().Header.Add(k.header, exported.SASCredentialGet(k.cred))
|
||||
}
|
||||
return req.Next()
|
||||
}
|
4
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_telemetry.go
generated
vendored
4
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/policy_telemetry.go
generated
vendored
|
@ -43,6 +43,10 @@ func NewTelemetryPolicy(mod, ver string, o *policy.TelemetryOptions) policy.Poli
|
|||
b.WriteString(o.ApplicationID)
|
||||
b.WriteRune(' ')
|
||||
}
|
||||
// mod might be the fully qualified name. in that case, we just want the package name
|
||||
if i := strings.LastIndex(mod, "/"); i > -1 {
|
||||
mod = mod[i+1:]
|
||||
}
|
||||
b.WriteString(formatTelemetry(mod, ver))
|
||||
b.WriteRune(' ')
|
||||
b.WriteString(platformInfo)
|
||||
|
|
112
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/poller.go
generated
vendored
112
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/poller.go
generated
vendored
|
@ -13,6 +13,8 @@ import (
|
|||
"flag"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
|
||||
|
@ -20,9 +22,11 @@ import (
|
|||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/async"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/body"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/fake"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/loc"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/op"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/tracing"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/internal/poller"
|
||||
)
|
||||
|
||||
|
@ -54,6 +58,9 @@ type NewPollerOptions[T any] struct {
|
|||
|
||||
// Handler[T] contains a custom polling implementation.
|
||||
Handler PollingHandler[T]
|
||||
|
||||
// Tracer contains the Tracer from the client that's creating the Poller.
|
||||
Tracer tracing.Tracer
|
||||
}
|
||||
|
||||
// NewPoller creates a Poller based on the provided initial response.
|
||||
|
@ -70,6 +77,7 @@ func NewPoller[T any](resp *http.Response, pl exported.Pipeline, options *NewPol
|
|||
op: options.Handler,
|
||||
resp: resp,
|
||||
result: result,
|
||||
tracer: options.Tracer,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -83,7 +91,9 @@ func NewPoller[T any](resp *http.Response, pl exported.Pipeline, options *NewPol
|
|||
// determine the polling method
|
||||
var opr PollingHandler[T]
|
||||
var err error
|
||||
if async.Applicable(resp) {
|
||||
if fake.Applicable(resp) {
|
||||
opr, err = fake.New[T](pl, resp)
|
||||
} else if async.Applicable(resp) {
|
||||
// async poller must be checked first as it can also have a location header
|
||||
opr, err = async.New[T](pl, resp, options.FinalStateVia)
|
||||
} else if op.Applicable(resp) {
|
||||
|
@ -110,6 +120,7 @@ func NewPoller[T any](resp *http.Response, pl exported.Pipeline, options *NewPol
|
|||
op: opr,
|
||||
resp: resp,
|
||||
result: result,
|
||||
tracer: options.Tracer,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -121,6 +132,9 @@ type NewPollerFromResumeTokenOptions[T any] struct {
|
|||
|
||||
// Handler[T] contains a custom polling implementation.
|
||||
Handler PollingHandler[T]
|
||||
|
||||
// Tracer contains the Tracer from the client that's creating the Poller.
|
||||
Tracer tracing.Tracer
|
||||
}
|
||||
|
||||
// NewPollerFromResumeToken creates a Poller from a resume token string.
|
||||
|
@ -140,14 +154,16 @@ func NewPollerFromResumeToken[T any](token string, pl exported.Pipeline, options
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var asJSON map[string]interface{}
|
||||
var asJSON map[string]any
|
||||
if err := json.Unmarshal(raw, &asJSON); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
opr := options.Handler
|
||||
// now rehydrate the poller based on the encoded poller type
|
||||
if opr != nil {
|
||||
if fake.CanResume(asJSON) {
|
||||
opr, _ = fake.New[T](pl, nil)
|
||||
} else if opr != nil {
|
||||
log.Writef(log.EventLRO, "Resuming custom poller %T.", opr)
|
||||
} else if async.CanResume(asJSON) {
|
||||
opr, _ = async.New[T](pl, nil, "")
|
||||
|
@ -166,6 +182,7 @@ func NewPollerFromResumeToken[T any](token string, pl exported.Pipeline, options
|
|||
return &Poller[T]{
|
||||
op: opr,
|
||||
result: result,
|
||||
tracer: options.Tracer,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -188,6 +205,7 @@ type Poller[T any] struct {
|
|||
resp *http.Response
|
||||
err error
|
||||
result *T
|
||||
tracer tracing.Tracer
|
||||
done bool
|
||||
}
|
||||
|
||||
|
@ -203,7 +221,7 @@ type PollUntilDoneOptions struct {
|
|||
// options: pass nil to accept the default values.
|
||||
// NOTE: the default polling frequency is 30 seconds which works well for most operations. However, some operations might
|
||||
// benefit from a shorter or longer duration.
|
||||
func (p *Poller[T]) PollUntilDone(ctx context.Context, options *PollUntilDoneOptions) (T, error) {
|
||||
func (p *Poller[T]) PollUntilDone(ctx context.Context, options *PollUntilDoneOptions) (res T, err error) {
|
||||
if options == nil {
|
||||
options = &PollUntilDoneOptions{}
|
||||
}
|
||||
|
@ -212,13 +230,17 @@ func (p *Poller[T]) PollUntilDone(ctx context.Context, options *PollUntilDoneOpt
|
|||
cp.Frequency = 30 * time.Second
|
||||
}
|
||||
|
||||
ctx, endSpan := StartSpan(ctx, fmt.Sprintf("%s.PollUntilDone", shortenTypeName(reflect.TypeOf(*p).Name())), p.tracer, nil)
|
||||
defer func() { endSpan(err) }()
|
||||
|
||||
// skip the floor check when executing tests so they don't take so long
|
||||
if isTest := flag.Lookup("test.v"); isTest == nil && cp.Frequency < time.Second {
|
||||
return *new(T), errors.New("polling frequency minimum is one second")
|
||||
err = errors.New("polling frequency minimum is one second")
|
||||
return
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
logPollUntilDoneExit := func(v interface{}) {
|
||||
logPollUntilDoneExit := func(v any) {
|
||||
log.Writef(log.EventLRO, "END PollUntilDone() for %T: %v, total time: %s", p.op, v, time.Since(start))
|
||||
}
|
||||
log.Writef(log.EventLRO, "BEGIN PollUntilDone() for %T", p.op)
|
||||
|
@ -226,22 +248,24 @@ func (p *Poller[T]) PollUntilDone(ctx context.Context, options *PollUntilDoneOpt
|
|||
// initial check for a retry-after header existing on the initial response
|
||||
if retryAfter := shared.RetryAfter(p.resp); retryAfter > 0 {
|
||||
log.Writef(log.EventLRO, "initial Retry-After delay for %s", retryAfter.String())
|
||||
if err := shared.Delay(ctx, retryAfter); err != nil {
|
||||
if err = shared.Delay(ctx, retryAfter); err != nil {
|
||||
logPollUntilDoneExit(err)
|
||||
return *new(T), err
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
// begin polling the endpoint until a terminal state is reached
|
||||
for {
|
||||
resp, err := p.Poll(ctx)
|
||||
var resp *http.Response
|
||||
resp, err = p.Poll(ctx)
|
||||
if err != nil {
|
||||
logPollUntilDoneExit(err)
|
||||
return *new(T), err
|
||||
return
|
||||
}
|
||||
if p.Done() {
|
||||
logPollUntilDoneExit("succeeded")
|
||||
return p.Result(ctx)
|
||||
res, err = p.Result(ctx)
|
||||
return
|
||||
}
|
||||
d := cp.Frequency
|
||||
if retryAfter := shared.RetryAfter(resp); retryAfter > 0 {
|
||||
|
@ -252,7 +276,7 @@ func (p *Poller[T]) PollUntilDone(ctx context.Context, options *PollUntilDoneOpt
|
|||
}
|
||||
if err = shared.Delay(ctx, d); err != nil {
|
||||
logPollUntilDoneExit(err)
|
||||
return *new(T), err
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -261,17 +285,22 @@ func (p *Poller[T]) PollUntilDone(ctx context.Context, options *PollUntilDoneOpt
|
|||
// If Poll succeeds, the poller's state is updated and the HTTP response is returned.
|
||||
// If Poll fails, the poller's state is unmodified and the error is returned.
|
||||
// Calling Poll on an LRO that has reached a terminal state will return the last HTTP response.
|
||||
func (p *Poller[T]) Poll(ctx context.Context) (*http.Response, error) {
|
||||
func (p *Poller[T]) Poll(ctx context.Context) (resp *http.Response, err error) {
|
||||
if p.Done() {
|
||||
// the LRO has reached a terminal state, don't poll again
|
||||
return p.resp, nil
|
||||
resp = p.resp
|
||||
return
|
||||
}
|
||||
resp, err := p.op.Poll(ctx)
|
||||
|
||||
ctx, endSpan := StartSpan(ctx, fmt.Sprintf("%s.Poll", shortenTypeName(reflect.TypeOf(*p).Name())), p.tracer, nil)
|
||||
defer func() { endSpan(err) }()
|
||||
|
||||
resp, err = p.op.Poll(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return
|
||||
}
|
||||
p.resp = resp
|
||||
return p.resp, nil
|
||||
return
|
||||
}
|
||||
|
||||
// Done returns true if the LRO has reached a terminal state.
|
||||
|
@ -284,31 +313,45 @@ func (p *Poller[T]) Done() bool {
|
|||
// If the LRO completed successfully, a populated instance of T is returned.
|
||||
// If the LRO failed or was canceled, an *azcore.ResponseError error is returned.
|
||||
// Calling this on an LRO in a non-terminal state will return an error.
|
||||
func (p *Poller[T]) Result(ctx context.Context) (T, error) {
|
||||
func (p *Poller[T]) Result(ctx context.Context) (res T, err error) {
|
||||
if !p.Done() {
|
||||
return *new(T), errors.New("poller is in a non-terminal state")
|
||||
err = errors.New("poller is in a non-terminal state")
|
||||
return
|
||||
}
|
||||
if p.done {
|
||||
// the result has already been retrieved, return the cached value
|
||||
if p.err != nil {
|
||||
return *new(T), p.err
|
||||
err = p.err
|
||||
return
|
||||
}
|
||||
return *p.result, nil
|
||||
res = *p.result
|
||||
return
|
||||
}
|
||||
err := p.op.Result(ctx, p.result)
|
||||
|
||||
ctx, endSpan := StartSpan(ctx, fmt.Sprintf("%s.Result", shortenTypeName(reflect.TypeOf(*p).Name())), p.tracer, nil)
|
||||
defer func() { endSpan(err) }()
|
||||
|
||||
err = p.op.Result(ctx, p.result)
|
||||
var respErr *exported.ResponseError
|
||||
if errors.As(err, &respErr) {
|
||||
if pollers.IsNonTerminalHTTPStatusCode(respErr.RawResponse) {
|
||||
// the request failed in a non-terminal way.
|
||||
// don't cache the error or mark the Poller as done
|
||||
return
|
||||
}
|
||||
// the LRO failed. record the error
|
||||
p.err = err
|
||||
} else if err != nil {
|
||||
// the call to Result failed, don't cache anything in this case
|
||||
return *new(T), err
|
||||
return
|
||||
}
|
||||
p.done = true
|
||||
if p.err != nil {
|
||||
return *new(T), p.err
|
||||
err = p.err
|
||||
return
|
||||
}
|
||||
return *p.result, nil
|
||||
res = *p.result
|
||||
return
|
||||
}
|
||||
|
||||
// ResumeToken returns a value representing the poller that can be used to resume
|
||||
|
@ -325,3 +368,22 @@ func (p *Poller[T]) ResumeToken() (string, error) {
|
|||
}
|
||||
return tk, err
|
||||
}
|
||||
|
||||
// extracts the type name from the string returned from reflect.Value.Name()
|
||||
func shortenTypeName(s string) string {
|
||||
// the value is formatted as follows
|
||||
// Poller[module/Package.Type].Method
|
||||
// we want to shorten the generic type parameter string to Type
|
||||
// anything we don't recognize will be left as-is
|
||||
begin := strings.Index(s, "[")
|
||||
end := strings.Index(s, "]")
|
||||
if begin == -1 || end == -1 {
|
||||
return s
|
||||
}
|
||||
|
||||
typeName := s[begin+1 : end]
|
||||
if i := strings.LastIndex(typeName, "."); i > -1 {
|
||||
typeName = typeName[i+1:]
|
||||
}
|
||||
return s[:begin+1] + typeName + s[end:]
|
||||
}
|
||||
|
|
225
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/request.go
generated
vendored
225
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/request.go
generated
vendored
|
@ -9,33 +9,33 @@ package runtime
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"net/textproto"
|
||||
"net/url"
|
||||
"path"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming"
|
||||
)
|
||||
|
||||
// Base64Encoding is usesd to specify which base-64 encoder/decoder to use when
|
||||
// encoding/decoding a slice of bytes to/from a string.
|
||||
type Base64Encoding int
|
||||
type Base64Encoding = exported.Base64Encoding
|
||||
|
||||
const (
|
||||
// Base64StdFormat uses base64.StdEncoding for encoding and decoding payloads.
|
||||
Base64StdFormat Base64Encoding = 0
|
||||
Base64StdFormat Base64Encoding = exported.Base64StdFormat
|
||||
|
||||
// Base64URLFormat uses base64.RawURLEncoding for encoding and decoding payloads.
|
||||
Base64URLFormat Base64Encoding = 1
|
||||
Base64URLFormat Base64Encoding = exported.Base64URLFormat
|
||||
)
|
||||
|
||||
// NewRequest creates a new policy.Request with the specified input.
|
||||
|
@ -44,6 +44,26 @@ func NewRequest(ctx context.Context, httpMethod string, endpoint string) (*polic
|
|||
return exported.NewRequest(ctx, httpMethod, endpoint)
|
||||
}
|
||||
|
||||
// EncodeQueryParams will parse and encode any query parameters in the specified URL.
|
||||
// Any semicolons will automatically be escaped.
|
||||
func EncodeQueryParams(u string) (string, error) {
|
||||
before, after, found := strings.Cut(u, "?")
|
||||
if !found {
|
||||
return u, nil
|
||||
}
|
||||
// starting in Go 1.17, url.ParseQuery will reject semicolons in query params.
|
||||
// so, we must escape them first. note that this assumes that semicolons aren't
|
||||
// being used as query param separators which is per the current RFC.
|
||||
// for more info:
|
||||
// https://github.com/golang/go/issues/25192
|
||||
// https://github.com/golang/go/issues/50034
|
||||
qp, err := url.ParseQuery(strings.ReplaceAll(after, ";", "%3B"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return before + "?" + qp.Encode(), nil
|
||||
}
|
||||
|
||||
// JoinPaths concatenates multiple URL path segments into one path,
|
||||
// inserting path separation characters as required. JoinPaths will preserve
|
||||
// query parameters in the root path
|
||||
|
@ -79,10 +99,7 @@ func JoinPaths(root string, paths ...string) string {
|
|||
|
||||
// EncodeByteArray will base-64 encode the byte slice v.
|
||||
func EncodeByteArray(v []byte, format Base64Encoding) string {
|
||||
if format == Base64URLFormat {
|
||||
return base64.RawURLEncoding.EncodeToString(v)
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(v)
|
||||
return exported.EncodeByteArray(v, format)
|
||||
}
|
||||
|
||||
// MarshalAsByteArray will base-64 encode the byte slice v, then calls SetBody.
|
||||
|
@ -90,23 +107,22 @@ func EncodeByteArray(v []byte, format Base64Encoding) string {
|
|||
func MarshalAsByteArray(req *policy.Request, v []byte, format Base64Encoding) error {
|
||||
// send as a JSON string
|
||||
encode := fmt.Sprintf("\"%s\"", EncodeByteArray(v, format))
|
||||
return req.SetBody(exported.NopCloser(strings.NewReader(encode)), shared.ContentTypeAppJSON)
|
||||
// tsp generated code can set Content-Type so we must prefer that
|
||||
return exported.SetBody(req, exported.NopCloser(strings.NewReader(encode)), shared.ContentTypeAppJSON, false)
|
||||
}
|
||||
|
||||
// MarshalAsJSON calls json.Marshal() to get the JSON encoding of v then calls SetBody.
|
||||
func MarshalAsJSON(req *policy.Request, v interface{}) error {
|
||||
if omit := os.Getenv("AZURE_SDK_GO_OMIT_READONLY"); omit == "true" {
|
||||
v = cloneWithoutReadOnlyFields(v)
|
||||
}
|
||||
func MarshalAsJSON(req *policy.Request, v any) error {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error marshalling type %T: %s", v, err)
|
||||
}
|
||||
return req.SetBody(exported.NopCloser(bytes.NewReader(b)), shared.ContentTypeAppJSON)
|
||||
// tsp generated code can set Content-Type so we must prefer that
|
||||
return exported.SetBody(req, exported.NopCloser(bytes.NewReader(b)), shared.ContentTypeAppJSON, false)
|
||||
}
|
||||
|
||||
// MarshalAsXML calls xml.Marshal() to get the XML encoding of v then calls SetBody.
|
||||
func MarshalAsXML(req *policy.Request, v interface{}) error {
|
||||
func MarshalAsXML(req *policy.Request, v any) error {
|
||||
b, err := xml.Marshal(v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error marshalling type %T: %s", v, err)
|
||||
|
@ -116,10 +132,10 @@ func MarshalAsXML(req *policy.Request, v interface{}) error {
|
|||
return req.SetBody(exported.NopCloser(bytes.NewReader(b)), shared.ContentTypeAppXML)
|
||||
}
|
||||
|
||||
// SetMultipartFormData writes the specified keys/values as multi-part form
|
||||
// fields with the specified value. File content must be specified as a ReadSeekCloser.
|
||||
// All other values are treated as string values.
|
||||
func SetMultipartFormData(req *policy.Request, formData map[string]interface{}) error {
|
||||
// SetMultipartFormData writes the specified keys/values as multi-part form fields with the specified value.
|
||||
// File content must be specified as an [io.ReadSeekCloser] or [streaming.MultipartContent].
|
||||
// Byte slices will be treated as JSON. All other values are treated as string values.
|
||||
func SetMultipartFormData(req *policy.Request, formData map[string]any) error {
|
||||
body := bytes.Buffer{}
|
||||
writer := multipart.NewWriter(&body)
|
||||
|
||||
|
@ -135,6 +151,60 @@ func SetMultipartFormData(req *policy.Request, formData map[string]interface{})
|
|||
return nil
|
||||
}
|
||||
|
||||
quoteEscaper := strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
|
||||
|
||||
writeMultipartContent := func(fieldname string, mpc streaming.MultipartContent) error {
|
||||
if mpc.Body == nil {
|
||||
return errors.New("streaming.MultipartContent.Body cannot be nil")
|
||||
}
|
||||
|
||||
// use fieldname for the file name when unspecified
|
||||
filename := fieldname
|
||||
|
||||
if mpc.ContentType == "" && mpc.Filename == "" {
|
||||
return writeContent(fieldname, filename, mpc.Body)
|
||||
}
|
||||
if mpc.Filename != "" {
|
||||
filename = mpc.Filename
|
||||
}
|
||||
// this is pretty much copied from multipart.Writer.CreateFormFile
|
||||
// but lets us set the caller provided Content-Type and filename
|
||||
h := make(textproto.MIMEHeader)
|
||||
h.Set("Content-Disposition",
|
||||
fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
|
||||
quoteEscaper.Replace(fieldname), quoteEscaper.Replace(filename)))
|
||||
contentType := "application/octet-stream"
|
||||
if mpc.ContentType != "" {
|
||||
contentType = mpc.ContentType
|
||||
}
|
||||
h.Set("Content-Type", contentType)
|
||||
fd, err := writer.CreatePart(h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// copy the data to the form file
|
||||
if _, err = io.Copy(fd, mpc.Body); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// the same as multipart.Writer.WriteField but lets us specify the Content-Type
|
||||
writeField := func(fieldname, contentType string, value string) error {
|
||||
h := make(textproto.MIMEHeader)
|
||||
h.Set("Content-Disposition",
|
||||
fmt.Sprintf(`form-data; name="%s"`, quoteEscaper.Replace(fieldname)))
|
||||
h.Set("Content-Type", contentType)
|
||||
fd, err := writer.CreatePart(h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = fd.Write([]byte(value)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
for k, v := range formData {
|
||||
if rsc, ok := v.(io.ReadSeekCloser); ok {
|
||||
if err := writeContent(k, k, rsc); err != nil {
|
||||
|
@ -148,13 +218,35 @@ func SetMultipartFormData(req *policy.Request, formData map[string]interface{})
|
|||
}
|
||||
}
|
||||
continue
|
||||
} else if mpc, ok := v.(streaming.MultipartContent); ok {
|
||||
if err := writeMultipartContent(k, mpc); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
} else if mpcs, ok := v.([]streaming.MultipartContent); ok {
|
||||
for _, mpc := range mpcs {
|
||||
if err := writeMultipartContent(k, mpc); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
// ensure the value is in string format
|
||||
s, ok := v.(string)
|
||||
if !ok {
|
||||
s = fmt.Sprintf("%v", v)
|
||||
|
||||
var content string
|
||||
contentType := shared.ContentTypeTextPlain
|
||||
switch tt := v.(type) {
|
||||
case []byte:
|
||||
// JSON, don't quote it
|
||||
content = string(tt)
|
||||
contentType = shared.ContentTypeAppJSON
|
||||
case string:
|
||||
content = tt
|
||||
default:
|
||||
// ensure the value is in string format
|
||||
content = fmt.Sprintf("%v", v)
|
||||
}
|
||||
if err := writer.WriteField(k, s); err != nil {
|
||||
|
||||
if err := writeField(k, contentType, content); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -169,80 +261,5 @@ func SkipBodyDownload(req *policy.Request) {
|
|||
req.SetOperationValue(bodyDownloadPolicyOpValues{Skip: true})
|
||||
}
|
||||
|
||||
// returns a clone of the object graph pointed to by v, omitting values of all read-only
|
||||
// fields. if there are no read-only fields in the object graph, no clone is created.
|
||||
func cloneWithoutReadOnlyFields(v interface{}) interface{} {
|
||||
val := reflect.Indirect(reflect.ValueOf(v))
|
||||
if val.Kind() != reflect.Struct {
|
||||
// not a struct, skip
|
||||
return v
|
||||
}
|
||||
// first walk the graph to find any R/O fields.
|
||||
// if there aren't any, skip cloning the graph.
|
||||
if !recursiveFindReadOnlyField(val) {
|
||||
return v
|
||||
}
|
||||
return recursiveCloneWithoutReadOnlyFields(val)
|
||||
}
|
||||
|
||||
// returns true if any field in the object graph of val contains the `azure:"ro"` tag value
|
||||
func recursiveFindReadOnlyField(val reflect.Value) bool {
|
||||
t := val.Type()
|
||||
// iterate over the fields, looking for the "azure" tag.
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
aztag := field.Tag.Get("azure")
|
||||
if azureTagIsReadOnly(aztag) {
|
||||
return true
|
||||
} else if reflect.Indirect(val.Field(i)).Kind() == reflect.Struct && recursiveFindReadOnlyField(reflect.Indirect(val.Field(i))) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// clones the object graph of val. all non-R/O properties are copied to the clone
|
||||
func recursiveCloneWithoutReadOnlyFields(val reflect.Value) interface{} {
|
||||
t := val.Type()
|
||||
clone := reflect.New(t)
|
||||
// iterate over the fields, looking for the "azure" tag.
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
aztag := field.Tag.Get("azure")
|
||||
if azureTagIsReadOnly(aztag) {
|
||||
// omit from payload
|
||||
continue
|
||||
}
|
||||
// clone field will receive the same value as the source field...
|
||||
value := val.Field(i)
|
||||
v := reflect.Indirect(value)
|
||||
if v.IsValid() && v.Type() != reflect.TypeOf(time.Time{}) && v.Kind() == reflect.Struct {
|
||||
// ...unless the source value is a struct, in which case we recurse to clone that struct.
|
||||
// (We can't recursively clone time.Time because it contains unexported fields.)
|
||||
c := recursiveCloneWithoutReadOnlyFields(v)
|
||||
if field.Anonymous {
|
||||
// NOTE: this does not handle the case of embedded fields of unexported struct types.
|
||||
// this should be ok as we don't generate any code like this at present
|
||||
value = reflect.Indirect(reflect.ValueOf(c))
|
||||
} else {
|
||||
value = reflect.ValueOf(c)
|
||||
}
|
||||
}
|
||||
reflect.Indirect(clone).Field(i).Set(value)
|
||||
}
|
||||
return clone.Interface()
|
||||
}
|
||||
|
||||
// returns true if the "azure" tag contains the option "ro"
|
||||
func azureTagIsReadOnly(tag string) bool {
|
||||
if tag == "" {
|
||||
return false
|
||||
}
|
||||
parts := strings.Split(tag, ",")
|
||||
for _, part := range parts {
|
||||
if part == "ro" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
// CtxAPINameKey is used as a context key for adding/retrieving the API name.
|
||||
type CtxAPINameKey = shared.CtxAPINameKey
|
||||
|
|
34
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/response.go
generated
vendored
34
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/response.go
generated
vendored
|
@ -8,13 +8,13 @@ package runtime
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
azexported "github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/internal/exported"
|
||||
)
|
||||
|
||||
|
@ -40,7 +40,7 @@ func UnmarshalAsByteArray(resp *http.Response, v *[]byte, format Base64Encoding)
|
|||
}
|
||||
|
||||
// UnmarshalAsJSON calls json.Unmarshal() to unmarshal the received payload into the value pointed to by v.
|
||||
func UnmarshalAsJSON(resp *http.Response, v interface{}) error {
|
||||
func UnmarshalAsJSON(resp *http.Response, v any) error {
|
||||
payload, err := Payload(resp)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -61,7 +61,7 @@ func UnmarshalAsJSON(resp *http.Response, v interface{}) error {
|
|||
}
|
||||
|
||||
// UnmarshalAsXML calls xml.Unmarshal() to unmarshal the received payload into the value pointed to by v.
|
||||
func UnmarshalAsXML(resp *http.Response, v interface{}) error {
|
||||
func UnmarshalAsXML(resp *http.Response, v any) error {
|
||||
payload, err := Payload(resp)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -105,31 +105,5 @@ func removeBOM(resp *http.Response) error {
|
|||
|
||||
// DecodeByteArray will base-64 decode the provided string into v.
|
||||
func DecodeByteArray(s string, v *[]byte, format Base64Encoding) error {
|
||||
if len(s) == 0 {
|
||||
return nil
|
||||
}
|
||||
payload := string(s)
|
||||
if payload[0] == '"' {
|
||||
// remove surrounding quotes
|
||||
payload = payload[1 : len(payload)-1]
|
||||
}
|
||||
switch format {
|
||||
case Base64StdFormat:
|
||||
decoded, err := base64.StdEncoding.DecodeString(payload)
|
||||
if err == nil {
|
||||
*v = decoded
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
case Base64URLFormat:
|
||||
// use raw encoding as URL format should not contain any '=' characters
|
||||
decoded, err := base64.RawURLEncoding.DecodeString(payload)
|
||||
if err == nil {
|
||||
*v = decoded
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
default:
|
||||
return fmt.Errorf("unrecognized byte array format: %d", format)
|
||||
}
|
||||
return azexported.DecodeByteArray(s, v, format)
|
||||
}
|
||||
|
|
15
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/transport_default_dialer_other.go
generated
vendored
Normal file
15
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/transport_default_dialer_other.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
//go:build !wasm
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
)
|
||||
|
||||
func defaultTransportDialContext(dialer *net.Dialer) func(context.Context, string, string) (net.Conn, error) {
|
||||
return dialer.DialContext
|
||||
}
|
15
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/transport_default_dialer_wasm.go
generated
vendored
Normal file
15
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/transport_default_dialer_wasm.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
//go:build (js && wasm) || wasip1
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
)
|
||||
|
||||
func defaultTransportDialContext(dialer *net.Dialer) func(context.Context, string, string) (net.Conn, error) {
|
||||
return nil
|
||||
}
|
|
@ -11,6 +11,8 @@ import (
|
|||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
|
||||
var defaultHTTPClient *http.Client
|
||||
|
@ -18,19 +20,28 @@ var defaultHTTPClient *http.Client
|
|||
func init() {
|
||||
defaultTransport := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
DialContext: defaultTransportDialContext(&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).DialContext,
|
||||
}),
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 100,
|
||||
MaxIdleConnsPerHost: 10,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
TLSClientConfig: &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
MinVersion: tls.VersionTLS12,
|
||||
Renegotiation: tls.RenegotiateFreelyAsClient,
|
||||
},
|
||||
}
|
||||
// TODO: evaluate removing this once https://github.com/golang/go/issues/59690 has been fixed
|
||||
if http2Transport, err := http2.ConfigureTransports(defaultTransport); err == nil {
|
||||
// if the connection has been idle for 10 seconds, send a ping frame for a health check
|
||||
http2Transport.ReadIdleTimeout = 10 * time.Second
|
||||
// if there's no response to the ping within the timeout, the connection will be closed
|
||||
http2Transport.PingTimeout = 5 * time.Second
|
||||
}
|
||||
defaultHTTPClient = &http.Client{
|
||||
Transport: defaultTransport,
|
||||
}
|
||||
|
|
14
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming/progress.go
generated
vendored
14
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming/progress.go
generated
vendored
|
@ -73,3 +73,17 @@ func (p *progress) Seek(offset int64, whence int) (int64, error) {
|
|||
func (p *progress) Close() error {
|
||||
return p.rc.Close()
|
||||
}
|
||||
|
||||
// MultipartContent contains streaming content used in multipart/form payloads.
|
||||
type MultipartContent struct {
|
||||
// Body contains the required content body.
|
||||
Body io.ReadSeekCloser
|
||||
|
||||
// ContentType optionally specifies the HTTP Content-Type for this Body.
|
||||
// The default value is application/octet-stream.
|
||||
ContentType string
|
||||
|
||||
// Filename optionally specifies the filename for this Body.
|
||||
// The default value is the field name for the multipart/form section.
|
||||
Filename string
|
||||
}
|
||||
|
|
61
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/tracing/tracing.go
generated
vendored
61
vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/tracing/tracing.go
generated
vendored
|
@ -31,12 +31,12 @@ type Provider struct {
|
|||
newTracerFn func(name, version string) Tracer
|
||||
}
|
||||
|
||||
// NewTracer creates a new Tracer for the specified name and version.
|
||||
// - name - the name of the tracer object, typically the fully qualified name of the service client
|
||||
// - version - the version of the module in which the service client resides
|
||||
func (p Provider) NewTracer(name, version string) (tracer Tracer) {
|
||||
// NewTracer creates a new Tracer for the specified module name and version.
|
||||
// - module - the fully qualified name of the module
|
||||
// - version - the version of the module
|
||||
func (p Provider) NewTracer(module, version string) (tracer Tracer) {
|
||||
if p.newTracerFn != nil {
|
||||
tracer = p.newTracerFn(name, version)
|
||||
tracer = p.newTracerFn(module, version)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -45,21 +45,28 @@ func (p Provider) NewTracer(name, version string) (tracer Tracer) {
|
|||
|
||||
// TracerOptions contains the optional values when creating a Tracer.
|
||||
type TracerOptions struct {
|
||||
// for future expansion
|
||||
// SpanFromContext contains the implementation for the Tracer.SpanFromContext method.
|
||||
SpanFromContext func(context.Context) Span
|
||||
}
|
||||
|
||||
// NewTracer creates a Tracer with the specified values.
|
||||
// - newSpanFn is the underlying implementation for creating Span instances
|
||||
// - options contains optional values; pass nil to accept the default value
|
||||
func NewTracer(newSpanFn func(ctx context.Context, spanName string, options *SpanOptions) (context.Context, Span), options *TracerOptions) Tracer {
|
||||
if options == nil {
|
||||
options = &TracerOptions{}
|
||||
}
|
||||
return Tracer{
|
||||
newSpanFn: newSpanFn,
|
||||
newSpanFn: newSpanFn,
|
||||
spanFromContextFn: options.SpanFromContext,
|
||||
}
|
||||
}
|
||||
|
||||
// Tracer is the factory that creates Span instances.
|
||||
type Tracer struct {
|
||||
newSpanFn func(ctx context.Context, spanName string, options *SpanOptions) (context.Context, Span)
|
||||
attrs []Attribute
|
||||
newSpanFn func(ctx context.Context, spanName string, options *SpanOptions) (context.Context, Span)
|
||||
spanFromContextFn func(ctx context.Context) Span
|
||||
}
|
||||
|
||||
// Start creates a new span and a context.Context that contains it.
|
||||
|
@ -68,11 +75,37 @@ type Tracer struct {
|
|||
// - options contains optional values for the span, pass nil to accept any defaults
|
||||
func (t Tracer) Start(ctx context.Context, spanName string, options *SpanOptions) (context.Context, Span) {
|
||||
if t.newSpanFn != nil {
|
||||
return t.newSpanFn(ctx, spanName, options)
|
||||
opts := SpanOptions{}
|
||||
if options != nil {
|
||||
opts = *options
|
||||
}
|
||||
opts.Attributes = append(opts.Attributes, t.attrs...)
|
||||
return t.newSpanFn(ctx, spanName, &opts)
|
||||
}
|
||||
return ctx, Span{}
|
||||
}
|
||||
|
||||
// SetAttributes sets attrs to be applied to each Span. If a key from attrs
|
||||
// already exists for an attribute of the Span it will be overwritten with
|
||||
// the value contained in attrs.
|
||||
func (t *Tracer) SetAttributes(attrs ...Attribute) {
|
||||
t.attrs = append(t.attrs, attrs...)
|
||||
}
|
||||
|
||||
// Enabled returns true if this Tracer is capable of creating Spans.
|
||||
func (t Tracer) Enabled() bool {
|
||||
return t.newSpanFn != nil
|
||||
}
|
||||
|
||||
// SpanFromContext returns the Span associated with the current context.
|
||||
// If the provided context has no Span, false is returned.
|
||||
func (t Tracer) SpanFromContext(ctx context.Context) Span {
|
||||
if t.spanFromContextFn != nil {
|
||||
return t.spanFromContextFn(ctx)
|
||||
}
|
||||
return Span{}
|
||||
}
|
||||
|
||||
// SpanOptions contains optional settings for creating a span.
|
||||
type SpanOptions struct {
|
||||
// Kind indicates the kind of Span.
|
||||
|
@ -97,9 +130,6 @@ type SpanImpl struct {
|
|||
// AddEvent contains the implementation for the Span.AddEvent method.
|
||||
AddEvent func(string, ...Attribute)
|
||||
|
||||
// AddError contains the implementation for the Span.AddError method.
|
||||
AddError func(err error)
|
||||
|
||||
// SetStatus contains the implementation for the Span.SetStatus method.
|
||||
SetStatus func(SpanStatus, string)
|
||||
}
|
||||
|
@ -140,13 +170,6 @@ func (s Span) AddEvent(name string, attrs ...Attribute) {
|
|||
}
|
||||
}
|
||||
|
||||
// AddError adds the specified error event to the span.
|
||||
func (s Span) AddError(err error) {
|
||||
if s.impl.AddError != nil {
|
||||
s.impl.AddError(err)
|
||||
}
|
||||
}
|
||||
|
||||
// SetStatus sets the status on the span along with a description.
|
||||
func (s Span) SetStatus(code SpanStatus, desc string) {
|
||||
if s.impl.SetStatus != nil {
|
||||
|
|
4
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/.gitignore
generated
vendored
Normal file
4
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
# live test artifacts
|
||||
Dockerfile
|
||||
k8s.yaml
|
||||
sshkey*
|
150
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/CHANGELOG.md
generated
vendored
150
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/CHANGELOG.md
generated
vendored
|
@ -1,5 +1,147 @@
|
|||
# Release History
|
||||
|
||||
## 1.6.0 (2024-06-10)
|
||||
|
||||
### Features Added
|
||||
* `NewOnBehalfOfCredentialWithClientAssertions` creates an on-behalf-of credential
|
||||
that authenticates with client assertions such as federated credentials
|
||||
|
||||
### Breaking Changes
|
||||
> These changes affect only code written against a beta version such as v1.6.0-beta.4
|
||||
* Removed `AzurePipelinesCredential` and the persistent token caching API.
|
||||
They will return in v1.7.0-beta.1
|
||||
|
||||
### Bugs Fixed
|
||||
* Managed identity bug fixes
|
||||
|
||||
## 1.6.0-beta.4 (2024-05-14)
|
||||
|
||||
### Features Added
|
||||
* `AzurePipelinesCredential` authenticates an Azure Pipeline service connection with
|
||||
workload identity federation
|
||||
|
||||
## 1.6.0-beta.3 (2024-04-09)
|
||||
|
||||
### Breaking Changes
|
||||
* `DefaultAzureCredential` now sends a probe request with no retries for IMDS managed identity
|
||||
environments to avoid excessive retry delays when the IMDS endpoint is not available. This
|
||||
should improve credential chain resolution for local development scenarios.
|
||||
|
||||
### Bugs Fixed
|
||||
* `ManagedIdentityCredential` now specifies resource IDs correctly for Azure Container Instances
|
||||
|
||||
## 1.5.2 (2024-04-09)
|
||||
|
||||
### Bugs Fixed
|
||||
* `ManagedIdentityCredential` now specifies resource IDs correctly for Azure Container Instances
|
||||
|
||||
### Other Changes
|
||||
* Restored v1.4.0 error behavior for empty tenant IDs
|
||||
* Upgraded dependencies
|
||||
|
||||
## 1.6.0-beta.2 (2024-02-06)
|
||||
|
||||
### Breaking Changes
|
||||
> These changes affect only code written against a beta version such as v1.6.0-beta.1
|
||||
* Replaced `ErrAuthenticationRequired` with `AuthenticationRequiredError`, a struct
|
||||
type that carries the `TokenRequestOptions` passed to the `GetToken` call which
|
||||
returned the error.
|
||||
|
||||
### Bugs Fixed
|
||||
* Fixed more cases in which credential chains like `DefaultAzureCredential`
|
||||
should try their next credential after attempting managed identity
|
||||
authentication in a Docker Desktop container
|
||||
|
||||
### Other Changes
|
||||
* `AzureCLICredential` uses the CLI's `expires_on` value for token expiration
|
||||
|
||||
## 1.6.0-beta.1 (2024-01-17)
|
||||
|
||||
### Features Added
|
||||
* Restored persistent token caching API first added in v1.5.0-beta.1
|
||||
* Added `AzureCLICredentialOptions.Subscription`
|
||||
|
||||
## 1.5.1 (2024-01-17)
|
||||
|
||||
### Bugs Fixed
|
||||
* `InteractiveBrowserCredential` handles `AdditionallyAllowedTenants` correctly
|
||||
|
||||
## 1.5.0 (2024-01-16)
|
||||
|
||||
### Breaking Changes
|
||||
> These changes affect only code written against a beta version such as v1.5.0-beta.1
|
||||
* Removed persistent token caching. It will return in v1.6.0-beta.1
|
||||
|
||||
### Bugs Fixed
|
||||
* Credentials now preserve MSAL headers e.g. X-Client-Sku
|
||||
|
||||
### Other Changes
|
||||
* Upgraded dependencies
|
||||
|
||||
## 1.5.0-beta.2 (2023-11-07)
|
||||
|
||||
### Features Added
|
||||
* `DefaultAzureCredential` and `ManagedIdentityCredential` support Azure ML managed identity
|
||||
* Added spans for distributed tracing.
|
||||
|
||||
## 1.5.0-beta.1 (2023-10-10)
|
||||
|
||||
### Features Added
|
||||
* Optional persistent token caching for most credentials. Set `TokenCachePersistenceOptions`
|
||||
on a credential's options to enable and configure this. See the package documentation for
|
||||
this version and [TOKEN_CACHING.md](https://aka.ms/azsdk/go/identity/caching) for more
|
||||
details.
|
||||
* `AzureDeveloperCLICredential` authenticates with the Azure Developer CLI (`azd`). This
|
||||
credential is also part of the `DefaultAzureCredential` authentication flow.
|
||||
|
||||
## 1.4.0 (2023-10-10)
|
||||
|
||||
### Bugs Fixed
|
||||
* `ManagedIdentityCredential` will now retry when IMDS responds 410 or 503
|
||||
|
||||
## 1.4.0-beta.5 (2023-09-12)
|
||||
|
||||
### Features Added
|
||||
* Service principal credentials can request CAE tokens
|
||||
|
||||
### Breaking Changes
|
||||
> These changes affect only code written against a beta version such as v1.4.0-beta.4
|
||||
* Whether `GetToken` requests a CAE token is now determined by `TokenRequestOptions.EnableCAE`. Azure
|
||||
SDK clients which support CAE will set this option automatically. Credentials no longer request CAE
|
||||
tokens by default or observe the environment variable "AZURE_IDENTITY_DISABLE_CP1".
|
||||
|
||||
### Bugs Fixed
|
||||
* Credential chains such as `DefaultAzureCredential` now try their next credential, if any, when
|
||||
managed identity authentication fails in a Docker Desktop container
|
||||
([#21417](https://github.com/Azure/azure-sdk-for-go/issues/21417))
|
||||
|
||||
## 1.4.0-beta.4 (2023-08-16)
|
||||
|
||||
### Other Changes
|
||||
* Upgraded dependencies
|
||||
|
||||
## 1.3.1 (2023-08-16)
|
||||
|
||||
### Other Changes
|
||||
* Upgraded dependencies
|
||||
|
||||
## 1.4.0-beta.3 (2023-08-08)
|
||||
|
||||
### Bugs Fixed
|
||||
* One invocation of `AzureCLICredential.GetToken()` and `OnBehalfOfCredential.GetToken()`
|
||||
can no longer make two authentication attempts
|
||||
|
||||
## 1.4.0-beta.2 (2023-07-14)
|
||||
|
||||
### Other Changes
|
||||
* `DefaultAzureCredentialOptions.TenantID` applies to workload identity authentication
|
||||
* Upgraded dependencies
|
||||
|
||||
## 1.4.0-beta.1 (2023-06-06)
|
||||
|
||||
### Other Changes
|
||||
* Re-enabled CAE support as in v1.3.0-beta.3
|
||||
|
||||
## 1.3.0 (2023-05-09)
|
||||
|
||||
### Breaking Changes
|
||||
|
@ -45,15 +187,15 @@
|
|||
|
||||
### Features Added
|
||||
* By default, credentials set client capability "CP1" to enable support for
|
||||
[Continuous Access Evaluation (CAE)](https://docs.microsoft.com/azure/active-directory/develop/app-resilience-continuous-access-evaluation).
|
||||
This indicates to Azure Active Directory that your application can handle CAE claims challenges.
|
||||
[Continuous Access Evaluation (CAE)](https://learn.microsoft.com/entra/identity-platform/app-resilience-continuous-access-evaluation).
|
||||
This indicates to Microsoft Entra ID that your application can handle CAE claims challenges.
|
||||
You can disable this behavior by setting the environment variable "AZURE_IDENTITY_DISABLE_CP1" to "true".
|
||||
* `InteractiveBrowserCredentialOptions.LoginHint` enables pre-populating the login
|
||||
prompt with a username ([#15599](https://github.com/Azure/azure-sdk-for-go/pull/15599))
|
||||
* Service principal and user credentials support ADFS authentication on Azure Stack.
|
||||
Specify "adfs" as the credential's tenant.
|
||||
* Applications running in private or disconnected clouds can prevent credentials from
|
||||
requesting Azure AD instance metadata by setting the `DisableInstanceDiscovery`
|
||||
requesting Microsoft Entra instance metadata by setting the `DisableInstanceDiscovery`
|
||||
field on credential options.
|
||||
* Many credentials can now be configured to authenticate in multiple tenants. The
|
||||
options types for these credentials have an `AdditionallyAllowedTenants` field
|
||||
|
@ -406,4 +548,4 @@
|
|||
|
||||
## 0.1.0 (2020-07-23)
|
||||
### Features Added
|
||||
* Initial Release. Azure Identity library that provides Azure Active Directory token authentication support for the SDK.
|
||||
* Initial Release. Azure Identity library that provides Microsoft Entra token authentication support for the SDK.
|
||||
|
|
6
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/MIGRATION.md
generated
vendored
6
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/MIGRATION.md
generated
vendored
|
@ -1,6 +1,6 @@
|
|||
# Migrating from autorest/adal to azidentity
|
||||
|
||||
`azidentity` provides Azure Active Directory (Azure AD) authentication for the newest Azure SDK modules (`github.com/azure-sdk-for-go/sdk/...`). Older Azure SDK packages (`github.com/azure-sdk-for-go/services/...`) use types from `github.com/go-autorest/autorest/adal` instead.
|
||||
`azidentity` provides Microsoft Entra ID ([formerly Azure Active Directory](https://learn.microsoft.com/entra/fundamentals/new-name)) authentication for the newest Azure SDK modules (`github.com/azure-sdk-for-go/sdk/...`). Older Azure SDK packages (`github.com/azure-sdk-for-go/services/...`) use types from `github.com/go-autorest/autorest/adal` instead.
|
||||
|
||||
This guide shows common authentication code using `autorest/adal` and its equivalent using `azidentity`.
|
||||
|
||||
|
@ -18,7 +18,7 @@ This guide shows common authentication code using `autorest/adal` and its equiva
|
|||
|
||||
### `autorest/adal`
|
||||
|
||||
Token providers require a token audience (resource identifier) and an instance of `adal.OAuthConfig`, which requires an Azure AD endpoint and tenant:
|
||||
Token providers require a token audience (resource identifier) and an instance of `adal.OAuthConfig`, which requires a Microsoft Entra endpoint and tenant:
|
||||
|
||||
```go
|
||||
import "github.com/Azure/go-autorest/autorest/adal"
|
||||
|
@ -284,7 +284,7 @@ if err == nil {
|
|||
}
|
||||
```
|
||||
|
||||
Note that `azidentity` credentials use the Azure AD v2.0 endpoint, which requires OAuth 2 scopes instead of the resource identifiers `autorest/adal` expects. For more information, see [Azure AD documentation](https://docs.microsoft.com/azure/active-directory/develop/v2-permissions-and-consent).
|
||||
Note that `azidentity` credentials use the Microsoft Entra endpoint, which requires OAuth 2 scopes instead of the resource identifiers `autorest/adal` expects. For more information, see [Microsoft Entra ID documentation](https://learn.microsoft.com/entra/identity-platform/permissions-consent-overview).
|
||||
|
||||
## Use azidentity credentials with older packages
|
||||
|
||||
|
|
56
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/README.md
generated
vendored
56
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/README.md
generated
vendored
|
@ -1,9 +1,9 @@
|
|||
# Azure Identity Client Module for Go
|
||||
|
||||
The Azure Identity module provides Azure Active Directory (Azure AD) token authentication support across the Azure SDK. It includes a set of `TokenCredential` implementations, which can be used with Azure SDK clients supporting token authentication.
|
||||
The Azure Identity module provides Microsoft Entra ID ([formerly Azure Active Directory](https://learn.microsoft.com/entra/fundamentals/new-name)) token authentication support across the Azure SDK. It includes a set of `TokenCredential` implementations, which can be used with Azure SDK clients supporting token authentication.
|
||||
|
||||
[![PkgGoDev](https://pkg.go.dev/badge/github.com/Azure/azure-sdk-for-go/sdk/azidentity)](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity)
|
||||
| [Azure Active Directory documentation](https://docs.microsoft.com/azure/active-directory/)
|
||||
| [Microsoft Entra ID documentation](https://learn.microsoft.com/entra/identity/)
|
||||
| [Source code](https://github.com/Azure/azure-sdk-for-go/tree/main/sdk/azidentity)
|
||||
|
||||
# Getting started
|
||||
|
@ -30,11 +30,17 @@ When debugging and executing code locally, developers typically use their own ac
|
|||
#### Authenticating via the Azure CLI
|
||||
|
||||
`DefaultAzureCredential` and `AzureCLICredential` can authenticate as the user
|
||||
signed in to the [Azure CLI](https://docs.microsoft.com/cli/azure). To sign in to the Azure CLI, run `az login`. On a system with a default web browser, the Azure CLI will launch the browser to authenticate a user.
|
||||
signed in to the [Azure CLI](https://learn.microsoft.com/cli/azure). To sign in to the Azure CLI, run `az login`. On a system with a default web browser, the Azure CLI will launch the browser to authenticate a user.
|
||||
|
||||
When no default browser is available, `az login` will use the device code
|
||||
authentication flow. This can also be selected manually by running `az login --use-device-code`.
|
||||
|
||||
#### Authenticate via the Azure Developer CLI
|
||||
|
||||
Developers coding outside of an IDE can also use the [Azure Developer CLI](https://aka.ms/azure-dev) to authenticate. Applications using the `DefaultAzureCredential` or the `AzureDeveloperCLICredential` can use the account logged in to the Azure Developer CLI to authenticate calls in their application when running locally.
|
||||
|
||||
To authenticate with the Azure Developer CLI, run `azd auth login`. On a system with a default web browser, `azd` will launch the browser to authenticate. On systems without a default web browser, run `azd auth login --use-device-code` to use the device code authentication flow.
|
||||
|
||||
## Key concepts
|
||||
|
||||
### Credentials
|
||||
|
@ -44,9 +50,7 @@ service client to authenticate requests. Service clients across the Azure SDK
|
|||
accept a credential instance when they are constructed, and use that credential
|
||||
to authenticate requests.
|
||||
|
||||
The `azidentity` module focuses on OAuth authentication with Azure Active
|
||||
Directory (AAD). It offers a variety of credential types capable of acquiring
|
||||
an Azure AD access token. See [Credential Types](#credential-types "Credential Types") for a list of this module's credential types.
|
||||
The `azidentity` module focuses on OAuth authentication with Microsoft Entra ID. It offers a variety of credential types capable of acquiring a Microsoft Entra access token. See [Credential Types](#credential-types "Credential Types") for a list of this module's credential types.
|
||||
|
||||
### DefaultAzureCredential
|
||||
|
||||
|
@ -58,20 +62,21 @@ an Azure AD access token. See [Credential Types](#credential-types "Credential T
|
|||
1. **Workload Identity** - If the app is deployed on Kubernetes with environment variables set by the workload identity webhook, `DefaultAzureCredential` will authenticate the configured identity.
|
||||
1. **Managed Identity** - If the app is deployed to an Azure host with managed identity enabled, `DefaultAzureCredential` will authenticate with it.
|
||||
1. **Azure CLI** - If a user or service principal has authenticated via the Azure CLI `az login` command, `DefaultAzureCredential` will authenticate that identity.
|
||||
1. **Azure Developer CLI** - If the developer has authenticated via the Azure Developer CLI `azd auth login` command, the `DefaultAzureCredential` will authenticate with that account.
|
||||
|
||||
> Note: `DefaultAzureCredential` is intended to simplify getting started with the SDK by handling common scenarios with reasonable default behaviors. Developers who want more control or whose scenario isn't served by the default settings should use other credential types.
|
||||
|
||||
## Managed Identity
|
||||
|
||||
`DefaultAzureCredential` and `ManagedIdentityCredential` support
|
||||
[managed identity authentication](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/overview)
|
||||
[managed identity authentication](https://learn.microsoft.com/entra/identity/managed-identities-azure-resources/overview)
|
||||
in any hosting environment which supports managed identities, such as (this list is not exhaustive):
|
||||
* [Azure App Service](https://docs.microsoft.com/azure/app-service/overview-managed-identity)
|
||||
* [Azure Arc](https://docs.microsoft.com/azure/azure-arc/servers/managed-identity-authentication)
|
||||
* [Azure Cloud Shell](https://docs.microsoft.com/azure/cloud-shell/msi-authorization)
|
||||
* [Azure Kubernetes Service](https://docs.microsoft.com/azure/aks/use-managed-identity)
|
||||
* [Azure Service Fabric](https://docs.microsoft.com/azure/service-fabric/concepts-managed-identity)
|
||||
* [Azure Virtual Machines](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/how-to-use-vm-token)
|
||||
* [Azure App Service](https://learn.microsoft.com/azure/app-service/overview-managed-identity)
|
||||
* [Azure Arc](https://learn.microsoft.com/azure/azure-arc/servers/managed-identity-authentication)
|
||||
* [Azure Cloud Shell](https://learn.microsoft.com/azure/cloud-shell/msi-authorization)
|
||||
* [Azure Kubernetes Service](https://learn.microsoft.com/azure/aks/use-managed-identity)
|
||||
* [Azure Service Fabric](https://learn.microsoft.com/azure/service-fabric/concepts-managed-identity)
|
||||
* [Azure Virtual Machines](https://learn.microsoft.com/entra/identity/managed-identities-azure-resources/how-to-use-vm-token)
|
||||
|
||||
## Examples
|
||||
|
||||
|
@ -152,6 +157,7 @@ client := armresources.NewResourceGroupsClient("subscription ID", chain, nil)
|
|||
|Credential|Usage
|
||||
|-|-
|
||||
|[AzureCLICredential](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity#AzureCLICredential)|Authenticate as the user signed in to the Azure CLI
|
||||
|[`AzureDeveloperCLICredential`](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity#AzureDeveloperCLICredential)|Authenticates as the user signed in to the Azure Developer CLI
|
||||
|
||||
## Environment Variables
|
||||
|
||||
|
@ -161,16 +167,16 @@ client := armresources.NewResourceGroupsClient("subscription ID", chain, nil)
|
|||
|
||||
|variable name|value
|
||||
|-|-
|
||||
|`AZURE_CLIENT_ID`|ID of an Azure Active Directory application
|
||||
|`AZURE_TENANT_ID`|ID of the application's Azure Active Directory tenant
|
||||
|`AZURE_CLIENT_ID`|ID of a Microsoft Entra application
|
||||
|`AZURE_TENANT_ID`|ID of the application's Microsoft Entra tenant
|
||||
|`AZURE_CLIENT_SECRET`|one of the application's client secrets
|
||||
|
||||
#### Service principal with certificate
|
||||
|
||||
|variable name|value
|
||||
|-|-
|
||||
|`AZURE_CLIENT_ID`|ID of an Azure Active Directory application
|
||||
|`AZURE_TENANT_ID`|ID of the application's Azure Active Directory tenant
|
||||
|`AZURE_CLIENT_ID`|ID of a Microsoft Entra application
|
||||
|`AZURE_TENANT_ID`|ID of the application's Microsoft Entra tenant
|
||||
|`AZURE_CLIENT_CERTIFICATE_PATH`|path to a certificate file including private key
|
||||
|`AZURE_CLIENT_CERTIFICATE_PASSWORD`|password of the certificate file, if any
|
||||
|
||||
|
@ -178,22 +184,30 @@ client := armresources.NewResourceGroupsClient("subscription ID", chain, nil)
|
|||
|
||||
|variable name|value
|
||||
|-|-
|
||||
|`AZURE_CLIENT_ID`|ID of an Azure Active Directory application
|
||||
|`AZURE_CLIENT_ID`|ID of a Microsoft Entra application
|
||||
|`AZURE_USERNAME`|a username (usually an email address)
|
||||
|`AZURE_PASSWORD`|that user's password
|
||||
|
||||
Configuration is attempted in the above order. For example, if values for a
|
||||
client secret and certificate are both present, the client secret will be used.
|
||||
|
||||
## Token caching
|
||||
|
||||
Token caching is an `azidentity` feature that allows apps to:
|
||||
|
||||
* Cache tokens in memory (default) or on disk (opt-in).
|
||||
* Improve resilience and performance.
|
||||
* Reduce the number of requests made to Microsoft Entra ID to obtain access tokens.
|
||||
|
||||
For more details, see the [token caching documentation](https://aka.ms/azsdk/go/identity/caching).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Error Handling
|
||||
|
||||
Credentials return an `error` when they fail to authenticate or lack data they require to authenticate. For guidance on resolving errors from specific credential types, see the [troubleshooting guide](https://aka.ms/azsdk/go/identity/troubleshoot).
|
||||
|
||||
For more details on handling specific Azure Active Directory errors please refer to the
|
||||
Azure Active Directory
|
||||
[error code documentation](https://docs.microsoft.com/azure/active-directory/develop/reference-aadsts-error-codes).
|
||||
For more details on handling specific Microsoft Entra errors, see the Microsoft Entra [error code documentation](https://learn.microsoft.com/entra/identity-platform/reference-error-codes).
|
||||
|
||||
### Logging
|
||||
|
||||
|
|
70
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/TOKEN_CACHING.MD
generated
vendored
Normal file
70
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/TOKEN_CACHING.MD
generated
vendored
Normal file
|
@ -0,0 +1,70 @@
|
|||
## Token caching in the Azure Identity client module
|
||||
|
||||
*Token caching* is a feature provided by the Azure Identity library that allows apps to:
|
||||
|
||||
- Improve their resilience and performance.
|
||||
- Reduce the number of requests made to Microsoft Entra ID to obtain access tokens.
|
||||
- Reduce the number of times the user is prompted to authenticate.
|
||||
|
||||
When an app needs to access a protected Azure resource, it typically needs to obtain an access token from Entra ID. Obtaining that token involves sending a request to Entra ID and may also involve prompting the user. Entra ID then validates the credentials provided in the request and issues an access token.
|
||||
|
||||
Token caching, via the Azure Identity library, allows the app to store this access token [in memory](#in-memory-token-caching), where it's accessible to the current process, or [on disk](#persistent-token-caching) where it can be accessed across application or process invocations. The token can then be retrieved quickly and easily the next time the app needs to access the same resource. The app can avoid making another request to Entra ID, which reduces network traffic and improves resilience. Additionally, in scenarios where the app is authenticating users, token caching also avoids prompting the user each time new tokens are requested.
|
||||
|
||||
### In-memory token caching
|
||||
|
||||
*In-memory token caching* is the default option provided by the Azure Identity library. This caching approach allows apps to store access tokens in memory. With in-memory token caching, the library first determines if a valid access token for the requested resource is already stored in memory. If a valid token is found, it's returned to the app without the need to make another request to Entra ID. If a valid token isn't found, the library will automatically acquire a token by sending a request to Entra ID. The in-memory token cache provided by the Azure Identity library is thread-safe.
|
||||
|
||||
**Note:** When Azure Identity library credentials are used with Azure service libraries (for example, Azure Blob Storage), the in-memory token caching is active in the `Pipeline` layer as well. All `TokenCredential` implementations are supported there, including custom implementations external to the Azure Identity library.
|
||||
|
||||
#### Caching cannot be disabled
|
||||
|
||||
As there are many levels of caching, it's not possible disable in-memory caching. However, the in-memory cache may be cleared by creating a new credential instance.
|
||||
|
||||
### Persistent token caching
|
||||
|
||||
> Only azidentity v1.5.0-beta versions support persistent token caching
|
||||
|
||||
*Persistent disk token caching* is an opt-in feature in the Azure Identity library. The feature allows apps to cache access tokens in an encrypted, persistent storage mechanism. As indicated in the following table, the storage mechanism differs across operating systems.
|
||||
|
||||
| Operating system | Storage mechanism |
|
||||
|------------------|---------------------------------------|
|
||||
| Linux | kernel key retention service (keyctl) |
|
||||
| macOS | Keychain |
|
||||
| Windows | DPAPI |
|
||||
|
||||
By default the token cache will protect any data which is persisted using the user data protection APIs available on the current platform.
|
||||
However, there are cases where no data protection is available, and applications may choose to allow storing the token cache in an unencrypted state by setting `TokenCachePersistenceOptions.AllowUnencryptedStorage` to `true`. This allows a credential to fall back to unencrypted storage if it can't encrypt the cache. However, we do not recommend using this storage method due to its significantly lower security measures. In addition, tokens are not encrypted solely to the current user, which could potentially allow unauthorized access to the cache by individuals with machine access.
|
||||
|
||||
With persistent disk token caching enabled, the library first determines if a valid access token for the requested resource is already stored in the persistent cache. If a valid token is found, it's returned to the app without the need to make another request to Entra ID. Additionally, the tokens are preserved across app runs, which:
|
||||
|
||||
- Makes the app more resilient to failures.
|
||||
- Ensures the app can continue to function during an Entra ID outage or disruption.
|
||||
- Avoids having to prompt users to authenticate each time the process is restarted.
|
||||
|
||||
>IMPORTANT! The token cache contains sensitive data and **MUST** be protected to prevent compromising accounts. All application decisions regarding the persistence of the token cache must consider that a breach of its content will fully compromise all the accounts it contains.
|
||||
|
||||
#### Example code
|
||||
|
||||
See the [package documentation](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity@v1.6.0-beta.2#pkg-overview) for example code demonstrating how to configure persistent caching and access cached data.
|
||||
|
||||
### Credentials supporting token caching
|
||||
|
||||
The following table indicates the state of in-memory and persistent caching in each credential type.
|
||||
|
||||
**Note:** In-memory caching is activated by default. Persistent token caching needs to be enabled as shown in [this example](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity@v1.5.0-beta.1#example-package-PersistentCache).
|
||||
|
||||
| Credential | In-memory token caching | Persistent token caching |
|
||||
|--------------------------------|---------------------------------------------------------------------|--------------------------|
|
||||
| `AzureCLICredential` | Not Supported | Not Supported |
|
||||
| `AzureDeveloperCLICredential` | Not Supported | Not Supported |
|
||||
| `ClientAssertionCredential` | Supported | Supported |
|
||||
| `ClientCertificateCredential` | Supported | Supported |
|
||||
| `ClientSecretCredential` | Supported | Supported |
|
||||
| `DefaultAzureCredential` | Supported if the target credential in the default chain supports it | Not Supported |
|
||||
| `DeviceCodeCredential` | Supported | Supported |
|
||||
| `EnvironmentCredential` | Supported | Not Supported |
|
||||
| `InteractiveBrowserCredential` | Supported | Supported |
|
||||
| `ManagedIdentityCredential` | Supported | Not Supported |
|
||||
| `OnBehalfOfCredential` | Supported | Supported |
|
||||
| `UsernamePasswordCredential` | Supported | Supported |
|
||||
| `WorkloadIdentityCredential` | Supported | Supported |
|
68
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/TROUBLESHOOTING.md
generated
vendored
68
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/TROUBLESHOOTING.md
generated
vendored
|
@ -8,7 +8,8 @@ This troubleshooting guide covers failure investigation techniques, common error
|
|||
- [Permission issues](#permission-issues)
|
||||
- [Find relevant information in errors](#find-relevant-information-in-errors)
|
||||
- [Enable and configure logging](#enable-and-configure-logging)
|
||||
- [Troubleshoot AzureCliCredential authentication issues](#troubleshoot-azureclicredential-authentication-issues)
|
||||
- [Troubleshoot AzureCLICredential authentication issues](#troubleshoot-azureclicredential-authentication-issues)
|
||||
- [Troubleshoot AzureDeveloperCLICredential authentication issues](#troubleshoot-azuredeveloperclicredential-authentication-issues)
|
||||
- [Troubleshoot ClientCertificateCredential authentication issues](#troubleshoot-clientcertificatecredential-authentication-issues)
|
||||
- [Troubleshoot ClientSecretCredential authentication issues](#troubleshoot-clientsecretcredential-authentication-issues)
|
||||
- [Troubleshoot DefaultAzureCredential authentication issues](#troubleshoot-defaultazurecredential-authentication-issues)
|
||||
|
@ -23,7 +24,7 @@ This troubleshooting guide covers failure investigation techniques, common error
|
|||
|
||||
## Handle azidentity errors
|
||||
|
||||
Any service client method that makes a request to the service may return an error due to authentication failure. This is because the credential authenticates on the first call to the service and on any subsequent call that needs to refresh an access token. Authentication errors include a description of the failure and possibly an error message from Azure Active Directory (Azure AD). Depending on the application, these errors may or may not be recoverable.
|
||||
Any service client method that makes a request to the service may return an error due to authentication failure. This is because the credential authenticates on the first call to the service and on any subsequent call that needs to refresh an access token. Authentication errors include a description of the failure and possibly an error message from Microsoft Entra ID. Depending on the application, these errors may or may not be recoverable.
|
||||
|
||||
### Permission issues
|
||||
|
||||
|
@ -31,7 +32,7 @@ Service client errors with a status code of 401 or 403 often indicate that authe
|
|||
|
||||
## Find relevant information in errors
|
||||
|
||||
Authentication errors can include responses from Azure AD and often contain information helpful in diagnosis. Consider the following error message:
|
||||
Authentication errors can include responses from Microsoft Entra ID and often contain information helpful in diagnosis. Consider the following error message:
|
||||
|
||||
```
|
||||
ClientSecretCredential authentication failed
|
||||
|
@ -57,9 +58,9 @@ This error contains several pieces of information:
|
|||
|
||||
- __Failing Credential Type__: The type of credential that failed to authenticate. This can be helpful when diagnosing issues with chained credential types such as `DefaultAzureCredential` or `ChainedTokenCredential`.
|
||||
|
||||
- __Azure AD Error Code and Message__: The error code and message returned by Azure AD. This can give insight into the specific reason the request failed. For instance, in this case authentication failed because the provided client secret is incorrect. [Azure AD documentation](https://docs.microsoft.com/azure/active-directory/develop/reference-aadsts-error-codes#aadsts-error-codes) has more information on AADSTS error codes.
|
||||
- __Microsoft Entra ID Error Code and Message__: The error code and message returned by Microsoft Entra ID. This can give insight into the specific reason the request failed. For instance, in this case authentication failed because the provided client secret is incorrect. [Microsoft Entra ID documentation](https://learn.microsoft.com/entra/identity-platform/reference-error-codes#aadsts-error-codes) has more information on AADSTS error codes.
|
||||
|
||||
- __Correlation ID and Timestamp__: The correlation ID and timestamp identify the request in server-side logs. This information can be useful to support engineers diagnosing unexpected Azure AD failures.
|
||||
- __Correlation ID and Timestamp__: The correlation ID and timestamp identify the request in server-side logs. This information can be useful to support engineers diagnosing unexpected Microsoft Entra failures.
|
||||
|
||||
### Enable and configure logging
|
||||
|
||||
|
@ -76,12 +77,14 @@ azlog.SetListener(func(event azlog.Event, s string) {
|
|||
azlog.SetEvents(azidentity.EventAuthentication)
|
||||
```
|
||||
|
||||
<a id="dac"></a>
|
||||
## Troubleshoot DefaultAzureCredential authentication issues
|
||||
|
||||
| Error |Description| Mitigation |
|
||||
|---|---|---|
|
||||
|"DefaultAzureCredential failed to acquire a token"|No credential in the `DefaultAzureCredential` chain provided a token|<ul><li>[Enable logging](#enable-and-configure-logging) to get further diagnostic information.</li><li>Consult the troubleshooting guide for underlying credential types for more information.</li><ul><li>[EnvironmentCredential](#troubleshoot-environmentcredential-authentication-issues)</li><li>[ManagedIdentityCredential](#troubleshoot-managedidentitycredential-authentication-issues)</li><li>[AzureCLICredential](#troubleshoot-azureclicredential-authentication-issues)</li></ul>|
|
||||
|Error from the client with a status code of 401 or 403|Authentication succeeded but the authorizing Azure service responded with a 401 (Unauthorized), or 403 (Forbidden) status code|<ul><li>[Enable logging](#enable-and-configure-logging) to determine which credential in the chain returned the authenticating token.</li><li>If an unexpected credential is returning a token, check application configuration such as environment variables.</li><li>Ensure the correct role is assigned to the authenticated identity. For example, a service specific role rather than the subscription Owner role.</li></ul>|
|
||||
|"managed identity timed out"|`DefaultAzureCredential` sets a short timeout on its first managed identity authentication attempt to prevent very long timeouts during local development when no managed identity is available. That timeout causes this error in production when an application requests a token before the hosting environment is ready to provide one.|Use [ManagedIdentityCredential](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity#ManagedIdentityCredential) directly, at least in production. It doesn't set a timeout on its authentication attempts.|
|
||||
|
||||
## Troubleshoot EnvironmentCredential authentication issues
|
||||
|
||||
|
@ -94,17 +97,17 @@ azlog.SetEvents(azidentity.EventAuthentication)
|
|||
|
||||
| Error Code | Issue | Mitigation |
|
||||
|---|---|---|
|
||||
|AADSTS7000215|An invalid client secret was provided.|Ensure the secret provided to the credential constructor is valid. If unsure, create a new client secret using the Azure portal. Details on creating a new client secret are in [Azure AD documentation](https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal#option-2-create-a-new-application-secret).|
|
||||
|AADSTS7000222|An expired client secret was provided.|Create a new client secret using the Azure portal. Details on creating a new client secret are in [Azure AD documentation](https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal#option-2-create-a-new-application-secret).|
|
||||
|AADSTS700016|The specified application wasn't found in the specified tenant.|Ensure the client and tenant IDs provided to the credential constructor are correct for your application registration. For multi-tenant apps, ensure the application has been added to the desired tenant by a tenant admin. To add a new application in the desired tenant, follow the [Azure AD instructions](https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal).|
|
||||
|AADSTS7000215|An invalid client secret was provided.|Ensure the secret provided to the credential constructor is valid. If unsure, create a new client secret using the Azure portal. Details on creating a new client secret are in [Microsoft Entra ID documentation](https://learn.microsoft.com/entra/identity-platform/howto-create-service-principal-portal#option-2-create-a-new-application-secret).|
|
||||
|AADSTS7000222|An expired client secret was provided.|Create a new client secret using the Azure portal. Details on creating a new client secret are in [Microsoft Entra ID documentation](https://learn.microsoft.com/entra/identity-platform/howto-create-service-principal-portal#option-2-create-a-new-application-secret).|
|
||||
|AADSTS700016|The specified application wasn't found in the specified tenant.|Ensure the client and tenant IDs provided to the credential constructor are correct for your application registration. For multi-tenant apps, ensure the application has been added to the desired tenant by a tenant admin. To add a new application in the desired tenant, follow the [Microsoft Entra ID instructions](https://learn.microsoft.com/entra/identity-platform/howto-create-service-principal-portal).|
|
||||
|
||||
<a id="client-cert"></a>
|
||||
## Troubleshoot ClientCertificateCredential authentication issues
|
||||
|
||||
| Error Code | Description | Mitigation |
|
||||
|---|---|---|
|
||||
|AADSTS700027|Client assertion contains an invalid signature.|Ensure the specified certificate has been uploaded to the application registration as described in [Azure AD documentation](https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal#option-1-upload-a-certificate).|
|
||||
|AADSTS700016|The specified application wasn't found in the specified tenant.|Ensure the client and tenant IDs provided to the credential constructor are correct for your application registration. For multi-tenant apps, ensure the application has been added to the desired tenant by a tenant admin. To add a new application in the desired tenant, follow the [Azure AD instructions](https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal).|
|
||||
|AADSTS700027|Client assertion contains an invalid signature.|Ensure the specified certificate has been uploaded to the application registration as described in [Microsoft Entra ID documentation](https://learn.microsoft.com/entra/identity-platform/howto-create-service-principal-portal#option-1-upload-a-certificate).|
|
||||
|AADSTS700016|The specified application wasn't found in the specified tenant.|Ensure the client and tenant IDs provided to the credential constructor are correct for your application registration. For multi-tenant apps, ensure the application has been added to the desired tenant by a tenant admin. To add a new application in the desired tenant, follow the [Microsoft Entra ID instructions](https://learn.microsoft.com/entra/identity-platform/howto-create-service-principal-portal).|
|
||||
|
||||
<a id="username-password"></a>
|
||||
## Troubleshoot UsernamePasswordCredential authentication issues
|
||||
|
@ -120,20 +123,20 @@ azlog.SetEvents(azidentity.EventAuthentication)
|
|||
|
||||
|Host Environment| | |
|
||||
|---|---|---|
|
||||
|Azure Virtual Machines and Scale Sets|[Configuration](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/qs-configure-portal-windows-vm)|[Troubleshooting](#azure-virtual-machine-managed-identity)|
|
||||
|Azure App Service and Azure Functions|[Configuration](https://docs.microsoft.com/azure/app-service/overview-managed-identity)|[Troubleshooting](#azure-app-service-and-azure-functions-managed-identity)|
|
||||
|Azure Virtual Machines and Scale Sets|[Configuration](https://learn.microsoft.com/entra/identity/managed-identities-azure-resources/qs-configure-portal-windows-vm)|[Troubleshooting](#azure-virtual-machine-managed-identity)|
|
||||
|Azure App Service and Azure Functions|[Configuration](https://learn.microsoft.com/azure/app-service/overview-managed-identity)|[Troubleshooting](#azure-app-service-and-azure-functions-managed-identity)|
|
||||
|Azure Kubernetes Service|[Configuration](https://azure.github.io/aad-pod-identity/docs/)|[Troubleshooting](#azure-kubernetes-service-managed-identity)|
|
||||
|Azure Arc|[Configuration](https://docs.microsoft.com/azure/azure-arc/servers/managed-identity-authentication)||
|
||||
|Azure Service Fabric|[Configuration](https://docs.microsoft.com/azure/service-fabric/concepts-managed-identity)||
|
||||
|Azure Arc|[Configuration](https://learn.microsoft.com/azure/azure-arc/servers/managed-identity-authentication)||
|
||||
|Azure Service Fabric|[Configuration](https://learn.microsoft.com/azure/service-fabric/concepts-managed-identity)||
|
||||
|
||||
### Azure Virtual Machine managed identity
|
||||
|
||||
| Error Message |Description| Mitigation |
|
||||
|---|---|---|
|
||||
|The requested identity hasn’t been assigned to this resource.|The IMDS endpoint responded with a status code of 400, indicating the requested identity isn’t assigned to the VM.|If using a user assigned identity, ensure the specified ID is correct.<p/><p/>If using a system assigned identity, make sure it has been enabled as described in [managed identity documentation](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/qs-configure-portal-windows-vm#enable-system-assigned-managed-identity-on-an-existing-vm).|
|
||||
|The requested identity hasn’t been assigned to this resource.|The IMDS endpoint responded with a status code of 400, indicating the requested identity isn’t assigned to the VM.|If using a user assigned identity, ensure the specified ID is correct.<p/><p/>If using a system assigned identity, make sure it has been enabled as described in [managed identity documentation](https://learn.microsoft.com/entra/identity/managed-identities-azure-resources/qs-configure-portal-windows-vm#enable-system-assigned-managed-identity-on-an-existing-vm).|
|
||||
|The request failed due to a gateway error.|The request to the IMDS endpoint failed due to a gateway error, 502 or 504 status code.|IMDS doesn't support requests via proxy or gateway. Disable proxies or gateways running on the VM for requests to the IMDS endpoint `http://169.254.169.254`|
|
||||
|No response received from the managed identity endpoint.|No response was received for the request to IMDS or the request timed out.|<ul><li>Ensure the VM is configured for managed identity as described in [managed identity documentation](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/qs-configure-portal-windows-vm).</li><li>Verify the IMDS endpoint is reachable on the VM. See [below](#verify-imds-is-available-on-the-vm) for instructions.</li></ul>|
|
||||
|Multiple attempts failed to obtain a token from the managed identity endpoint.|The credential has exhausted its retries for a token request.|<ul><li>Refer to the error message for more details on specific failures.<li>Ensure the VM is configured for managed identity as described in [managed identity documentation](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/qs-configure-portal-windows-vm).</li><li>Verify the IMDS endpoint is reachable on the VM. See [below](#verify-imds-is-available-on-the-vm) for instructions.</li></ul>|
|
||||
|No response received from the managed identity endpoint.|No response was received for the request to IMDS or the request timed out.|<ul><li>Ensure the VM is configured for managed identity as described in [managed identity documentation](https://learn.microsoft.com/entra/identity/managed-identities-azure-resources/qs-configure-portal-windows-vm).</li><li>Verify the IMDS endpoint is reachable on the VM. See [below](#verify-imds-is-available-on-the-vm) for instructions.</li></ul>|
|
||||
|Multiple attempts failed to obtain a token from the managed identity endpoint.|The credential has exhausted its retries for a token request.|<ul><li>Refer to the error message for more details on specific failures.<li>Ensure the VM is configured for managed identity as described in [managed identity documentation](https://learn.microsoft.com/entra/identity/managed-identities-azure-resources/qs-configure-portal-windows-vm).</li><li>Verify the IMDS endpoint is reachable on the VM. See [below](#verify-imds-is-available-on-the-vm) for instructions.</li></ul>|
|
||||
|
||||
#### Verify IMDS is available on the VM
|
||||
|
||||
|
@ -149,7 +152,7 @@ curl 'http://169.254.169.254/metadata/identity/oauth2/token?resource=https://man
|
|||
|
||||
| Error Message |Description| Mitigation |
|
||||
|---|---|---|
|
||||
|Get "`http://169.254.169.254/...`" i/o timeout|The App Service host hasn't set environment variables for managed identity configuration.|<ul><li>Ensure the App Service is configured for managed identity as described in [App Service documentation](https://docs.microsoft.com/azure/app-service/overview-managed-identity).</li><li>Verify the App Service environment is properly configured and the managed identity endpoint is available. See [below](#verify-the-app-service-managed-identity-endpoint-is-available) for instructions.</li></ul>|
|
||||
|Get "`http://169.254.169.254/...`" i/o timeout|The App Service host hasn't set environment variables for managed identity configuration.|<ul><li>Ensure the App Service is configured for managed identity as described in [App Service documentation](https://learn.microsoft.com/azure/app-service/overview-managed-identity).</li><li>Verify the App Service environment is properly configured and the managed identity endpoint is available. See [below](#verify-the-app-service-managed-identity-endpoint-is-available) for instructions.</li></ul>|
|
||||
|
||||
#### Verify the App Service managed identity endpoint is available
|
||||
|
||||
|
@ -170,12 +173,12 @@ curl "$IDENTITY_ENDPOINT?resource=https://management.core.windows.net&api-versio
|
|||
|"no azure identity found for request clientID"|The application attempted to authenticate before an identity was assigned to its pod|Verify the pod is labeled correctly. This also occurs when a correctly labeled pod authenticates before the identity is ready. To prevent initialization races, configure NMI to set the Retry-After header in its responses as described in [Pod Identity documentation](https://azure.github.io/aad-pod-identity/docs/configure/feature_flags/#set-retry-after-header-in-nmi-response).
|
||||
|
||||
<a id="azure-cli"></a>
|
||||
## Troubleshoot AzureCliCredential authentication issues
|
||||
## Troubleshoot AzureCLICredential authentication issues
|
||||
|
||||
| Error Message |Description| Mitigation |
|
||||
|---|---|---|
|
||||
|Azure CLI not found on path|The Azure CLI isn’t installed or isn't on the application's path.|<ul><li>Ensure the Azure CLI is installed as described in [Azure CLI documentation](https://docs.microsoft.com/cli/azure/install-azure-cli).</li><li>Validate the installation location is in the application's `PATH` environment variable.</li></ul>|
|
||||
|Please run 'az login' to set up account|No account is currently logged into the Azure CLI, or the login has expired.|<ul><li>Run `az login` to log into the Azure CLI. More information about Azure CLI authentication is available in the [Azure CLI documentation](https://docs.microsoft.com/cli/azure/authenticate-azure-cli).</li><li>Verify that the Azure CLI can obtain tokens. See [below](#verify-the-azure-cli-can-obtain-tokens) for instructions.</li></ul>|
|
||||
|Azure CLI not found on path|The Azure CLI isn’t installed or isn't on the application's path.|<ul><li>Ensure the Azure CLI is installed as described in [Azure CLI documentation](https://learn.microsoft.com/cli/azure/install-azure-cli).</li><li>Validate the installation location is in the application's `PATH` environment variable.</li></ul>|
|
||||
|Please run 'az login' to set up account|No account is currently logged into the Azure CLI, or the login has expired.|<ul><li>Run `az login` to log into the Azure CLI. More information about Azure CLI authentication is available in the [Azure CLI documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli).</li><li>Verify that the Azure CLI can obtain tokens. See [below](#verify-the-azure-cli-can-obtain-tokens) for instructions.</li></ul>|
|
||||
|
||||
#### Verify the Azure CLI can obtain tokens
|
||||
|
||||
|
@ -193,6 +196,29 @@ az account get-access-token --output json --resource https://management.core.win
|
|||
|
||||
> This command's output will contain an access token and SHOULD NOT BE SHARED, to avoid compromising account security.
|
||||
|
||||
<a id="azd"></a>
|
||||
## Troubleshoot AzureDeveloperCLICredential authentication issues
|
||||
|
||||
| Error Message |Description| Mitigation |
|
||||
|---|---|---|
|
||||
|Azure Developer CLI not found on path|The Azure Developer CLI isn't installed or couldn't be found.|<ul><li>Ensure the Azure Developer CLI is properly installed. See the installation instructions at [Install or update the Azure Developer CLI](https://learn.microsoft.com/azure/developer/azure-developer-cli/install-azd).</li><li>Validate the installation location has been added to the `PATH` environment variable.</li></ul>|
|
||||
|Please run "azd auth login"|No account is logged into the Azure Developer CLI, or the login has expired.|<ul><li>Log in to the Azure Developer CLI using the `azd login` command.</li><li>Validate that the Azure Developer CLI can obtain tokens. For instructions, see [Verify the Azure Developer CLI can obtain tokens](#verify-the-azure-developer-cli-can-obtain-tokens).</li></ul>|
|
||||
|
||||
#### Verify the Azure Developer CLI can obtain tokens
|
||||
|
||||
You can manually verify that the Azure Developer CLI is properly authenticated and can obtain tokens. First, use the `config` command to verify the account that is currently logged in to the Azure Developer CLI.
|
||||
|
||||
```sh
|
||||
azd config list
|
||||
```
|
||||
|
||||
Once you've verified the Azure Developer CLI is using correct account, you can validate that it's able to obtain tokens for this account.
|
||||
|
||||
```sh
|
||||
azd auth token --output json --scope https://management.core.windows.net/.default
|
||||
```
|
||||
>Note that output of this command will contain a valid access token, and SHOULD NOT BE SHARED to avoid compromising account security.
|
||||
|
||||
<a id="workload"></a>
|
||||
## Troubleshoot `WorkloadIdentityCredential` authentication issues
|
||||
|
||||
|
|
2
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/assets.json
generated
vendored
2
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/assets.json
generated
vendored
|
@ -2,5 +2,5 @@
|
|||
"AssetsRepo": "Azure/azure-sdk-assets",
|
||||
"AssetsRepoPrefixPath": "go",
|
||||
"TagPrefix": "go/azidentity",
|
||||
"Tag": "go/azidentity_6225ab0470"
|
||||
"Tag": "go/azidentity_087379b475"
|
||||
}
|
||||
|
|
95
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/authentication_record.go
generated
vendored
Normal file
95
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/authentication_record.go
generated
vendored
Normal file
|
@ -0,0 +1,95 @@
|
|||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package azidentity
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/public"
|
||||
)
|
||||
|
||||
var supportedAuthRecordVersions = []string{"1.0"}
|
||||
|
||||
// authenticationRecord is non-secret account information about an authenticated user that user credentials such as
|
||||
// [DeviceCodeCredential] and [InteractiveBrowserCredential] can use to access previously cached authentication
|
||||
// data. Call these credentials' Authenticate method to get an authenticationRecord for a user.
|
||||
type authenticationRecord struct {
|
||||
// Authority is the URL of the authority that issued the token.
|
||||
Authority string `json:"authority"`
|
||||
|
||||
// ClientID is the ID of the application that authenticated the user.
|
||||
ClientID string `json:"clientId"`
|
||||
|
||||
// HomeAccountID uniquely identifies the account.
|
||||
HomeAccountID string `json:"homeAccountId"`
|
||||
|
||||
// TenantID identifies the tenant in which the user authenticated.
|
||||
TenantID string `json:"tenantId"`
|
||||
|
||||
// Username is the user's preferred username.
|
||||
Username string `json:"username"`
|
||||
|
||||
// Version of the AuthenticationRecord.
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler for AuthenticationRecord
|
||||
func (a *authenticationRecord) UnmarshalJSON(b []byte) error {
|
||||
// Default unmarshaling is fine but we want to return an error if the record's version isn't supported i.e., we
|
||||
// want to inspect the unmarshalled values before deciding whether to return an error. Unmarshaling a formally
|
||||
// different type enables this by assigning all the fields without recursing into this method.
|
||||
type r authenticationRecord
|
||||
err := json.Unmarshal(b, (*r)(a))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if a.Version == "" {
|
||||
return errors.New("AuthenticationRecord must have a version")
|
||||
}
|
||||
for _, v := range supportedAuthRecordVersions {
|
||||
if a.Version == v {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("unsupported AuthenticationRecord version %q. This module supports %v", a.Version, supportedAuthRecordVersions)
|
||||
}
|
||||
|
||||
// account returns the AuthenticationRecord as an MSAL Account. The account is zero-valued when the AuthenticationRecord is zero-valued.
|
||||
func (a *authenticationRecord) account() public.Account {
|
||||
return public.Account{
|
||||
Environment: a.Authority,
|
||||
HomeAccountID: a.HomeAccountID,
|
||||
PreferredUsername: a.Username,
|
||||
}
|
||||
}
|
||||
|
||||
func newAuthenticationRecord(ar public.AuthResult) (authenticationRecord, error) {
|
||||
u, err := url.Parse(ar.IDToken.Issuer)
|
||||
if err != nil {
|
||||
return authenticationRecord{}, fmt.Errorf("Authenticate expected a URL issuer but got %q", ar.IDToken.Issuer)
|
||||
}
|
||||
tenant := ar.IDToken.TenantID
|
||||
if tenant == "" {
|
||||
tenant = strings.Trim(u.Path, "/")
|
||||
}
|
||||
username := ar.IDToken.PreferredUsername
|
||||
if username == "" {
|
||||
username = ar.IDToken.UPN
|
||||
}
|
||||
return authenticationRecord{
|
||||
Authority: fmt.Sprintf("%s://%s", u.Scheme, u.Host),
|
||||
ClientID: ar.IDToken.Audience,
|
||||
HomeAccountID: ar.Account.HomeAccountID,
|
||||
TenantID: tenant,
|
||||
Username: username,
|
||||
Version: "1.0",
|
||||
}, nil
|
||||
}
|
148
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/azidentity.go
generated
vendored
148
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/azidentity.go
generated
vendored
|
@ -10,17 +10,17 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azidentity/internal"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/public"
|
||||
)
|
||||
|
@ -41,65 +41,25 @@ const (
|
|||
organizationsTenantID = "organizations"
|
||||
developerSignOnClientID = "04b07795-8ddb-461a-bbee-02f9e1bf7b46"
|
||||
defaultSuffix = "/.default"
|
||||
tenantIDValidationErr = "invalid tenantID. You can locate your tenantID by following the instructions listed here: https://docs.microsoft.com/partner-center/find-ids-and-domain-names"
|
||||
|
||||
traceNamespace = "Microsoft.Entra"
|
||||
traceOpGetToken = "GetToken"
|
||||
traceOpAuthenticate = "Authenticate"
|
||||
)
|
||||
|
||||
var (
|
||||
// capability CP1 indicates the client application is capable of handling CAE claims challenges
|
||||
cp1 = []string{"CP1"}
|
||||
// CP1 is disabled until CAE support is added back
|
||||
disableCP1 = true
|
||||
cp1 = []string{"CP1"}
|
||||
errInvalidTenantID = errors.New("invalid tenantID. You can locate your tenantID by following the instructions listed here: https://learn.microsoft.com/partner-center/find-ids-and-domain-names")
|
||||
)
|
||||
|
||||
var getConfidentialClient = func(clientID, tenantID string, cred confidential.Credential, co *azcore.ClientOptions, additionalOpts ...confidential.Option) (confidentialClient, error) {
|
||||
if !validTenantID(tenantID) {
|
||||
return confidential.Client{}, errors.New(tenantIDValidationErr)
|
||||
}
|
||||
authorityHost, err := setAuthorityHost(co.Cloud)
|
||||
if err != nil {
|
||||
return confidential.Client{}, err
|
||||
}
|
||||
authority := runtime.JoinPaths(authorityHost, tenantID)
|
||||
o := []confidential.Option{
|
||||
confidential.WithAzureRegion(os.Getenv(azureRegionalAuthorityName)),
|
||||
confidential.WithHTTPClient(newPipelineAdapter(co)),
|
||||
}
|
||||
if !disableCP1 {
|
||||
o = append(o, confidential.WithClientCapabilities(cp1))
|
||||
}
|
||||
o = append(o, additionalOpts...)
|
||||
if strings.ToLower(tenantID) == "adfs" {
|
||||
o = append(o, confidential.WithInstanceDiscovery(false))
|
||||
}
|
||||
return confidential.New(authority, clientID, cred, o...)
|
||||
}
|
||||
|
||||
var getPublicClient = func(clientID, tenantID string, co *azcore.ClientOptions, additionalOpts ...public.Option) (public.Client, error) {
|
||||
if !validTenantID(tenantID) {
|
||||
return public.Client{}, errors.New(tenantIDValidationErr)
|
||||
}
|
||||
authorityHost, err := setAuthorityHost(co.Cloud)
|
||||
if err != nil {
|
||||
return public.Client{}, err
|
||||
}
|
||||
o := []public.Option{
|
||||
public.WithAuthority(runtime.JoinPaths(authorityHost, tenantID)),
|
||||
public.WithHTTPClient(newPipelineAdapter(co)),
|
||||
}
|
||||
if !disableCP1 {
|
||||
o = append(o, public.WithClientCapabilities(cp1))
|
||||
}
|
||||
o = append(o, additionalOpts...)
|
||||
if strings.ToLower(tenantID) == "adfs" {
|
||||
o = append(o, public.WithInstanceDiscovery(false))
|
||||
}
|
||||
return public.New(clientID, o...)
|
||||
}
|
||||
// tokenCachePersistenceOptions contains options for persistent token caching
|
||||
type tokenCachePersistenceOptions = internal.TokenCachePersistenceOptions
|
||||
|
||||
// setAuthorityHost initializes the authority host for credentials. Precedence is:
|
||||
// 1. cloud.Configuration.ActiveDirectoryAuthorityHost value set by user
|
||||
// 2. value of AZURE_AUTHORITY_HOST
|
||||
// 3. default: Azure Public Cloud
|
||||
// 1. cloud.Configuration.ActiveDirectoryAuthorityHost value set by user
|
||||
// 2. value of AZURE_AUTHORITY_HOST
|
||||
// 3. default: Azure Public Cloud
|
||||
func setAuthorityHost(cc cloud.Configuration) (string, error) {
|
||||
host := cc.ActiveDirectoryAuthorityHost
|
||||
if host == "" {
|
||||
|
@ -121,29 +81,58 @@ func setAuthorityHost(cc cloud.Configuration) (string, error) {
|
|||
return host, nil
|
||||
}
|
||||
|
||||
// validTenantID return true is it receives a valid tenantID, returns false otherwise
|
||||
// resolveAdditionalTenants returns a copy of tenants, simplified when tenants contains a wildcard
|
||||
func resolveAdditionalTenants(tenants []string) []string {
|
||||
if len(tenants) == 0 {
|
||||
return nil
|
||||
}
|
||||
for _, t := range tenants {
|
||||
// a wildcard makes all other values redundant
|
||||
if t == "*" {
|
||||
return []string{"*"}
|
||||
}
|
||||
}
|
||||
cp := make([]string, len(tenants))
|
||||
copy(cp, tenants)
|
||||
return cp
|
||||
}
|
||||
|
||||
// resolveTenant returns the correct tenant for a token request
|
||||
func resolveTenant(defaultTenant, specified, credName string, additionalTenants []string) (string, error) {
|
||||
if specified == "" || specified == defaultTenant {
|
||||
return defaultTenant, nil
|
||||
}
|
||||
if defaultTenant == "adfs" {
|
||||
return "", errors.New("ADFS doesn't support tenants")
|
||||
}
|
||||
if !validTenantID(specified) {
|
||||
return "", errInvalidTenantID
|
||||
}
|
||||
for _, t := range additionalTenants {
|
||||
if t == "*" || t == specified {
|
||||
return specified, nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf(`%s isn't configured to acquire tokens for tenant %q. To enable acquiring tokens for this tenant add it to the AdditionallyAllowedTenants on the credential options, or add "*" to allow acquiring tokens for any tenant`, credName, specified)
|
||||
}
|
||||
|
||||
func alphanumeric(r rune) bool {
|
||||
return ('0' <= r && r <= '9') || ('a' <= r && r <= 'z') || ('A' <= r && r <= 'Z')
|
||||
}
|
||||
|
||||
func validTenantID(tenantID string) bool {
|
||||
match, err := regexp.MatchString("^[0-9a-zA-Z-.]+$", tenantID)
|
||||
if err != nil {
|
||||
if len(tenantID) < 1 {
|
||||
return false
|
||||
}
|
||||
return match
|
||||
for _, r := range tenantID {
|
||||
if !(alphanumeric(r) || r == '.' || r == '-') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func newPipelineAdapter(opts *azcore.ClientOptions) pipelineAdapter {
|
||||
pl := runtime.NewPipeline(component, version, runtime.PipelineOptions{}, opts)
|
||||
return pipelineAdapter{pl: pl}
|
||||
}
|
||||
|
||||
type pipelineAdapter struct {
|
||||
pl runtime.Pipeline
|
||||
}
|
||||
|
||||
func (p pipelineAdapter) CloseIdleConnections() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
func (p pipelineAdapter) Do(r *http.Request) (*http.Response, error) {
|
||||
func doForClient(client *azcore.Client, r *http.Request) (*http.Response, error) {
|
||||
req, err := runtime.NewRequest(r.Context(), r.Method, r.URL.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -165,7 +154,18 @@ func (p pipelineAdapter) Do(r *http.Request) (*http.Response, error) {
|
|||
return nil, err
|
||||
}
|
||||
}
|
||||
resp, err := p.pl.Do(req)
|
||||
|
||||
// copy headers to the new request, ignoring any for which the new request has a value
|
||||
h := req.Raw().Header
|
||||
for key, vals := range r.Header {
|
||||
if _, has := h[key]; !has {
|
||||
for _, val := range vals {
|
||||
h.Add(key, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := client.Pipeline().Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ func (p pipelineAdapter) Do(r *http.Request) (*http.Response, error) {
|
|||
}
|
||||
|
||||
// enables fakes for test scenarios
|
||||
type confidentialClient interface {
|
||||
type msalConfidentialClient interface {
|
||||
AcquireTokenSilent(ctx context.Context, scopes []string, options ...confidential.AcquireSilentOption) (confidential.AuthResult, error)
|
||||
AcquireTokenByAuthCode(ctx context.Context, code string, redirectURI string, scopes []string, options ...confidential.AcquireByAuthCodeOption) (confidential.AuthResult, error)
|
||||
AcquireTokenByCredential(ctx context.Context, scopes []string, options ...confidential.AcquireByCredentialOption) (confidential.AuthResult, error)
|
||||
|
@ -181,7 +181,7 @@ type confidentialClient interface {
|
|||
}
|
||||
|
||||
// enables fakes for test scenarios
|
||||
type publicClient interface {
|
||||
type msalPublicClient interface {
|
||||
AcquireTokenSilent(ctx context.Context, scopes []string, options ...public.AcquireSilentOption) (public.AuthResult, error)
|
||||
AcquireTokenByUsernamePassword(ctx context.Context, scopes []string, username string, password string, options ...public.AcquireByUsernamePasswordOption) (public.AuthResult, error)
|
||||
AcquireTokenByDeviceCode(ctx context.Context, scopes []string, options ...public.AcquireByDeviceCodeOption) (public.DeviceCode, error)
|
||||
|
|
190
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/azure_cli_credential.go
generated
vendored
190
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/azure_cli_credential.go
generated
vendored
|
@ -14,22 +14,19 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/internal/log"
|
||||
)
|
||||
|
||||
const (
|
||||
credNameAzureCLI = "AzureCLICredential"
|
||||
timeoutCLIRequest = 10 * time.Second
|
||||
)
|
||||
const credNameAzureCLI = "AzureCLICredential"
|
||||
|
||||
// used by tests to fake invoking the CLI
|
||||
type azureCLITokenProvider func(ctx context.Context, resource string, tenantID string) ([]byte, error)
|
||||
type azTokenProvider func(ctx context.Context, scopes []string, tenant, subscription string) ([]byte, error)
|
||||
|
||||
// AzureCLICredentialOptions contains optional parameters for AzureCLICredential.
|
||||
type AzureCLICredentialOptions struct {
|
||||
|
@ -37,24 +34,32 @@ type AzureCLICredentialOptions struct {
|
|||
// to TenantID. Add the wildcard value "*" to allow the credential to acquire tokens for any tenant the
|
||||
// logged in account can access.
|
||||
AdditionallyAllowedTenants []string
|
||||
|
||||
// Subscription is the name or ID of a subscription. Set this to acquire tokens for an account other
|
||||
// than the Azure CLI's current account.
|
||||
Subscription string
|
||||
|
||||
// TenantID identifies the tenant the credential should authenticate in.
|
||||
// Defaults to the CLI's default tenant, which is typically the home tenant of the logged in user.
|
||||
TenantID string
|
||||
|
||||
tokenProvider azureCLITokenProvider
|
||||
// inDefaultChain is true when the credential is part of DefaultAzureCredential
|
||||
inDefaultChain bool
|
||||
// tokenProvider is used by tests to fake invoking az
|
||||
tokenProvider azTokenProvider
|
||||
}
|
||||
|
||||
// init returns an instance of AzureCLICredentialOptions initialized with default values.
|
||||
func (o *AzureCLICredentialOptions) init() {
|
||||
if o.tokenProvider == nil {
|
||||
o.tokenProvider = defaultTokenProvider()
|
||||
o.tokenProvider = defaultAzTokenProvider
|
||||
}
|
||||
}
|
||||
|
||||
// AzureCLICredential authenticates as the identity logged in to the Azure CLI.
|
||||
type AzureCLICredential struct {
|
||||
s *syncer
|
||||
tokenProvider azureCLITokenProvider
|
||||
mu *sync.Mutex
|
||||
opts AzureCLICredentialOptions
|
||||
}
|
||||
|
||||
// NewAzureCLICredential constructs an AzureCLICredential. Pass nil to accept default options.
|
||||
|
@ -63,111 +68,116 @@ func NewAzureCLICredential(options *AzureCLICredentialOptions) (*AzureCLICredent
|
|||
if options != nil {
|
||||
cp = *options
|
||||
}
|
||||
for _, r := range cp.Subscription {
|
||||
if !(alphanumeric(r) || r == '-' || r == '_' || r == ' ' || r == '.') {
|
||||
return nil, fmt.Errorf("%s: invalid Subscription %q", credNameAzureCLI, cp.Subscription)
|
||||
}
|
||||
}
|
||||
if cp.TenantID != "" && !validTenantID(cp.TenantID) {
|
||||
return nil, errInvalidTenantID
|
||||
}
|
||||
cp.init()
|
||||
c := AzureCLICredential{tokenProvider: cp.tokenProvider}
|
||||
c.s = newSyncer(credNameAzureCLI, cp.TenantID, cp.AdditionallyAllowedTenants, c.requestToken, c.requestToken)
|
||||
return &c, nil
|
||||
cp.AdditionallyAllowedTenants = resolveAdditionalTenants(cp.AdditionallyAllowedTenants)
|
||||
return &AzureCLICredential{mu: &sync.Mutex{}, opts: cp}, nil
|
||||
}
|
||||
|
||||
// GetToken requests a token from the Azure CLI. This credential doesn't cache tokens, so every call invokes the CLI.
|
||||
// This method is called automatically by Azure SDK clients.
|
||||
func (c *AzureCLICredential) GetToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
at := azcore.AccessToken{}
|
||||
if len(opts.Scopes) != 1 {
|
||||
return azcore.AccessToken{}, errors.New(credNameAzureCLI + ": GetToken() requires exactly one scope")
|
||||
return at, errors.New(credNameAzureCLI + ": GetToken() requires exactly one scope")
|
||||
}
|
||||
// CLI expects an AAD v1 resource, not a v2 scope
|
||||
opts.Scopes = []string{strings.TrimSuffix(opts.Scopes[0], defaultSuffix)}
|
||||
return c.s.GetToken(ctx, opts)
|
||||
}
|
||||
|
||||
func (c *AzureCLICredential) requestToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
b, err := c.tokenProvider(ctx, opts.Scopes[0], opts.TenantID)
|
||||
if !validScope(opts.Scopes[0]) {
|
||||
return at, fmt.Errorf("%s.GetToken(): invalid scope %q", credNameAzureCLI, opts.Scopes[0])
|
||||
}
|
||||
tenant, err := resolveTenant(c.opts.TenantID, opts.TenantID, credNameAzureCLI, c.opts.AdditionallyAllowedTenants)
|
||||
if err != nil {
|
||||
return azcore.AccessToken{}, err
|
||||
return at, err
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
b, err := c.opts.tokenProvider(ctx, opts.Scopes, tenant, c.opts.Subscription)
|
||||
if err == nil {
|
||||
at, err = c.createAccessToken(b)
|
||||
}
|
||||
at, err := c.createAccessToken(b)
|
||||
if err != nil {
|
||||
return azcore.AccessToken{}, err
|
||||
err = unavailableIfInChain(err, c.opts.inDefaultChain)
|
||||
return at, err
|
||||
}
|
||||
msg := fmt.Sprintf("%s.GetToken() acquired a token for scope %q", credNameAzureCLI, strings.Join(opts.Scopes, ", "))
|
||||
log.Write(EventAuthentication, msg)
|
||||
return at, nil
|
||||
}
|
||||
|
||||
func defaultTokenProvider() func(ctx context.Context, resource string, tenantID string) ([]byte, error) {
|
||||
return func(ctx context.Context, resource string, tenantID string) ([]byte, error) {
|
||||
match, err := regexp.MatchString("^[0-9a-zA-Z-.:/]+$", resource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !match {
|
||||
return nil, fmt.Errorf(`%s: unexpected scope "%s". Only alphanumeric characters and ".", ";", "-", and "/" are allowed`, credNameAzureCLI, resource)
|
||||
}
|
||||
|
||||
// set a default timeout for this authentication iff the application hasn't done so already
|
||||
var cancel context.CancelFunc
|
||||
if _, hasDeadline := ctx.Deadline(); !hasDeadline {
|
||||
ctx, cancel = context.WithTimeout(ctx, timeoutCLIRequest)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
commandLine := "az account get-access-token -o json --resource " + resource
|
||||
if tenantID != "" {
|
||||
commandLine += " --tenant " + tenantID
|
||||
}
|
||||
var cliCmd *exec.Cmd
|
||||
if runtime.GOOS == "windows" {
|
||||
dir := os.Getenv("SYSTEMROOT")
|
||||
if dir == "" {
|
||||
return nil, newCredentialUnavailableError(credNameAzureCLI, "environment variable 'SYSTEMROOT' has no value")
|
||||
}
|
||||
cliCmd = exec.CommandContext(ctx, "cmd.exe", "/c", commandLine)
|
||||
cliCmd.Dir = dir
|
||||
} else {
|
||||
cliCmd = exec.CommandContext(ctx, "/bin/sh", "-c", commandLine)
|
||||
cliCmd.Dir = "/bin"
|
||||
}
|
||||
cliCmd.Env = os.Environ()
|
||||
var stderr bytes.Buffer
|
||||
cliCmd.Stderr = &stderr
|
||||
|
||||
output, err := cliCmd.Output()
|
||||
if err != nil {
|
||||
msg := stderr.String()
|
||||
var exErr *exec.ExitError
|
||||
if errors.As(err, &exErr) && exErr.ExitCode() == 127 || strings.HasPrefix(msg, "'az' is not recognized") {
|
||||
msg = "Azure CLI not found on path"
|
||||
}
|
||||
if msg == "" {
|
||||
msg = err.Error()
|
||||
}
|
||||
return nil, newCredentialUnavailableError(credNameAzureCLI, msg)
|
||||
}
|
||||
|
||||
return output, nil
|
||||
// defaultAzTokenProvider invokes the Azure CLI to acquire a token. It assumes
|
||||
// callers have verified that all string arguments are safe to pass to the CLI.
|
||||
var defaultAzTokenProvider azTokenProvider = func(ctx context.Context, scopes []string, tenantID, subscription string) ([]byte, error) {
|
||||
// pass the CLI a Microsoft Entra ID v1 resource because we don't know which CLI version is installed and older ones don't support v2 scopes
|
||||
resource := strings.TrimSuffix(scopes[0], defaultSuffix)
|
||||
// set a default timeout for this authentication iff the application hasn't done so already
|
||||
var cancel context.CancelFunc
|
||||
if _, hasDeadline := ctx.Deadline(); !hasDeadline {
|
||||
ctx, cancel = context.WithTimeout(ctx, cliTimeout)
|
||||
defer cancel()
|
||||
}
|
||||
commandLine := "az account get-access-token -o json --resource " + resource
|
||||
if tenantID != "" {
|
||||
commandLine += " --tenant " + tenantID
|
||||
}
|
||||
if subscription != "" {
|
||||
// subscription needs quotes because it may contain spaces
|
||||
commandLine += ` --subscription "` + subscription + `"`
|
||||
}
|
||||
var cliCmd *exec.Cmd
|
||||
if runtime.GOOS == "windows" {
|
||||
dir := os.Getenv("SYSTEMROOT")
|
||||
if dir == "" {
|
||||
return nil, newCredentialUnavailableError(credNameAzureCLI, "environment variable 'SYSTEMROOT' has no value")
|
||||
}
|
||||
cliCmd = exec.CommandContext(ctx, "cmd.exe", "/c", commandLine)
|
||||
cliCmd.Dir = dir
|
||||
} else {
|
||||
cliCmd = exec.CommandContext(ctx, "/bin/sh", "-c", commandLine)
|
||||
cliCmd.Dir = "/bin"
|
||||
}
|
||||
cliCmd.Env = os.Environ()
|
||||
var stderr bytes.Buffer
|
||||
cliCmd.Stderr = &stderr
|
||||
|
||||
output, err := cliCmd.Output()
|
||||
if err != nil {
|
||||
msg := stderr.String()
|
||||
var exErr *exec.ExitError
|
||||
if errors.As(err, &exErr) && exErr.ExitCode() == 127 || strings.HasPrefix(msg, "'az' is not recognized") {
|
||||
msg = "Azure CLI not found on path"
|
||||
}
|
||||
if msg == "" {
|
||||
msg = err.Error()
|
||||
}
|
||||
return nil, newCredentialUnavailableError(credNameAzureCLI, msg)
|
||||
}
|
||||
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func (c *AzureCLICredential) createAccessToken(tk []byte) (azcore.AccessToken, error) {
|
||||
t := struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
Authority string `json:"_authority"`
|
||||
ClientID string `json:"_clientId"`
|
||||
ExpiresOn string `json:"expiresOn"`
|
||||
IdentityProvider string `json:"identityProvider"`
|
||||
IsMRRT bool `json:"isMRRT"`
|
||||
RefreshToken string `json:"refreshToken"`
|
||||
Resource string `json:"resource"`
|
||||
TokenType string `json:"tokenType"`
|
||||
UserID string `json:"userId"`
|
||||
AccessToken string `json:"accessToken"`
|
||||
Expires_On int64 `json:"expires_on"`
|
||||
ExpiresOn string `json:"expiresOn"`
|
||||
}{}
|
||||
err := json.Unmarshal(tk, &t)
|
||||
if err != nil {
|
||||
return azcore.AccessToken{}, err
|
||||
}
|
||||
|
||||
// the Azure CLI's "expiresOn" is local time
|
||||
exp, err := time.ParseInLocation("2006-01-02 15:04:05.999999", t.ExpiresOn, time.Local)
|
||||
if err != nil {
|
||||
return azcore.AccessToken{}, fmt.Errorf("Error parsing token expiration time %q: %v", t.ExpiresOn, err)
|
||||
exp := time.Unix(t.Expires_On, 0)
|
||||
if t.Expires_On == 0 {
|
||||
exp, err = time.ParseInLocation("2006-01-02 15:04:05.999999", t.ExpiresOn, time.Local)
|
||||
if err != nil {
|
||||
return azcore.AccessToken{}, fmt.Errorf("%s: error parsing token expiration time %q: %v", credNameAzureCLI, t.ExpiresOn, err)
|
||||
}
|
||||
}
|
||||
|
||||
converted := azcore.AccessToken{
|
||||
|
|
169
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/azure_developer_cli_credential.go
generated
vendored
Normal file
169
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/azure_developer_cli_credential.go
generated
vendored
Normal file
|
@ -0,0 +1,169 @@
|
|||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package azidentity
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/internal/log"
|
||||
)
|
||||
|
||||
const credNameAzureDeveloperCLI = "AzureDeveloperCLICredential"
|
||||
|
||||
type azdTokenProvider func(ctx context.Context, scopes []string, tenant string) ([]byte, error)
|
||||
|
||||
// AzureDeveloperCLICredentialOptions contains optional parameters for AzureDeveloperCLICredential.
|
||||
type AzureDeveloperCLICredentialOptions struct {
|
||||
// AdditionallyAllowedTenants specifies tenants for which the credential may acquire tokens, in addition
|
||||
// to TenantID. Add the wildcard value "*" to allow the credential to acquire tokens for any tenant the
|
||||
// logged in account can access.
|
||||
AdditionallyAllowedTenants []string
|
||||
|
||||
// TenantID identifies the tenant the credential should authenticate in. Defaults to the azd environment,
|
||||
// which is the tenant of the selected Azure subscription.
|
||||
TenantID string
|
||||
|
||||
// inDefaultChain is true when the credential is part of DefaultAzureCredential
|
||||
inDefaultChain bool
|
||||
// tokenProvider is used by tests to fake invoking azd
|
||||
tokenProvider azdTokenProvider
|
||||
}
|
||||
|
||||
// AzureDeveloperCLICredential authenticates as the identity logged in to the [Azure Developer CLI].
|
||||
//
|
||||
// [Azure Developer CLI]: https://learn.microsoft.com/azure/developer/azure-developer-cli/overview
|
||||
type AzureDeveloperCLICredential struct {
|
||||
mu *sync.Mutex
|
||||
opts AzureDeveloperCLICredentialOptions
|
||||
}
|
||||
|
||||
// NewAzureDeveloperCLICredential constructs an AzureDeveloperCLICredential. Pass nil to accept default options.
|
||||
func NewAzureDeveloperCLICredential(options *AzureDeveloperCLICredentialOptions) (*AzureDeveloperCLICredential, error) {
|
||||
cp := AzureDeveloperCLICredentialOptions{}
|
||||
if options != nil {
|
||||
cp = *options
|
||||
}
|
||||
if cp.TenantID != "" && !validTenantID(cp.TenantID) {
|
||||
return nil, errInvalidTenantID
|
||||
}
|
||||
if cp.tokenProvider == nil {
|
||||
cp.tokenProvider = defaultAzdTokenProvider
|
||||
}
|
||||
return &AzureDeveloperCLICredential{mu: &sync.Mutex{}, opts: cp}, nil
|
||||
}
|
||||
|
||||
// GetToken requests a token from the Azure Developer CLI. This credential doesn't cache tokens, so every call invokes azd.
|
||||
// This method is called automatically by Azure SDK clients.
|
||||
func (c *AzureDeveloperCLICredential) GetToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
at := azcore.AccessToken{}
|
||||
if len(opts.Scopes) == 0 {
|
||||
return at, errors.New(credNameAzureDeveloperCLI + ": GetToken() requires at least one scope")
|
||||
}
|
||||
for _, scope := range opts.Scopes {
|
||||
if !validScope(scope) {
|
||||
return at, fmt.Errorf("%s.GetToken(): invalid scope %q", credNameAzureDeveloperCLI, scope)
|
||||
}
|
||||
}
|
||||
tenant, err := resolveTenant(c.opts.TenantID, opts.TenantID, credNameAzureDeveloperCLI, c.opts.AdditionallyAllowedTenants)
|
||||
if err != nil {
|
||||
return at, err
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
b, err := c.opts.tokenProvider(ctx, opts.Scopes, tenant)
|
||||
if err == nil {
|
||||
at, err = c.createAccessToken(b)
|
||||
}
|
||||
if err != nil {
|
||||
err = unavailableIfInChain(err, c.opts.inDefaultChain)
|
||||
return at, err
|
||||
}
|
||||
msg := fmt.Sprintf("%s.GetToken() acquired a token for scope %q", credNameAzureDeveloperCLI, strings.Join(opts.Scopes, ", "))
|
||||
log.Write(EventAuthentication, msg)
|
||||
return at, nil
|
||||
}
|
||||
|
||||
// defaultAzTokenProvider invokes the Azure Developer CLI to acquire a token. It assumes
|
||||
// callers have verified that all string arguments are safe to pass to the CLI.
|
||||
var defaultAzdTokenProvider azdTokenProvider = func(ctx context.Context, scopes []string, tenant string) ([]byte, error) {
|
||||
// set a default timeout for this authentication iff the application hasn't done so already
|
||||
var cancel context.CancelFunc
|
||||
if _, hasDeadline := ctx.Deadline(); !hasDeadline {
|
||||
ctx, cancel = context.WithTimeout(ctx, cliTimeout)
|
||||
defer cancel()
|
||||
}
|
||||
commandLine := "azd auth token -o json"
|
||||
if tenant != "" {
|
||||
commandLine += " --tenant-id " + tenant
|
||||
}
|
||||
for _, scope := range scopes {
|
||||
commandLine += " --scope " + scope
|
||||
}
|
||||
var cliCmd *exec.Cmd
|
||||
if runtime.GOOS == "windows" {
|
||||
dir := os.Getenv("SYSTEMROOT")
|
||||
if dir == "" {
|
||||
return nil, newCredentialUnavailableError(credNameAzureDeveloperCLI, "environment variable 'SYSTEMROOT' has no value")
|
||||
}
|
||||
cliCmd = exec.CommandContext(ctx, "cmd.exe", "/c", commandLine)
|
||||
cliCmd.Dir = dir
|
||||
} else {
|
||||
cliCmd = exec.CommandContext(ctx, "/bin/sh", "-c", commandLine)
|
||||
cliCmd.Dir = "/bin"
|
||||
}
|
||||
cliCmd.Env = os.Environ()
|
||||
var stderr bytes.Buffer
|
||||
cliCmd.Stderr = &stderr
|
||||
output, err := cliCmd.Output()
|
||||
if err != nil {
|
||||
msg := stderr.String()
|
||||
var exErr *exec.ExitError
|
||||
if errors.As(err, &exErr) && exErr.ExitCode() == 127 || strings.HasPrefix(msg, "'azd' is not recognized") {
|
||||
msg = "Azure Developer CLI not found on path"
|
||||
} else if strings.Contains(msg, "azd auth login") {
|
||||
msg = `please run "azd auth login" from a command prompt to authenticate before using this credential`
|
||||
}
|
||||
if msg == "" {
|
||||
msg = err.Error()
|
||||
}
|
||||
return nil, newCredentialUnavailableError(credNameAzureDeveloperCLI, msg)
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func (c *AzureDeveloperCLICredential) createAccessToken(tk []byte) (azcore.AccessToken, error) {
|
||||
t := struct {
|
||||
AccessToken string `json:"token"`
|
||||
ExpiresOn string `json:"expiresOn"`
|
||||
}{}
|
||||
err := json.Unmarshal(tk, &t)
|
||||
if err != nil {
|
||||
return azcore.AccessToken{}, err
|
||||
}
|
||||
exp, err := time.Parse("2006-01-02T15:04:05Z", t.ExpiresOn)
|
||||
if err != nil {
|
||||
return azcore.AccessToken{}, fmt.Errorf("error parsing token expiration time %q: %v", t.ExpiresOn, err)
|
||||
}
|
||||
return azcore.AccessToken{
|
||||
ExpiresOn: exp.UTC(),
|
||||
Token: t.AccessToken,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var _ azcore.TokenCredential = (*AzureDeveloperCLICredential)(nil)
|
130
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/azure_pipelines_credential.go
generated
vendored
Normal file
130
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/azure_pipelines_credential.go
generated
vendored
Normal file
|
@ -0,0 +1,130 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package azidentity
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
|
||||
)
|
||||
|
||||
const (
|
||||
credNameAzurePipelines = "AzurePipelinesCredential"
|
||||
oidcAPIVersion = "7.1"
|
||||
systemAccessToken = "SYSTEM_ACCESSTOKEN"
|
||||
systemOIDCRequestURI = "SYSTEM_OIDCREQUESTURI"
|
||||
)
|
||||
|
||||
// azurePipelinesCredential authenticates with workload identity federation in an Azure Pipeline. See
|
||||
// [Azure Pipelines documentation] for more information.
|
||||
//
|
||||
// [Azure Pipelines documentation]: https://learn.microsoft.com/azure/devops/pipelines/library/connect-to-azure?view=azure-devops#create-an-azure-resource-manager-service-connection-that-uses-workload-identity-federation
|
||||
type azurePipelinesCredential struct {
|
||||
connectionID, oidcURI, systemAccessToken string
|
||||
cred *ClientAssertionCredential
|
||||
}
|
||||
|
||||
// azurePipelinesCredentialOptions contains optional parameters for AzurePipelinesCredential.
|
||||
type azurePipelinesCredentialOptions struct {
|
||||
azcore.ClientOptions
|
||||
|
||||
// AdditionallyAllowedTenants specifies additional tenants for which the credential may acquire tokens.
|
||||
// Add the wildcard value "*" to allow the credential to acquire tokens for any tenant in which the
|
||||
// application is registered.
|
||||
AdditionallyAllowedTenants []string
|
||||
|
||||
// DisableInstanceDiscovery should be set true only by applications authenticating in disconnected clouds, or
|
||||
// private clouds such as Azure Stack. It determines whether the credential requests Microsoft Entra instance metadata
|
||||
// from https://login.microsoft.com before authenticating. Setting this to true will skip this request, making
|
||||
// the application responsible for ensuring the configured authority is valid and trustworthy.
|
||||
DisableInstanceDiscovery bool
|
||||
}
|
||||
|
||||
// newAzurePipelinesCredential is the constructor for AzurePipelinesCredential. In addition to its required arguments,
|
||||
// it reads a security token for the running build, which is required to authenticate the service connection, from the
|
||||
// environment variable SYSTEM_ACCESSTOKEN. See the [Azure Pipelines documentation] for an example showing how to set
|
||||
// this variable in build job YAML.
|
||||
//
|
||||
// [Azure Pipelines documentation]: https://learn.microsoft.com/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml#systemaccesstoken
|
||||
func newAzurePipelinesCredential(tenantID, clientID, serviceConnectionID string, options *azurePipelinesCredentialOptions) (*azurePipelinesCredential, error) {
|
||||
if options == nil {
|
||||
options = &azurePipelinesCredentialOptions{}
|
||||
}
|
||||
u := os.Getenv(systemOIDCRequestURI)
|
||||
if u == "" {
|
||||
return nil, fmt.Errorf("no value for environment variable %s. This should be set by Azure Pipelines", systemOIDCRequestURI)
|
||||
}
|
||||
sat := os.Getenv(systemAccessToken)
|
||||
if sat == "" {
|
||||
return nil, errors.New("no value for environment variable " + systemAccessToken)
|
||||
}
|
||||
a := azurePipelinesCredential{
|
||||
connectionID: serviceConnectionID,
|
||||
oidcURI: u,
|
||||
systemAccessToken: sat,
|
||||
}
|
||||
caco := ClientAssertionCredentialOptions{
|
||||
AdditionallyAllowedTenants: options.AdditionallyAllowedTenants,
|
||||
ClientOptions: options.ClientOptions,
|
||||
DisableInstanceDiscovery: options.DisableInstanceDiscovery,
|
||||
}
|
||||
cred, err := NewClientAssertionCredential(tenantID, clientID, a.getAssertion, &caco)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cred.client.name = credNameAzurePipelines
|
||||
a.cred = cred
|
||||
return &a, nil
|
||||
}
|
||||
|
||||
// GetToken requests an access token from Microsoft Entra ID. Azure SDK clients call this method automatically.
|
||||
func (a *azurePipelinesCredential) GetToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
var err error
|
||||
ctx, endSpan := runtime.StartSpan(ctx, credNameAzurePipelines+"."+traceOpGetToken, a.cred.client.azClient.Tracer(), nil)
|
||||
defer func() { endSpan(err) }()
|
||||
tk, err := a.cred.GetToken(ctx, opts)
|
||||
return tk, err
|
||||
}
|
||||
|
||||
func (a *azurePipelinesCredential) getAssertion(ctx context.Context) (string, error) {
|
||||
url := a.oidcURI + "?api-version=" + oidcAPIVersion + "&serviceConnectionId=" + a.connectionID
|
||||
url, err := runtime.EncodeQueryParams(url)
|
||||
if err != nil {
|
||||
return "", newAuthenticationFailedError(credNameAzurePipelines, "couldn't encode OIDC URL: "+err.Error(), nil, nil)
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, nil)
|
||||
if err != nil {
|
||||
return "", newAuthenticationFailedError(credNameAzurePipelines, "couldn't create OIDC token request: "+err.Error(), nil, nil)
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer "+a.systemAccessToken)
|
||||
res, err := doForClient(a.cred.client.azClient, req)
|
||||
if err != nil {
|
||||
return "", newAuthenticationFailedError(credNameAzurePipelines, "couldn't send OIDC token request: "+err.Error(), nil, nil)
|
||||
}
|
||||
if res.StatusCode != http.StatusOK {
|
||||
msg := res.Status + " response from the OIDC endpoint. Check service connection ID and Pipeline configuration"
|
||||
// include the response because its body, if any, probably contains an error message.
|
||||
// OK responses aren't included with errors because they probably contain secrets
|
||||
return "", newAuthenticationFailedError(credNameAzurePipelines, msg, res, nil)
|
||||
}
|
||||
b, err := runtime.Payload(res)
|
||||
if err != nil {
|
||||
return "", newAuthenticationFailedError(credNameAzurePipelines, "couldn't read OIDC response content: "+err.Error(), nil, nil)
|
||||
}
|
||||
var r struct {
|
||||
OIDCToken string `json:"oidcToken"`
|
||||
}
|
||||
err = json.Unmarshal(b, &r)
|
||||
if err != nil {
|
||||
return "", newAuthenticationFailedError(credNameAzurePipelines, "unexpected response from OIDC endpoint", nil, nil)
|
||||
}
|
||||
return r.OIDCToken, nil
|
||||
}
|
2
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/chained_token_credential.go
generated
vendored
2
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/chained_token_credential.go
generated
vendored
|
@ -86,7 +86,7 @@ func (c *ChainedTokenCredential) GetToken(ctx context.Context, opts policy.Token
|
|||
errs []error
|
||||
successfulCredential azcore.TokenCredential
|
||||
token azcore.AccessToken
|
||||
unavailableErr *credentialUnavailableError
|
||||
unavailableErr credentialUnavailable
|
||||
)
|
||||
for _, cred := range c.sources {
|
||||
token, err = cred.GetToken(ctx, opts)
|
||||
|
|
51
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/ci.yml
generated
vendored
51
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/ci.yml
generated
vendored
|
@ -8,7 +8,7 @@ trigger:
|
|||
- release/*
|
||||
paths:
|
||||
include:
|
||||
- sdk/azidentity/
|
||||
- sdk/azidentity/
|
||||
|
||||
pr:
|
||||
branches:
|
||||
|
@ -19,29 +19,28 @@ pr:
|
|||
- release/*
|
||||
paths:
|
||||
include:
|
||||
- sdk/azidentity/
|
||||
- sdk/azidentity/
|
||||
|
||||
stages:
|
||||
- template: /eng/pipelines/templates/jobs/archetype-sdk-client.yml
|
||||
parameters:
|
||||
RunLiveTests: true
|
||||
ServiceDirectory: 'azidentity'
|
||||
PreSteps:
|
||||
- pwsh: |
|
||||
[System.Convert]::FromBase64String($env:PFX_CONTENTS) | Set-Content -Path $(Agent.TempDirectory)/test.pfx -AsByteStream
|
||||
Set-Content -Path $(Agent.TempDirectory)/test.pem -Value $env:PEM_CONTENTS
|
||||
[System.Convert]::FromBase64String($env:SNI_CONTENTS) | Set-Content -Path $(Agent.TempDirectory)/testsni.pfx -AsByteStream
|
||||
env:
|
||||
PFX_CONTENTS: $(net-identity-spcert-pfx)
|
||||
PEM_CONTENTS: $(net-identity-spcert-pem)
|
||||
SNI_CONTENTS: $(net-identity-spcert-sni)
|
||||
EnvVars:
|
||||
AZURE_IDENTITY_TEST_TENANTID: $(net-identity-tenantid)
|
||||
AZURE_IDENTITY_TEST_USERNAME: $(net-identity-username)
|
||||
AZURE_IDENTITY_TEST_PASSWORD: $(net-identity-password)
|
||||
IDENTITY_SP_TENANT_ID: $(net-identity-sp-tenantid)
|
||||
IDENTITY_SP_CLIENT_ID: $(net-identity-sp-clientid)
|
||||
IDENTITY_SP_CLIENT_SECRET: $(net-identity-sp-clientsecret)
|
||||
IDENTITY_SP_CERT_PEM: $(Agent.TempDirectory)/test.pem
|
||||
IDENTITY_SP_CERT_PFX: $(Agent.TempDirectory)/test.pfx
|
||||
IDENTITY_SP_CERT_SNI: $(Agent.TempDirectory)/testsni.pfx
|
||||
extends:
|
||||
template: /eng/pipelines/templates/jobs/archetype-sdk-client.yml
|
||||
parameters:
|
||||
CloudConfig:
|
||||
Public:
|
||||
SubscriptionConfigurations:
|
||||
- $(sub-config-azure-cloud-test-resources)
|
||||
- $(sub-config-identity-test-resources)
|
||||
EnvVars:
|
||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||
RunLiveTests: true
|
||||
ServiceDirectory: azidentity
|
||||
UsePipelineProxy: false
|
||||
|
||||
${{ if endsWith(variables['Build.DefinitionName'], 'weekly') }}:
|
||||
MatrixConfigs:
|
||||
- Name: managed_identity_matrix
|
||||
GenerateVMJobs: true
|
||||
Path: sdk/azidentity/managed-identity-matrix.json
|
||||
Selection: sparse
|
||||
MatrixReplace:
|
||||
- Pool=.*LINUXPOOL.*/azsdk-pool-mms-ubuntu-2204-identitymsi
|
||||
- OSVmImage=.*LINUXNEXTVMIMAGE.*/azsdk-pool-mms-ubuntu-2204-1espt
|
||||
|
|
44
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/client_assertion_credential.go
generated
vendored
44
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/client_assertion_credential.go
generated
vendored
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential"
|
||||
)
|
||||
|
||||
|
@ -20,12 +21,11 @@ const credNameAssertion = "ClientAssertionCredential"
|
|||
// ClientAssertionCredential authenticates an application with assertions provided by a callback function.
|
||||
// This credential is for advanced scenarios. [ClientCertificateCredential] has a more convenient API for
|
||||
// the most common assertion scenario, authenticating a service principal with a certificate. See
|
||||
// [Azure AD documentation] for details of the assertion format.
|
||||
// [Microsoft Entra ID documentation] for details of the assertion format.
|
||||
//
|
||||
// [Azure AD documentation]: https://docs.microsoft.com/azure/active-directory/develop/active-directory-certificate-credentials#assertion-format
|
||||
// [Microsoft Entra ID documentation]: https://learn.microsoft.com/entra/identity-platform/certificate-credentials#assertion-format
|
||||
type ClientAssertionCredential struct {
|
||||
client confidentialClient
|
||||
s *syncer
|
||||
client *confidentialClient
|
||||
}
|
||||
|
||||
// ClientAssertionCredentialOptions contains optional parameters for ClientAssertionCredential.
|
||||
|
@ -36,11 +36,15 @@ type ClientAssertionCredentialOptions struct {
|
|||
// Add the wildcard value "*" to allow the credential to acquire tokens for any tenant in which the
|
||||
// application is registered.
|
||||
AdditionallyAllowedTenants []string
|
||||
|
||||
// DisableInstanceDiscovery should be set true only by applications authenticating in disconnected clouds, or
|
||||
// private clouds such as Azure Stack. It determines whether the credential requests Azure AD instance metadata
|
||||
// private clouds such as Azure Stack. It determines whether the credential requests Microsoft Entra instance metadata
|
||||
// from https://login.microsoft.com before authenticating. Setting this to true will skip this request, making
|
||||
// the application responsible for ensuring the configured authority is valid and trustworthy.
|
||||
DisableInstanceDiscovery bool
|
||||
|
||||
// tokenCachePersistenceOptions enables persistent token caching when not nil.
|
||||
tokenCachePersistenceOptions *tokenCachePersistenceOptions
|
||||
}
|
||||
|
||||
// NewClientAssertionCredential constructs a ClientAssertionCredential. The getAssertion function must be thread safe. Pass nil for options to accept defaults.
|
||||
|
@ -56,28 +60,26 @@ func NewClientAssertionCredential(tenantID, clientID string, getAssertion func(c
|
|||
return getAssertion(ctx)
|
||||
},
|
||||
)
|
||||
c, err := getConfidentialClient(clientID, tenantID, cred, &options.ClientOptions, confidential.WithInstanceDiscovery(!options.DisableInstanceDiscovery))
|
||||
msalOpts := confidentialClientOptions{
|
||||
AdditionallyAllowedTenants: options.AdditionallyAllowedTenants,
|
||||
ClientOptions: options.ClientOptions,
|
||||
DisableInstanceDiscovery: options.DisableInstanceDiscovery,
|
||||
tokenCachePersistenceOptions: options.tokenCachePersistenceOptions,
|
||||
}
|
||||
c, err := newConfidentialClient(tenantID, clientID, credNameAssertion, cred, msalOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cac := ClientAssertionCredential{client: c}
|
||||
cac.s = newSyncer(credNameAssertion, tenantID, options.AdditionallyAllowedTenants, cac.requestToken, cac.silentAuth)
|
||||
return &cac, nil
|
||||
return &ClientAssertionCredential{client: c}, nil
|
||||
}
|
||||
|
||||
// GetToken requests an access token from Azure Active Directory. This method is called automatically by Azure SDK clients.
|
||||
// GetToken requests an access token from Microsoft Entra ID. This method is called automatically by Azure SDK clients.
|
||||
func (c *ClientAssertionCredential) GetToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
return c.s.GetToken(ctx, opts)
|
||||
}
|
||||
|
||||
func (c *ClientAssertionCredential) silentAuth(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
ar, err := c.client.AcquireTokenSilent(ctx, opts.Scopes, confidential.WithTenantID(opts.TenantID))
|
||||
return azcore.AccessToken{Token: ar.AccessToken, ExpiresOn: ar.ExpiresOn.UTC()}, err
|
||||
}
|
||||
|
||||
func (c *ClientAssertionCredential) requestToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
ar, err := c.client.AcquireTokenByCredential(ctx, opts.Scopes, confidential.WithTenantID(opts.TenantID))
|
||||
return azcore.AccessToken{Token: ar.AccessToken, ExpiresOn: ar.ExpiresOn.UTC()}, err
|
||||
var err error
|
||||
ctx, endSpan := runtime.StartSpan(ctx, credNameAssertion+"."+traceOpGetToken, c.client.azClient.Tracer(), nil)
|
||||
defer func() { endSpan(err) }()
|
||||
tk, err := c.client.GetToken(ctx, opts)
|
||||
return tk, err
|
||||
}
|
||||
|
||||
var _ azcore.TokenCredential = (*ClientAssertionCredential)(nil)
|
||||
|
|
54
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/client_certificate_credential.go
generated
vendored
54
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/client_certificate_credential.go
generated
vendored
|
@ -15,6 +15,7 @@ import (
|
|||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential"
|
||||
"golang.org/x/crypto/pkcs12"
|
||||
)
|
||||
|
@ -29,24 +30,29 @@ type ClientCertificateCredentialOptions struct {
|
|||
// Add the wildcard value "*" to allow the credential to acquire tokens for any tenant in which the
|
||||
// application is registered.
|
||||
AdditionallyAllowedTenants []string
|
||||
|
||||
// DisableInstanceDiscovery should be set true only by applications authenticating in disconnected clouds, or
|
||||
// private clouds such as Azure Stack. It determines whether the credential requests Azure AD instance metadata
|
||||
// private clouds such as Azure Stack. It determines whether the credential requests Microsoft Entra instance metadata
|
||||
// from https://login.microsoft.com before authenticating. Setting this to true will skip this request, making
|
||||
// the application responsible for ensuring the configured authority is valid and trustworthy.
|
||||
DisableInstanceDiscovery bool
|
||||
|
||||
// SendCertificateChain controls whether the credential sends the public certificate chain in the x5c
|
||||
// header of each token request's JWT. This is required for Subject Name/Issuer (SNI) authentication.
|
||||
// Defaults to False.
|
||||
SendCertificateChain bool
|
||||
|
||||
// tokenCachePersistenceOptions enables persistent token caching when not nil.
|
||||
tokenCachePersistenceOptions *tokenCachePersistenceOptions
|
||||
}
|
||||
|
||||
// ClientCertificateCredential authenticates a service principal with a certificate.
|
||||
type ClientCertificateCredential struct {
|
||||
client confidentialClient
|
||||
s *syncer
|
||||
client *confidentialClient
|
||||
}
|
||||
|
||||
// NewClientCertificateCredential constructs a ClientCertificateCredential. Pass nil for options to accept defaults.
|
||||
// NewClientCertificateCredential constructs a ClientCertificateCredential. Pass nil for options to accept defaults. See
|
||||
// [ParseCertificates] for help loading a certificate.
|
||||
func NewClientCertificateCredential(tenantID string, clientID string, certs []*x509.Certificate, key crypto.PrivateKey, options *ClientCertificateCredentialOptions) (*ClientCertificateCredential, error) {
|
||||
if len(certs) == 0 {
|
||||
return nil, errors.New("at least one certificate is required")
|
||||
|
@ -58,37 +64,33 @@ func NewClientCertificateCredential(tenantID string, clientID string, certs []*x
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var o []confidential.Option
|
||||
if options.SendCertificateChain {
|
||||
o = append(o, confidential.WithX5C())
|
||||
msalOpts := confidentialClientOptions{
|
||||
AdditionallyAllowedTenants: options.AdditionallyAllowedTenants,
|
||||
ClientOptions: options.ClientOptions,
|
||||
DisableInstanceDiscovery: options.DisableInstanceDiscovery,
|
||||
SendX5C: options.SendCertificateChain,
|
||||
tokenCachePersistenceOptions: options.tokenCachePersistenceOptions,
|
||||
}
|
||||
o = append(o, confidential.WithInstanceDiscovery(!options.DisableInstanceDiscovery))
|
||||
c, err := getConfidentialClient(clientID, tenantID, cred, &options.ClientOptions, o...)
|
||||
c, err := newConfidentialClient(tenantID, clientID, credNameCert, cred, msalOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := ClientCertificateCredential{client: c}
|
||||
cc.s = newSyncer(credNameCert, tenantID, options.AdditionallyAllowedTenants, cc.requestToken, cc.silentAuth)
|
||||
return &cc, nil
|
||||
return &ClientCertificateCredential{client: c}, nil
|
||||
}
|
||||
|
||||
// GetToken requests an access token from Azure Active Directory. This method is called automatically by Azure SDK clients.
|
||||
// GetToken requests an access token from Microsoft Entra ID. This method is called automatically by Azure SDK clients.
|
||||
func (c *ClientCertificateCredential) GetToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
return c.s.GetToken(ctx, opts)
|
||||
var err error
|
||||
ctx, endSpan := runtime.StartSpan(ctx, credNameCert+"."+traceOpGetToken, c.client.azClient.Tracer(), nil)
|
||||
defer func() { endSpan(err) }()
|
||||
tk, err := c.client.GetToken(ctx, opts)
|
||||
return tk, err
|
||||
}
|
||||
|
||||
func (c *ClientCertificateCredential) silentAuth(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
ar, err := c.client.AcquireTokenSilent(ctx, opts.Scopes, confidential.WithTenantID(opts.TenantID))
|
||||
return azcore.AccessToken{Token: ar.AccessToken, ExpiresOn: ar.ExpiresOn.UTC()}, err
|
||||
}
|
||||
|
||||
func (c *ClientCertificateCredential) requestToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
ar, err := c.client.AcquireTokenByCredential(ctx, opts.Scopes, confidential.WithTenantID(opts.TenantID))
|
||||
return azcore.AccessToken{Token: ar.AccessToken, ExpiresOn: ar.ExpiresOn.UTC()}, err
|
||||
}
|
||||
|
||||
// ParseCertificates loads certificates and a private key, in PEM or PKCS12 format, for use with NewClientCertificateCredential.
|
||||
// Pass nil for password if the private key isn't encrypted. This function can't decrypt keys in PEM format.
|
||||
// ParseCertificates loads certificates and a private key, in PEM or PKCS#12 format, for use with [NewClientCertificateCredential].
|
||||
// Pass nil for password if the private key isn't encrypted. This function has limitations, for example it can't decrypt keys in
|
||||
// PEM format or PKCS#12 certificates that use SHA256 for message authentication. If you encounter such limitations, consider
|
||||
// using another module to load the certificate and private key.
|
||||
func ParseCertificates(certData []byte, password []byte) ([]*x509.Certificate, crypto.PrivateKey, error) {
|
||||
var blocks []*pem.Block
|
||||
var err error
|
||||
|
|
42
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/client_secret_credential.go
generated
vendored
42
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/client_secret_credential.go
generated
vendored
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential"
|
||||
)
|
||||
|
||||
|
@ -24,17 +25,20 @@ type ClientSecretCredentialOptions struct {
|
|||
// Add the wildcard value "*" to allow the credential to acquire tokens for any tenant in which the
|
||||
// application is registered.
|
||||
AdditionallyAllowedTenants []string
|
||||
|
||||
// DisableInstanceDiscovery should be set true only by applications authenticating in disconnected clouds, or
|
||||
// private clouds such as Azure Stack. It determines whether the credential requests Azure AD instance metadata
|
||||
// private clouds such as Azure Stack. It determines whether the credential requests Microsoft Entra instance metadata
|
||||
// from https://login.microsoft.com before authenticating. Setting this to true will skip this request, making
|
||||
// the application responsible for ensuring the configured authority is valid and trustworthy.
|
||||
DisableInstanceDiscovery bool
|
||||
|
||||
// tokenCachePersistenceOptions enables persistent token caching when not nil.
|
||||
tokenCachePersistenceOptions *tokenCachePersistenceOptions
|
||||
}
|
||||
|
||||
// ClientSecretCredential authenticates an application with a client secret.
|
||||
type ClientSecretCredential struct {
|
||||
client confidentialClient
|
||||
s *syncer
|
||||
client *confidentialClient
|
||||
}
|
||||
|
||||
// NewClientSecretCredential constructs a ClientSecretCredential. Pass nil for options to accept defaults.
|
||||
|
@ -46,30 +50,26 @@ func NewClientSecretCredential(tenantID string, clientID string, clientSecret st
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c, err := getConfidentialClient(
|
||||
clientID, tenantID, cred, &options.ClientOptions, confidential.WithInstanceDiscovery(!options.DisableInstanceDiscovery),
|
||||
)
|
||||
msalOpts := confidentialClientOptions{
|
||||
AdditionallyAllowedTenants: options.AdditionallyAllowedTenants,
|
||||
ClientOptions: options.ClientOptions,
|
||||
DisableInstanceDiscovery: options.DisableInstanceDiscovery,
|
||||
tokenCachePersistenceOptions: options.tokenCachePersistenceOptions,
|
||||
}
|
||||
c, err := newConfidentialClient(tenantID, clientID, credNameSecret, cred, msalOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
csc := ClientSecretCredential{client: c}
|
||||
csc.s = newSyncer(credNameSecret, tenantID, options.AdditionallyAllowedTenants, csc.requestToken, csc.silentAuth)
|
||||
return &csc, nil
|
||||
return &ClientSecretCredential{client: c}, nil
|
||||
}
|
||||
|
||||
// GetToken requests an access token from Azure Active Directory. This method is called automatically by Azure SDK clients.
|
||||
// GetToken requests an access token from Microsoft Entra ID. This method is called automatically by Azure SDK clients.
|
||||
func (c *ClientSecretCredential) GetToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
return c.s.GetToken(ctx, opts)
|
||||
}
|
||||
|
||||
func (c *ClientSecretCredential) silentAuth(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
ar, err := c.client.AcquireTokenSilent(ctx, opts.Scopes, confidential.WithTenantID(opts.TenantID))
|
||||
return azcore.AccessToken{Token: ar.AccessToken, ExpiresOn: ar.ExpiresOn.UTC()}, err
|
||||
}
|
||||
|
||||
func (c *ClientSecretCredential) requestToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
ar, err := c.client.AcquireTokenByCredential(ctx, opts.Scopes, confidential.WithTenantID(opts.TenantID))
|
||||
return azcore.AccessToken{Token: ar.AccessToken, ExpiresOn: ar.ExpiresOn.UTC()}, err
|
||||
var err error
|
||||
ctx, endSpan := runtime.StartSpan(ctx, credNameSecret+"."+traceOpGetToken, c.client.azClient.Tracer(), nil)
|
||||
defer func() { endSpan(err) }()
|
||||
tk, err := c.client.GetToken(ctx, opts)
|
||||
return tk, err
|
||||
}
|
||||
|
||||
var _ azcore.TokenCredential = (*ClientSecretCredential)(nil)
|
||||
|
|
184
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/confidential_client.go
generated
vendored
Normal file
184
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/confidential_client.go
generated
vendored
Normal file
|
@ -0,0 +1,184 @@
|
|||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package azidentity
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azidentity/internal"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/internal/log"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential"
|
||||
)
|
||||
|
||||
type confidentialClientOptions struct {
|
||||
azcore.ClientOptions
|
||||
|
||||
AdditionallyAllowedTenants []string
|
||||
// Assertion for on-behalf-of authentication
|
||||
Assertion string
|
||||
DisableInstanceDiscovery, SendX5C bool
|
||||
tokenCachePersistenceOptions *tokenCachePersistenceOptions
|
||||
}
|
||||
|
||||
// confidentialClient wraps the MSAL confidential client
|
||||
type confidentialClient struct {
|
||||
cae, noCAE msalConfidentialClient
|
||||
caeMu, noCAEMu, clientMu *sync.Mutex
|
||||
clientID, tenantID string
|
||||
cred confidential.Credential
|
||||
host string
|
||||
name string
|
||||
opts confidentialClientOptions
|
||||
region string
|
||||
azClient *azcore.Client
|
||||
}
|
||||
|
||||
func newConfidentialClient(tenantID, clientID, name string, cred confidential.Credential, opts confidentialClientOptions) (*confidentialClient, error) {
|
||||
if !validTenantID(tenantID) {
|
||||
return nil, errInvalidTenantID
|
||||
}
|
||||
host, err := setAuthorityHost(opts.Cloud)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err := azcore.NewClient(module, version, runtime.PipelineOptions{
|
||||
Tracing: runtime.TracingOptions{
|
||||
Namespace: traceNamespace,
|
||||
},
|
||||
}, &opts.ClientOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts.AdditionallyAllowedTenants = resolveAdditionalTenants(opts.AdditionallyAllowedTenants)
|
||||
return &confidentialClient{
|
||||
caeMu: &sync.Mutex{},
|
||||
clientID: clientID,
|
||||
clientMu: &sync.Mutex{},
|
||||
cred: cred,
|
||||
host: host,
|
||||
name: name,
|
||||
noCAEMu: &sync.Mutex{},
|
||||
opts: opts,
|
||||
region: os.Getenv(azureRegionalAuthorityName),
|
||||
tenantID: tenantID,
|
||||
azClient: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetToken requests an access token from MSAL, checking the cache first.
|
||||
func (c *confidentialClient) GetToken(ctx context.Context, tro policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
if len(tro.Scopes) < 1 {
|
||||
return azcore.AccessToken{}, fmt.Errorf("%s.GetToken() requires at least one scope", c.name)
|
||||
}
|
||||
// we don't resolve the tenant for managed identities because they acquire tokens only from their home tenants
|
||||
if c.name != credNameManagedIdentity {
|
||||
tenant, err := c.resolveTenant(tro.TenantID)
|
||||
if err != nil {
|
||||
return azcore.AccessToken{}, err
|
||||
}
|
||||
tro.TenantID = tenant
|
||||
}
|
||||
client, mu, err := c.client(tro)
|
||||
if err != nil {
|
||||
return azcore.AccessToken{}, err
|
||||
}
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
var ar confidential.AuthResult
|
||||
if c.opts.Assertion != "" {
|
||||
ar, err = client.AcquireTokenOnBehalfOf(ctx, c.opts.Assertion, tro.Scopes, confidential.WithClaims(tro.Claims), confidential.WithTenantID(tro.TenantID))
|
||||
} else {
|
||||
ar, err = client.AcquireTokenSilent(ctx, tro.Scopes, confidential.WithClaims(tro.Claims), confidential.WithTenantID(tro.TenantID))
|
||||
if err != nil {
|
||||
ar, err = client.AcquireTokenByCredential(ctx, tro.Scopes, confidential.WithClaims(tro.Claims), confidential.WithTenantID(tro.TenantID))
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
// We could get a credentialUnavailableError from managed identity authentication because in that case the error comes from our code.
|
||||
// We return it directly because it affects the behavior of credential chains. Otherwise, we return AuthenticationFailedError.
|
||||
var unavailableErr credentialUnavailable
|
||||
if !errors.As(err, &unavailableErr) {
|
||||
res := getResponseFromError(err)
|
||||
err = newAuthenticationFailedError(c.name, err.Error(), res, err)
|
||||
}
|
||||
} else {
|
||||
msg := fmt.Sprintf("%s.GetToken() acquired a token for scope %q", c.name, strings.Join(ar.GrantedScopes, ", "))
|
||||
log.Write(EventAuthentication, msg)
|
||||
}
|
||||
return azcore.AccessToken{Token: ar.AccessToken, ExpiresOn: ar.ExpiresOn.UTC()}, err
|
||||
}
|
||||
|
||||
func (c *confidentialClient) client(tro policy.TokenRequestOptions) (msalConfidentialClient, *sync.Mutex, error) {
|
||||
c.clientMu.Lock()
|
||||
defer c.clientMu.Unlock()
|
||||
if tro.EnableCAE {
|
||||
if c.cae == nil {
|
||||
client, err := c.newMSALClient(true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
c.cae = client
|
||||
}
|
||||
return c.cae, c.caeMu, nil
|
||||
}
|
||||
if c.noCAE == nil {
|
||||
client, err := c.newMSALClient(false)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
c.noCAE = client
|
||||
}
|
||||
return c.noCAE, c.noCAEMu, nil
|
||||
}
|
||||
|
||||
func (c *confidentialClient) newMSALClient(enableCAE bool) (msalConfidentialClient, error) {
|
||||
cache, err := internal.NewCache(c.opts.tokenCachePersistenceOptions, enableCAE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authority := runtime.JoinPaths(c.host, c.tenantID)
|
||||
o := []confidential.Option{
|
||||
confidential.WithAzureRegion(c.region),
|
||||
confidential.WithCache(cache),
|
||||
confidential.WithHTTPClient(c),
|
||||
}
|
||||
if enableCAE {
|
||||
o = append(o, confidential.WithClientCapabilities(cp1))
|
||||
}
|
||||
if c.opts.SendX5C {
|
||||
o = append(o, confidential.WithX5C())
|
||||
}
|
||||
if c.opts.DisableInstanceDiscovery || strings.ToLower(c.tenantID) == "adfs" {
|
||||
o = append(o, confidential.WithInstanceDiscovery(false))
|
||||
}
|
||||
return confidential.New(authority, c.clientID, c.cred, o...)
|
||||
}
|
||||
|
||||
// resolveTenant returns the correct WithTenantID() argument for a token request given the client's
|
||||
// configuration, or an error when that configuration doesn't allow the specified tenant
|
||||
func (c *confidentialClient) resolveTenant(specified string) (string, error) {
|
||||
return resolveTenant(c.tenantID, specified, c.name, c.opts.AdditionallyAllowedTenants)
|
||||
}
|
||||
|
||||
// these methods satisfy the MSAL ops.HTTPClient interface
|
||||
|
||||
func (c *confidentialClient) CloseIdleConnections() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
func (c *confidentialClient) Do(r *http.Request) (*http.Response, error) {
|
||||
return doForClient(c.azClient, r)
|
||||
}
|
92
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/default_azure_credential.go
generated
vendored
92
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/default_azure_credential.go
generated
vendored
|
@ -8,10 +8,8 @@ package azidentity
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
|
@ -21,6 +19,8 @@ import (
|
|||
// DefaultAzureCredentialOptions contains optional parameters for DefaultAzureCredential.
|
||||
// These options may not apply to all credentials in the chain.
|
||||
type DefaultAzureCredentialOptions struct {
|
||||
// ClientOptions has additional options for credentials that use an Azure SDK HTTP pipeline. These options don't apply
|
||||
// to credential types that authenticate via external tools such as the Azure CLI.
|
||||
azcore.ClientOptions
|
||||
|
||||
// AdditionallyAllowedTenants specifies additional tenants for which the credential may acquire tokens. Add
|
||||
|
@ -28,12 +28,11 @@ type DefaultAzureCredentialOptions struct {
|
|||
// set as a semicolon delimited list of tenants in the environment variable AZURE_ADDITIONALLY_ALLOWED_TENANTS.
|
||||
AdditionallyAllowedTenants []string
|
||||
// DisableInstanceDiscovery should be set true only by applications authenticating in disconnected clouds, or
|
||||
// private clouds such as Azure Stack. It determines whether the credential requests Azure AD instance metadata
|
||||
// private clouds such as Azure Stack. It determines whether the credential requests Microsoft Entra instance metadata
|
||||
// from https://login.microsoft.com before authenticating. Setting this to true will skip this request, making
|
||||
// the application responsible for ensuring the configured authority is valid and trustworthy.
|
||||
DisableInstanceDiscovery bool
|
||||
// TenantID identifies the tenant the Azure CLI should authenticate in.
|
||||
// Defaults to the CLI's default tenant, which is typically the home tenant of the user logged in to the CLI.
|
||||
// TenantID sets the default tenant for authentication via the Azure CLI and workload identity.
|
||||
TenantID string
|
||||
}
|
||||
|
||||
|
@ -48,6 +47,7 @@ type DefaultAzureCredentialOptions struct {
|
|||
// more control over its configuration.
|
||||
// - [ManagedIdentityCredential]
|
||||
// - [AzureCLICredential]
|
||||
// - [AzureDeveloperCLICredential]
|
||||
//
|
||||
// Consult the documentation for these credential types for more information on how they authenticate.
|
||||
// Once a credential has successfully authenticated, DefaultAzureCredential will use that credential for
|
||||
|
@ -83,11 +83,11 @@ func NewDefaultAzureCredential(options *DefaultAzureCredentialOptions) (*Default
|
|||
creds = append(creds, &defaultCredentialErrorReporter{credType: "EnvironmentCredential", err: err})
|
||||
}
|
||||
|
||||
// workload identity requires values for AZURE_AUTHORITY_HOST, AZURE_CLIENT_ID, AZURE_FEDERATED_TOKEN_FILE, AZURE_TENANT_ID
|
||||
wic, err := NewWorkloadIdentityCredential(&WorkloadIdentityCredentialOptions{
|
||||
AdditionallyAllowedTenants: additionalTenants,
|
||||
ClientOptions: options.ClientOptions,
|
||||
DisableInstanceDiscovery: options.DisableInstanceDiscovery,
|
||||
TenantID: options.TenantID,
|
||||
})
|
||||
if err == nil {
|
||||
creds = append(creds, wic)
|
||||
|
@ -95,13 +95,14 @@ func NewDefaultAzureCredential(options *DefaultAzureCredentialOptions) (*Default
|
|||
errorMessages = append(errorMessages, credNameWorkloadIdentity+": "+err.Error())
|
||||
creds = append(creds, &defaultCredentialErrorReporter{credType: credNameWorkloadIdentity, err: err})
|
||||
}
|
||||
o := &ManagedIdentityCredentialOptions{ClientOptions: options.ClientOptions}
|
||||
|
||||
o := &ManagedIdentityCredentialOptions{ClientOptions: options.ClientOptions, dac: true}
|
||||
if ID, ok := os.LookupEnv(azureClientID); ok {
|
||||
o.ID = ClientID(ID)
|
||||
}
|
||||
miCred, err := NewManagedIdentityCredential(o)
|
||||
if err == nil {
|
||||
creds = append(creds, &timeoutWrapper{mic: miCred, timeout: time.Second})
|
||||
creds = append(creds, miCred)
|
||||
} else {
|
||||
errorMessages = append(errorMessages, credNameManagedIdentity+": "+err.Error())
|
||||
creds = append(creds, &defaultCredentialErrorReporter{credType: credNameManagedIdentity, err: err})
|
||||
|
@ -115,9 +116,19 @@ func NewDefaultAzureCredential(options *DefaultAzureCredentialOptions) (*Default
|
|||
creds = append(creds, &defaultCredentialErrorReporter{credType: credNameAzureCLI, err: err})
|
||||
}
|
||||
|
||||
err = defaultAzureCredentialConstructorErrorHandler(len(creds), errorMessages)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
azdCred, err := NewAzureDeveloperCLICredential(&AzureDeveloperCLICredentialOptions{
|
||||
AdditionallyAllowedTenants: additionalTenants,
|
||||
TenantID: options.TenantID,
|
||||
})
|
||||
if err == nil {
|
||||
creds = append(creds, azdCred)
|
||||
} else {
|
||||
errorMessages = append(errorMessages, credNameAzureDeveloperCLI+": "+err.Error())
|
||||
creds = append(creds, &defaultCredentialErrorReporter{credType: credNameAzureDeveloperCLI, err: err})
|
||||
}
|
||||
|
||||
if len(errorMessages) > 0 {
|
||||
log.Writef(EventAuthentication, "NewDefaultAzureCredential failed to initialize some credentials:\n\t%s", strings.Join(errorMessages, "\n\t"))
|
||||
}
|
||||
|
||||
chain, err := NewChainedTokenCredential(creds, nil)
|
||||
|
@ -128,27 +139,13 @@ func NewDefaultAzureCredential(options *DefaultAzureCredentialOptions) (*Default
|
|||
return &DefaultAzureCredential{chain: chain}, nil
|
||||
}
|
||||
|
||||
// GetToken requests an access token from Azure Active Directory. This method is called automatically by Azure SDK clients.
|
||||
// GetToken requests an access token from Microsoft Entra ID. This method is called automatically by Azure SDK clients.
|
||||
func (c *DefaultAzureCredential) GetToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
return c.chain.GetToken(ctx, opts)
|
||||
}
|
||||
|
||||
var _ azcore.TokenCredential = (*DefaultAzureCredential)(nil)
|
||||
|
||||
func defaultAzureCredentialConstructorErrorHandler(numberOfSuccessfulCredentials int, errorMessages []string) (err error) {
|
||||
errorMessage := strings.Join(errorMessages, "\n\t")
|
||||
|
||||
if numberOfSuccessfulCredentials == 0 {
|
||||
return errors.New(errorMessage)
|
||||
}
|
||||
|
||||
if len(errorMessages) != 0 {
|
||||
log.Writef(EventAuthentication, "NewDefaultAzureCredential failed to initialize some credentials:\n\t%s", errorMessage)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// defaultCredentialErrorReporter is a substitute for credentials that couldn't be constructed.
|
||||
// Its GetToken method always returns a credentialUnavailableError having the same message as
|
||||
// the error that prevented constructing the credential. This ensures the message is present
|
||||
|
@ -159,51 +156,10 @@ type defaultCredentialErrorReporter struct {
|
|||
}
|
||||
|
||||
func (d *defaultCredentialErrorReporter) GetToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
if _, ok := d.err.(*credentialUnavailableError); ok {
|
||||
if _, ok := d.err.(credentialUnavailable); ok {
|
||||
return azcore.AccessToken{}, d.err
|
||||
}
|
||||
return azcore.AccessToken{}, newCredentialUnavailableError(d.credType, d.err.Error())
|
||||
}
|
||||
|
||||
var _ azcore.TokenCredential = (*defaultCredentialErrorReporter)(nil)
|
||||
|
||||
// timeoutWrapper prevents a potentially very long timeout when managed identity isn't available
|
||||
type timeoutWrapper struct {
|
||||
mic *ManagedIdentityCredential
|
||||
// timeout applies to all auth attempts until one doesn't time out
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// GetToken wraps DefaultAzureCredential's initial managed identity auth attempt with a short timeout
|
||||
// because managed identity may not be available and connecting to IMDS can take several minutes to time out.
|
||||
func (w *timeoutWrapper) GetToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
var tk azcore.AccessToken
|
||||
var err error
|
||||
// no need to synchronize around this value because it's written only within ChainedTokenCredential's critical section
|
||||
if w.timeout > 0 {
|
||||
c, cancel := context.WithTimeout(ctx, w.timeout)
|
||||
defer cancel()
|
||||
tk, err = w.mic.GetToken(c, opts)
|
||||
if isAuthFailedDueToContext(err) {
|
||||
err = newCredentialUnavailableError(credNameManagedIdentity, "managed identity timed out")
|
||||
} else {
|
||||
// some managed identity implementation is available, so don't apply the timeout to future calls
|
||||
w.timeout = 0
|
||||
}
|
||||
} else {
|
||||
tk, err = w.mic.GetToken(ctx, opts)
|
||||
}
|
||||
return tk, err
|
||||
}
|
||||
|
||||
// unwraps nested AuthenticationFailedErrors to get the root error
|
||||
func isAuthFailedDueToContext(err error) bool {
|
||||
for {
|
||||
var authFailedErr *AuthenticationFailedError
|
||||
if !errors.As(err, &authFailedErr) {
|
||||
break
|
||||
}
|
||||
err = authFailedErr.err
|
||||
}
|
||||
return errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded)
|
||||
}
|
||||
|
|
38
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/developer_credential_util.go
generated
vendored
Normal file
38
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/developer_credential_util.go
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package azidentity
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
// cliTimeout is the default timeout for authentication attempts via CLI tools
|
||||
const cliTimeout = 10 * time.Second
|
||||
|
||||
// unavailableIfInChain returns err or, if the credential was invoked by DefaultAzureCredential, a
|
||||
// credentialUnavailableError having the same message. This ensures DefaultAzureCredential will try
|
||||
// the next credential in its chain (another developer credential).
|
||||
func unavailableIfInChain(err error, inDefaultChain bool) error {
|
||||
if err != nil && inDefaultChain {
|
||||
var unavailableErr credentialUnavailable
|
||||
if !errors.As(err, &unavailableErr) {
|
||||
err = newCredentialUnavailableError(credNameAzureDeveloperCLI, err.Error())
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// validScope is for credentials authenticating via external tools. The authority validates scopes for all other credentials.
|
||||
func validScope(scope string) bool {
|
||||
for _, r := range scope {
|
||||
if !(alphanumeric(r) || r == '.' || r == '-' || r == '_' || r == '/' || r == ':') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
96
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/device_code_credential.go
generated
vendored
96
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/device_code_credential.go
generated
vendored
|
@ -12,7 +12,7 @@ import (
|
|||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/public"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
|
||||
)
|
||||
|
||||
const credNameDeviceCode = "DeviceCodeCredential"
|
||||
|
@ -24,19 +24,34 @@ type DeviceCodeCredentialOptions struct {
|
|||
// AdditionallyAllowedTenants specifies additional tenants for which the credential may acquire
|
||||
// tokens. Add the wildcard value "*" to allow the credential to acquire tokens for any tenant.
|
||||
AdditionallyAllowedTenants []string
|
||||
|
||||
// authenticationRecord returned by a call to a credential's Authenticate method. Set this option
|
||||
// to enable the credential to use data from a previous authentication.
|
||||
authenticationRecord authenticationRecord
|
||||
|
||||
// ClientID is the ID of the application users will authenticate to.
|
||||
// Defaults to the ID of an Azure development application.
|
||||
ClientID string
|
||||
|
||||
// disableAutomaticAuthentication prevents the credential from automatically prompting the user to authenticate.
|
||||
// When this option is true, GetToken will return authenticationRequiredError when user interaction is necessary
|
||||
// to acquire a token.
|
||||
disableAutomaticAuthentication bool
|
||||
|
||||
// DisableInstanceDiscovery should be set true only by applications authenticating in disconnected clouds, or
|
||||
// private clouds such as Azure Stack. It determines whether the credential requests Azure AD instance metadata
|
||||
// private clouds such as Azure Stack. It determines whether the credential requests Microsoft Entra instance metadata
|
||||
// from https://login.microsoft.com before authenticating. Setting this to true will skip this request, making
|
||||
// the application responsible for ensuring the configured authority is valid and trustworthy.
|
||||
DisableInstanceDiscovery bool
|
||||
// TenantID is the Azure Active Directory tenant the credential authenticates in. Defaults to the
|
||||
|
||||
// TenantID is the Microsoft Entra tenant the credential authenticates in. Defaults to the
|
||||
// "organizations" tenant, which can authenticate work and school accounts. Required for single-tenant
|
||||
// applications.
|
||||
TenantID string
|
||||
|
||||
// tokenCachePersistenceOptions enables persistent token caching when not nil.
|
||||
tokenCachePersistenceOptions *tokenCachePersistenceOptions
|
||||
|
||||
// UserPrompt controls how the credential presents authentication instructions. The credential calls
|
||||
// this function with authentication details when it receives a device code. By default, the credential
|
||||
// prints these details to stdout.
|
||||
|
@ -64,20 +79,17 @@ type DeviceCodeMessage struct {
|
|||
UserCode string `json:"user_code"`
|
||||
// VerificationURL is the URL at which the user must authenticate.
|
||||
VerificationURL string `json:"verification_uri"`
|
||||
// Message is user instruction from Azure Active Directory.
|
||||
// Message is user instruction from Microsoft Entra ID.
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// DeviceCodeCredential acquires tokens for a user via the device code flow, which has the
|
||||
// user browse to an Azure Active Directory URL, enter a code, and authenticate. It's useful
|
||||
// user browse to a Microsoft Entra URL, enter a code, and authenticate. It's useful
|
||||
// for authenticating a user in an environment without a web browser, such as an SSH session.
|
||||
// If a web browser is available, InteractiveBrowserCredential is more convenient because it
|
||||
// If a web browser is available, [InteractiveBrowserCredential] is more convenient because it
|
||||
// automatically opens a browser to the login page.
|
||||
type DeviceCodeCredential struct {
|
||||
account public.Account
|
||||
client publicClient
|
||||
s *syncer
|
||||
prompt func(context.Context, DeviceCodeMessage) error
|
||||
client *publicClient
|
||||
}
|
||||
|
||||
// NewDeviceCodeCredential creates a DeviceCodeCredential. Pass nil to accept default options.
|
||||
|
@ -87,50 +99,40 @@ func NewDeviceCodeCredential(options *DeviceCodeCredentialOptions) (*DeviceCodeC
|
|||
cp = *options
|
||||
}
|
||||
cp.init()
|
||||
c, err := getPublicClient(
|
||||
cp.ClientID, cp.TenantID, &cp.ClientOptions, public.WithInstanceDiscovery(!cp.DisableInstanceDiscovery),
|
||||
)
|
||||
msalOpts := publicClientOptions{
|
||||
AdditionallyAllowedTenants: cp.AdditionallyAllowedTenants,
|
||||
ClientOptions: cp.ClientOptions,
|
||||
DeviceCodePrompt: cp.UserPrompt,
|
||||
DisableAutomaticAuthentication: cp.disableAutomaticAuthentication,
|
||||
DisableInstanceDiscovery: cp.DisableInstanceDiscovery,
|
||||
Record: cp.authenticationRecord,
|
||||
TokenCachePersistenceOptions: cp.tokenCachePersistenceOptions,
|
||||
}
|
||||
c, err := newPublicClient(cp.TenantID, cp.ClientID, credNameDeviceCode, msalOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cred := DeviceCodeCredential{client: c, prompt: cp.UserPrompt}
|
||||
cred.s = newSyncer(credNameDeviceCode, cp.TenantID, cp.AdditionallyAllowedTenants, cred.requestToken, cred.silentAuth)
|
||||
return &cred, nil
|
||||
c.name = credNameDeviceCode
|
||||
return &DeviceCodeCredential{client: c}, nil
|
||||
}
|
||||
|
||||
// GetToken requests an access token from Azure Active Directory. It will begin the device code flow and poll until the user completes authentication.
|
||||
// Authenticate a user via the device code flow. Subsequent calls to GetToken will automatically use the returned AuthenticationRecord.
|
||||
func (c *DeviceCodeCredential) authenticate(ctx context.Context, opts *policy.TokenRequestOptions) (authenticationRecord, error) {
|
||||
var err error
|
||||
ctx, endSpan := runtime.StartSpan(ctx, credNameDeviceCode+"."+traceOpAuthenticate, c.client.azClient.Tracer(), nil)
|
||||
defer func() { endSpan(err) }()
|
||||
tk, err := c.client.Authenticate(ctx, opts)
|
||||
return tk, err
|
||||
}
|
||||
|
||||
// GetToken requests an access token from Microsoft Entra ID. It will begin the device code flow and poll until the user completes authentication.
|
||||
// This method is called automatically by Azure SDK clients.
|
||||
func (c *DeviceCodeCredential) GetToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
return c.s.GetToken(ctx, opts)
|
||||
}
|
||||
|
||||
func (c *DeviceCodeCredential) requestToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
dc, err := c.client.AcquireTokenByDeviceCode(ctx, opts.Scopes, public.WithTenantID(opts.TenantID))
|
||||
if err != nil {
|
||||
return azcore.AccessToken{}, err
|
||||
}
|
||||
err = c.prompt(ctx, DeviceCodeMessage{
|
||||
Message: dc.Result.Message,
|
||||
UserCode: dc.Result.UserCode,
|
||||
VerificationURL: dc.Result.VerificationURL,
|
||||
})
|
||||
if err != nil {
|
||||
return azcore.AccessToken{}, err
|
||||
}
|
||||
ar, err := dc.AuthenticationResult(ctx)
|
||||
if err != nil {
|
||||
return azcore.AccessToken{}, err
|
||||
}
|
||||
c.account = ar.Account
|
||||
return azcore.AccessToken{Token: ar.AccessToken, ExpiresOn: ar.ExpiresOn.UTC()}, err
|
||||
}
|
||||
|
||||
func (c *DeviceCodeCredential) silentAuth(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
ar, err := c.client.AcquireTokenSilent(ctx, opts.Scopes,
|
||||
public.WithSilentAccount(c.account),
|
||||
public.WithTenantID(opts.TenantID),
|
||||
)
|
||||
return azcore.AccessToken{Token: ar.AccessToken, ExpiresOn: ar.ExpiresOn.UTC()}, err
|
||||
var err error
|
||||
ctx, endSpan := runtime.StartSpan(ctx, credNameDeviceCode+"."+traceOpGetToken, c.client.azClient.Tracer(), nil)
|
||||
defer func() { endSpan(err) }()
|
||||
tk, err := c.client.GetToken(ctx, opts)
|
||||
return tk, err
|
||||
}
|
||||
|
||||
var _ azcore.TokenCredential = (*DeviceCodeCredential)(nil)
|
||||
|
|
9
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/environment_credential.go
generated
vendored
9
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/environment_credential.go
generated
vendored
|
@ -25,7 +25,7 @@ type EnvironmentCredentialOptions struct {
|
|||
azcore.ClientOptions
|
||||
|
||||
// DisableInstanceDiscovery should be set true only by applications authenticating in disconnected clouds, or
|
||||
// private clouds such as Azure Stack. It determines whether the credential requests Azure AD instance metadata
|
||||
// private clouds such as Azure Stack. It determines whether the credential requests Microsoft Entra instance metadata
|
||||
// from https://login.microsoft.com before authenticating. Setting this to true will skip this request, making
|
||||
// the application responsible for ensuring the configured authority is valid and trustworthy.
|
||||
DisableInstanceDiscovery bool
|
||||
|
@ -57,6 +57,9 @@ type EnvironmentCredentialOptions struct {
|
|||
//
|
||||
// AZURE_CLIENT_CERTIFICATE_PASSWORD: (optional) password for the certificate file.
|
||||
//
|
||||
// Note that this credential uses [ParseCertificates] to load the certificate and key from the file. If this
|
||||
// function isn't able to parse your certificate, use [ClientCertificateCredential] instead.
|
||||
//
|
||||
// # User with username and password
|
||||
//
|
||||
// AZURE_TENANT_ID: (optional) tenant to authenticate in. Defaults to "organizations".
|
||||
|
@ -121,7 +124,7 @@ func NewEnvironmentCredential(options *EnvironmentCredentialOptions) (*Environme
|
|||
}
|
||||
certs, key, err := ParseCertificates(certData, password)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to load certificate from "%s": %v`, certPath, err)
|
||||
return nil, fmt.Errorf("failed to parse %q due to error %q. This may be due to a limitation of this module's certificate loader. Consider calling NewClientCertificateCredential instead", certPath, err.Error())
|
||||
}
|
||||
o := &ClientCertificateCredentialOptions{
|
||||
AdditionallyAllowedTenants: additionalTenants,
|
||||
|
@ -156,7 +159,7 @@ func NewEnvironmentCredential(options *EnvironmentCredentialOptions) (*Environme
|
|||
return nil, errors.New("incomplete environment variable configuration. Only AZURE_TENANT_ID and AZURE_CLIENT_ID are set")
|
||||
}
|
||||
|
||||
// GetToken requests an access token from Azure Active Directory. This method is called automatically by Azure SDK clients.
|
||||
// GetToken requests an access token from Microsoft Entra ID. This method is called automatically by Azure SDK clients.
|
||||
func (c *EnvironmentCredential) GetToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
return c.cred.GetToken(ctx, opts)
|
||||
}
|
||||
|
|
65
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/errors.go
generated
vendored
65
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/errors.go
generated
vendored
|
@ -11,9 +11,10 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/internal/errorinfo"
|
||||
msal "github.com/AzureAD/microsoft-authentication-library-for-go/apps/errors"
|
||||
)
|
||||
|
@ -52,22 +53,27 @@ func (e *AuthenticationFailedError) Error() string {
|
|||
return e.credType + ": " + e.message
|
||||
}
|
||||
msg := &bytes.Buffer{}
|
||||
fmt.Fprintf(msg, e.credType+" authentication failed\n")
|
||||
fmt.Fprintf(msg, "%s %s://%s%s\n", e.RawResponse.Request.Method, e.RawResponse.Request.URL.Scheme, e.RawResponse.Request.URL.Host, e.RawResponse.Request.URL.Path)
|
||||
fmt.Fprintf(msg, "%s authentication failed. %s\n", e.credType, e.message)
|
||||
if e.RawResponse.Request != nil {
|
||||
fmt.Fprintf(msg, "%s %s://%s%s\n", e.RawResponse.Request.Method, e.RawResponse.Request.URL.Scheme, e.RawResponse.Request.URL.Host, e.RawResponse.Request.URL.Path)
|
||||
} else {
|
||||
// this happens when the response is created from a custom HTTP transporter,
|
||||
// which doesn't guarantee to bind the original request to the response
|
||||
fmt.Fprintln(msg, "Request information not available")
|
||||
}
|
||||
fmt.Fprintln(msg, "--------------------------------------------------------------------------------")
|
||||
fmt.Fprintf(msg, "RESPONSE %s\n", e.RawResponse.Status)
|
||||
fmt.Fprintln(msg, "--------------------------------------------------------------------------------")
|
||||
body, err := io.ReadAll(e.RawResponse.Body)
|
||||
e.RawResponse.Body.Close()
|
||||
if err != nil {
|
||||
body, err := runtime.Payload(e.RawResponse)
|
||||
switch {
|
||||
case err != nil:
|
||||
fmt.Fprintf(msg, "Error reading response body: %v", err)
|
||||
} else if len(body) > 0 {
|
||||
e.RawResponse.Body = io.NopCloser(bytes.NewReader(body))
|
||||
case len(body) > 0:
|
||||
if err := json.Indent(msg, body, "", " "); err != nil {
|
||||
// failed to pretty-print so just dump it verbatim
|
||||
fmt.Fprint(msg, string(body))
|
||||
}
|
||||
} else {
|
||||
default:
|
||||
fmt.Fprint(msg, "Response contained no body")
|
||||
}
|
||||
fmt.Fprintln(msg, "\n--------------------------------------------------------------------------------")
|
||||
|
@ -75,6 +81,8 @@ func (e *AuthenticationFailedError) Error() string {
|
|||
switch e.credType {
|
||||
case credNameAzureCLI:
|
||||
anchor = "azure-cli"
|
||||
case credNameAzureDeveloperCLI:
|
||||
anchor = "azd"
|
||||
case credNameCert:
|
||||
anchor = "client-cert"
|
||||
case credNameSecret:
|
||||
|
@ -99,8 +107,34 @@ func (*AuthenticationFailedError) NonRetriable() {
|
|||
|
||||
var _ errorinfo.NonRetriable = (*AuthenticationFailedError)(nil)
|
||||
|
||||
// credentialUnavailableError indicates a credential can't attempt authentication because it lacks required
|
||||
// data or state
|
||||
// authenticationRequiredError indicates a credential's Authenticate method must be called to acquire a token
|
||||
// because the credential requires user interaction and is configured not to request it automatically.
|
||||
type authenticationRequiredError struct {
|
||||
credentialUnavailableError
|
||||
|
||||
// TokenRequestOptions for the required token. Pass this to the credential's Authenticate method.
|
||||
TokenRequestOptions policy.TokenRequestOptions
|
||||
}
|
||||
|
||||
func newauthenticationRequiredError(credType string, tro policy.TokenRequestOptions) error {
|
||||
return &authenticationRequiredError{
|
||||
credentialUnavailableError: credentialUnavailableError{
|
||||
credType + " can't acquire a token without user interaction. Call Authenticate to authenticate a user interactively",
|
||||
},
|
||||
TokenRequestOptions: tro,
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
_ credentialUnavailable = (*authenticationRequiredError)(nil)
|
||||
_ errorinfo.NonRetriable = (*authenticationRequiredError)(nil)
|
||||
)
|
||||
|
||||
type credentialUnavailable interface {
|
||||
error
|
||||
credentialUnavailable()
|
||||
}
|
||||
|
||||
type credentialUnavailableError struct {
|
||||
message string
|
||||
}
|
||||
|
@ -124,6 +158,11 @@ func (e *credentialUnavailableError) Error() string {
|
|||
}
|
||||
|
||||
// NonRetriable is a marker method indicating this error should not be retried. It has no implementation.
|
||||
func (e *credentialUnavailableError) NonRetriable() {}
|
||||
func (*credentialUnavailableError) NonRetriable() {}
|
||||
|
||||
var _ errorinfo.NonRetriable = (*credentialUnavailableError)(nil)
|
||||
func (*credentialUnavailableError) credentialUnavailable() {}
|
||||
|
||||
var (
|
||||
_ credentialUnavailable = (*credentialUnavailableError)(nil)
|
||||
_ errorinfo.NonRetriable = (*credentialUnavailableError)(nil)
|
||||
)
|
||||
|
|
6
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/go.work
generated
vendored
Normal file
6
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/go.work
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
go 1.18
|
||||
|
||||
use (
|
||||
.
|
||||
./cache
|
||||
)
|
60
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/go.work.sum
generated
vendored
Normal file
60
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/go.work.sum
generated
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0-beta.1 h1:ODs3brnqQM99Tq1PffODpAViYv3Bf8zOg464MU7p5ew=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0-beta.1/go.mod h1:3Ug6Qzto9anB6mGlEdgYMDF5zHQ+wwhEaYR4s17PHMw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0 h1:fb8kj/Dh4CSwgsOzHeZY4Xh68cFVbzXx+ONXGMY//4w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0/go.mod h1:uReU2sSxZExRPBAg3qKzmAucSi51+SP1OhohieR821Q=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2/go.mod h1:yInRyqWXAuaPrgI7p70+lDDgh3mlBohis29jGMISnmc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/keybase/dbus v0.0.0-20220506165403-5aa21ea2c23a/go.mod h1:YPNKjjE7Ubp9dTbnWvsP3HT+hYnY6TfXzubYTBeUxc8=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
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/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -11,7 +11,7 @@ import (
|
|||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/public"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
|
||||
)
|
||||
|
||||
const credNameBrowser = "InteractiveBrowserCredential"
|
||||
|
@ -23,26 +23,40 @@ type InteractiveBrowserCredentialOptions struct {
|
|||
// AdditionallyAllowedTenants specifies additional tenants for which the credential may acquire
|
||||
// tokens. Add the wildcard value "*" to allow the credential to acquire tokens for any tenant.
|
||||
AdditionallyAllowedTenants []string
|
||||
|
||||
// authenticationRecord returned by a call to a credential's Authenticate method. Set this option
|
||||
// to enable the credential to use data from a previous authentication.
|
||||
authenticationRecord authenticationRecord
|
||||
|
||||
// ClientID is the ID of the application users will authenticate to.
|
||||
// Defaults to the ID of an Azure development application.
|
||||
ClientID string
|
||||
|
||||
// disableAutomaticAuthentication prevents the credential from automatically prompting the user to authenticate.
|
||||
// When this option is true, GetToken will return authenticationRequiredError when user interaction is necessary
|
||||
// to acquire a token.
|
||||
disableAutomaticAuthentication bool
|
||||
|
||||
// DisableInstanceDiscovery should be set true only by applications authenticating in disconnected clouds, or
|
||||
// private clouds such as Azure Stack. It determines whether the credential requests Azure AD instance metadata
|
||||
// private clouds such as Azure Stack. It determines whether the credential requests Microsoft Entra instance metadata
|
||||
// from https://login.microsoft.com before authenticating. Setting this to true will skip this request, making
|
||||
// the application responsible for ensuring the configured authority is valid and trustworthy.
|
||||
DisableInstanceDiscovery bool
|
||||
|
||||
// LoginHint pre-populates the account prompt with a username. Users may choose to authenticate a different account.
|
||||
LoginHint string
|
||||
// RedirectURL is the URL Azure Active Directory will redirect to with the access token. This is required
|
||||
|
||||
// RedirectURL is the URL Microsoft Entra ID will redirect to with the access token. This is required
|
||||
// only when setting ClientID, and must match a redirect URI in the application's registration.
|
||||
// Applications which have registered "http://localhost" as a redirect URI need not set this option.
|
||||
RedirectURL string
|
||||
|
||||
// TenantID is the Azure Active Directory tenant the credential authenticates in. Defaults to the
|
||||
// TenantID is the Microsoft Entra tenant the credential authenticates in. Defaults to the
|
||||
// "organizations" tenant, which can authenticate work and school accounts.
|
||||
TenantID string
|
||||
|
||||
// tokenCachePersistenceOptions enables persistent token caching when not nil.
|
||||
tokenCachePersistenceOptions *tokenCachePersistenceOptions
|
||||
}
|
||||
|
||||
func (o *InteractiveBrowserCredentialOptions) init() {
|
||||
|
@ -56,10 +70,7 @@ func (o *InteractiveBrowserCredentialOptions) init() {
|
|||
|
||||
// InteractiveBrowserCredential opens a browser to interactively authenticate a user.
|
||||
type InteractiveBrowserCredential struct {
|
||||
account public.Account
|
||||
client publicClient
|
||||
options InteractiveBrowserCredentialOptions
|
||||
s *syncer
|
||||
client *publicClient
|
||||
}
|
||||
|
||||
// NewInteractiveBrowserCredential constructs a new InteractiveBrowserCredential. Pass nil to accept default options.
|
||||
|
@ -69,38 +80,39 @@ func NewInteractiveBrowserCredential(options *InteractiveBrowserCredentialOption
|
|||
cp = *options
|
||||
}
|
||||
cp.init()
|
||||
c, err := getPublicClient(cp.ClientID, cp.TenantID, &cp.ClientOptions, public.WithInstanceDiscovery(!cp.DisableInstanceDiscovery))
|
||||
msalOpts := publicClientOptions{
|
||||
AdditionallyAllowedTenants: cp.AdditionallyAllowedTenants,
|
||||
ClientOptions: cp.ClientOptions,
|
||||
DisableAutomaticAuthentication: cp.disableAutomaticAuthentication,
|
||||
DisableInstanceDiscovery: cp.DisableInstanceDiscovery,
|
||||
LoginHint: cp.LoginHint,
|
||||
Record: cp.authenticationRecord,
|
||||
RedirectURL: cp.RedirectURL,
|
||||
TokenCachePersistenceOptions: cp.tokenCachePersistenceOptions,
|
||||
}
|
||||
c, err := newPublicClient(cp.TenantID, cp.ClientID, credNameBrowser, msalOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ibc := InteractiveBrowserCredential{client: c, options: cp}
|
||||
ibc.s = newSyncer(credNameBrowser, cp.TenantID, cp.AdditionallyAllowedTenants, ibc.requestToken, ibc.silentAuth)
|
||||
return &ibc, nil
|
||||
return &InteractiveBrowserCredential{client: c}, nil
|
||||
}
|
||||
|
||||
// GetToken requests an access token from Azure Active Directory. This method is called automatically by Azure SDK clients.
|
||||
// Authenticate a user via the default browser. Subsequent calls to GetToken will automatically use the returned AuthenticationRecord.
|
||||
func (c *InteractiveBrowserCredential) authenticate(ctx context.Context, opts *policy.TokenRequestOptions) (authenticationRecord, error) {
|
||||
var err error
|
||||
ctx, endSpan := runtime.StartSpan(ctx, credNameBrowser+"."+traceOpAuthenticate, c.client.azClient.Tracer(), nil)
|
||||
defer func() { endSpan(err) }()
|
||||
tk, err := c.client.Authenticate(ctx, opts)
|
||||
return tk, err
|
||||
}
|
||||
|
||||
// GetToken requests an access token from Microsoft Entra ID. This method is called automatically by Azure SDK clients.
|
||||
func (c *InteractiveBrowserCredential) GetToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
return c.s.GetToken(ctx, opts)
|
||||
}
|
||||
|
||||
func (c *InteractiveBrowserCredential) requestToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
ar, err := c.client.AcquireTokenInteractive(ctx, opts.Scopes,
|
||||
public.WithLoginHint(c.options.LoginHint),
|
||||
public.WithRedirectURI(c.options.RedirectURL),
|
||||
public.WithTenantID(opts.TenantID),
|
||||
)
|
||||
if err == nil {
|
||||
c.account = ar.Account
|
||||
}
|
||||
return azcore.AccessToken{Token: ar.AccessToken, ExpiresOn: ar.ExpiresOn.UTC()}, err
|
||||
}
|
||||
|
||||
func (c *InteractiveBrowserCredential) silentAuth(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
ar, err := c.client.AcquireTokenSilent(ctx, opts.Scopes,
|
||||
public.WithSilentAccount(c.account),
|
||||
public.WithTenantID(opts.TenantID),
|
||||
)
|
||||
return azcore.AccessToken{Token: ar.AccessToken, ExpiresOn: ar.ExpiresOn.UTC()}, err
|
||||
var err error
|
||||
ctx, endSpan := runtime.StartSpan(ctx, credNameBrowser+"."+traceOpGetToken, c.client.azClient.Tracer(), nil)
|
||||
defer func() { endSpan(err) }()
|
||||
tk, err := c.client.GetToken(ctx, opts)
|
||||
return tk, err
|
||||
}
|
||||
|
||||
var _ azcore.TokenCredential = (*InteractiveBrowserCredential)(nil)
|
||||
|
|
18
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/internal/exported.go
generated
vendored
Normal file
18
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/internal/exported.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package internal
|
||||
|
||||
// TokenCachePersistenceOptions contains options for persistent token caching
|
||||
type TokenCachePersistenceOptions struct {
|
||||
// AllowUnencryptedStorage controls whether the cache should fall back to storing its data in plain text
|
||||
// when encryption isn't possible. Setting this true doesn't disable encryption. The cache always attempts
|
||||
// encryption before falling back to plaintext storage.
|
||||
AllowUnencryptedStorage bool
|
||||
|
||||
// Name identifies the cache. Set this to isolate data from other applications.
|
||||
Name string
|
||||
}
|
31
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/internal/internal.go
generated
vendored
Normal file
31
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/internal/internal.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/cache"
|
||||
)
|
||||
|
||||
var errMissingImport = errors.New("import github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache to enable persistent caching")
|
||||
|
||||
// NewCache constructs a persistent token cache when "o" isn't nil. Applications that intend to
|
||||
// use a persistent cache must first import the cache module, which will replace this function
|
||||
// with a platform-specific implementation.
|
||||
var NewCache = func(o *TokenCachePersistenceOptions, enableCAE bool) (cache.ExportReplace, error) {
|
||||
if o == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errMissingImport
|
||||
}
|
||||
|
||||
// CacheFilePath returns the path to the cache file for the given name.
|
||||
// Defining it in this package makes it available to azidentity tests.
|
||||
var CacheFilePath = func(name string) (string, error) {
|
||||
return "", errMissingImport
|
||||
}
|
17
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/managed-identity-matrix.json
generated
vendored
Normal file
17
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/managed-identity-matrix.json
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"include": [
|
||||
{
|
||||
"Agent": {
|
||||
"msi_image": {
|
||||
"ArmTemplateParameters": "@{deployResources = $true}",
|
||||
"OSVmImage": "env:LINUXNEXTVMIMAGE",
|
||||
"Pool": "env:LINUXPOOL"
|
||||
}
|
||||
},
|
||||
"GoVersion": [
|
||||
"1.22.1"
|
||||
],
|
||||
"IDENTITY_IMDS_AVAILABLE": "1"
|
||||
}
|
||||
]
|
||||
}
|
193
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/managed_identity_client.go
generated
vendored
193
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/managed_identity_client.go
generated
vendored
|
@ -14,13 +14,15 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
|
||||
azruntime "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/internal/log"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential"
|
||||
|
@ -28,37 +30,57 @@ import (
|
|||
|
||||
const (
|
||||
arcIMDSEndpoint = "IMDS_ENDPOINT"
|
||||
defaultIdentityClientID = "DEFAULT_IDENTITY_CLIENT_ID"
|
||||
identityEndpoint = "IDENTITY_ENDPOINT"
|
||||
identityHeader = "IDENTITY_HEADER"
|
||||
identityServerThumbprint = "IDENTITY_SERVER_THUMBPRINT"
|
||||
headerMetadata = "Metadata"
|
||||
imdsEndpoint = "http://169.254.169.254/metadata/identity/oauth2/token"
|
||||
miResID = "mi_res_id"
|
||||
msiEndpoint = "MSI_ENDPOINT"
|
||||
msiResID = "msi_res_id"
|
||||
msiSecret = "MSI_SECRET"
|
||||
imdsAPIVersion = "2018-02-01"
|
||||
azureArcAPIVersion = "2019-08-15"
|
||||
qpClientID = "client_id"
|
||||
serviceFabricAPIVersion = "2019-07-01-preview"
|
||||
|
||||
qpClientID = "client_id"
|
||||
qpResID = "mi_res_id"
|
||||
)
|
||||
|
||||
var imdsProbeTimeout = time.Second
|
||||
|
||||
type msiType int
|
||||
|
||||
const (
|
||||
msiTypeAppService msiType = iota
|
||||
msiTypeAzureArc
|
||||
msiTypeAzureML
|
||||
msiTypeCloudShell
|
||||
msiTypeIMDS
|
||||
msiTypeServiceFabric
|
||||
)
|
||||
|
||||
// managedIdentityClient provides the base for authenticating in managed identity environments
|
||||
// This type includes an runtime.Pipeline and TokenCredentialOptions.
|
||||
type managedIdentityClient struct {
|
||||
pipeline runtime.Pipeline
|
||||
msiType msiType
|
||||
endpoint string
|
||||
id ManagedIDKind
|
||||
azClient *azcore.Client
|
||||
endpoint string
|
||||
id ManagedIDKind
|
||||
msiType msiType
|
||||
probeIMDS bool
|
||||
}
|
||||
|
||||
// arcKeyDirectory returns the directory expected to contain Azure Arc keys
|
||||
var arcKeyDirectory = func() (string, error) {
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
return "/var/opt/azcmagent/tokens", nil
|
||||
case "windows":
|
||||
pd := os.Getenv("ProgramData")
|
||||
if pd == "" {
|
||||
return "", errors.New("environment variable ProgramData has no value")
|
||||
}
|
||||
return filepath.Join(pd, "AzureConnectedMachineAgent", "Tokens"), nil
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported OS %q", runtime.GOOS)
|
||||
}
|
||||
}
|
||||
|
||||
type wrappedNumber json.Number
|
||||
|
@ -84,13 +106,15 @@ func setIMDSRetryOptionDefaults(o *policy.RetryOptions) {
|
|||
}
|
||||
if o.StatusCodes == nil {
|
||||
o.StatusCodes = []int{
|
||||
// IMDS docs recommend retrying 404, 429 and all 5xx
|
||||
// https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/how-to-use-vm-token#error-handling
|
||||
// IMDS docs recommend retrying 404, 410, 429 and 5xx
|
||||
// https://learn.microsoft.com/entra/identity/managed-identities-azure-resources/how-to-use-vm-token#error-handling
|
||||
http.StatusNotFound, // 404
|
||||
http.StatusGone, // 410
|
||||
http.StatusTooManyRequests, // 429
|
||||
http.StatusInternalServerError, // 500
|
||||
http.StatusNotImplemented, // 501
|
||||
http.StatusBadGateway, // 502
|
||||
http.StatusServiceUnavailable, // 503
|
||||
http.StatusGatewayTimeout, // 504
|
||||
http.StatusHTTPVersionNotSupported, // 505
|
||||
http.StatusVariantAlsoNegotiates, // 506
|
||||
|
@ -133,13 +157,28 @@ func newManagedIdentityClient(options *ManagedIdentityCredentialOptions) (*manag
|
|||
c.msiType = msiTypeAzureArc
|
||||
}
|
||||
} else if endpoint, ok := os.LookupEnv(msiEndpoint); ok {
|
||||
env = "Cloud Shell"
|
||||
c.endpoint = endpoint
|
||||
c.msiType = msiTypeCloudShell
|
||||
if _, ok := os.LookupEnv(msiSecret); ok {
|
||||
env = "Azure ML"
|
||||
c.msiType = msiTypeAzureML
|
||||
} else {
|
||||
env = "Cloud Shell"
|
||||
c.msiType = msiTypeCloudShell
|
||||
}
|
||||
} else {
|
||||
c.probeIMDS = options.dac
|
||||
setIMDSRetryOptionDefaults(&cp.Retry)
|
||||
}
|
||||
c.pipeline = runtime.NewPipeline(component, version, runtime.PipelineOptions{}, &cp)
|
||||
|
||||
client, err := azcore.NewClient(module, version, azruntime.PipelineOptions{
|
||||
Tracing: azruntime.TracingOptions{
|
||||
Namespace: traceNamespace,
|
||||
},
|
||||
}, &cp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.azClient = client
|
||||
|
||||
if log.Should(EventAuthentication) {
|
||||
log.Writef(EventAuthentication, "Managed Identity Credential will use %s managed identity", env)
|
||||
|
@ -161,25 +200,60 @@ func (c *managedIdentityClient) provideToken(ctx context.Context, params confide
|
|||
|
||||
// authenticate acquires an access token
|
||||
func (c *managedIdentityClient) authenticate(ctx context.Context, id ManagedIDKind, scopes []string) (azcore.AccessToken, error) {
|
||||
// no need to synchronize around this value because it's true only when DefaultAzureCredential constructed the client,
|
||||
// and in that case ChainedTokenCredential.GetToken synchronizes goroutines that would execute this block
|
||||
if c.probeIMDS {
|
||||
cx, cancel := context.WithTimeout(ctx, imdsProbeTimeout)
|
||||
defer cancel()
|
||||
cx = policy.WithRetryOptions(cx, policy.RetryOptions{MaxRetries: -1})
|
||||
req, err := azruntime.NewRequest(cx, http.MethodGet, c.endpoint)
|
||||
if err == nil {
|
||||
_, err = c.azClient.Pipeline().Do(req)
|
||||
}
|
||||
if err != nil {
|
||||
msg := err.Error()
|
||||
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
|
||||
msg = "managed identity timed out. See https://aka.ms/azsdk/go/identity/troubleshoot#dac for more information"
|
||||
}
|
||||
return azcore.AccessToken{}, newCredentialUnavailableError(credNameManagedIdentity, msg)
|
||||
}
|
||||
// send normal token requests from now on because something responded
|
||||
c.probeIMDS = false
|
||||
}
|
||||
|
||||
msg, err := c.createAuthRequest(ctx, id, scopes)
|
||||
if err != nil {
|
||||
return azcore.AccessToken{}, err
|
||||
}
|
||||
|
||||
resp, err := c.pipeline.Do(msg)
|
||||
resp, err := c.azClient.Pipeline().Do(msg)
|
||||
if err != nil {
|
||||
return azcore.AccessToken{}, newAuthenticationFailedError(credNameManagedIdentity, err.Error(), nil, err)
|
||||
}
|
||||
|
||||
if runtime.HasStatusCode(resp, http.StatusOK, http.StatusCreated) {
|
||||
if azruntime.HasStatusCode(resp, http.StatusOK, http.StatusCreated) {
|
||||
return c.createAccessToken(resp)
|
||||
}
|
||||
|
||||
if c.msiType == msiTypeIMDS && resp.StatusCode == 400 {
|
||||
if id != nil {
|
||||
return azcore.AccessToken{}, newAuthenticationFailedError(credNameManagedIdentity, "the requested identity isn't assigned to this resource", resp, nil)
|
||||
if c.msiType == msiTypeIMDS {
|
||||
switch resp.StatusCode {
|
||||
case http.StatusBadRequest:
|
||||
if id != nil {
|
||||
return azcore.AccessToken{}, newAuthenticationFailedError(credNameManagedIdentity, "the requested identity isn't assigned to this resource", resp, nil)
|
||||
}
|
||||
msg := "failed to authenticate a system assigned identity"
|
||||
if body, err := azruntime.Payload(resp); err == nil && len(body) > 0 {
|
||||
msg += fmt.Sprintf(". The endpoint responded with %s", body)
|
||||
}
|
||||
return azcore.AccessToken{}, newCredentialUnavailableError(credNameManagedIdentity, msg)
|
||||
case http.StatusForbidden:
|
||||
// Docker Desktop runs a proxy that responds 403 to IMDS token requests. If we get that response,
|
||||
// we return credentialUnavailableError so credential chains continue to their next credential
|
||||
body, err := azruntime.Payload(resp)
|
||||
if err == nil && strings.Contains(string(body), "unreachable") {
|
||||
return azcore.AccessToken{}, newCredentialUnavailableError(credNameManagedIdentity, fmt.Sprintf("unexpected response %q", string(body)))
|
||||
}
|
||||
}
|
||||
return azcore.AccessToken{}, newCredentialUnavailableError(credNameManagedIdentity, "no default identity is assigned to this resource")
|
||||
}
|
||||
|
||||
return azcore.AccessToken{}, newAuthenticationFailedError(credNameManagedIdentity, "authentication failed", resp, nil)
|
||||
|
@ -193,7 +267,7 @@ func (c *managedIdentityClient) createAccessToken(res *http.Response) (azcore.Ac
|
|||
ExpiresIn wrappedNumber `json:"expires_in,omitempty"` // this field should always return the number of seconds for which a token is valid
|
||||
ExpiresOn interface{} `json:"expires_on,omitempty"` // the value returned in this field varies between a number and a date string
|
||||
}{}
|
||||
if err := runtime.UnmarshalAsJSON(res, &value); err != nil {
|
||||
if err := azruntime.UnmarshalAsJSON(res, &value); err != nil {
|
||||
return azcore.AccessToken{}, fmt.Errorf("internal AccessToken: %v", err)
|
||||
}
|
||||
if value.ExpiresIn != "" {
|
||||
|
@ -231,6 +305,8 @@ func (c *managedIdentityClient) createAuthRequest(ctx context.Context, id Manage
|
|||
return nil, newAuthenticationFailedError(credNameManagedIdentity, msg, nil, err)
|
||||
}
|
||||
return c.createAzureArcAuthRequest(ctx, id, scopes, key)
|
||||
case msiTypeAzureML:
|
||||
return c.createAzureMLAuthRequest(ctx, id, scopes)
|
||||
case msiTypeServiceFabric:
|
||||
return c.createServiceFabricAuthRequest(ctx, id, scopes)
|
||||
case msiTypeCloudShell:
|
||||
|
@ -241,7 +317,7 @@ func (c *managedIdentityClient) createAuthRequest(ctx context.Context, id Manage
|
|||
}
|
||||
|
||||
func (c *managedIdentityClient) createIMDSAuthRequest(ctx context.Context, id ManagedIDKind, scopes []string) (*policy.Request, error) {
|
||||
request, err := runtime.NewRequest(ctx, http.MethodGet, c.endpoint)
|
||||
request, err := azruntime.NewRequest(ctx, http.MethodGet, c.endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -251,7 +327,7 @@ func (c *managedIdentityClient) createIMDSAuthRequest(ctx context.Context, id Ma
|
|||
q.Add("resource", strings.Join(scopes, " "))
|
||||
if id != nil {
|
||||
if id.idKind() == miResourceID {
|
||||
q.Add(qpResID, id.String())
|
||||
q.Add(msiResID, id.String())
|
||||
} else {
|
||||
q.Add(qpClientID, id.String())
|
||||
}
|
||||
|
@ -261,7 +337,7 @@ func (c *managedIdentityClient) createIMDSAuthRequest(ctx context.Context, id Ma
|
|||
}
|
||||
|
||||
func (c *managedIdentityClient) createAppServiceAuthRequest(ctx context.Context, id ManagedIDKind, scopes []string) (*policy.Request, error) {
|
||||
request, err := runtime.NewRequest(ctx, http.MethodGet, c.endpoint)
|
||||
request, err := azruntime.NewRequest(ctx, http.MethodGet, c.endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -271,7 +347,7 @@ func (c *managedIdentityClient) createAppServiceAuthRequest(ctx context.Context,
|
|||
q.Add("resource", scopes[0])
|
||||
if id != nil {
|
||||
if id.idKind() == miResourceID {
|
||||
q.Add(qpResID, id.String())
|
||||
q.Add(miResID, id.String())
|
||||
} else {
|
||||
q.Add(qpClientID, id.String())
|
||||
}
|
||||
|
@ -280,8 +356,31 @@ func (c *managedIdentityClient) createAppServiceAuthRequest(ctx context.Context,
|
|||
return request, nil
|
||||
}
|
||||
|
||||
func (c *managedIdentityClient) createAzureMLAuthRequest(ctx context.Context, id ManagedIDKind, scopes []string) (*policy.Request, error) {
|
||||
request, err := azruntime.NewRequest(ctx, http.MethodGet, c.endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
request.Raw().Header.Set("secret", os.Getenv(msiSecret))
|
||||
q := request.Raw().URL.Query()
|
||||
q.Add("api-version", "2017-09-01")
|
||||
q.Add("resource", strings.Join(scopes, " "))
|
||||
q.Add("clientid", os.Getenv(defaultIdentityClientID))
|
||||
if id != nil {
|
||||
if id.idKind() == miResourceID {
|
||||
log.Write(EventAuthentication, "WARNING: Azure ML doesn't support specifying a managed identity by resource ID")
|
||||
q.Set("clientid", "")
|
||||
q.Set(miResID, id.String())
|
||||
} else {
|
||||
q.Set("clientid", id.String())
|
||||
}
|
||||
}
|
||||
request.Raw().URL.RawQuery = q.Encode()
|
||||
return request, nil
|
||||
}
|
||||
|
||||
func (c *managedIdentityClient) createServiceFabricAuthRequest(ctx context.Context, id ManagedIDKind, scopes []string) (*policy.Request, error) {
|
||||
request, err := runtime.NewRequest(ctx, http.MethodGet, c.endpoint)
|
||||
request, err := azruntime.NewRequest(ctx, http.MethodGet, c.endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -293,7 +392,7 @@ func (c *managedIdentityClient) createServiceFabricAuthRequest(ctx context.Conte
|
|||
if id != nil {
|
||||
log.Write(EventAuthentication, "WARNING: Service Fabric doesn't support selecting a user-assigned identity at runtime")
|
||||
if id.idKind() == miResourceID {
|
||||
q.Add(qpResID, id.String())
|
||||
q.Add(miResID, id.String())
|
||||
} else {
|
||||
q.Add(qpClientID, id.String())
|
||||
}
|
||||
|
@ -304,7 +403,7 @@ func (c *managedIdentityClient) createServiceFabricAuthRequest(ctx context.Conte
|
|||
|
||||
func (c *managedIdentityClient) getAzureArcSecretKey(ctx context.Context, resources []string) (string, error) {
|
||||
// create the request to retreive the secret key challenge provided by the HIMDS service
|
||||
request, err := runtime.NewRequest(ctx, http.MethodGet, c.endpoint)
|
||||
request, err := azruntime.NewRequest(ctx, http.MethodGet, c.endpoint)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -314,7 +413,7 @@ func (c *managedIdentityClient) getAzureArcSecretKey(ctx context.Context, resour
|
|||
q.Add("resource", strings.Join(resources, " "))
|
||||
request.Raw().URL.RawQuery = q.Encode()
|
||||
// send the initial request to get the short-lived secret key
|
||||
response, err := c.pipeline.Do(request)
|
||||
response, err := c.azClient.Pipeline().Do(request)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -326,22 +425,36 @@ func (c *managedIdentityClient) getAzureArcSecretKey(ctx context.Context, resour
|
|||
}
|
||||
header := response.Header.Get("WWW-Authenticate")
|
||||
if len(header) == 0 {
|
||||
return "", errors.New("did not receive a value from WWW-Authenticate header")
|
||||
return "", newAuthenticationFailedError(credNameManagedIdentity, "HIMDS response has no WWW-Authenticate header", nil, nil)
|
||||
}
|
||||
// the WWW-Authenticate header is expected in the following format: Basic realm=/some/file/path.key
|
||||
pos := strings.LastIndex(header, "=")
|
||||
if pos == -1 {
|
||||
return "", fmt.Errorf("did not receive a correct value from WWW-Authenticate header: %s", header)
|
||||
_, p, found := strings.Cut(header, "=")
|
||||
if !found {
|
||||
return "", newAuthenticationFailedError(credNameManagedIdentity, "unexpected WWW-Authenticate header from HIMDS: "+header, nil, nil)
|
||||
}
|
||||
key, err := os.ReadFile(header[pos+1:])
|
||||
expected, err := arcKeyDirectory()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not read file (%s) contents: %v", header[pos+1:], err)
|
||||
return "", err
|
||||
}
|
||||
if filepath.Dir(p) != expected || !strings.HasSuffix(p, ".key") {
|
||||
return "", newAuthenticationFailedError(credNameManagedIdentity, "unexpected file path from HIMDS service: "+p, nil, nil)
|
||||
}
|
||||
f, err := os.Stat(p)
|
||||
if err != nil {
|
||||
return "", newAuthenticationFailedError(credNameManagedIdentity, fmt.Sprintf("could not stat %q: %v", p, err), nil, nil)
|
||||
}
|
||||
if s := f.Size(); s > 4096 {
|
||||
return "", newAuthenticationFailedError(credNameManagedIdentity, fmt.Sprintf("key is too large (%d bytes)", s), nil, nil)
|
||||
}
|
||||
key, err := os.ReadFile(p)
|
||||
if err != nil {
|
||||
return "", newAuthenticationFailedError(credNameManagedIdentity, fmt.Sprintf("could not read %q: %v", p, err), nil, nil)
|
||||
}
|
||||
return string(key), nil
|
||||
}
|
||||
|
||||
func (c *managedIdentityClient) createAzureArcAuthRequest(ctx context.Context, id ManagedIDKind, resources []string, key string) (*policy.Request, error) {
|
||||
request, err := runtime.NewRequest(ctx, http.MethodGet, c.endpoint)
|
||||
request, err := azruntime.NewRequest(ctx, http.MethodGet, c.endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -353,7 +466,7 @@ func (c *managedIdentityClient) createAzureArcAuthRequest(ctx context.Context, i
|
|||
if id != nil {
|
||||
log.Write(EventAuthentication, "WARNING: Azure Arc doesn't support user-assigned managed identities")
|
||||
if id.idKind() == miResourceID {
|
||||
q.Add(qpResID, id.String())
|
||||
q.Add(miResID, id.String())
|
||||
} else {
|
||||
q.Add(qpClientID, id.String())
|
||||
}
|
||||
|
@ -363,7 +476,7 @@ func (c *managedIdentityClient) createAzureArcAuthRequest(ctx context.Context, i
|
|||
}
|
||||
|
||||
func (c *managedIdentityClient) createCloudShellAuthRequest(ctx context.Context, id ManagedIDKind, scopes []string) (*policy.Request, error) {
|
||||
request, err := runtime.NewRequest(ctx, http.MethodPost, c.endpoint)
|
||||
request, err := azruntime.NewRequest(ctx, http.MethodPost, c.endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -379,7 +492,7 @@ func (c *managedIdentityClient) createCloudShellAuthRequest(ctx context.Context,
|
|||
log.Write(EventAuthentication, "WARNING: Cloud Shell doesn't support user-assigned managed identities")
|
||||
q := request.Raw().URL.Query()
|
||||
if id.idKind() == miResourceID {
|
||||
q.Add(qpResID, id.String())
|
||||
q.Add(miResID, id.String())
|
||||
} else {
|
||||
q.Add(qpClientID, id.String())
|
||||
}
|
||||
|
|
47
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/managed_identity_credential.go
generated
vendored
47
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/managed_identity_credential.go
generated
vendored
|
@ -8,12 +8,12 @@ package azidentity
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential"
|
||||
)
|
||||
|
||||
|
@ -64,16 +64,22 @@ type ManagedIdentityCredentialOptions struct {
|
|||
// instead of the hosting environment's default. The value may be the identity's client ID or resource ID, but note that
|
||||
// some platforms don't accept resource IDs.
|
||||
ID ManagedIDKind
|
||||
|
||||
// dac indicates whether the credential is part of DefaultAzureCredential. When true, and the environment doesn't have
|
||||
// configuration for a specific managed identity API, the credential tries to determine whether IMDS is available before
|
||||
// sending its first token request. It does this by sending a malformed request with a short timeout. Any response to that
|
||||
// request is taken to mean IMDS is available, in which case the credential will send ordinary token requests thereafter
|
||||
// with no special timeout. The purpose of this behavior is to prevent a very long timeout when IMDS isn't available.
|
||||
dac bool
|
||||
}
|
||||
|
||||
// ManagedIdentityCredential authenticates an Azure managed identity in any hosting environment supporting managed identities.
|
||||
// This credential authenticates a system-assigned identity by default. Use ManagedIdentityCredentialOptions.ID to specify a
|
||||
// user-assigned identity. See Azure Active Directory documentation for more information about managed identities:
|
||||
// https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/overview
|
||||
// user-assigned identity. See Microsoft Entra ID documentation for more information about managed identities:
|
||||
// https://learn.microsoft.com/entra/identity/managed-identities-azure-resources/overview
|
||||
type ManagedIdentityCredential struct {
|
||||
client confidentialClient
|
||||
client *confidentialClient
|
||||
mic *managedIdentityClient
|
||||
s *syncer
|
||||
}
|
||||
|
||||
// NewManagedIdentityCredential creates a ManagedIdentityCredential. Pass nil to accept default options.
|
||||
|
@ -93,35 +99,30 @@ func NewManagedIdentityCredential(options *ManagedIdentityCredentialOptions) (*M
|
|||
if options.ID != nil {
|
||||
clientID = options.ID.String()
|
||||
}
|
||||
// similarly, it's okay to give MSAL an incorrect authority URL because that URL won't be used
|
||||
c, err := confidential.New("https://login.microsoftonline.com/common", clientID, cred)
|
||||
// similarly, it's okay to give MSAL an incorrect tenant because MSAL won't use the value
|
||||
c, err := newConfidentialClient("common", clientID, credNameManagedIdentity, cred, confidentialClientOptions{
|
||||
ClientOptions: options.ClientOptions,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := ManagedIdentityCredential{client: c, mic: mic}
|
||||
m.s = newSyncer(credNameManagedIdentity, "", nil, m.requestToken, m.silentAuth)
|
||||
return &m, nil
|
||||
return &ManagedIdentityCredential{client: c, mic: mic}, nil
|
||||
}
|
||||
|
||||
// GetToken requests an access token from the hosting environment. This method is called automatically by Azure SDK clients.
|
||||
func (c *ManagedIdentityCredential) GetToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
var err error
|
||||
ctx, endSpan := runtime.StartSpan(ctx, credNameManagedIdentity+"."+traceOpGetToken, c.client.azClient.Tracer(), nil)
|
||||
defer func() { endSpan(err) }()
|
||||
|
||||
if len(opts.Scopes) != 1 {
|
||||
err := errors.New(credNameManagedIdentity + ": GetToken() requires exactly one scope")
|
||||
err = fmt.Errorf("%s.GetToken() requires exactly one scope", credNameManagedIdentity)
|
||||
return azcore.AccessToken{}, err
|
||||
}
|
||||
// managed identity endpoints require an AADv1 resource (i.e. token audience), not a v2 scope, so we remove "/.default" here
|
||||
// managed identity endpoints require a Microsoft Entra ID v1 resource (i.e. token audience), not a v2 scope, so we remove "/.default" here
|
||||
opts.Scopes = []string{strings.TrimSuffix(opts.Scopes[0], defaultSuffix)}
|
||||
return c.s.GetToken(ctx, opts)
|
||||
}
|
||||
|
||||
func (c *ManagedIdentityCredential) requestToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
ar, err := c.client.AcquireTokenByCredential(ctx, opts.Scopes)
|
||||
return azcore.AccessToken{Token: ar.AccessToken, ExpiresOn: ar.ExpiresOn.UTC()}, err
|
||||
}
|
||||
|
||||
func (c *ManagedIdentityCredential) silentAuth(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
ar, err := c.client.AcquireTokenSilent(ctx, opts.Scopes)
|
||||
return azcore.AccessToken{Token: ar.AccessToken, ExpiresOn: ar.ExpiresOn.UTC()}, err
|
||||
tk, err := c.client.GetToken(ctx, opts)
|
||||
return tk, err
|
||||
}
|
||||
|
||||
var _ azcore.TokenCredential = (*ManagedIdentityCredential)(nil)
|
||||
|
|
56
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/on_behalf_of_credential.go
generated
vendored
56
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/on_behalf_of_credential.go
generated
vendored
|
@ -10,9 +10,11 @@ import (
|
|||
"context"
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential"
|
||||
)
|
||||
|
||||
|
@ -21,13 +23,11 @@ const credNameOBO = "OnBehalfOfCredential"
|
|||
// OnBehalfOfCredential authenticates a service principal via the on-behalf-of flow. This is typically used by
|
||||
// middle-tier services that authorize requests to other services with a delegated user identity. Because this
|
||||
// is not an interactive authentication flow, an application using it must have admin consent for any delegated
|
||||
// permissions before requesting tokens for them. See [Azure Active Directory documentation] for more details.
|
||||
// permissions before requesting tokens for them. See [Microsoft Entra ID documentation] for more details.
|
||||
//
|
||||
// [Azure Active Directory documentation]: https://docs.microsoft.com/azure/active-directory/develop/v2-oauth2-on-behalf-of-flow
|
||||
// [Microsoft Entra ID documentation]: https://learn.microsoft.com/entra/identity-platform/v2-oauth2-on-behalf-of-flow
|
||||
type OnBehalfOfCredential struct {
|
||||
assertion string
|
||||
client confidentialClient
|
||||
s *syncer
|
||||
client *confidentialClient
|
||||
}
|
||||
|
||||
// OnBehalfOfCredentialOptions contains optional parameters for OnBehalfOfCredential
|
||||
|
@ -38,11 +38,13 @@ type OnBehalfOfCredentialOptions struct {
|
|||
// Add the wildcard value "*" to allow the credential to acquire tokens for any tenant in which the
|
||||
// application is registered.
|
||||
AdditionallyAllowedTenants []string
|
||||
|
||||
// DisableInstanceDiscovery should be set true only by applications authenticating in disconnected clouds, or
|
||||
// private clouds such as Azure Stack. It determines whether the credential requests Azure AD instance metadata
|
||||
// private clouds such as Azure Stack. It determines whether the credential requests Microsoft Entra instance metadata
|
||||
// from https://login.microsoft.com before authenticating. Setting this to true will skip this request, making
|
||||
// the application responsible for ensuring the configured authority is valid and trustworthy.
|
||||
DisableInstanceDiscovery bool
|
||||
|
||||
// SendCertificateChain applies only when the credential is configured to authenticate with a certificate.
|
||||
// This setting controls whether the credential sends the public certificate chain in the x5c header of each
|
||||
// token request's JWT. This is required for, and only used in, Subject Name/Issuer (SNI) authentication.
|
||||
|
@ -59,6 +61,19 @@ func NewOnBehalfOfCredentialWithCertificate(tenantID, clientID, userAssertion st
|
|||
return newOnBehalfOfCredential(tenantID, clientID, userAssertion, cred, options)
|
||||
}
|
||||
|
||||
// NewOnBehalfOfCredentialWithClientAssertions constructs an OnBehalfOfCredential that authenticates with client assertions.
|
||||
// userAssertion is the user's access token for the application. The getAssertion function should return client assertions
|
||||
// that authenticate the application to Microsoft Entra ID, such as federated credentials.
|
||||
func NewOnBehalfOfCredentialWithClientAssertions(tenantID, clientID, userAssertion string, getAssertion func(context.Context) (string, error), options *OnBehalfOfCredentialOptions) (*OnBehalfOfCredential, error) {
|
||||
if getAssertion == nil {
|
||||
return nil, errors.New("getAssertion can't be nil. It must be a function that returns client assertions")
|
||||
}
|
||||
cred := confidential.NewCredFromAssertionCallback(func(ctx context.Context, _ confidential.AssertionRequestOptions) (string, error) {
|
||||
return getAssertion(ctx)
|
||||
})
|
||||
return newOnBehalfOfCredential(tenantID, clientID, userAssertion, cred, options)
|
||||
}
|
||||
|
||||
// NewOnBehalfOfCredentialWithSecret constructs an OnBehalfOfCredential that authenticates with a client secret.
|
||||
func NewOnBehalfOfCredentialWithSecret(tenantID, clientID, userAssertion, clientSecret string, options *OnBehalfOfCredentialOptions) (*OnBehalfOfCredential, error) {
|
||||
cred, err := confidential.NewCredFromSecret(clientSecret)
|
||||
|
@ -72,28 +87,27 @@ func newOnBehalfOfCredential(tenantID, clientID, userAssertion string, cred conf
|
|||
if options == nil {
|
||||
options = &OnBehalfOfCredentialOptions{}
|
||||
}
|
||||
opts := []confidential.Option{}
|
||||
if options.SendCertificateChain {
|
||||
opts = append(opts, confidential.WithX5C())
|
||||
opts := confidentialClientOptions{
|
||||
AdditionallyAllowedTenants: options.AdditionallyAllowedTenants,
|
||||
Assertion: userAssertion,
|
||||
ClientOptions: options.ClientOptions,
|
||||
DisableInstanceDiscovery: options.DisableInstanceDiscovery,
|
||||
SendX5C: options.SendCertificateChain,
|
||||
}
|
||||
opts = append(opts, confidential.WithInstanceDiscovery(!options.DisableInstanceDiscovery))
|
||||
c, err := getConfidentialClient(clientID, tenantID, cred, &options.ClientOptions, opts...)
|
||||
c, err := newConfidentialClient(tenantID, clientID, credNameOBO, cred, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
obo := OnBehalfOfCredential{assertion: userAssertion, client: c}
|
||||
obo.s = newSyncer(credNameOBO, tenantID, options.AdditionallyAllowedTenants, obo.requestToken, obo.requestToken)
|
||||
return &obo, nil
|
||||
return &OnBehalfOfCredential{c}, nil
|
||||
}
|
||||
|
||||
// GetToken requests an access token from Azure Active Directory. This method is called automatically by Azure SDK clients.
|
||||
// GetToken requests an access token from Microsoft Entra ID. This method is called automatically by Azure SDK clients.
|
||||
func (o *OnBehalfOfCredential) GetToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
return o.s.GetToken(ctx, opts)
|
||||
}
|
||||
|
||||
func (o *OnBehalfOfCredential) requestToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
ar, err := o.client.AcquireTokenOnBehalfOf(ctx, o.assertion, opts.Scopes, confidential.WithTenantID(opts.TenantID))
|
||||
return azcore.AccessToken{Token: ar.AccessToken, ExpiresOn: ar.ExpiresOn.UTC()}, err
|
||||
var err error
|
||||
ctx, endSpan := runtime.StartSpan(ctx, credNameOBO+"."+traceOpGetToken, o.client.azClient.Tracer(), nil)
|
||||
defer func() { endSpan(err) }()
|
||||
tk, err := o.client.GetToken(ctx, opts)
|
||||
return tk, err
|
||||
}
|
||||
|
||||
var _ azcore.TokenCredential = (*OnBehalfOfCredential)(nil)
|
||||
|
|
273
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/public_client.go
generated
vendored
Normal file
273
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/public_client.go
generated
vendored
Normal file
|
@ -0,0 +1,273 @@
|
|||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package azidentity
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azidentity/internal"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/internal/log"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/public"
|
||||
|
||||
// this import ensures well-known configurations in azcore/cloud have ARM audiences for Authenticate()
|
||||
_ "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime"
|
||||
)
|
||||
|
||||
type publicClientOptions struct {
|
||||
azcore.ClientOptions
|
||||
|
||||
AdditionallyAllowedTenants []string
|
||||
DeviceCodePrompt func(context.Context, DeviceCodeMessage) error
|
||||
DisableAutomaticAuthentication bool
|
||||
DisableInstanceDiscovery bool
|
||||
LoginHint, RedirectURL string
|
||||
Record authenticationRecord
|
||||
TokenCachePersistenceOptions *tokenCachePersistenceOptions
|
||||
Username, Password string
|
||||
}
|
||||
|
||||
// publicClient wraps the MSAL public client
|
||||
type publicClient struct {
|
||||
cae, noCAE msalPublicClient
|
||||
caeMu, noCAEMu, clientMu *sync.Mutex
|
||||
clientID, tenantID string
|
||||
defaultScope []string
|
||||
host string
|
||||
name string
|
||||
opts publicClientOptions
|
||||
record authenticationRecord
|
||||
azClient *azcore.Client
|
||||
}
|
||||
|
||||
var errScopeRequired = errors.New("authenticating in this environment requires specifying a scope in TokenRequestOptions")
|
||||
|
||||
func newPublicClient(tenantID, clientID, name string, o publicClientOptions) (*publicClient, error) {
|
||||
if !validTenantID(tenantID) {
|
||||
return nil, errInvalidTenantID
|
||||
}
|
||||
host, err := setAuthorityHost(o.Cloud)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// if the application specified a cloud configuration, use its ARM audience as the default scope for Authenticate()
|
||||
audience := o.Cloud.Services[cloud.ResourceManager].Audience
|
||||
if audience == "" {
|
||||
// no cloud configuration, or no ARM audience, specified; try to map the host to a well-known one (all of which have a trailing slash)
|
||||
if !strings.HasSuffix(host, "/") {
|
||||
host += "/"
|
||||
}
|
||||
switch host {
|
||||
case cloud.AzureChina.ActiveDirectoryAuthorityHost:
|
||||
audience = cloud.AzureChina.Services[cloud.ResourceManager].Audience
|
||||
case cloud.AzureGovernment.ActiveDirectoryAuthorityHost:
|
||||
audience = cloud.AzureGovernment.Services[cloud.ResourceManager].Audience
|
||||
case cloud.AzurePublic.ActiveDirectoryAuthorityHost:
|
||||
audience = cloud.AzurePublic.Services[cloud.ResourceManager].Audience
|
||||
}
|
||||
}
|
||||
// if we didn't come up with an audience, the application will have to specify a scope for Authenticate()
|
||||
var defaultScope []string
|
||||
if audience != "" {
|
||||
defaultScope = []string{audience + defaultSuffix}
|
||||
}
|
||||
client, err := azcore.NewClient(module, version, runtime.PipelineOptions{
|
||||
Tracing: runtime.TracingOptions{
|
||||
Namespace: traceNamespace,
|
||||
},
|
||||
}, &o.ClientOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
o.AdditionallyAllowedTenants = resolveAdditionalTenants(o.AdditionallyAllowedTenants)
|
||||
return &publicClient{
|
||||
caeMu: &sync.Mutex{},
|
||||
clientID: clientID,
|
||||
clientMu: &sync.Mutex{},
|
||||
defaultScope: defaultScope,
|
||||
host: host,
|
||||
name: name,
|
||||
noCAEMu: &sync.Mutex{},
|
||||
opts: o,
|
||||
record: o.Record,
|
||||
tenantID: tenantID,
|
||||
azClient: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *publicClient) Authenticate(ctx context.Context, tro *policy.TokenRequestOptions) (authenticationRecord, error) {
|
||||
if tro == nil {
|
||||
tro = &policy.TokenRequestOptions{}
|
||||
}
|
||||
if len(tro.Scopes) == 0 {
|
||||
if p.defaultScope == nil {
|
||||
return authenticationRecord{}, errScopeRequired
|
||||
}
|
||||
tro.Scopes = p.defaultScope
|
||||
}
|
||||
client, mu, err := p.client(*tro)
|
||||
if err != nil {
|
||||
return authenticationRecord{}, err
|
||||
}
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
_, err = p.reqToken(ctx, client, *tro)
|
||||
if err == nil {
|
||||
scope := strings.Join(tro.Scopes, ", ")
|
||||
msg := fmt.Sprintf("%s.Authenticate() acquired a token for scope %q", p.name, scope)
|
||||
log.Write(EventAuthentication, msg)
|
||||
}
|
||||
return p.record, err
|
||||
}
|
||||
|
||||
// GetToken requests an access token from MSAL, checking the cache first.
|
||||
func (p *publicClient) GetToken(ctx context.Context, tro policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
if len(tro.Scopes) < 1 {
|
||||
return azcore.AccessToken{}, fmt.Errorf("%s.GetToken() requires at least one scope", p.name)
|
||||
}
|
||||
tenant, err := p.resolveTenant(tro.TenantID)
|
||||
if err != nil {
|
||||
return azcore.AccessToken{}, err
|
||||
}
|
||||
client, mu, err := p.client(tro)
|
||||
if err != nil {
|
||||
return azcore.AccessToken{}, err
|
||||
}
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
ar, err := client.AcquireTokenSilent(ctx, tro.Scopes, public.WithSilentAccount(p.record.account()), public.WithClaims(tro.Claims), public.WithTenantID(tenant))
|
||||
if err == nil {
|
||||
return p.token(ar, err)
|
||||
}
|
||||
if p.opts.DisableAutomaticAuthentication {
|
||||
return azcore.AccessToken{}, newauthenticationRequiredError(p.name, tro)
|
||||
}
|
||||
at, err := p.reqToken(ctx, client, tro)
|
||||
if err == nil {
|
||||
msg := fmt.Sprintf("%s.GetToken() acquired a token for scope %q", p.name, strings.Join(ar.GrantedScopes, ", "))
|
||||
log.Write(EventAuthentication, msg)
|
||||
}
|
||||
return at, err
|
||||
}
|
||||
|
||||
// reqToken requests a token from the MSAL public client. It's separate from GetToken() to enable Authenticate() to bypass the cache.
|
||||
func (p *publicClient) reqToken(ctx context.Context, c msalPublicClient, tro policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
tenant, err := p.resolveTenant(tro.TenantID)
|
||||
if err != nil {
|
||||
return azcore.AccessToken{}, err
|
||||
}
|
||||
var ar public.AuthResult
|
||||
switch p.name {
|
||||
case credNameBrowser:
|
||||
ar, err = c.AcquireTokenInteractive(ctx, tro.Scopes,
|
||||
public.WithClaims(tro.Claims),
|
||||
public.WithLoginHint(p.opts.LoginHint),
|
||||
public.WithRedirectURI(p.opts.RedirectURL),
|
||||
public.WithTenantID(tenant),
|
||||
)
|
||||
case credNameDeviceCode:
|
||||
dc, e := c.AcquireTokenByDeviceCode(ctx, tro.Scopes, public.WithClaims(tro.Claims), public.WithTenantID(tenant))
|
||||
if e != nil {
|
||||
return azcore.AccessToken{}, e
|
||||
}
|
||||
err = p.opts.DeviceCodePrompt(ctx, DeviceCodeMessage{
|
||||
Message: dc.Result.Message,
|
||||
UserCode: dc.Result.UserCode,
|
||||
VerificationURL: dc.Result.VerificationURL,
|
||||
})
|
||||
if err == nil {
|
||||
ar, err = dc.AuthenticationResult(ctx)
|
||||
}
|
||||
case credNameUserPassword:
|
||||
ar, err = c.AcquireTokenByUsernamePassword(ctx, tro.Scopes, p.opts.Username, p.opts.Password, public.WithClaims(tro.Claims), public.WithTenantID(tenant))
|
||||
default:
|
||||
return azcore.AccessToken{}, fmt.Errorf("unknown credential %q", p.name)
|
||||
}
|
||||
return p.token(ar, err)
|
||||
}
|
||||
|
||||
func (p *publicClient) client(tro policy.TokenRequestOptions) (msalPublicClient, *sync.Mutex, error) {
|
||||
p.clientMu.Lock()
|
||||
defer p.clientMu.Unlock()
|
||||
if tro.EnableCAE {
|
||||
if p.cae == nil {
|
||||
client, err := p.newMSALClient(true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
p.cae = client
|
||||
}
|
||||
return p.cae, p.caeMu, nil
|
||||
}
|
||||
if p.noCAE == nil {
|
||||
client, err := p.newMSALClient(false)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
p.noCAE = client
|
||||
}
|
||||
return p.noCAE, p.noCAEMu, nil
|
||||
}
|
||||
|
||||
func (p *publicClient) newMSALClient(enableCAE bool) (msalPublicClient, error) {
|
||||
cache, err := internal.NewCache(p.opts.TokenCachePersistenceOptions, enableCAE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
o := []public.Option{
|
||||
public.WithAuthority(runtime.JoinPaths(p.host, p.tenantID)),
|
||||
public.WithCache(cache),
|
||||
public.WithHTTPClient(p),
|
||||
}
|
||||
if enableCAE {
|
||||
o = append(o, public.WithClientCapabilities(cp1))
|
||||
}
|
||||
if p.opts.DisableInstanceDiscovery || strings.ToLower(p.tenantID) == "adfs" {
|
||||
o = append(o, public.WithInstanceDiscovery(false))
|
||||
}
|
||||
return public.New(p.clientID, o...)
|
||||
}
|
||||
|
||||
func (p *publicClient) token(ar public.AuthResult, err error) (azcore.AccessToken, error) {
|
||||
if err == nil {
|
||||
p.record, err = newAuthenticationRecord(ar)
|
||||
} else {
|
||||
res := getResponseFromError(err)
|
||||
err = newAuthenticationFailedError(p.name, err.Error(), res, err)
|
||||
}
|
||||
return azcore.AccessToken{Token: ar.AccessToken, ExpiresOn: ar.ExpiresOn.UTC()}, err
|
||||
}
|
||||
|
||||
// resolveTenant returns the correct WithTenantID() argument for a token request given the client's
|
||||
// configuration, or an error when that configuration doesn't allow the specified tenant
|
||||
func (p *publicClient) resolveTenant(specified string) (string, error) {
|
||||
t, err := resolveTenant(p.tenantID, specified, p.name, p.opts.AdditionallyAllowedTenants)
|
||||
if t == p.tenantID {
|
||||
// callers pass this value to MSAL's WithTenantID(). There's no need to redundantly specify
|
||||
// the client's default tenant and doing so is an error when that tenant is "organizations"
|
||||
t = ""
|
||||
}
|
||||
return t, err
|
||||
}
|
||||
|
||||
// these methods satisfy the MSAL ops.HTTPClient interface
|
||||
|
||||
func (p *publicClient) CloseIdleConnections() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
func (p *publicClient) Do(r *http.Request) (*http.Response, error) {
|
||||
return doForClient(p.azClient, r)
|
||||
}
|
130
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/syncer.go
generated
vendored
130
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/syncer.go
generated
vendored
|
@ -1,130 +0,0 @@
|
|||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package azidentity
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/internal/log"
|
||||
)
|
||||
|
||||
type authFn func(context.Context, policy.TokenRequestOptions) (azcore.AccessToken, error)
|
||||
|
||||
// syncer synchronizes authentication calls so that goroutines can share a credential instance
|
||||
type syncer struct {
|
||||
addlTenants []string
|
||||
authing bool
|
||||
cond *sync.Cond
|
||||
reqToken, silent authFn
|
||||
name, tenant string
|
||||
}
|
||||
|
||||
func newSyncer(name, tenant string, additionalTenants []string, reqToken, silentAuth authFn) *syncer {
|
||||
return &syncer{
|
||||
addlTenants: resolveAdditionalTenants(additionalTenants),
|
||||
cond: &sync.Cond{L: &sync.Mutex{}},
|
||||
name: name,
|
||||
reqToken: reqToken,
|
||||
silent: silentAuth,
|
||||
tenant: tenant,
|
||||
}
|
||||
}
|
||||
|
||||
// GetToken ensures that only one goroutine authenticates at a time
|
||||
func (s *syncer) GetToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
var at azcore.AccessToken
|
||||
var err error
|
||||
if len(opts.Scopes) == 0 {
|
||||
return at, errors.New(s.name + ".GetToken() requires at least one scope")
|
||||
}
|
||||
// we don't resolve the tenant for managed identities because they can acquire tokens only from their home tenants
|
||||
if s.name != credNameManagedIdentity {
|
||||
tenant, err := s.resolveTenant(opts.TenantID)
|
||||
if err != nil {
|
||||
return at, err
|
||||
}
|
||||
opts.TenantID = tenant
|
||||
}
|
||||
auth := false
|
||||
s.cond.L.Lock()
|
||||
defer s.cond.L.Unlock()
|
||||
for {
|
||||
at, err = s.silent(ctx, opts)
|
||||
if err == nil {
|
||||
// got a token
|
||||
break
|
||||
}
|
||||
if !s.authing {
|
||||
// this goroutine will request a token
|
||||
s.authing, auth = true, true
|
||||
break
|
||||
}
|
||||
// another goroutine is acquiring a token; wait for it to finish, then try silent auth again
|
||||
s.cond.Wait()
|
||||
}
|
||||
if auth {
|
||||
s.authing = false
|
||||
at, err = s.reqToken(ctx, opts)
|
||||
s.cond.Broadcast()
|
||||
}
|
||||
if err != nil {
|
||||
// Return credentialUnavailableError directly because that type affects the behavior of credential chains.
|
||||
// Otherwise, return AuthenticationFailedError.
|
||||
var unavailableErr *credentialUnavailableError
|
||||
if !errors.As(err, &unavailableErr) {
|
||||
res := getResponseFromError(err)
|
||||
err = newAuthenticationFailedError(s.name, err.Error(), res, err)
|
||||
}
|
||||
} else if log.Should(EventAuthentication) {
|
||||
scope := strings.Join(opts.Scopes, ", ")
|
||||
msg := fmt.Sprintf(`%s.GetToken() acquired a token for scope "%s"\n`, s.name, scope)
|
||||
log.Write(EventAuthentication, msg)
|
||||
}
|
||||
return at, err
|
||||
}
|
||||
|
||||
// resolveTenant returns the correct tenant for a token request given the credential's
|
||||
// configuration, or an error when the specified tenant isn't allowed by that configuration
|
||||
func (s *syncer) resolveTenant(requested string) (string, error) {
|
||||
if requested == "" || requested == s.tenant {
|
||||
return s.tenant, nil
|
||||
}
|
||||
if s.tenant == "adfs" {
|
||||
return "", errors.New("ADFS doesn't support tenants")
|
||||
}
|
||||
if !validTenantID(requested) {
|
||||
return "", errors.New(tenantIDValidationErr)
|
||||
}
|
||||
for _, t := range s.addlTenants {
|
||||
if t == "*" || t == requested {
|
||||
return requested, nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf(`%s isn't configured to acquire tokens for tenant %q. To enable acquiring tokens for this tenant add it to the AdditionallyAllowedTenants on the credential options, or add "*" to allow acquiring tokens for any tenant`, s.name, requested)
|
||||
}
|
||||
|
||||
// resolveAdditionalTenants returns a copy of tenants, simplified when tenants contains a wildcard
|
||||
func resolveAdditionalTenants(tenants []string) []string {
|
||||
if len(tenants) == 0 {
|
||||
return nil
|
||||
}
|
||||
for _, t := range tenants {
|
||||
// a wildcard makes all other values redundant
|
||||
if t == "*" {
|
||||
return []string{"*"}
|
||||
}
|
||||
}
|
||||
cp := make([]string, len(tenants))
|
||||
copy(cp, tenants)
|
||||
return cp
|
||||
}
|
112
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/test-resources-post.ps1
generated
vendored
Normal file
112
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/test-resources-post.ps1
generated
vendored
Normal file
|
@ -0,0 +1,112 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# IMPORTANT: Do not invoke this file directly. Please instead run eng/common/TestResources/New-TestResources.ps1 from the repository root.
|
||||
|
||||
param (
|
||||
[hashtable] $AdditionalParameters = @{},
|
||||
[hashtable] $DeploymentOutputs
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$PSNativeCommandUseErrorActionPreference = $true
|
||||
|
||||
if ($CI) {
|
||||
if (!$AdditionalParameters['deployResources']) {
|
||||
Write-Host "Skipping post-provisioning script because resources weren't deployed"
|
||||
return
|
||||
}
|
||||
az login --service-principal -u $DeploymentOutputs['AZIDENTITY_CLIENT_ID'] -p $DeploymentOutputs['AZIDENTITY_CLIENT_SECRET'] --tenant $DeploymentOutputs['AZIDENTITY_TENANT_ID']
|
||||
az account set --subscription $DeploymentOutputs['AZIDENTITY_SUBSCRIPTION_ID']
|
||||
}
|
||||
|
||||
Write-Host "Building container"
|
||||
$image = "$($DeploymentOutputs['AZIDENTITY_ACR_LOGIN_SERVER'])/azidentity-managed-id-test"
|
||||
Set-Content -Path "$PSScriptRoot/Dockerfile" -Value @"
|
||||
FROM mcr.microsoft.com/oss/go/microsoft/golang:latest as builder
|
||||
ENV GOARCH=amd64 GOWORK=off
|
||||
COPY . /azidentity
|
||||
WORKDIR /azidentity/testdata/managed-id-test
|
||||
RUN go mod tidy
|
||||
RUN go build -o /build/managed-id-test .
|
||||
RUN GOOS=windows go build -o /build/managed-id-test.exe .
|
||||
|
||||
FROM mcr.microsoft.com/mirror/docker/library/alpine:3.16
|
||||
RUN apk add gcompat
|
||||
COPY --from=builder /build/* .
|
||||
RUN chmod +x managed-id-test
|
||||
CMD ["./managed-id-test"]
|
||||
"@
|
||||
# build from sdk/azidentity because we need that dir in the context (because the test app uses local azidentity)
|
||||
docker build -t $image "$PSScriptRoot"
|
||||
az acr login -n $DeploymentOutputs['AZIDENTITY_ACR_NAME']
|
||||
docker push $image
|
||||
|
||||
$rg = $DeploymentOutputs['AZIDENTITY_RESOURCE_GROUP']
|
||||
|
||||
# ACI is easier to provision here than in the bicep file because the image isn't available before now
|
||||
Write-Host "Deploying Azure Container Instance"
|
||||
$aciName = "azidentity-test"
|
||||
az container create -g $rg -n $aciName --image $image `
|
||||
--acr-identity $($DeploymentOutputs['AZIDENTITY_USER_ASSIGNED_IDENTITY']) `
|
||||
--assign-identity [system] $($DeploymentOutputs['AZIDENTITY_USER_ASSIGNED_IDENTITY']) `
|
||||
--role "Storage Blob Data Reader" `
|
||||
--scope $($DeploymentOutputs['AZIDENTITY_STORAGE_ID']) `
|
||||
-e AZIDENTITY_STORAGE_NAME=$($DeploymentOutputs['AZIDENTITY_STORAGE_NAME']) `
|
||||
AZIDENTITY_STORAGE_NAME_USER_ASSIGNED=$($DeploymentOutputs['AZIDENTITY_STORAGE_NAME_USER_ASSIGNED']) `
|
||||
AZIDENTITY_USER_ASSIGNED_IDENTITY=$($DeploymentOutputs['AZIDENTITY_USER_ASSIGNED_IDENTITY']) `
|
||||
FUNCTIONS_CUSTOMHANDLER_PORT=80
|
||||
Write-Host "##vso[task.setvariable variable=AZIDENTITY_ACI_NAME;]$aciName"
|
||||
|
||||
# Azure Functions deployment: copy the Windows binary from the Docker image, deploy it in a zip
|
||||
Write-Host "Deploying to Azure Functions"
|
||||
$container = docker create $image
|
||||
docker cp ${container}:managed-id-test.exe "$PSScriptRoot/testdata/managed-id-test/"
|
||||
docker rm -v $container
|
||||
Compress-Archive -Path "$PSScriptRoot/testdata/managed-id-test/*" -DestinationPath func.zip -Force
|
||||
az functionapp deploy -g $rg -n $DeploymentOutputs['AZIDENTITY_FUNCTION_NAME'] --src-path func.zip --type zip
|
||||
|
||||
Write-Host "Creating federated identity"
|
||||
$aksName = $DeploymentOutputs['AZIDENTITY_AKS_NAME']
|
||||
$idName = $DeploymentOutputs['AZIDENTITY_USER_ASSIGNED_IDENTITY_NAME']
|
||||
$issuer = az aks show -g $rg -n $aksName --query "oidcIssuerProfile.issuerUrl" -otsv
|
||||
$podName = "azidentity-test"
|
||||
$serviceAccountName = "workload-identity-sa"
|
||||
az identity federated-credential create -g $rg --identity-name $idName --issuer $issuer --name $idName --subject system:serviceaccount:default:$serviceAccountName
|
||||
Write-Host "Deploying to AKS"
|
||||
az aks get-credentials -g $rg -n $aksName
|
||||
az aks update --attach-acr $DeploymentOutputs['AZIDENTITY_ACR_NAME'] -g $rg -n $aksName
|
||||
Set-Content -Path "$PSScriptRoot/k8s.yaml" -Value @"
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
annotations:
|
||||
azure.workload.identity/client-id: $($DeploymentOutputs['AZIDENTITY_USER_ASSIGNED_IDENTITY_CLIENT_ID'])
|
||||
name: $serviceAccountName
|
||||
namespace: default
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: $podName
|
||||
namespace: default
|
||||
labels:
|
||||
app: $podName
|
||||
azure.workload.identity/use: "true"
|
||||
spec:
|
||||
serviceAccountName: $serviceAccountName
|
||||
containers:
|
||||
- name: $podName
|
||||
image: $image
|
||||
env:
|
||||
- name: AZIDENTITY_STORAGE_NAME
|
||||
value: $($DeploymentOutputs['AZIDENTITY_STORAGE_NAME_USER_ASSIGNED'])
|
||||
- name: AZIDENTITY_USE_WORKLOAD_IDENTITY
|
||||
value: "true"
|
||||
- name: FUNCTIONS_CUSTOMHANDLER_PORT
|
||||
value: "80"
|
||||
nodeSelector:
|
||||
kubernetes.io/os: linux
|
||||
"@
|
||||
kubectl apply -f "$PSScriptRoot/k8s.yaml"
|
||||
Write-Host "##vso[task.setvariable variable=AZIDENTITY_POD_NAME;]$podName"
|
44
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/test-resources-pre.ps1
generated
vendored
Normal file
44
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/test-resources-pre.ps1
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# IMPORTANT: Do not invoke this file directly. Please instead run eng/common/TestResources/New-TestResources.ps1 from the repository root.
|
||||
|
||||
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
|
||||
param (
|
||||
[hashtable] $AdditionalParameters = @{},
|
||||
|
||||
# Captures any arguments from eng/New-TestResources.ps1 not declared here (no parameter errors).
|
||||
[Parameter(ValueFromRemainingArguments = $true)]
|
||||
$RemainingArguments
|
||||
)
|
||||
|
||||
if (-not (Test-Path "$PSScriptRoot/sshkey.pub")) {
|
||||
ssh-keygen -t rsa -b 4096 -f "$PSScriptRoot/sshkey" -N '' -C ''
|
||||
}
|
||||
$templateFileParameters['sshPubKey'] = Get-Content "$PSScriptRoot/sshkey.pub"
|
||||
|
||||
if (!$CI) {
|
||||
# TODO: Remove this once auto-cloud config downloads are supported locally
|
||||
Write-Host "Skipping cert setup in local testing mode"
|
||||
return
|
||||
}
|
||||
|
||||
if ($null -eq $EnvironmentVariables -or $EnvironmentVariables.Count -eq 0) {
|
||||
throw "EnvironmentVariables must be set in the calling script New-TestResources.ps1"
|
||||
}
|
||||
|
||||
$tmp = $env:TEMP ? $env:TEMP : [System.IO.Path]::GetTempPath()
|
||||
$pfxPath = Join-Path $tmp "test.pfx"
|
||||
$pemPath = Join-Path $tmp "test.pem"
|
||||
|
||||
Write-Host "Creating identity test files: $pfxPath $pemPath"
|
||||
|
||||
[System.Convert]::FromBase64String($EnvironmentVariables['PFX_CONTENTS']) | Set-Content -Path $pfxPath -AsByteStream
|
||||
Set-Content -Path $pemPath -Value $EnvironmentVariables['PEM_CONTENTS']
|
||||
|
||||
# Set for pipeline
|
||||
Write-Host "##vso[task.setvariable variable=IDENTITY_SP_CERT_PFX;]$pfxPath"
|
||||
Write-Host "##vso[task.setvariable variable=IDENTITY_SP_CERT_PEM;]$pemPath"
|
||||
# Set for local
|
||||
$env:IDENTITY_SP_CERT_PFX = $pfxPath
|
||||
$env:IDENTITY_SP_CERT_PEM = $pemPath
|
219
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/test-resources.bicep
generated
vendored
Normal file
219
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/test-resources.bicep
generated
vendored
Normal file
|
@ -0,0 +1,219 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
@description('Kubernetes cluster admin user name.')
|
||||
param adminUser string = 'azureuser'
|
||||
|
||||
@minLength(6)
|
||||
@maxLength(23)
|
||||
@description('The base resource name.')
|
||||
param baseName string = resourceGroup().name
|
||||
|
||||
@description('Whether to deploy resources. When set to false, this file deploys nothing.')
|
||||
param deployResources bool = false
|
||||
|
||||
param sshPubKey string = ''
|
||||
|
||||
@description('The location of the resource. By default, this is the same as the resource group.')
|
||||
param location string = resourceGroup().location
|
||||
|
||||
// https://learn.microsoft.com/azure/role-based-access-control/built-in-roles
|
||||
var acrPull = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')
|
||||
var blobReader = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1')
|
||||
|
||||
resource sa 'Microsoft.Storage/storageAccounts@2021-08-01' = if (deployResources) {
|
||||
kind: 'StorageV2'
|
||||
location: location
|
||||
name: 'sa${uniqueString(baseName)}'
|
||||
properties: {
|
||||
accessTier: 'Hot'
|
||||
}
|
||||
sku: {
|
||||
name: 'Standard_LRS'
|
||||
}
|
||||
}
|
||||
|
||||
resource saUserAssigned 'Microsoft.Storage/storageAccounts@2021-08-01' = if (deployResources) {
|
||||
kind: 'StorageV2'
|
||||
location: location
|
||||
name: 'sa2${uniqueString(baseName)}'
|
||||
properties: {
|
||||
accessTier: 'Hot'
|
||||
}
|
||||
sku: {
|
||||
name: 'Standard_LRS'
|
||||
}
|
||||
}
|
||||
|
||||
resource usermgdid 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = if (deployResources) {
|
||||
location: location
|
||||
name: baseName
|
||||
}
|
||||
|
||||
resource acrPullContainerInstance 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (deployResources) {
|
||||
name: guid(resourceGroup().id, acrPull, 'containerInstance')
|
||||
properties: {
|
||||
principalId: deployResources ? usermgdid.properties.principalId : ''
|
||||
principalType: 'ServicePrincipal'
|
||||
roleDefinitionId: acrPull
|
||||
}
|
||||
scope: containerRegistry
|
||||
}
|
||||
|
||||
resource blobRoleUserAssigned 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (deployResources) {
|
||||
scope: saUserAssigned
|
||||
name: guid(resourceGroup().id, blobReader, usermgdid.id)
|
||||
properties: {
|
||||
principalId: deployResources ? usermgdid.properties.principalId : ''
|
||||
principalType: 'ServicePrincipal'
|
||||
roleDefinitionId: blobReader
|
||||
}
|
||||
}
|
||||
|
||||
resource blobRoleFunc 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (deployResources) {
|
||||
name: guid(resourceGroup().id, blobReader, 'azfunc')
|
||||
properties: {
|
||||
principalId: deployResources ? azfunc.identity.principalId : ''
|
||||
roleDefinitionId: blobReader
|
||||
principalType: 'ServicePrincipal'
|
||||
}
|
||||
scope: sa
|
||||
}
|
||||
|
||||
resource containerRegistry 'Microsoft.ContainerRegistry/registries@2023-01-01-preview' = if (deployResources) {
|
||||
location: location
|
||||
name: uniqueString(resourceGroup().id)
|
||||
properties: {
|
||||
adminUserEnabled: true
|
||||
}
|
||||
sku: {
|
||||
name: 'Basic'
|
||||
}
|
||||
}
|
||||
|
||||
resource farm 'Microsoft.Web/serverfarms@2021-03-01' = if (deployResources) {
|
||||
kind: 'app'
|
||||
location: location
|
||||
name: '${baseName}_asp'
|
||||
properties: {}
|
||||
sku: {
|
||||
capacity: 1
|
||||
family: 'B'
|
||||
name: 'B1'
|
||||
size: 'B1'
|
||||
tier: 'Basic'
|
||||
}
|
||||
}
|
||||
|
||||
resource azfunc 'Microsoft.Web/sites@2021-03-01' = if (deployResources) {
|
||||
identity: {
|
||||
type: 'SystemAssigned, UserAssigned'
|
||||
userAssignedIdentities: {
|
||||
'${deployResources ? usermgdid.id : ''}': {}
|
||||
}
|
||||
}
|
||||
kind: 'functionapp'
|
||||
location: location
|
||||
name: '${baseName}func'
|
||||
properties: {
|
||||
enabled: true
|
||||
httpsOnly: true
|
||||
keyVaultReferenceIdentity: 'SystemAssigned'
|
||||
serverFarmId: farm.id
|
||||
siteConfig: {
|
||||
alwaysOn: true
|
||||
appSettings: [
|
||||
{
|
||||
name: 'AZIDENTITY_STORAGE_NAME'
|
||||
value: deployResources ? sa.name : null
|
||||
}
|
||||
{
|
||||
name: 'AZIDENTITY_STORAGE_NAME_USER_ASSIGNED'
|
||||
value: deployResources ? saUserAssigned.name : null
|
||||
}
|
||||
{
|
||||
name: 'AZIDENTITY_USER_ASSIGNED_IDENTITY'
|
||||
value: deployResources ? usermgdid.id : null
|
||||
}
|
||||
{
|
||||
name: 'AzureWebJobsStorage'
|
||||
value: 'DefaultEndpointsProtocol=https;AccountName=${deployResources ? sa.name : ''};EndpointSuffix=${deployResources ? environment().suffixes.storage : ''};AccountKey=${deployResources ? sa.listKeys().keys[0].value : ''}'
|
||||
}
|
||||
{
|
||||
name: 'FUNCTIONS_EXTENSION_VERSION'
|
||||
value: '~4'
|
||||
}
|
||||
{
|
||||
name: 'FUNCTIONS_WORKER_RUNTIME'
|
||||
value: 'custom'
|
||||
}
|
||||
{
|
||||
name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING'
|
||||
value: 'DefaultEndpointsProtocol=https;AccountName=${deployResources ? sa.name : ''};EndpointSuffix=${deployResources ? environment().suffixes.storage : ''};AccountKey=${deployResources ? sa.listKeys().keys[0].value : ''}'
|
||||
}
|
||||
{
|
||||
name: 'WEBSITE_CONTENTSHARE'
|
||||
value: toLower('${baseName}-func')
|
||||
}
|
||||
]
|
||||
http20Enabled: true
|
||||
minTlsVersion: '1.2'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource aks 'Microsoft.ContainerService/managedClusters@2023-06-01' = if (deployResources) {
|
||||
name: baseName
|
||||
location: location
|
||||
identity: {
|
||||
type: 'SystemAssigned'
|
||||
}
|
||||
properties: {
|
||||
agentPoolProfiles: [
|
||||
{
|
||||
count: 1
|
||||
enableAutoScaling: false
|
||||
kubeletDiskType: 'OS'
|
||||
mode: 'System'
|
||||
name: 'agentpool'
|
||||
osDiskSizeGB: 128
|
||||
osDiskType: 'Managed'
|
||||
osSKU: 'Ubuntu'
|
||||
osType: 'Linux'
|
||||
type: 'VirtualMachineScaleSets'
|
||||
vmSize: 'Standard_D2s_v3'
|
||||
}
|
||||
]
|
||||
dnsPrefix: 'identitytest'
|
||||
enableRBAC: true
|
||||
linuxProfile: {
|
||||
adminUsername: adminUser
|
||||
ssh: {
|
||||
publicKeys: [
|
||||
{
|
||||
keyData: sshPubKey
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
oidcIssuerProfile: {
|
||||
enabled: true
|
||||
}
|
||||
securityProfile: {
|
||||
workloadIdentity: {
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output AZIDENTITY_ACR_LOGIN_SERVER string = deployResources ? containerRegistry.properties.loginServer : ''
|
||||
output AZIDENTITY_ACR_NAME string = deployResources ? containerRegistry.name : ''
|
||||
output AZIDENTITY_AKS_NAME string = deployResources ? aks.name : ''
|
||||
output AZIDENTITY_FUNCTION_NAME string = deployResources ? azfunc.name : ''
|
||||
output AZIDENTITY_STORAGE_ID string = deployResources ? sa.id : ''
|
||||
output AZIDENTITY_STORAGE_NAME string = deployResources ? sa.name : ''
|
||||
output AZIDENTITY_STORAGE_NAME_USER_ASSIGNED string = deployResources ? saUserAssigned.name : ''
|
||||
output AZIDENTITY_USER_ASSIGNED_IDENTITY string = deployResources ? usermgdid.id : ''
|
||||
output AZIDENTITY_USER_ASSIGNED_IDENTITY_CLIENT_ID string = deployResources ? usermgdid.properties.clientId : ''
|
||||
output AZIDENTITY_USER_ASSIGNED_IDENTITY_NAME string = deployResources ? usermgdid.name : ''
|
65
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/username_password_credential.go
generated
vendored
65
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/username_password_credential.go
generated
vendored
|
@ -11,7 +11,7 @@ import (
|
|||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/public"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
|
||||
)
|
||||
|
||||
const credNameUserPassword = "UsernamePasswordCredential"
|
||||
|
@ -24,11 +24,19 @@ type UsernamePasswordCredentialOptions struct {
|
|||
// Add the wildcard value "*" to allow the credential to acquire tokens for any tenant in which the
|
||||
// application is registered.
|
||||
AdditionallyAllowedTenants []string
|
||||
|
||||
// authenticationRecord returned by a call to a credential's Authenticate method. Set this option
|
||||
// to enable the credential to use data from a previous authentication.
|
||||
authenticationRecord authenticationRecord
|
||||
|
||||
// DisableInstanceDiscovery should be set true only by applications authenticating in disconnected clouds, or
|
||||
// private clouds such as Azure Stack. It determines whether the credential requests Azure AD instance metadata
|
||||
// private clouds such as Azure Stack. It determines whether the credential requests Microsoft Entra instance metadata
|
||||
// from https://login.microsoft.com before authenticating. Setting this to true will skip this request, making
|
||||
// the application responsible for ensuring the configured authority is valid and trustworthy.
|
||||
DisableInstanceDiscovery bool
|
||||
|
||||
// tokenCachePersistenceOptions enables persistent token caching when not nil.
|
||||
tokenCachePersistenceOptions *tokenCachePersistenceOptions
|
||||
}
|
||||
|
||||
// UsernamePasswordCredential authenticates a user with a password. Microsoft doesn't recommend this kind of authentication,
|
||||
|
@ -36,10 +44,7 @@ type UsernamePasswordCredentialOptions struct {
|
|||
// with any form of multi-factor authentication, and the application must already have user or admin consent.
|
||||
// This credential can only authenticate work and school accounts; it can't authenticate Microsoft accounts.
|
||||
type UsernamePasswordCredential struct {
|
||||
account public.Account
|
||||
client publicClient
|
||||
password, username string
|
||||
s *syncer
|
||||
client *publicClient
|
||||
}
|
||||
|
||||
// NewUsernamePasswordCredential creates a UsernamePasswordCredential. clientID is the ID of the application the user
|
||||
|
@ -48,34 +53,38 @@ func NewUsernamePasswordCredential(tenantID string, clientID string, username st
|
|||
if options == nil {
|
||||
options = &UsernamePasswordCredentialOptions{}
|
||||
}
|
||||
c, err := getPublicClient(clientID, tenantID, &options.ClientOptions, public.WithInstanceDiscovery(!options.DisableInstanceDiscovery))
|
||||
opts := publicClientOptions{
|
||||
AdditionallyAllowedTenants: options.AdditionallyAllowedTenants,
|
||||
ClientOptions: options.ClientOptions,
|
||||
DisableInstanceDiscovery: options.DisableInstanceDiscovery,
|
||||
Password: password,
|
||||
Record: options.authenticationRecord,
|
||||
TokenCachePersistenceOptions: options.tokenCachePersistenceOptions,
|
||||
Username: username,
|
||||
}
|
||||
c, err := newPublicClient(tenantID, clientID, credNameUserPassword, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
upc := UsernamePasswordCredential{client: c, password: password, username: username}
|
||||
upc.s = newSyncer(credNameUserPassword, tenantID, options.AdditionallyAllowedTenants, upc.requestToken, upc.silentAuth)
|
||||
return &upc, nil
|
||||
return &UsernamePasswordCredential{client: c}, err
|
||||
}
|
||||
|
||||
// GetToken requests an access token from Azure Active Directory. This method is called automatically by Azure SDK clients.
|
||||
// Authenticate the user. Subsequent calls to GetToken will automatically use the returned AuthenticationRecord.
|
||||
func (c *UsernamePasswordCredential) authenticate(ctx context.Context, opts *policy.TokenRequestOptions) (authenticationRecord, error) {
|
||||
var err error
|
||||
ctx, endSpan := runtime.StartSpan(ctx, credNameUserPassword+"."+traceOpAuthenticate, c.client.azClient.Tracer(), nil)
|
||||
defer func() { endSpan(err) }()
|
||||
tk, err := c.client.Authenticate(ctx, opts)
|
||||
return tk, err
|
||||
}
|
||||
|
||||
// GetToken requests an access token from Microsoft Entra ID. This method is called automatically by Azure SDK clients.
|
||||
func (c *UsernamePasswordCredential) GetToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
return c.s.GetToken(ctx, opts)
|
||||
}
|
||||
|
||||
func (c *UsernamePasswordCredential) requestToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
ar, err := c.client.AcquireTokenByUsernamePassword(ctx, opts.Scopes, c.username, c.password, public.WithTenantID(opts.TenantID))
|
||||
if err == nil {
|
||||
c.account = ar.Account
|
||||
}
|
||||
return azcore.AccessToken{Token: ar.AccessToken, ExpiresOn: ar.ExpiresOn.UTC()}, err
|
||||
}
|
||||
|
||||
func (c *UsernamePasswordCredential) silentAuth(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
ar, err := c.client.AcquireTokenSilent(ctx, opts.Scopes,
|
||||
public.WithSilentAccount(c.account),
|
||||
public.WithTenantID(opts.TenantID),
|
||||
)
|
||||
return azcore.AccessToken{Token: ar.AccessToken, ExpiresOn: ar.ExpiresOn.UTC()}, err
|
||||
var err error
|
||||
ctx, endSpan := runtime.StartSpan(ctx, credNameUserPassword+"."+traceOpGetToken, c.client.azClient.Tracer(), nil)
|
||||
defer func() { endSpan(err) }()
|
||||
tk, err := c.client.GetToken(ctx, opts)
|
||||
return tk, err
|
||||
}
|
||||
|
||||
var _ azcore.TokenCredential = (*UsernamePasswordCredential)(nil)
|
||||
|
|
5
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/version.go
generated
vendored
5
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/version.go
generated
vendored
|
@ -10,6 +10,9 @@ const (
|
|||
// UserAgent is the string to be used in the user agent string when making requests.
|
||||
component = "azidentity"
|
||||
|
||||
// module is the fully qualified name of the module used in telemetry and distributed tracing.
|
||||
module = "github.com/Azure/azure-sdk-for-go/sdk/" + component
|
||||
|
||||
// Version is the semantic version (see http://semver.org) of this module.
|
||||
version = "v1.3.0"
|
||||
version = "v1.6.0"
|
||||
)
|
||||
|
|
15
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/workload_identity.go
generated
vendored
15
vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/workload_identity.go
generated
vendored
|
@ -15,6 +15,7 @@ import (
|
|||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
|
||||
)
|
||||
|
||||
const credNameWorkloadIdentity = "WorkloadIdentityCredential"
|
||||
|
@ -41,13 +42,13 @@ type WorkloadIdentityCredentialOptions struct {
|
|||
// ClientID of the service principal. Defaults to the value of the environment variable AZURE_CLIENT_ID.
|
||||
ClientID string
|
||||
// DisableInstanceDiscovery should be set true only by applications authenticating in disconnected clouds, or
|
||||
// private clouds such as Azure Stack. It determines whether the credential requests Azure AD instance metadata
|
||||
// private clouds such as Azure Stack. It determines whether the credential requests Microsoft Entra instance metadata
|
||||
// from https://login.microsoft.com before authenticating. Setting this to true will skip this request, making
|
||||
// the application responsible for ensuring the configured authority is valid and trustworthy.
|
||||
DisableInstanceDiscovery bool
|
||||
// TenantID of the service principal. Defaults to the value of the environment variable AZURE_TENANT_ID.
|
||||
TenantID string
|
||||
// TokenFilePath is the path a file containing the workload identity token. Defaults to the value of the
|
||||
// TokenFilePath is the path of a file containing a Kubernetes service account token. Defaults to the value of the
|
||||
// environment variable AZURE_FEDERATED_TOKEN_FILE.
|
||||
TokenFilePath string
|
||||
}
|
||||
|
@ -88,14 +89,18 @@ func NewWorkloadIdentityCredential(options *WorkloadIdentityCredentialOptions) (
|
|||
return nil, err
|
||||
}
|
||||
// we want "WorkloadIdentityCredential" in log messages, not "ClientAssertionCredential"
|
||||
cred.s.name = credNameWorkloadIdentity
|
||||
cred.client.name = credNameWorkloadIdentity
|
||||
w.cred = cred
|
||||
return &w, nil
|
||||
}
|
||||
|
||||
// GetToken requests an access token from Azure Active Directory. Azure SDK clients call this method automatically.
|
||||
// GetToken requests an access token from Microsoft Entra ID. Azure SDK clients call this method automatically.
|
||||
func (w *WorkloadIdentityCredential) GetToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
return w.cred.GetToken(ctx, opts)
|
||||
var err error
|
||||
ctx, endSpan := runtime.StartSpan(ctx, credNameWorkloadIdentity+"."+traceOpGetToken, w.cred.client.azClient.Tracer(), nil)
|
||||
defer func() { endSpan(err) }()
|
||||
tk, err := w.cred.GetToken(ctx, opts)
|
||||
return tk, err
|
||||
}
|
||||
|
||||
// getAssertion returns the specified file's content, which is expected to be a Kubernetes service account token.
|
||||
|
|
30
vendor/github.com/Azure/azure-sdk-for-go/sdk/internal/errorinfo/errorinfo.go
generated
vendored
30
vendor/github.com/Azure/azure-sdk-for-go/sdk/internal/errorinfo/errorinfo.go
generated
vendored
|
@ -14,3 +14,33 @@ type NonRetriable interface {
|
|||
error
|
||||
NonRetriable()
|
||||
}
|
||||
|
||||
// NonRetriableError marks the specified error as non-retriable.
|
||||
// This function takes an error as input and returns a new error that is marked as non-retriable.
|
||||
func NonRetriableError(err error) error {
|
||||
return &nonRetriableError{err}
|
||||
}
|
||||
|
||||
// nonRetriableError is a struct that embeds the error interface.
|
||||
// It is used to represent errors that should not be retried.
|
||||
type nonRetriableError struct {
|
||||
error
|
||||
}
|
||||
|
||||
// Error method for nonRetriableError struct.
|
||||
// It returns the error message of the embedded error.
|
||||
func (p *nonRetriableError) Error() string {
|
||||
return p.error.Error()
|
||||
}
|
||||
|
||||
// NonRetriable is a marker method for nonRetriableError struct.
|
||||
// Non-functional and indicates that the error is non-retriable.
|
||||
func (*nonRetriableError) NonRetriable() {
|
||||
// marker method
|
||||
}
|
||||
|
||||
// Unwrap method for nonRetriableError struct.
|
||||
// It returns the original error that was marked as non-retriable.
|
||||
func (p *nonRetriableError) Unwrap() error {
|
||||
return p.error
|
||||
}
|
||||
|
|
5
vendor/github.com/Azure/azure-sdk-for-go/sdk/internal/exported/exported.go
generated
vendored
5
vendor/github.com/Azure/azure-sdk-for-go/sdk/internal/exported/exported.go
generated
vendored
|
@ -39,6 +39,11 @@ type PayloadOptions struct {
|
|||
// Subsequent reads will access the cached value.
|
||||
// Exported as runtime.Payload() WITHOUT the opts parameter.
|
||||
func Payload(resp *http.Response, opts *PayloadOptions) ([]byte, error) {
|
||||
if resp.Body == nil {
|
||||
// this shouldn't happen in real-world scenarios as a
|
||||
// response with no body should set it to http.NoBody
|
||||
return nil, nil
|
||||
}
|
||||
modifyBytes := func(b []byte) []byte { return b }
|
||||
if opts != nil && opts.BytesModifier != nil {
|
||||
modifyBytes = opts.BytesModifier
|
||||
|
|
|
@ -48,8 +48,8 @@ duplication.
|
|||
|
||||
.Net People, Take note on X509:
|
||||
This uses x509.Certificates and private keys. x509 does not store private keys. .Net
|
||||
has some x509.Certificate2 thing that has private keys, but that is just some bullcrap that .Net
|
||||
added, it doesn't exist in real life. As such I've put a PEM decoder into here.
|
||||
has a x509.Certificate2 abstraction that has private keys, but that just a strange invention.
|
||||
As such I've put a PEM decoder into here.
|
||||
*/
|
||||
|
||||
// TODO(msal): This should have example code for each method on client using Go's example doc framework.
|
||||
|
@ -59,6 +59,8 @@ added, it doesn't exist in real life. As such I've put a PEM decoder into here.
|
|||
// For details see https://aka.ms/msal-net-authenticationresult
|
||||
type AuthResult = base.AuthResult
|
||||
|
||||
type AuthenticationScheme = authority.AuthenticationScheme
|
||||
|
||||
type Account = shared.Account
|
||||
|
||||
// CertFromPEM converts a PEM file (.pem or .key) for use with [NewCredFromCert]. The file
|
||||
|
@ -454,6 +456,33 @@ func WithClaims(claims string) interface {
|
|||
}
|
||||
}
|
||||
|
||||
// WithAuthenticationScheme is an extensibility mechanism designed to be used only by Azure Arc for proof of possession access tokens.
|
||||
func WithAuthenticationScheme(authnScheme AuthenticationScheme) interface {
|
||||
AcquireSilentOption
|
||||
AcquireByCredentialOption
|
||||
options.CallOption
|
||||
} {
|
||||
return struct {
|
||||
AcquireSilentOption
|
||||
AcquireByCredentialOption
|
||||
options.CallOption
|
||||
}{
|
||||
CallOption: options.NewCallOption(
|
||||
func(a any) error {
|
||||
switch t := a.(type) {
|
||||
case *acquireTokenSilentOptions:
|
||||
t.authnScheme = authnScheme
|
||||
case *acquireTokenByCredentialOptions:
|
||||
t.authnScheme = authnScheme
|
||||
default:
|
||||
return fmt.Errorf("unexpected options type %T", a)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// WithTenantID specifies a tenant for a single authentication. It may be different than the tenant set in [New].
|
||||
// This option is valid for any token acquisition method.
|
||||
func WithTenantID(tenantID string) interface {
|
||||
|
@ -499,6 +528,7 @@ func WithTenantID(tenantID string) interface {
|
|||
type acquireTokenSilentOptions struct {
|
||||
account Account
|
||||
claims, tenantID string
|
||||
authnScheme AuthenticationScheme
|
||||
}
|
||||
|
||||
// AcquireSilentOption is implemented by options for AcquireTokenSilent
|
||||
|
@ -549,6 +579,7 @@ func (cca Client) AcquireTokenSilent(ctx context.Context, scopes []string, opts
|
|||
Credential: cca.cred,
|
||||
IsAppCache: o.account.IsZero(),
|
||||
TenantID: o.tenantID,
|
||||
AuthnScheme: o.authnScheme,
|
||||
}
|
||||
|
||||
return cca.base.AcquireTokenSilent(ctx, silentParameters)
|
||||
|
@ -614,6 +645,7 @@ func (cca Client) AcquireTokenByAuthCode(ctx context.Context, code string, redir
|
|||
// acquireTokenByCredentialOptions contains optional configuration for AcquireTokenByCredential
|
||||
type acquireTokenByCredentialOptions struct {
|
||||
claims, tenantID string
|
||||
authnScheme AuthenticationScheme
|
||||
}
|
||||
|
||||
// AcquireByCredentialOption is implemented by options for AcquireTokenByCredential
|
||||
|
@ -637,7 +669,9 @@ func (cca Client) AcquireTokenByCredential(ctx context.Context, scopes []string,
|
|||
authParams.Scopes = scopes
|
||||
authParams.AuthorizationType = authority.ATClientCredentials
|
||||
authParams.Claims = o.claims
|
||||
|
||||
if o.authnScheme != nil {
|
||||
authParams.AuthnScheme = o.authnScheme
|
||||
}
|
||||
token, err := cca.base.Token.Credential(ctx, authParams, cca.cred)
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
|
|
|
@ -54,6 +54,7 @@ type AcquireTokenSilentParameters struct {
|
|||
UserAssertion string
|
||||
AuthorizationType authority.AuthorizeType
|
||||
Claims string
|
||||
AuthnScheme authority.AuthenticationScheme
|
||||
}
|
||||
|
||||
// AcquireTokenAuthCodeParameters contains the parameters required to acquire an access token using the auth code flow.
|
||||
|
@ -289,6 +290,9 @@ func (b Client) AcquireTokenSilent(ctx context.Context, silent AcquireTokenSilen
|
|||
authParams.AuthorizationType = silent.AuthorizationType
|
||||
authParams.Claims = silent.Claims
|
||||
authParams.UserAssertion = silent.UserAssertion
|
||||
if silent.AuthnScheme != nil {
|
||||
authParams.AuthnScheme = silent.AuthnScheme
|
||||
}
|
||||
|
||||
m := b.pmanager
|
||||
if authParams.AuthorizationType != authority.ATOnBehalfOf {
|
||||
|
@ -313,6 +317,7 @@ func (b Client) AcquireTokenSilent(ctx context.Context, silent AcquireTokenSilen
|
|||
if silent.Claims == "" {
|
||||
ar, err = AuthResultFromStorage(storageTokenResponse)
|
||||
if err == nil {
|
||||
ar.AccessToken, err = authParams.AuthnScheme.FormatAccessToken(ar.AccessToken)
|
||||
return ar, err
|
||||
}
|
||||
}
|
||||
|
@ -417,6 +422,11 @@ func (b Client) AuthResultFromToken(ctx context.Context, authParams authority.Au
|
|||
if err == nil && b.cacheAccessor != nil {
|
||||
err = b.cacheAccessor.Export(ctx, b.manager, cache.ExportHints{PartitionKey: key})
|
||||
}
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
|
||||
ar.AccessToken, err = authParams.AuthnScheme.FormatAccessToken(ar.AccessToken)
|
||||
return ar, err
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
internalTime "github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/json/types/time"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/accesstokens"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/authority"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/shared"
|
||||
)
|
||||
|
||||
|
@ -75,12 +76,14 @@ type AccessToken struct {
|
|||
ExtendedExpiresOn internalTime.Unix `json:"extended_expires_on,omitempty"`
|
||||
CachedAt internalTime.Unix `json:"cached_at,omitempty"`
|
||||
UserAssertionHash string `json:"user_assertion_hash,omitempty"`
|
||||
TokenType string `json:"token_type,omitempty"`
|
||||
AuthnSchemeKeyID string `json:"keyid,omitempty"`
|
||||
|
||||
AdditionalFields map[string]interface{}
|
||||
}
|
||||
|
||||
// NewAccessToken is the constructor for AccessToken.
|
||||
func NewAccessToken(homeID, env, realm, clientID string, cachedAt, expiresOn, extendedExpiresOn time.Time, scopes, token string) AccessToken {
|
||||
func NewAccessToken(homeID, env, realm, clientID string, cachedAt, expiresOn, extendedExpiresOn time.Time, scopes, token, tokenType, authnSchemeKeyID string) AccessToken {
|
||||
return AccessToken{
|
||||
HomeAccountID: homeID,
|
||||
Environment: env,
|
||||
|
@ -92,15 +95,23 @@ func NewAccessToken(homeID, env, realm, clientID string, cachedAt, expiresOn, ex
|
|||
CachedAt: internalTime.Unix{T: cachedAt.UTC()},
|
||||
ExpiresOn: internalTime.Unix{T: expiresOn.UTC()},
|
||||
ExtendedExpiresOn: internalTime.Unix{T: extendedExpiresOn.UTC()},
|
||||
TokenType: tokenType,
|
||||
AuthnSchemeKeyID: authnSchemeKeyID,
|
||||
}
|
||||
}
|
||||
|
||||
// Key outputs the key that can be used to uniquely look up this entry in a map.
|
||||
func (a AccessToken) Key() string {
|
||||
return strings.Join(
|
||||
key := strings.Join(
|
||||
[]string{a.HomeAccountID, a.Environment, a.CredentialType, a.ClientID, a.Realm, a.Scopes},
|
||||
shared.CacheKeySeparator,
|
||||
)
|
||||
// add token type to key for new access tokens types. skip for bearer token type to
|
||||
// preserve fwd and back compat between a common cache and msal clients
|
||||
if !strings.EqualFold(a.TokenType, authority.AccessTokenTypeBearer) {
|
||||
key = strings.Join([]string{key, a.TokenType}, shared.CacheKeySeparator)
|
||||
}
|
||||
return strings.ToLower(key)
|
||||
}
|
||||
|
||||
// FakeValidate enables tests to fake access token validation
|
||||
|
@ -167,10 +178,11 @@ func NewIDToken(homeID, env, realm, clientID, idToken string) IDToken {
|
|||
|
||||
// Key outputs the key that can be used to uniquely look up this entry in a map.
|
||||
func (id IDToken) Key() string {
|
||||
return strings.Join(
|
||||
key := strings.Join(
|
||||
[]string{id.HomeAccountID, id.Environment, id.CredentialType, id.ClientID, id.Realm},
|
||||
shared.CacheKeySeparator,
|
||||
)
|
||||
return strings.ToLower(key)
|
||||
}
|
||||
|
||||
// AppMetaData is the JSON representation of application metadata for encoding to storage.
|
||||
|
@ -193,8 +205,9 @@ func NewAppMetaData(familyID, clientID, environment string) AppMetaData {
|
|||
|
||||
// Key outputs the key that can be used to uniquely look up this entry in a map.
|
||||
func (a AppMetaData) Key() string {
|
||||
return strings.Join(
|
||||
key := strings.Join(
|
||||
[]string{"AppMetaData", a.Environment, a.ClientID},
|
||||
shared.CacheKeySeparator,
|
||||
)
|
||||
return strings.ToLower(key)
|
||||
}
|
||||
|
|
|
@ -41,6 +41,8 @@ func (m *PartitionedManager) Read(ctx context.Context, authParameters authority.
|
|||
realm := authParameters.AuthorityInfo.Tenant
|
||||
clientID := authParameters.ClientID
|
||||
scopes := authParameters.Scopes
|
||||
authnSchemeKeyID := authParameters.AuthnScheme.KeyID()
|
||||
tokenType := authParameters.AuthnScheme.AccessTokenType()
|
||||
|
||||
// fetch metadata if instanceDiscovery is enabled
|
||||
aliases := []string{authParameters.AuthorityInfo.Host}
|
||||
|
@ -57,7 +59,7 @@ func (m *PartitionedManager) Read(ctx context.Context, authParameters authority.
|
|||
|
||||
// errors returned by read* methods indicate a cache miss and are therefore non-fatal. We continue populating
|
||||
// TokenResponse fields so that e.g. lack of an ID token doesn't prevent the caller from receiving a refresh token.
|
||||
accessToken, err := m.readAccessToken(aliases, realm, clientID, userAssertionHash, scopes, partitionKeyFromRequest)
|
||||
accessToken, err := m.readAccessToken(aliases, realm, clientID, userAssertionHash, scopes, partitionKeyFromRequest, tokenType, authnSchemeKeyID)
|
||||
if err == nil {
|
||||
tr.AccessToken = accessToken
|
||||
}
|
||||
|
@ -84,7 +86,7 @@ func (m *PartitionedManager) Read(ctx context.Context, authParameters authority.
|
|||
|
||||
// Write writes a token response to the cache and returns the account information the token is stored with.
|
||||
func (m *PartitionedManager) Write(authParameters authority.AuthParams, tokenResponse accesstokens.TokenResponse) (shared.Account, error) {
|
||||
authParameters.HomeAccountID = tokenResponse.ClientInfo.HomeAccountID()
|
||||
authParameters.HomeAccountID = tokenResponse.HomeAccountID()
|
||||
homeAccountID := authParameters.HomeAccountID
|
||||
environment := authParameters.AuthorityInfo.Host
|
||||
realm := authParameters.AuthorityInfo.Tenant
|
||||
|
@ -92,7 +94,7 @@ func (m *PartitionedManager) Write(authParameters authority.AuthParams, tokenRes
|
|||
target := strings.Join(tokenResponse.GrantedScopes.Slice, scopeSeparator)
|
||||
userAssertionHash := authParameters.AssertionHash()
|
||||
cachedAt := time.Now()
|
||||
|
||||
authnSchemeKeyID := authParameters.AuthnScheme.KeyID()
|
||||
var account shared.Account
|
||||
|
||||
if len(tokenResponse.RefreshToken) > 0 {
|
||||
|
@ -116,6 +118,8 @@ func (m *PartitionedManager) Write(authParameters authority.AuthParams, tokenRes
|
|||
tokenResponse.ExtExpiresOn.T,
|
||||
target,
|
||||
tokenResponse.AccessToken,
|
||||
tokenResponse.TokenType,
|
||||
authnSchemeKeyID,
|
||||
)
|
||||
if authParameters.AuthorizationType == authority.ATOnBehalfOf {
|
||||
accessToken.UserAssertionHash = userAssertionHash // get Hash method on this
|
||||
|
@ -215,7 +219,7 @@ func (m *PartitionedManager) aadMetadata(ctx context.Context, authorityInfo auth
|
|||
return m.aadCache[authorityInfo.Host], nil
|
||||
}
|
||||
|
||||
func (m *PartitionedManager) readAccessToken(envAliases []string, realm, clientID, userAssertionHash string, scopes []string, partitionKey string) (AccessToken, error) {
|
||||
func (m *PartitionedManager) readAccessToken(envAliases []string, realm, clientID, userAssertionHash string, scopes []string, partitionKey, tokenType, authnSchemeKeyID string) (AccessToken, error) {
|
||||
m.contractMu.RLock()
|
||||
defer m.contractMu.RUnlock()
|
||||
if accessTokens, ok := m.contract.AccessTokensPartition[partitionKey]; ok {
|
||||
|
@ -224,9 +228,11 @@ func (m *PartitionedManager) readAccessToken(envAliases []string, realm, clientI
|
|||
// an issue, however if it does become a problem then we know where to look.
|
||||
for _, at := range accessTokens {
|
||||
if at.Realm == realm && at.ClientID == clientID && at.UserAssertionHash == userAssertionHash {
|
||||
if checkAlias(at.Environment, envAliases) {
|
||||
if isMatchingScopes(scopes, at.Scopes) {
|
||||
return at, nil
|
||||
if at.TokenType == tokenType && at.AuthnSchemeKeyID == authnSchemeKeyID {
|
||||
if checkAlias(at.Environment, envAliases) {
|
||||
if isMatchingScopes(scopes, at.Scopes) {
|
||||
return at, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,6 +82,39 @@ func isMatchingScopes(scopesOne []string, scopesTwo string) bool {
|
|||
return scopeCounter == len(scopesOne)
|
||||
}
|
||||
|
||||
// needsUpgrade returns true if the given key follows the v1.0 schema i.e.,
|
||||
// it contains an uppercase character (v1.1+ keys are all lowercase)
|
||||
func needsUpgrade(key string) bool {
|
||||
for _, r := range key {
|
||||
if 'A' <= r && r <= 'Z' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// upgrade a v1.0 cache item by adding a v1.1+ item having the same value and deleting
|
||||
// the v1.0 item. Callers must hold an exclusive lock on m.
|
||||
func upgrade[T any](m map[string]T, k string) T {
|
||||
v1_1Key := strings.ToLower(k)
|
||||
v, ok := m[k]
|
||||
if !ok {
|
||||
// another goroutine did the upgrade while this one was waiting for the write lock
|
||||
return m[v1_1Key]
|
||||
}
|
||||
if v2, ok := m[v1_1Key]; ok {
|
||||
// cache has an equivalent v1.1+ item, which we prefer because we know it was added
|
||||
// by a newer version of the module and is therefore more likely to remain valid.
|
||||
// The v1.0 item may have expired because only v1.0 or earlier would update it.
|
||||
v = v2
|
||||
} else {
|
||||
// add an equivalent item according to the v1.1 schema
|
||||
m[v1_1Key] = v
|
||||
}
|
||||
delete(m, k)
|
||||
return v
|
||||
}
|
||||
|
||||
// Read reads a storage token from the cache if it exists.
|
||||
func (m *Manager) Read(ctx context.Context, authParameters authority.AuthParams) (TokenResponse, error) {
|
||||
tr := TokenResponse{}
|
||||
|
@ -89,6 +122,8 @@ func (m *Manager) Read(ctx context.Context, authParameters authority.AuthParams)
|
|||
realm := authParameters.AuthorityInfo.Tenant
|
||||
clientID := authParameters.ClientID
|
||||
scopes := authParameters.Scopes
|
||||
authnSchemeKeyID := authParameters.AuthnScheme.KeyID()
|
||||
tokenType := authParameters.AuthnScheme.AccessTokenType()
|
||||
|
||||
// fetch metadata if instanceDiscovery is enabled
|
||||
aliases := []string{authParameters.AuthorityInfo.Host}
|
||||
|
@ -100,7 +135,7 @@ func (m *Manager) Read(ctx context.Context, authParameters authority.AuthParams)
|
|||
aliases = metadata.Aliases
|
||||
}
|
||||
|
||||
accessToken := m.readAccessToken(homeAccountID, aliases, realm, clientID, scopes)
|
||||
accessToken := m.readAccessToken(homeAccountID, aliases, realm, clientID, scopes, tokenType, authnSchemeKeyID)
|
||||
tr.AccessToken = accessToken
|
||||
|
||||
if homeAccountID == "" {
|
||||
|
@ -134,13 +169,13 @@ const scopeSeparator = " "
|
|||
|
||||
// Write writes a token response to the cache and returns the account information the token is stored with.
|
||||
func (m *Manager) Write(authParameters authority.AuthParams, tokenResponse accesstokens.TokenResponse) (shared.Account, error) {
|
||||
authParameters.HomeAccountID = tokenResponse.ClientInfo.HomeAccountID()
|
||||
homeAccountID := authParameters.HomeAccountID
|
||||
homeAccountID := tokenResponse.HomeAccountID()
|
||||
environment := authParameters.AuthorityInfo.Host
|
||||
realm := authParameters.AuthorityInfo.Tenant
|
||||
clientID := authParameters.ClientID
|
||||
target := strings.Join(tokenResponse.GrantedScopes.Slice, scopeSeparator)
|
||||
cachedAt := time.Now()
|
||||
authnSchemeKeyID := authParameters.AuthnScheme.KeyID()
|
||||
|
||||
var account shared.Account
|
||||
|
||||
|
@ -162,6 +197,8 @@ func (m *Manager) Write(authParameters authority.AuthParams, tokenResponse acces
|
|||
tokenResponse.ExtExpiresOn.T,
|
||||
target,
|
||||
tokenResponse.AccessToken,
|
||||
tokenResponse.TokenType,
|
||||
authnSchemeKeyID,
|
||||
)
|
||||
|
||||
// Since we have a valid access token, cache it before moving on.
|
||||
|
@ -249,21 +286,27 @@ func (m *Manager) aadMetadata(ctx context.Context, authorityInfo authority.Info)
|
|||
return m.aadCache[authorityInfo.Host], nil
|
||||
}
|
||||
|
||||
func (m *Manager) readAccessToken(homeID string, envAliases []string, realm, clientID string, scopes []string) AccessToken {
|
||||
func (m *Manager) readAccessToken(homeID string, envAliases []string, realm, clientID string, scopes []string, tokenType, authnSchemeKeyID string) AccessToken {
|
||||
m.contractMu.RLock()
|
||||
defer m.contractMu.RUnlock()
|
||||
// TODO: linear search (over a map no less) is slow for a large number (thousands) of tokens.
|
||||
// this shows up as the dominating node in a profile. for real-world scenarios this likely isn't
|
||||
// an issue, however if it does become a problem then we know where to look.
|
||||
for _, at := range m.contract.AccessTokens {
|
||||
for k, at := range m.contract.AccessTokens {
|
||||
if at.HomeAccountID == homeID && at.Realm == realm && at.ClientID == clientID {
|
||||
if checkAlias(at.Environment, envAliases) {
|
||||
if isMatchingScopes(scopes, at.Scopes) {
|
||||
if (strings.EqualFold(at.TokenType, tokenType) && at.AuthnSchemeKeyID == authnSchemeKeyID) || (at.TokenType == "" && (tokenType == "" || tokenType == "Bearer")) {
|
||||
if checkAlias(at.Environment, envAliases) && isMatchingScopes(scopes, at.Scopes) {
|
||||
m.contractMu.RUnlock()
|
||||
if needsUpgrade(k) {
|
||||
m.contractMu.Lock()
|
||||
defer m.contractMu.Unlock()
|
||||
at = upgrade(m.contract.AccessTokens, k)
|
||||
}
|
||||
return at
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m.contractMu.RUnlock()
|
||||
return AccessToken{}
|
||||
}
|
||||
|
||||
|
@ -304,15 +347,21 @@ func (m *Manager) readRefreshToken(homeID string, envAliases []string, familyID,
|
|||
// If app is part of the family or if we DO NOT KNOW if it's part of the family, search by family ID, then by client_id (we will know if an app is part of the family after the first token response).
|
||||
// https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/blob/311fe8b16e7c293462806f397e189a6aa1159769/src/client/Microsoft.Identity.Client/Internal/Requests/Silent/CacheSilentStrategy.cs#L95
|
||||
m.contractMu.RLock()
|
||||
defer m.contractMu.RUnlock()
|
||||
for _, matcher := range matchers {
|
||||
for _, rt := range m.contract.RefreshTokens {
|
||||
for k, rt := range m.contract.RefreshTokens {
|
||||
if matcher(rt) {
|
||||
m.contractMu.RUnlock()
|
||||
if needsUpgrade(k) {
|
||||
m.contractMu.Lock()
|
||||
defer m.contractMu.Unlock()
|
||||
rt = upgrade(m.contract.RefreshTokens, k)
|
||||
}
|
||||
return rt, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m.contractMu.RUnlock()
|
||||
return accesstokens.RefreshToken{}, fmt.Errorf("refresh token not found")
|
||||
}
|
||||
|
||||
|
@ -334,14 +383,20 @@ func (m *Manager) writeRefreshToken(refreshToken accesstokens.RefreshToken) erro
|
|||
|
||||
func (m *Manager) readIDToken(homeID string, envAliases []string, realm, clientID string) (IDToken, error) {
|
||||
m.contractMu.RLock()
|
||||
defer m.contractMu.RUnlock()
|
||||
for _, idt := range m.contract.IDTokens {
|
||||
for k, idt := range m.contract.IDTokens {
|
||||
if idt.HomeAccountID == homeID && idt.Realm == realm && idt.ClientID == clientID {
|
||||
if checkAlias(idt.Environment, envAliases) {
|
||||
m.contractMu.RUnlock()
|
||||
if needsUpgrade(k) {
|
||||
m.contractMu.Lock()
|
||||
defer m.contractMu.Unlock()
|
||||
idt = upgrade(m.contract.IDTokens, k)
|
||||
}
|
||||
return idt, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
m.contractMu.RUnlock()
|
||||
return IDToken{}, fmt.Errorf("token not found")
|
||||
}
|
||||
|
||||
|
@ -380,7 +435,6 @@ func (m *Manager) Account(homeAccountID string) shared.Account {
|
|||
|
||||
func (m *Manager) readAccount(homeAccountID string, envAliases []string, realm string) (shared.Account, error) {
|
||||
m.contractMu.RLock()
|
||||
defer m.contractMu.RUnlock()
|
||||
|
||||
// You might ask why, if cache.Accounts is a map, we would loop through all of these instead of using a key.
|
||||
// We only use a map because the storage contract shared between all language implementations says use a map.
|
||||
|
@ -388,11 +442,18 @@ func (m *Manager) readAccount(homeAccountID string, envAliases []string, realm s
|
|||
// a match in multiple envs (envAlias). That means we either need to hash each possible keyand do the lookup
|
||||
// or just statically check. Since the design is to have a storage.Manager per user, the amount of keys stored
|
||||
// is really low (say 2). Each hash is more expensive than the entire iteration.
|
||||
for _, acc := range m.contract.Accounts {
|
||||
for k, acc := range m.contract.Accounts {
|
||||
if acc.HomeAccountID == homeAccountID && checkAlias(acc.Environment, envAliases) && acc.Realm == realm {
|
||||
m.contractMu.RUnlock()
|
||||
if needsUpgrade(k) {
|
||||
m.contractMu.Lock()
|
||||
defer m.contractMu.Unlock()
|
||||
acc = upgrade(m.contract.Accounts, k)
|
||||
}
|
||||
return acc, nil
|
||||
}
|
||||
}
|
||||
m.contractMu.RUnlock()
|
||||
return shared.Account{}, fmt.Errorf("account not found")
|
||||
}
|
||||
|
||||
|
@ -406,13 +467,18 @@ func (m *Manager) writeAccount(account shared.Account) error {
|
|||
|
||||
func (m *Manager) readAppMetaData(envAliases []string, clientID string) (AppMetaData, error) {
|
||||
m.contractMu.RLock()
|
||||
defer m.contractMu.RUnlock()
|
||||
|
||||
for _, app := range m.contract.AppMetaData {
|
||||
for k, app := range m.contract.AppMetaData {
|
||||
if checkAlias(app.Environment, envAliases) && app.ClientID == clientID {
|
||||
m.contractMu.RUnlock()
|
||||
if needsUpgrade(k) {
|
||||
m.contractMu.Lock()
|
||||
defer m.contractMu.Unlock()
|
||||
app = upgrade(m.contract.AppMetaData, k)
|
||||
}
|
||||
return app, nil
|
||||
}
|
||||
}
|
||||
m.contractMu.RUnlock()
|
||||
return AppMetaData{}, fmt.Errorf("not found")
|
||||
}
|
||||
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
{
|
||||
"Account": {
|
||||
"uid.utid-login.windows.net-contoso": {
|
||||
"username": "John Doe",
|
||||
"local_account_id": "object1234",
|
||||
"realm": "contoso",
|
||||
"environment": "login.windows.net",
|
||||
"home_account_id": "uid.utid",
|
||||
"authority_type": "MSSTS"
|
||||
}
|
||||
},
|
||||
"RefreshToken": {
|
||||
"uid.utid-login.windows.net-refreshtoken-my_client_id--s2 s1 s3": {
|
||||
"target": "s2 s1 s3",
|
||||
"environment": "login.windows.net",
|
||||
"credential_type": "RefreshToken",
|
||||
"secret": "a refresh token",
|
||||
"client_id": "my_client_id",
|
||||
"home_account_id": "uid.utid"
|
||||
}
|
||||
},
|
||||
"AccessToken": {
|
||||
"an-entry": {
|
||||
"foo": "bar"
|
||||
},
|
||||
"uid.utid-login.windows.net-accesstoken-my_client_id-contoso-s2 s1 s3": {
|
||||
"environment": "login.windows.net",
|
||||
"credential_type": "AccessToken",
|
||||
"secret": "an access token",
|
||||
"realm": "contoso",
|
||||
"target": "s2 s1 s3",
|
||||
"client_id": "my_client_id",
|
||||
"cached_at": "1000",
|
||||
"home_account_id": "uid.utid",
|
||||
"extended_expires_on": "4600",
|
||||
"expires_on": "4600"
|
||||
}
|
||||
},
|
||||
"IdToken": {
|
||||
"uid.utid-login.windows.net-idtoken-my_client_id-contoso-": {
|
||||
"realm": "contoso",
|
||||
"environment": "login.windows.net",
|
||||
"credential_type": "IdToken",
|
||||
"secret": "header.eyJvaWQiOiAib2JqZWN0MTIzNCIsICJwcmVmZXJyZWRfdXNlcm5hbWUiOiAiSm9obiBEb2UiLCAic3ViIjogInN1YiJ9.signature",
|
||||
"client_id": "my_client_id",
|
||||
"home_account_id": "uid.utid"
|
||||
}
|
||||
},
|
||||
"unknownEntity": {"field1":"1","field2":"whats"},
|
||||
"AppMetadata": {
|
||||
"AppMetadata-login.windows.net-my_client_id": {
|
||||
"environment": "login.windows.net",
|
||||
"client_id": "my_client_id"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -119,6 +119,7 @@ func (t *Client) Credential(ctx context.Context, authParams authority.AuthParams
|
|||
return accesstokens.TokenResponse{}, err
|
||||
}
|
||||
return accesstokens.TokenResponse{
|
||||
TokenType: authParams.AuthnScheme.AccessTokenType(),
|
||||
AccessToken: tr.AccessToken,
|
||||
ExpiresOn: internalTime.DurationTime{
|
||||
T: now.Add(time.Duration(tr.ExpiresInSeconds) * time.Second),
|
||||
|
|
|
@ -30,7 +30,7 @@ import (
|
|||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/authority"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/internal/grant"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/wstrust"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
|
@ -380,6 +380,12 @@ func (c Client) FromSamlGrant(ctx context.Context, authParameters authority.Auth
|
|||
|
||||
func (c Client) doTokenResp(ctx context.Context, authParams authority.AuthParams, qv url.Values) (TokenResponse, error) {
|
||||
resp := TokenResponse{}
|
||||
if authParams.AuthnScheme != nil {
|
||||
trParams := authParams.AuthnScheme.TokenRequestParams()
|
||||
for k, v := range trParams {
|
||||
qv.Set(k, v)
|
||||
}
|
||||
}
|
||||
err := c.Comm.URLFormCall(ctx, authParams.Endpoints.TokenEndpoint, qv, &resp)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue