From 321233043f04025bf97a83a6ac593c0ed48e871d Mon Sep 17 00:00:00 2001 From: Youn Jae Kim Date: Wed, 20 Nov 2024 15:18:21 -0800 Subject: [PATCH 1/2] Signed-off-by: Youn Jae Kim Deprecating ADAL Signed-off-by: Youn Jae Kim --- config/manager/kustomization.yaml | 2 + config/service_account/kustomization.yaml | 5 + go.mod | 2 - go.sum | 10 - .../azure/azure_aad_workload_identity.go | 85 +- pkg/scalers/azure/azure_app_insights.go | 35 +- pkg/scalers/azure/azure_app_insights_test.go | 26 +- .../Azure/azure-amqp-common-go/v4/LICENSE | 21 - .../go-autorest/autorest/azure/auth/LICENSE | 191 ----- .../go-autorest/autorest/azure/auth/README.md | 152 ---- .../go-autorest/autorest/azure/auth/auth.go | 772 ------------------ .../autorest/azure/auth/go_mod_tidy_hack.go | 25 - .../go-autorest/autorest/azure/cli/LICENSE | 191 ----- .../autorest/azure/cli/go_mod_tidy_hack.go | 25 - .../go-autorest/autorest/azure/cli/profile.go | 83 -- vendor/modules.txt | 12 - 16 files changed, 37 insertions(+), 1600 deletions(-) delete mode 100644 vendor/github.com/Azure/azure-amqp-common-go/v4/LICENSE delete mode 100644 vendor/github.com/Azure/go-autorest/autorest/azure/auth/LICENSE delete mode 100644 vendor/github.com/Azure/go-autorest/autorest/azure/auth/README.md delete mode 100644 vendor/github.com/Azure/go-autorest/autorest/azure/auth/auth.go delete mode 100644 vendor/github.com/Azure/go-autorest/autorest/azure/auth/go_mod_tidy_hack.go delete mode 100644 vendor/github.com/Azure/go-autorest/autorest/azure/cli/LICENSE delete mode 100644 vendor/github.com/Azure/go-autorest/autorest/azure/cli/go_mod_tidy_hack.go delete mode 100644 vendor/github.com/Azure/go-autorest/autorest/azure/cli/profile.go diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 1ca9f662392..9e965c91137 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -8,3 +8,5 @@ images: - name: ghcr.io/kedacore/keda newName: ghcr.io/kedacore/keda newTag: main +commonLabels: + azure.workload.identity/use: "true" diff --git a/config/service_account/kustomization.yaml b/config/service_account/kustomization.yaml index 4256b2be3c7..548237d3da8 100644 --- a/config/service_account/kustomization.yaml +++ b/config/service_account/kustomization.yaml @@ -3,3 +3,8 @@ resources: apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization +commonLabels: + azure.workload.identity/use: "true" +commonAnnotations: + azure.workload.identity/client-id: "" + azure.workload.identity/tenant-id: "" diff --git a/go.mod b/go.mod index ab53e4324fa..01368597010 100644 --- a/go.mod +++ b/go.mod @@ -172,8 +172,6 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.0 // indirect github.com/Azure/go-amqp v1.1.0 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect - github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect diff --git a/go.sum b/go.sum index 64cfcb8f531..b7ccb7b3d29 100644 --- a/go.sum +++ b/go.sum @@ -1340,8 +1340,6 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= -github.com/Azure/azure-amqp-common-go/v4 v4.2.0 h1:q/jLx1KJ8xeI8XGfkOWMN9XrXzAfVTkyvCxPvHCjd2I= -github.com/Azure/azure-amqp-common-go/v4 v4.2.0/go.mod h1:GD3m/WPPma+621UaU6KNjKEo5Hl09z86viKwQjTpV0Q= github.com/Azure/azure-kusto-go v0.16.1 h1:vCBWcQghmC1qIErUUgVNWHxGhZVStu1U/hki6iBA14k= github.com/Azure/azure-kusto-go v0.16.1/go.mod h1:9F2zvXH8B6eWzgI1S4k1ZXAIufnBZ1bv1cW1kB1n3D0= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= @@ -1384,17 +1382,11 @@ github.com/Azure/go-amqp v1.1.0 h1:XUhx5f4lZFVf6LQc5kBUFECW0iJW9VLxKCYrBeGwl0U= github.com/Azure/go-amqp v1.1.0/go.mod h1:vZAogwdrkbyK3Mla8m/CxSc/aKdnTZ4IbPxl51Y5WZE= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA= github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw= github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs= -github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk= github.com/Azure/go-autorest/autorest/adal v0.9.23 h1:Yepx8CvFxwNKpH6ja7RZ+sKX+DWYNldbLiALMC3BTz8= github.com/Azure/go-autorest/autorest/adal v0.9.23/go.mod h1:5pcMqFkdPhviJdlEy3kC/v1ZLnQl0MH6XA5YCcMhy4c= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.13 h1:Ov8avRZi2vmrE2JcXw+tu5K/yB41r7xK9GZDiBF7NdM= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.13/go.mod h1:5BAVfWLWXihP47vYrPuBKKf4cS0bXI+KM9Qx6ETDJYo= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 h1:w77/uPk80ZET2F+AfQExZyEWtn+0Rk/uw17m9fv5Ajc= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.6/go.mod h1:piCfgPho7BiIDdEQ1+g4VmKyD5y+p/XtSNqE6Hc4QD0= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= @@ -1604,8 +1596,6 @@ github.com/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE= github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= -github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= diff --git a/pkg/scalers/azure/azure_aad_workload_identity.go b/pkg/scalers/azure/azure_aad_workload_identity.go index 06be2f09c97..49b982a1ce4 100644 --- a/pkg/scalers/azure/azure_aad_workload_identity.go +++ b/pkg/scalers/azure/azure_aad_workload_identity.go @@ -19,16 +19,11 @@ package azure import ( "context" "fmt" + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential" "os" "strconv" "strings" - "time" - - amqpAuth "github.com/Azure/azure-amqp-common-go/v4/auth" - "github.com/Azure/azure-sdk-for-go/sdk/azidentity" - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/azure/auth" - "github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential" ) // Azure AD Workload Identity Webhook will inject the following environment variables. @@ -128,24 +123,6 @@ func getScopedResource(resource string) string { return resource } -type ADWorkloadIdentityConfig struct { - ctx context.Context - IdentityID string - IdentityTenantID string - IdentityAuthorityHost string - Resource string -} - -func NewAzureADWorkloadIdentityConfig(ctx context.Context, identityID, identityTenantID, identityAuthorityHost, resource string) auth.AuthorizerConfig { - return ADWorkloadIdentityConfig{ctx: ctx, IdentityID: identityID, IdentityTenantID: identityTenantID, IdentityAuthorityHost: identityAuthorityHost, Resource: resource} -} - -// Authorizer implements the auth.AuthorizerConfig interface -func (aadWiConfig ADWorkloadIdentityConfig) Authorizer() (autorest.Authorizer, error) { - return autorest.NewBearerAuthorizer(NewAzureADWorkloadIdentityTokenProvider( - aadWiConfig.ctx, aadWiConfig.IdentityID, aadWiConfig.IdentityTenantID, aadWiConfig.IdentityAuthorityHost, aadWiConfig.Resource)), nil -} - func NewADWorkloadIdentityCredential(identityID, identityTenantID string) (*azidentity.WorkloadIdentityCredential, error) { options := &azidentity.WorkloadIdentityCredentialOptions{} if identityID != "" { @@ -156,61 +133,3 @@ func NewADWorkloadIdentityCredential(identityID, identityTenantID string) (*azid } return azidentity.NewWorkloadIdentityCredential(options) } - -// ADWorkloadIdentityTokenProvider is a type that implements the adal.OAuthTokenProvider and adal.Refresher interfaces. -// The OAuthTokenProvider interface is used by the BearerAuthorizer to get the token when preparing the HTTP Header. -// The Refresher interface is used by the BearerAuthorizer to refresh the token. -type ADWorkloadIdentityTokenProvider struct { - ctx context.Context - IdentityID string - IdentityTenantID string - IdentityAuthorityHost string - Resource string - aadToken AADToken -} - -func NewAzureADWorkloadIdentityTokenProvider(ctx context.Context, identityID, identityTenantID, identityAuthorityHost, resource string) *ADWorkloadIdentityTokenProvider { - return &ADWorkloadIdentityTokenProvider{ctx: ctx, IdentityID: identityID, IdentityTenantID: identityTenantID, IdentityAuthorityHost: identityAuthorityHost, Resource: resource} -} - -// OAuthToken is for implementing the adal.OAuthTokenProvider interface. It returns the current access token. -func (wiTokenProvider *ADWorkloadIdentityTokenProvider) OAuthToken() string { - return wiTokenProvider.aadToken.AccessToken -} - -// Refresh is for implementing the adal.Refresher interface -func (wiTokenProvider *ADWorkloadIdentityTokenProvider) Refresh() error { - if time.Now().Before(wiTokenProvider.aadToken.ExpiresOnTimeObject) { - return nil - } - - aadToken, err := GetAzureADWorkloadIdentityToken(wiTokenProvider.ctx, wiTokenProvider.IdentityID, wiTokenProvider.IdentityTenantID, wiTokenProvider.IdentityAuthorityHost, wiTokenProvider.Resource) - if err != nil { - return err - } - - wiTokenProvider.aadToken = aadToken - return nil -} - -// RefreshExchange is for implementing the adal.Refresher interface -func (wiTokenProvider *ADWorkloadIdentityTokenProvider) RefreshExchange(resource string) error { - wiTokenProvider.Resource = resource - return wiTokenProvider.Refresh() -} - -// EnsureFresh is for implementing the adal.Refresher interface -func (wiTokenProvider *ADWorkloadIdentityTokenProvider) EnsureFresh() error { - return wiTokenProvider.Refresh() -} - -// GetToken is for implementing the auth.TokenProvider interface -func (wiTokenProvider *ADWorkloadIdentityTokenProvider) GetToken(_ string) (*amqpAuth.Token, error) { - err := wiTokenProvider.Refresh() - if err != nil { - return nil, err - } - - return amqpAuth.NewToken(amqpAuth.CBSTokenTypeJWT, wiTokenProvider.aadToken.AccessToken, - wiTokenProvider.aadToken.ExpiresOn), nil -} diff --git a/pkg/scalers/azure/azure_app_insights.go b/pkg/scalers/azure/azure_app_insights.go index 35ca3549a57..f139bcdd8e8 100644 --- a/pkg/scalers/azure/azure_app_insights.go +++ b/pkg/scalers/azure/azure_app_insights.go @@ -8,7 +8,6 @@ import ( "strings" "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/azure/auth" logf "sigs.k8s.io/controller-runtime/pkg/log" kedav1alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1" @@ -59,18 +58,18 @@ func toISO8601(time string) (string, error) { return fmt.Sprintf("PT%02dH%02dM", hours, minutes), nil } -func getAuthConfig(ctx context.Context, info AppInsightsInfo, podIdentity kedav1alpha1.AuthPodIdentity) auth.AuthorizerConfig { - switch podIdentity.Provider { - case "", kedav1alpha1.PodIdentityProviderNone: - config := auth.NewClientCredentialsConfig(info.ClientID, info.ClientPassword, info.TenantID) - config.Resource = info.AppInsightsResourceURL - config.AADEndpoint = info.ActiveDirectoryEndpoint - return config - case kedav1alpha1.PodIdentityProviderAzureWorkload: - return NewAzureADWorkloadIdentityConfig(ctx, podIdentity.GetIdentityID(), podIdentity.GetIdentityTenantID(), podIdentity.GetIdentityAuthorityHost(), info.AppInsightsResourceURL) - } - return nil -} +//func getAuthConfig(ctx context.Context, info AppInsightsInfo, podIdentity kedav1alpha1.AuthPodIdentity) auth.AuthorizerConfig { +// switch podIdentity.Provider { +// case "", kedav1alpha1.PodIdentityProviderNone: +// config := auth.NewClientCredentialsConfig(info.ClientID, info.ClientPassword, info.TenantID) +// config.Resource = info.AppInsightsResourceURL +// config.AADEndpoint = info.ActiveDirectoryEndpoint +// return config +// case kedav1alpha1.PodIdentityProviderAzureWorkload: +// return NewAzureADWorkloadIdentityConfig(ctx, podIdentity.GetIdentityID(), podIdentity.GetIdentityTenantID(), podIdentity.GetIdentityAuthorityHost(), info.AppInsightsResourceURL) +// } +// return nil +//} func extractAppInsightValue(info AppInsightsInfo, metric ApplicationInsightsMetric) (float64, error) { if _, ok := metric.Value[info.MetricID]; !ok { @@ -111,8 +110,11 @@ func queryParamsForAppInsightsRequest(info AppInsightsInfo) (map[string]interfac // GetAzureAppInsightsMetricValue returns the value of an Azure App Insights metric, rounded to the nearest int func GetAzureAppInsightsMetricValue(ctx context.Context, info AppInsightsInfo, podIdentity kedav1alpha1.AuthPodIdentity, ignoreNullValues bool) (float64, error) { - config := getAuthConfig(ctx, info, podIdentity) - authorizer, err := config.Authorizer() + + //config := getAuthConfig(ctx, info, podIdentity) + + token, err := GetAzureADWorkloadIdentityToken(ctx, info.ClientID, info.TenantID, "", info.AppInsightsResourceURL) + //MSAL get Token here instead of the config if err != nil { return -1, err } @@ -129,7 +131,8 @@ func GetAzureAppInsightsMetricValue(ctx context.Context, info AppInsightsInfo, p autorest.WithPath("metrics"), autorest.WithPath(info.MetricID), autorest.WithQueryParameters(queryParams), - authorizer.WithAuthorization()) + // MSAL here, use the autorest.WithBearerAuthorization(token) + autorest.WithBearerAuthorization(token.AccessToken)) if err != nil { return -1, err } diff --git a/pkg/scalers/azure/azure_app_insights_test.go b/pkg/scalers/azure/azure_app_insights_test.go index 22a4c62ca49..1c470e38d77 100644 --- a/pkg/scalers/azure/azure_app_insights_test.go +++ b/pkg/scalers/azure/azure_app_insights_test.go @@ -4,8 +4,6 @@ import ( "context" "testing" - "github.com/Azure/go-autorest/autorest/azure/auth" - kedav1alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1" ) @@ -88,23 +86,17 @@ var testAppInsightsAuthConfigData = []testAppInsightsAuthConfigTestData{ {"azure workload identity", workloadIdentityConfig, AppInsightsInfo{}, kedav1alpha1.PodIdentityProviderAzureWorkload}, } -func TestAzAppInfoGetAuthConfig(t *testing.T) { +func TestAzAppInfoGetToken(t *testing.T) { for _, testData := range testAppInsightsAuthConfigData { - authConfig := getAuthConfig(context.TODO(), testData.info, kedav1alpha1.AuthPodIdentity{Provider: testData.podIdentity}) - switch testData.config { - case msiConfig: - if _, ok := authConfig.(auth.MSIConfig); !ok { - t.Errorf("Test %v; incorrect auth config. expected MSI config", testData.testName) - } - case clientCredentialsConfig: - if _, ok := authConfig.(auth.ClientCredentialsConfig); !ok { - t.Errorf("Test: %v; incorrect auth config. expected client credentials config", testData.testName) - } - case workloadIdentityConfig: - if _, ok := authConfig.(ADWorkloadIdentityConfig); !ok { - t.Errorf("Test: %v; incorrect auth config. expected ad workload identity config", testData.testName) - } + authToken, err := GetAzureADWorkloadIdentityToken(context.TODO(), testData.info.ClientID, testData.info.TenantID, "", testData.info.AppInsightsResourceURL) + + if err != nil { + t.Errorf("Test %v; Expected success but got error: %v", testData.testName, err) + } + if authToken.AccessToken == "" { + t.Errorf("Test %v; Expected token but got empty token: %v", testData.testName, authToken) } + t.Logf("Test %v; data: %v, token: %v", testData.testName, testData.info, authToken) } } diff --git a/vendor/github.com/Azure/azure-amqp-common-go/v4/LICENSE b/vendor/github.com/Azure/azure-amqp-common-go/v4/LICENSE deleted file mode 100644 index 21071075c24..00000000000 --- a/vendor/github.com/Azure/azure-amqp-common-go/v4/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ - MIT License - - Copyright (c) Microsoft Corporation. All rights reserved. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/auth/LICENSE b/vendor/github.com/Azure/go-autorest/autorest/azure/auth/LICENSE deleted file mode 100644 index b9d6a27ea92..00000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/azure/auth/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2015 Microsoft Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/auth/README.md b/vendor/github.com/Azure/go-autorest/autorest/azure/auth/README.md deleted file mode 100644 index 05bef8a8002..00000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/azure/auth/README.md +++ /dev/null @@ -1,152 +0,0 @@ -# NOTE: This module will go out of support by March 31, 2023. For authenticating with Azure AD, use module [azidentity](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity) instead. For help migrating from `auth` to `azidentiy` please consult the [migration guide](https://aka.ms/azsdk/go/identity/migration). General information about the retirement of this and other legacy modules can be found [here](https://azure.microsoft.com/updates/support-for-azure-sdk-libraries-that-do-not-conform-to-our-current-azure-sdk-guidelines-will-be-retired-as-of-31-march-2023/). - -## Authentication - -Typical SDK operations must be authenticated and authorized. The `autorest.Authorizer` -interface allows use of any auth style in requests, such as inserting an OAuth2 -Authorization header and bearer token received from Azure AD. - -The SDK itself provides a simple way to get an authorizer which first checks -for OAuth client credentials in environment variables and then falls back to -Azure's [Managed Service Identity]() when available, e.g. when on an Azure -VM. The following snippet from [the previous section](#use) demonstrates -this helper. - -```go -import "github.com/Azure/go-autorest/autorest/azure/auth" - -// create a VirtualNetworks client -vnetClient := network.NewVirtualNetworksClient("") - -// create an authorizer from env vars or Azure Managed Service Idenity -authorizer, err := auth.NewAuthorizerFromEnvironment() -if err != nil { - handle(err) -} - -vnetClient.Authorizer = authorizer - -// call the VirtualNetworks CreateOrUpdate API -vnetClient.CreateOrUpdate(context.Background(), -// ... -``` - -The following environment variables help determine authentication configuration: - -- `AZURE_ENVIRONMENT`: Specifies the Azure Environment to use. If not set, it - defaults to `AzurePublicCloud`. Not applicable to authentication with Managed - Service Identity (MSI). -- `AZURE_AD_RESOURCE`: Specifies the AAD resource ID to use. If not set, it - defaults to `ResourceManagerEndpoint` for operations with Azure Resource - Manager. You can also choose an alternate resource programmatically with - `auth.NewAuthorizerFromEnvironmentWithResource(resource string)`. - -### More Authentication Details - -The previous is the first and most recommended of several authentication -options offered by the SDK because it allows seamless use of both service -principals and [Azure Managed Service Identity][]. Other options are listed -below. - -> Note: If you need to create a new service principal, run `az ad sp create-for-rbac -n ""` in the -> [azure-cli](https://github.com/Azure/azure-cli). See [these -> docs](https://docs.microsoft.com/cli/azure/create-an-azure-service-principal-azure-cli?view=azure-cli-latest) -> for more info. Copy the new principal's ID, secret, and tenant ID for use in -> your app, or consider the `--sdk-auth` parameter for serialized output. - -[azure managed service identity]: https://docs.microsoft.com/azure/active-directory/msi-overview - -- The `auth.NewAuthorizerFromEnvironment()` described above creates an authorizer - from the first available of the following configuration: - - 1. **Client Credentials**: Azure AD Application ID and Secret. - - - `AZURE_TENANT_ID`: Specifies the Tenant to which to authenticate. - - `AZURE_CLIENT_ID`: Specifies the app client ID to use. - - `AZURE_CLIENT_SECRET`: Specifies the app secret to use. - - 2. **Client Certificate**: Azure AD Application ID and X.509 Certificate. - - - `AZURE_TENANT_ID`: Specifies the Tenant to which to authenticate. - - `AZURE_CLIENT_ID`: Specifies the app client ID to use. - - `AZURE_CERTIFICATE_PATH`: Specifies the certificate Path to use. - - `AZURE_CERTIFICATE_PASSWORD`: Specifies the certificate password to use. - - 3. **Resource Owner Password**: Azure AD User and Password. This grant type is *not - recommended*, use device login instead if you need interactive login. - - - `AZURE_TENANT_ID`: Specifies the Tenant to which to authenticate. - - `AZURE_CLIENT_ID`: Specifies the app client ID to use. - - `AZURE_USERNAME`: Specifies the username to use. - - `AZURE_PASSWORD`: Specifies the password to use. - - 4. **Azure Managed Service Identity**: Delegate credential management to the - platform. Requires that code is running in Azure, e.g. on a VM. All - configuration is handled by Azure. See [Azure Managed Service - Identity](https://docs.microsoft.com/azure/active-directory/msi-overview) - for more details. - -- The `auth.NewAuthorizerFromFile()` method creates an authorizer using - credentials from an auth file created by the [Azure CLI][]. Follow these - steps to utilize: - - 1. Create a service principal and output an auth file using `az ad sp create-for-rbac --sdk-auth > client_credentials.json`. - 2. Set environment variable `AZURE_AUTH_LOCATION` to the path of the saved - output file. - 3. Use the authorizer returned by `auth.NewAuthorizerFromFile()` in your - client as described above. - -- The `auth.NewAuthorizerFromCLI()` method creates an authorizer which - uses [Azure CLI][] to obtain its credentials. - - The default audience being requested is `https://management.azure.com` (Azure ARM API). - To specify your own audience, export `AZURE_AD_RESOURCE` as an evironment variable. - This is read by `auth.NewAuthorizerFromCLI()` and passed to Azure CLI to acquire the access token. - - For example, to request an access token for Azure Key Vault, export - ``` - AZURE_AD_RESOURCE="https://vault.azure.net" - ``` - -- `auth.NewAuthorizerFromCLIWithResource(AUDIENCE_URL_OR_APPLICATION_ID)` - this method is self contained and does - not require exporting environment variables. For example, to request an access token for Azure Key Vault: - ``` - auth.NewAuthorizerFromCLIWithResource("https://vault.azure.net") - ``` - - To use `NewAuthorizerFromCLI()` or `NewAuthorizerFromCLIWithResource()`, follow these steps: - - 1. Install [Azure CLI v2.0.12](https://docs.microsoft.com/cli/azure/install-azure-cli) or later. Upgrade earlier versions. - 2. Use `az login` to sign in to Azure. - - If you receive an error, use `az account get-access-token` to verify access. - - If Azure CLI is not installed to the default directory, you may receive an error - reporting that `az` cannot be found. - Use the `AzureCLIPath` environment variable to define the Azure CLI installation folder. - - If you are signed in to Azure CLI using multiple accounts or your account has - access to multiple subscriptions, you need to specify the specific subscription - to be used. To do so, use: - - ``` - az account set --subscription - ``` - - To verify the current account settings, use: - - ``` - az account list - ``` - -[azure cli]: https://github.com/Azure/azure-cli - -- Finally, you can use OAuth's [Device Flow][] by calling - `auth.NewDeviceFlowConfig()` and extracting the Authorizer as follows: - - ```go - config := auth.NewDeviceFlowConfig(clientID, tenantID) - a, err := config.Authorizer() - ``` - -[device flow]: https://oauth.net/2/device-flow/ diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/auth/auth.go b/vendor/github.com/Azure/go-autorest/autorest/azure/auth/auth.go deleted file mode 100644 index 25697b3c854..00000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/azure/auth/auth.go +++ /dev/null @@ -1,772 +0,0 @@ -package auth - -// Copyright 2017 Microsoft Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import ( - "bytes" - "context" - "encoding/binary" - "encoding/json" - "errors" - "fmt" - "io" - "log" - "os" - "strings" - "unicode/utf16" - - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/adal" - "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/autorest/azure/cli" - "github.com/Azure/go-autorest/logger" - "github.com/dimchansky/utfbom" -) - -// The possible keys in the Values map. -const ( - SubscriptionID = "AZURE_SUBSCRIPTION_ID" - TenantID = "AZURE_TENANT_ID" - AuxiliaryTenantIDs = "AZURE_AUXILIARY_TENANT_IDS" - ClientID = "AZURE_CLIENT_ID" - ClientSecret = "AZURE_CLIENT_SECRET" - CertificatePath = "AZURE_CERTIFICATE_PATH" - CertificatePassword = "AZURE_CERTIFICATE_PASSWORD" - Username = "AZURE_USERNAME" - Password = "AZURE_PASSWORD" - EnvironmentName = "AZURE_ENVIRONMENT" - Resource = "AZURE_AD_RESOURCE" - ActiveDirectoryEndpoint = "ActiveDirectoryEndpoint" - ResourceManagerEndpoint = "ResourceManagerEndpoint" - GraphResourceID = "GraphResourceID" - SQLManagementEndpoint = "SQLManagementEndpoint" - GalleryEndpoint = "GalleryEndpoint" - ManagementEndpoint = "ManagementEndpoint" -) - -// NewAuthorizerFromEnvironment creates an Authorizer configured from environment variables in the order: -// 1. Client credentials -// 2. Client certificate -// 3. Username password -// 4. MSI -func NewAuthorizerFromEnvironment() (autorest.Authorizer, error) { - logger.Instance.Writeln(logger.LogInfo, "NewAuthorizerFromEnvironment() determining authentication mechanism") - settings, err := GetSettingsFromEnvironment() - if err != nil { - return nil, err - } - return settings.GetAuthorizer() -} - -// NewAuthorizerFromEnvironmentWithResource creates an Authorizer configured from environment variables in the order: -// 1. Client credentials -// 2. Client certificate -// 3. Username password -// 4. MSI -func NewAuthorizerFromEnvironmentWithResource(resource string) (autorest.Authorizer, error) { - logger.Instance.Writeln(logger.LogInfo, "NewAuthorizerFromEnvironmentWithResource() determining authentication mechanism") - settings, err := GetSettingsFromEnvironment() - if err != nil { - return nil, err - } - settings.Values[Resource] = resource - return settings.GetAuthorizer() -} - -// EnvironmentSettings contains the available authentication settings. -type EnvironmentSettings struct { - Values map[string]string - Environment azure.Environment -} - -// GetSettingsFromEnvironment returns the available authentication settings from the environment. -func GetSettingsFromEnvironment() (s EnvironmentSettings, err error) { - s = EnvironmentSettings{ - Values: map[string]string{}, - } - s.setValue(SubscriptionID) - s.setValue(TenantID) - s.setValue(AuxiliaryTenantIDs) - s.setValue(ClientID) - s.setValue(ClientSecret) - s.setValue(CertificatePath) - s.setValue(CertificatePassword) - s.setValue(Username) - s.setValue(Password) - s.setValue(EnvironmentName) - s.setValue(Resource) - if v := s.Values[EnvironmentName]; v == "" { - s.Environment = azure.PublicCloud - } else { - s.Environment, err = azure.EnvironmentFromName(v) - } - if s.Values[Resource] == "" { - s.Values[Resource] = s.Environment.ResourceManagerEndpoint - } - return -} - -// GetSubscriptionID returns the available subscription ID or an empty string. -func (settings EnvironmentSettings) GetSubscriptionID() string { - return settings.Values[SubscriptionID] -} - -// adds the specified environment variable value to the Values map if it exists -func (settings EnvironmentSettings) setValue(key string) { - if v := os.Getenv(key); v != "" { - logger.Instance.Writef(logger.LogInfo, "GetSettingsFromEnvironment() found environment var %s\n", key) - settings.Values[key] = v - } -} - -// helper to return client and tenant IDs -func (settings EnvironmentSettings) getClientAndTenant() (string, string) { - clientID := settings.Values[ClientID] - tenantID := settings.Values[TenantID] - return clientID, tenantID -} - -// GetClientCredentials creates a config object from the available client credentials. -// An error is returned if no client credentials are available. -func (settings EnvironmentSettings) GetClientCredentials() (ClientCredentialsConfig, error) { - secret := settings.Values[ClientSecret] - if secret == "" { - logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetClientCredentials() missing client secret") - return ClientCredentialsConfig{}, errors.New("missing client secret") - } - clientID, tenantID := settings.getClientAndTenant() - config := NewClientCredentialsConfig(clientID, secret, tenantID) - config.AADEndpoint = settings.Environment.ActiveDirectoryEndpoint - config.Resource = settings.Values[Resource] - if auxTenants, ok := settings.Values[AuxiliaryTenantIDs]; ok { - config.AuxTenants = strings.Split(auxTenants, ";") - for i := range config.AuxTenants { - config.AuxTenants[i] = strings.TrimSpace(config.AuxTenants[i]) - } - } - return config, nil -} - -// GetClientCertificate creates a config object from the available certificate credentials. -// An error is returned if no certificate credentials are available. -func (settings EnvironmentSettings) GetClientCertificate() (ClientCertificateConfig, error) { - certPath := settings.Values[CertificatePath] - if certPath == "" { - logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetClientCertificate() missing certificate path") - return ClientCertificateConfig{}, errors.New("missing certificate path") - } - certPwd := settings.Values[CertificatePassword] - clientID, tenantID := settings.getClientAndTenant() - config := NewClientCertificateConfig(certPath, certPwd, clientID, tenantID) - config.AADEndpoint = settings.Environment.ActiveDirectoryEndpoint - config.Resource = settings.Values[Resource] - return config, nil -} - -// GetUsernamePassword creates a config object from the available username/password credentials. -// An error is returned if no username/password credentials are available. -func (settings EnvironmentSettings) GetUsernamePassword() (UsernamePasswordConfig, error) { - username := settings.Values[Username] - password := settings.Values[Password] - if username == "" || password == "" { - logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetUsernamePassword() missing username and/or password") - return UsernamePasswordConfig{}, errors.New("missing username/password") - } - clientID, tenantID := settings.getClientAndTenant() - config := NewUsernamePasswordConfig(username, password, clientID, tenantID) - config.AADEndpoint = settings.Environment.ActiveDirectoryEndpoint - config.Resource = settings.Values[Resource] - return config, nil -} - -// GetMSI creates a MSI config object from the available client ID. -func (settings EnvironmentSettings) GetMSI() MSIConfig { - config := NewMSIConfig() - config.Resource = settings.Values[Resource] - config.ClientID = settings.Values[ClientID] - return config -} - -// GetDeviceFlow creates a device-flow config object from the available client and tenant IDs. -func (settings EnvironmentSettings) GetDeviceFlow() DeviceFlowConfig { - clientID, tenantID := settings.getClientAndTenant() - config := NewDeviceFlowConfig(clientID, tenantID) - config.AADEndpoint = settings.Environment.ActiveDirectoryEndpoint - config.Resource = settings.Values[Resource] - return config -} - -// GetAuthorizer creates an Authorizer configured from environment variables in the order: -// 1. Client credentials -// 2. Client certificate -// 3. Username password -// 4. MSI -func (settings EnvironmentSettings) GetAuthorizer() (autorest.Authorizer, error) { - //1.Client Credentials - if c, e := settings.GetClientCredentials(); e == nil { - logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetAuthorizer() using client secret credentials") - return c.Authorizer() - } - - //2. Client Certificate - if c, e := settings.GetClientCertificate(); e == nil { - logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetAuthorizer() using client certificate credentials") - return c.Authorizer() - } - - //3. Username Password - if c, e := settings.GetUsernamePassword(); e == nil { - logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetAuthorizer() using user name/password credentials") - return c.Authorizer() - } - - // 4. MSI - if !adal.MSIAvailable(context.Background(), nil) { - return nil, errors.New("MSI not available") - } - logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetAuthorizer() using MSI authentication") - return settings.GetMSI().Authorizer() -} - -// NewAuthorizerFromFile creates an Authorizer configured from a configuration file in the following order. -// 1. Client credentials -// 2. Client certificate -// The path to the configuration file must be specified in the AZURE_AUTH_LOCATION environment variable. -// resourceBaseURI - used to determine the resource type -func NewAuthorizerFromFile(resourceBaseURI string) (autorest.Authorizer, error) { - settings, err := GetSettingsFromFile() - if err != nil { - return nil, err - } - return settings.GetAuthorizer(resourceBaseURI) -} - -// GetAuthorizer create an Authorizer in the following order. -// 1. Client credentials -// 2. Client certificate -// resourceBaseURI - used to determine the resource type -func (settings FileSettings) GetAuthorizer(resourceBaseURI string) (autorest.Authorizer, error) { - if resourceBaseURI == "" { - resourceBaseURI = azure.PublicCloud.ServiceManagementEndpoint - } - if a, err := settings.ClientCredentialsAuthorizer(resourceBaseURI); err == nil { - return a, err - } - if a, err := settings.ClientCertificateAuthorizer(resourceBaseURI); err == nil { - return a, err - } - return nil, errors.New("auth file missing client and certificate credentials") -} - -// NewAuthorizerFromFileWithResource creates an Authorizer configured from a configuration file in the following order. -// 1. Client credentials -// 2. Client certificate -// The path to the configuration file must be specified in the AZURE_AUTH_LOCATION environment variable. -func NewAuthorizerFromFileWithResource(resource string) (autorest.Authorizer, error) { - s, err := GetSettingsFromFile() - if err != nil { - return nil, err - } - if a, err := s.ClientCredentialsAuthorizerWithResource(resource); err == nil { - return a, err - } - if a, err := s.ClientCertificateAuthorizerWithResource(resource); err == nil { - return a, err - } - return nil, errors.New("auth file missing client and certificate credentials") -} - -// NewAuthorizerFromCLI creates an Authorizer configured from Azure CLI 2.0 for local development scenarios. -func NewAuthorizerFromCLI() (autorest.Authorizer, error) { - settings, err := GetSettingsFromEnvironment() - if err != nil { - return nil, err - } - - if settings.Values[Resource] == "" { - settings.Values[Resource] = settings.Environment.ResourceManagerEndpoint - } - - return NewAuthorizerFromCLIWithResource(settings.Values[Resource]) -} - -// NewAuthorizerFromCLIWithResource creates an Authorizer configured from Azure CLI 2.0 for local development scenarios. -func NewAuthorizerFromCLIWithResource(resource string) (autorest.Authorizer, error) { - token, err := cli.GetTokenFromCLI(resource) - if err != nil { - return nil, err - } - - adalToken, err := token.ToADALToken() - if err != nil { - return nil, err - } - - return autorest.NewBearerAuthorizer(&adalToken), nil -} - -// GetSettingsFromFile returns the available authentication settings from an Azure CLI authentication file. -func GetSettingsFromFile() (FileSettings, error) { - s := FileSettings{} - fileLocation := os.Getenv("AZURE_AUTH_LOCATION") - if fileLocation == "" { - return s, errors.New("environment variable AZURE_AUTH_LOCATION is not set") - } - - contents, err := os.ReadFile(fileLocation) - if err != nil { - return s, err - } - - // Auth file might be encoded - decoded, err := decode(contents) - if err != nil { - return s, err - } - - authFile := map[string]interface{}{} - err = json.Unmarshal(decoded, &authFile) - if err != nil { - return s, err - } - - s.Values = map[string]string{} - s.setKeyValue(ClientID, authFile["clientId"]) - s.setKeyValue(ClientSecret, authFile["clientSecret"]) - s.setKeyValue(CertificatePath, authFile["clientCertificate"]) - s.setKeyValue(CertificatePassword, authFile["clientCertificatePassword"]) - s.setKeyValue(SubscriptionID, authFile["subscriptionId"]) - s.setKeyValue(TenantID, authFile["tenantId"]) - s.setKeyValue(ActiveDirectoryEndpoint, authFile["activeDirectoryEndpointUrl"]) - s.setKeyValue(ResourceManagerEndpoint, authFile["resourceManagerEndpointUrl"]) - s.setKeyValue(GraphResourceID, authFile["activeDirectoryGraphResourceId"]) - s.setKeyValue(SQLManagementEndpoint, authFile["sqlManagementEndpointUrl"]) - s.setKeyValue(GalleryEndpoint, authFile["galleryEndpointUrl"]) - s.setKeyValue(ManagementEndpoint, authFile["managementEndpointUrl"]) - return s, nil -} - -// FileSettings contains the available authentication settings. -type FileSettings struct { - Values map[string]string -} - -// GetSubscriptionID returns the available subscription ID or an empty string. -func (settings FileSettings) GetSubscriptionID() string { - return settings.Values[SubscriptionID] -} - -// adds the specified value to the Values map if it isn't nil -func (settings FileSettings) setKeyValue(key string, val interface{}) { - if val != nil { - settings.Values[key] = val.(string) - } -} - -// returns the specified AAD endpoint or the public cloud endpoint if unspecified -func (settings FileSettings) getAADEndpoint() string { - if v, ok := settings.Values[ActiveDirectoryEndpoint]; ok { - return v - } - return azure.PublicCloud.ActiveDirectoryEndpoint -} - -// ServicePrincipalTokenFromClientCredentials creates a ServicePrincipalToken from the available client credentials. -func (settings FileSettings) ServicePrincipalTokenFromClientCredentials(baseURI string) (*adal.ServicePrincipalToken, error) { - resource, err := settings.getResourceForToken(baseURI) - if err != nil { - return nil, err - } - return settings.ServicePrincipalTokenFromClientCredentialsWithResource(resource) -} - -// ClientCredentialsAuthorizer creates an authorizer from the available client credentials. -func (settings FileSettings) ClientCredentialsAuthorizer(baseURI string) (autorest.Authorizer, error) { - resource, err := settings.getResourceForToken(baseURI) - if err != nil { - return nil, err - } - return settings.ClientCredentialsAuthorizerWithResource(resource) -} - -// ServicePrincipalTokenFromClientCredentialsWithResource creates a ServicePrincipalToken -// from the available client credentials and the specified resource. -func (settings FileSettings) ServicePrincipalTokenFromClientCredentialsWithResource(resource string) (*adal.ServicePrincipalToken, error) { - if _, ok := settings.Values[ClientSecret]; !ok { - return nil, errors.New("missing client secret") - } - config, err := adal.NewOAuthConfig(settings.getAADEndpoint(), settings.Values[TenantID]) - if err != nil { - return nil, err - } - return adal.NewServicePrincipalToken(*config, settings.Values[ClientID], settings.Values[ClientSecret], resource) -} - -func (settings FileSettings) clientCertificateConfigWithResource(resource string) (ClientCertificateConfig, error) { - if _, ok := settings.Values[CertificatePath]; !ok { - return ClientCertificateConfig{}, errors.New("missing certificate path") - } - cfg := NewClientCertificateConfig(settings.Values[CertificatePath], settings.Values[CertificatePassword], settings.Values[ClientID], settings.Values[TenantID]) - cfg.AADEndpoint = settings.getAADEndpoint() - cfg.Resource = resource - return cfg, nil -} - -// ClientCredentialsAuthorizerWithResource creates an authorizer from the available client credentials and the specified resource. -func (settings FileSettings) ClientCredentialsAuthorizerWithResource(resource string) (autorest.Authorizer, error) { - spToken, err := settings.ServicePrincipalTokenFromClientCredentialsWithResource(resource) - if err != nil { - return nil, err - } - return autorest.NewBearerAuthorizer(spToken), nil -} - -// ServicePrincipalTokenFromClientCertificate creates a ServicePrincipalToken from the available certificate credentials. -func (settings FileSettings) ServicePrincipalTokenFromClientCertificate(baseURI string) (*adal.ServicePrincipalToken, error) { - resource, err := settings.getResourceForToken(baseURI) - if err != nil { - return nil, err - } - return settings.ServicePrincipalTokenFromClientCertificateWithResource(resource) -} - -// ClientCertificateAuthorizer creates an authorizer from the available certificate credentials. -func (settings FileSettings) ClientCertificateAuthorizer(baseURI string) (autorest.Authorizer, error) { - resource, err := settings.getResourceForToken(baseURI) - if err != nil { - return nil, err - } - return settings.ClientCertificateAuthorizerWithResource(resource) -} - -// ServicePrincipalTokenFromClientCertificateWithResource creates a ServicePrincipalToken from the available certificate credentials. -func (settings FileSettings) ServicePrincipalTokenFromClientCertificateWithResource(resource string) (*adal.ServicePrincipalToken, error) { - cfg, err := settings.clientCertificateConfigWithResource(resource) - if err != nil { - return nil, err - } - return cfg.ServicePrincipalToken() -} - -// ClientCertificateAuthorizerWithResource creates an authorizer from the available certificate credentials and the specified resource. -func (settings FileSettings) ClientCertificateAuthorizerWithResource(resource string) (autorest.Authorizer, error) { - cfg, err := settings.clientCertificateConfigWithResource(resource) - if err != nil { - return nil, err - } - return cfg.Authorizer() -} - -func decode(b []byte) ([]byte, error) { - reader, enc := utfbom.Skip(bytes.NewReader(b)) - - switch enc { - case utfbom.UTF16LittleEndian: - u16 := make([]uint16, (len(b)/2)-1) - err := binary.Read(reader, binary.LittleEndian, &u16) - if err != nil { - return nil, err - } - return []byte(string(utf16.Decode(u16))), nil - case utfbom.UTF16BigEndian: - u16 := make([]uint16, (len(b)/2)-1) - err := binary.Read(reader, binary.BigEndian, &u16) - if err != nil { - return nil, err - } - return []byte(string(utf16.Decode(u16))), nil - } - return io.ReadAll(reader) -} - -func (settings FileSettings) getResourceForToken(baseURI string) (string, error) { - // Compare default base URI from the SDK to the endpoints from the public cloud - // Base URI and token resource are the same string. This func finds the authentication - // file field that matches the SDK base URI. The SDK defines the public cloud - // endpoint as its default base URI - if !strings.HasSuffix(baseURI, "/") { - baseURI += "/" - } - switch baseURI { - case azure.PublicCloud.ServiceManagementEndpoint: - return settings.Values[ManagementEndpoint], nil - case azure.PublicCloud.ResourceManagerEndpoint: - return settings.Values[ResourceManagerEndpoint], nil - case azure.PublicCloud.ActiveDirectoryEndpoint: - return settings.Values[ActiveDirectoryEndpoint], nil - case azure.PublicCloud.GalleryEndpoint: - return settings.Values[GalleryEndpoint], nil - case azure.PublicCloud.GraphEndpoint: - return settings.Values[GraphResourceID], nil - } - return "", fmt.Errorf("auth: base URI not found in endpoints") -} - -// NewClientCredentialsConfig creates an AuthorizerConfig object configured to obtain an Authorizer through Client Credentials. -// Defaults to Public Cloud and Resource Manager Endpoint. -func NewClientCredentialsConfig(clientID string, clientSecret string, tenantID string) ClientCredentialsConfig { - return ClientCredentialsConfig{ - ClientID: clientID, - ClientSecret: clientSecret, - TenantID: tenantID, - Resource: azure.PublicCloud.ResourceManagerEndpoint, - AADEndpoint: azure.PublicCloud.ActiveDirectoryEndpoint, - } -} - -// NewClientCertificateConfig creates a ClientCertificateConfig object configured to obtain an Authorizer through client certificate. -// Defaults to Public Cloud and Resource Manager Endpoint. -func NewClientCertificateConfig(certificatePath string, certificatePassword string, clientID string, tenantID string) ClientCertificateConfig { - return ClientCertificateConfig{ - CertificatePath: certificatePath, - CertificatePassword: certificatePassword, - ClientID: clientID, - TenantID: tenantID, - Resource: azure.PublicCloud.ResourceManagerEndpoint, - AADEndpoint: azure.PublicCloud.ActiveDirectoryEndpoint, - } -} - -// NewUsernamePasswordConfig creates an UsernamePasswordConfig object configured to obtain an Authorizer through username and password. -// Defaults to Public Cloud and Resource Manager Endpoint. -func NewUsernamePasswordConfig(username string, password string, clientID string, tenantID string) UsernamePasswordConfig { - return UsernamePasswordConfig{ - Username: username, - Password: password, - ClientID: clientID, - TenantID: tenantID, - Resource: azure.PublicCloud.ResourceManagerEndpoint, - AADEndpoint: azure.PublicCloud.ActiveDirectoryEndpoint, - } -} - -// NewMSIConfig creates an MSIConfig object configured to obtain an Authorizer through MSI. -func NewMSIConfig() MSIConfig { - return MSIConfig{ - Resource: azure.PublicCloud.ResourceManagerEndpoint, - } -} - -// NewDeviceFlowConfig creates a DeviceFlowConfig object configured to obtain an Authorizer through device flow. -// Defaults to Public Cloud and Resource Manager Endpoint. -func NewDeviceFlowConfig(clientID string, tenantID string) DeviceFlowConfig { - return DeviceFlowConfig{ - ClientID: clientID, - TenantID: tenantID, - Resource: azure.PublicCloud.ResourceManagerEndpoint, - AADEndpoint: azure.PublicCloud.ActiveDirectoryEndpoint, - } -} - -// AuthorizerConfig provides an authorizer from the configuration provided. -type AuthorizerConfig interface { - Authorizer() (autorest.Authorizer, error) -} - -// ClientCredentialsConfig provides the options to get a bearer authorizer from client credentials. -type ClientCredentialsConfig struct { - ClientID string - ClientSecret string - TenantID string - AuxTenants []string - AADEndpoint string - Resource string -} - -// ServicePrincipalToken creates a ServicePrincipalToken from client credentials. -func (ccc ClientCredentialsConfig) ServicePrincipalToken() (*adal.ServicePrincipalToken, error) { - oauthConfig, err := adal.NewOAuthConfig(ccc.AADEndpoint, ccc.TenantID) - if err != nil { - return nil, err - } - return adal.NewServicePrincipalToken(*oauthConfig, ccc.ClientID, ccc.ClientSecret, ccc.Resource) -} - -// MultiTenantServicePrincipalToken creates a MultiTenantServicePrincipalToken from client credentials. -func (ccc ClientCredentialsConfig) MultiTenantServicePrincipalToken() (*adal.MultiTenantServicePrincipalToken, error) { - oauthConfig, err := adal.NewMultiTenantOAuthConfig(ccc.AADEndpoint, ccc.TenantID, ccc.AuxTenants, adal.OAuthOptions{}) - if err != nil { - return nil, err - } - return adal.NewMultiTenantServicePrincipalToken(oauthConfig, ccc.ClientID, ccc.ClientSecret, ccc.Resource) -} - -// Authorizer gets the authorizer from client credentials. -func (ccc ClientCredentialsConfig) Authorizer() (autorest.Authorizer, error) { - if len(ccc.AuxTenants) == 0 { - spToken, err := ccc.ServicePrincipalToken() - if err != nil { - return nil, fmt.Errorf("failed to get SPT from client credentials: %v", err) - } - return autorest.NewBearerAuthorizer(spToken), nil - } - mtSPT, err := ccc.MultiTenantServicePrincipalToken() - if err != nil { - return nil, fmt.Errorf("failed to get multitenant SPT from client credentials: %v", err) - } - return autorest.NewMultiTenantServicePrincipalTokenAuthorizer(mtSPT), nil -} - -// ClientCertificateConfig provides the options to get a bearer authorizer from a client certificate. -type ClientCertificateConfig struct { - ClientID string - CertificatePath string - CertificatePassword string - TenantID string - AuxTenants []string - AADEndpoint string - Resource string -} - -// ServicePrincipalToken creates a ServicePrincipalToken from client certificate. -func (ccc ClientCertificateConfig) ServicePrincipalToken() (*adal.ServicePrincipalToken, error) { - oauthConfig, err := adal.NewOAuthConfig(ccc.AADEndpoint, ccc.TenantID) - if err != nil { - return nil, err - } - certData, err := os.ReadFile(ccc.CertificatePath) - if err != nil { - return nil, fmt.Errorf("failed to read the certificate file (%s): %v", ccc.CertificatePath, err) - } - certificate, rsaPrivateKey, err := adal.DecodePfxCertificateData(certData, ccc.CertificatePassword) - if err != nil { - return nil, fmt.Errorf("failed to decode pkcs12 certificate while creating spt: %v", err) - } - return adal.NewServicePrincipalTokenFromCertificate(*oauthConfig, ccc.ClientID, certificate, rsaPrivateKey, ccc.Resource) -} - -// MultiTenantServicePrincipalToken creates a MultiTenantServicePrincipalToken from client certificate. -func (ccc ClientCertificateConfig) MultiTenantServicePrincipalToken() (*adal.MultiTenantServicePrincipalToken, error) { - oauthConfig, err := adal.NewMultiTenantOAuthConfig(ccc.AADEndpoint, ccc.TenantID, ccc.AuxTenants, adal.OAuthOptions{}) - if err != nil { - return nil, err - } - certData, err := os.ReadFile(ccc.CertificatePath) - if err != nil { - return nil, fmt.Errorf("failed to read the certificate file (%s): %v", ccc.CertificatePath, err) - } - certificate, rsaPrivateKey, err := adal.DecodePfxCertificateData(certData, ccc.CertificatePassword) - if err != nil { - return nil, fmt.Errorf("failed to decode pkcs12 certificate while creating spt: %v", err) - } - return adal.NewMultiTenantServicePrincipalTokenFromCertificate(oauthConfig, ccc.ClientID, certificate, rsaPrivateKey, ccc.Resource) -} - -// Authorizer gets an authorizer object from client certificate. -func (ccc ClientCertificateConfig) Authorizer() (autorest.Authorizer, error) { - if len(ccc.AuxTenants) == 0 { - spToken, err := ccc.ServicePrincipalToken() - if err != nil { - return nil, fmt.Errorf("failed to get oauth token from certificate auth: %v", err) - } - return autorest.NewBearerAuthorizer(spToken), nil - } - mtSPT, err := ccc.MultiTenantServicePrincipalToken() - if err != nil { - return nil, fmt.Errorf("failed to get multitenant SPT from certificate auth: %v", err) - } - return autorest.NewMultiTenantServicePrincipalTokenAuthorizer(mtSPT), nil -} - -// DeviceFlowConfig provides the options to get a bearer authorizer using device flow authentication. -type DeviceFlowConfig struct { - ClientID string - TenantID string - AADEndpoint string - Resource string -} - -// Authorizer gets the authorizer from device flow. -func (dfc DeviceFlowConfig) Authorizer() (autorest.Authorizer, error) { - spToken, err := dfc.ServicePrincipalToken() - if err != nil { - return nil, fmt.Errorf("failed to get oauth token from device flow: %v", err) - } - return autorest.NewBearerAuthorizer(spToken), nil -} - -// ServicePrincipalToken gets the service principal token from device flow. -func (dfc DeviceFlowConfig) ServicePrincipalToken() (*adal.ServicePrincipalToken, error) { - oauthConfig, err := adal.NewOAuthConfig(dfc.AADEndpoint, dfc.TenantID) - if err != nil { - return nil, err - } - oauthClient := &autorest.Client{} - deviceCode, err := adal.InitiateDeviceAuth(oauthClient, *oauthConfig, dfc.ClientID, dfc.Resource) - if err != nil { - return nil, fmt.Errorf("failed to start device auth flow: %s", err) - } - log.Println(*deviceCode.Message) - token, err := adal.WaitForUserCompletion(oauthClient, deviceCode) - if err != nil { - return nil, fmt.Errorf("failed to finish device auth flow: %s", err) - } - return adal.NewServicePrincipalTokenFromManualToken(*oauthConfig, dfc.ClientID, dfc.Resource, *token) -} - -// UsernamePasswordConfig provides the options to get a bearer authorizer from a username and a password. -type UsernamePasswordConfig struct { - ClientID string - Username string - Password string - TenantID string - AADEndpoint string - Resource string -} - -// ServicePrincipalToken creates a ServicePrincipalToken from username and password. -func (ups UsernamePasswordConfig) ServicePrincipalToken() (*adal.ServicePrincipalToken, error) { - oauthConfig, err := adal.NewOAuthConfig(ups.AADEndpoint, ups.TenantID) - if err != nil { - return nil, err - } - return adal.NewServicePrincipalTokenFromUsernamePassword(*oauthConfig, ups.ClientID, ups.Username, ups.Password, ups.Resource) -} - -// Authorizer gets the authorizer from a username and a password. -func (ups UsernamePasswordConfig) Authorizer() (autorest.Authorizer, error) { - spToken, err := ups.ServicePrincipalToken() - if err != nil { - return nil, fmt.Errorf("failed to get oauth token from username and password auth: %v", err) - } - return autorest.NewBearerAuthorizer(spToken), nil -} - -// MSIConfig provides the options to get a bearer authorizer through MSI. -type MSIConfig struct { - Resource string - ClientID string -} - -// ServicePrincipalToken creates a ServicePrincipalToken from MSI. -func (mc MSIConfig) ServicePrincipalToken() (*adal.ServicePrincipalToken, error) { - spToken, err := adal.NewServicePrincipalTokenFromManagedIdentity(mc.Resource, &adal.ManagedIdentityOptions{ - ClientID: mc.ClientID, - }) - if err != nil { - return nil, fmt.Errorf("failed to get oauth token from MSI: %v", err) - } - return spToken, nil -} - -// Authorizer gets the authorizer from MSI. -func (mc MSIConfig) Authorizer() (autorest.Authorizer, error) { - spToken, err := mc.ServicePrincipalToken() - if err != nil { - return nil, err - } - - return autorest.NewBearerAuthorizer(spToken), nil -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/auth/go_mod_tidy_hack.go b/vendor/github.com/Azure/go-autorest/autorest/azure/auth/go_mod_tidy_hack.go deleted file mode 100644 index f7eb26fd366..00000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/azure/auth/go_mod_tidy_hack.go +++ /dev/null @@ -1,25 +0,0 @@ -//go:build modhack -// +build modhack - -package auth - -// Copyright 2017 Microsoft Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// This file, and the github.com/Azure/go-autorest import, won't actually become part of -// the resultant binary. - -// Necessary for safely adding multi-module repo. -// See: https://github.com/golang/go/wiki/Modules#is-it-possible-to-add-a-module-to-a-multi-module-repository -import _ "github.com/Azure/go-autorest" diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/cli/LICENSE b/vendor/github.com/Azure/go-autorest/autorest/azure/cli/LICENSE deleted file mode 100644 index b9d6a27ea92..00000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/azure/cli/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2015 Microsoft Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/cli/go_mod_tidy_hack.go b/vendor/github.com/Azure/go-autorest/autorest/azure/cli/go_mod_tidy_hack.go deleted file mode 100644 index 50d6f0391a5..00000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/azure/cli/go_mod_tidy_hack.go +++ /dev/null @@ -1,25 +0,0 @@ -//go:build modhack -// +build modhack - -package cli - -// Copyright 2017 Microsoft Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// This file, and the github.com/Azure/go-autorest import, won't actually become part of -// the resultant binary. - -// Necessary for safely adding multi-module repo. -// See: https://github.com/golang/go/wiki/Modules#is-it-possible-to-add-a-module-to-a-multi-module-repository -import _ "github.com/Azure/go-autorest" diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/cli/profile.go b/vendor/github.com/Azure/go-autorest/autorest/azure/cli/profile.go deleted file mode 100644 index f45c3a516d9..00000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/azure/cli/profile.go +++ /dev/null @@ -1,83 +0,0 @@ -package cli - -// Copyright 2017 Microsoft Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import ( - "bytes" - "encoding/json" - "fmt" - "io/ioutil" - "os" - "path/filepath" - - "github.com/dimchansky/utfbom" - "github.com/mitchellh/go-homedir" -) - -// Profile represents a Profile from the Azure CLI -type Profile struct { - InstallationID string `json:"installationId"` - Subscriptions []Subscription `json:"subscriptions"` -} - -// Subscription represents a Subscription from the Azure CLI -type Subscription struct { - EnvironmentName string `json:"environmentName"` - ID string `json:"id"` - IsDefault bool `json:"isDefault"` - Name string `json:"name"` - State string `json:"state"` - TenantID string `json:"tenantId"` - User *User `json:"user"` -} - -// User represents a User from the Azure CLI -type User struct { - Name string `json:"name"` - Type string `json:"type"` -} - -const azureProfileJSON = "azureProfile.json" - -func configDir() string { - return os.Getenv("AZURE_CONFIG_DIR") -} - -// ProfilePath returns the path where the Azure Profile is stored from the Azure CLI -func ProfilePath() (string, error) { - if cfgDir := configDir(); cfgDir != "" { - return filepath.Join(cfgDir, azureProfileJSON), nil - } - return homedir.Expand("~/.azure/" + azureProfileJSON) -} - -// LoadProfile restores a Profile object from a file located at 'path'. -func LoadProfile(path string) (result Profile, err error) { - var contents []byte - contents, err = ioutil.ReadFile(path) - if err != nil { - err = fmt.Errorf("failed to open file (%s) while loading token: %v", path, err) - return - } - reader := utfbom.SkipOnly(bytes.NewReader(contents)) - - dec := json.NewDecoder(reader) - if err = dec.Decode(&result); err != nil { - err = fmt.Errorf("failed to decode contents of file (%s) into a Profile representation: %v", path, err) - return - } - - return -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 4ee7a52cdd4..a5e7ede290c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -57,9 +57,6 @@ dario.cat/mergo ## explicit; go 1.20 filippo.io/edwards25519 filippo.io/edwards25519/field -# github.com/Azure/azure-amqp-common-go/v4 v4.2.0 -## explicit; go 1.18 -github.com/Azure/azure-amqp-common-go/v4/auth # github.com/Azure/azure-kusto-go v0.16.1 ## explicit; go 1.19 github.com/Azure/azure-kusto-go/kusto @@ -201,12 +198,6 @@ github.com/Azure/go-autorest/autorest/azure # github.com/Azure/go-autorest/autorest/adal v0.9.23 ## explicit; go 1.15 github.com/Azure/go-autorest/autorest/adal -# github.com/Azure/go-autorest/autorest/azure/auth v0.5.13 -## explicit; go 1.15 -github.com/Azure/go-autorest/autorest/azure/auth -# github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 -## explicit; go 1.15 -github.com/Azure/go-autorest/autorest/azure/cli # github.com/Azure/go-autorest/autorest/date v0.3.0 ## explicit; go 1.12 github.com/Azure/go-autorest/autorest/date @@ -529,9 +520,6 @@ github.com/dennwc/varint # github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f ## explicit github.com/dgryski/go-rendezvous -# github.com/dimchansky/utfbom v1.1.1 -## explicit -github.com/dimchansky/utfbom # github.com/dysnix/predictkube-libs v0.0.4-0.20230109175007-5a82fccd31c7 ## explicit; go 1.17 github.com/dysnix/predictkube-libs/external/configs From 03aecf1c925000bb31ac9d05dc7ea402c459868c Mon Sep 17 00:00:00 2001 From: Youn Jae Kim Date: Wed, 20 Nov 2024 18:28:49 -0800 Subject: [PATCH 2/2] RabbitMQ scaler changes Signed-off-by: Youn Jae Kim --- go.mod | 5 +- .../azure/azure_aad_workload_identity.go | 5 +- pkg/scalers/azure/azure_app_insights.go | 33 ++- pkg/scalers/azure/azure_app_insights_test.go | 36 --- pkg/scalers/rabbitmq_scaler.go | 29 ++- .../azure-amqp-common-go/v4/auth/token.go | 58 ----- .../go-autorest/autorest/azure/cli/token.go | 227 ------------------ .../github.com/dimchansky/utfbom/.gitignore | 37 --- .../github.com/dimchansky/utfbom/.travis.yml | 29 --- vendor/github.com/dimchansky/utfbom/LICENSE | 201 ---------------- vendor/github.com/dimchansky/utfbom/README.md | 66 ----- vendor/github.com/dimchansky/utfbom/utfbom.go | 192 --------------- 12 files changed, 43 insertions(+), 875 deletions(-) delete mode 100644 vendor/github.com/Azure/azure-amqp-common-go/v4/auth/token.go delete mode 100644 vendor/github.com/Azure/go-autorest/autorest/azure/cli/token.go delete mode 100644 vendor/github.com/dimchansky/utfbom/.gitignore delete mode 100644 vendor/github.com/dimchansky/utfbom/.travis.yml delete mode 100644 vendor/github.com/dimchansky/utfbom/LICENSE delete mode 100644 vendor/github.com/dimchansky/utfbom/README.md delete mode 100644 vendor/github.com/dimchansky/utfbom/utfbom.go diff --git a/go.mod b/go.mod index 01368597010..e4b68f7fbe2 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( cloud.google.com/go/secretmanager v1.14.2 cloud.google.com/go/storage v1.43.0 dario.cat/mergo v1.0.1 - github.com/Azure/azure-amqp-common-go/v4 v4.2.0 github.com/Azure/azure-kusto-go v0.16.1 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 @@ -20,7 +19,6 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.1 github.com/Azure/azure-sdk-for-go/sdk/storage/azqueue v1.0.0 github.com/Azure/go-autorest/autorest v0.11.29 - github.com/Azure/go-autorest/autorest/azure/auth v0.5.13 github.com/AzureAD/microsoft-authentication-library-for-go v1.2.3 github.com/DataDog/datadog-api-client-go v1.16.0 github.com/Huawei/gophercloud v1.0.21 @@ -118,6 +116,8 @@ require ( sigs.k8s.io/kustomize/kustomize/v5 v5.5.0 ) +require github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect + replace ( // pin k8s.io to v0.31.2 & sigs.k8s.io/controller-runtime to v0.19.1 github.com/google/cel-go => github.com/google/cel-go v0.20.1 @@ -204,7 +204,6 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dennwc/varint v1.0.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/dimchansky/utfbom v1.1.1 // indirect github.com/eapache/go-resiliency v1.7.0 // indirect github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 // indirect github.com/eapache/queue v1.1.0 // indirect diff --git a/pkg/scalers/azure/azure_aad_workload_identity.go b/pkg/scalers/azure/azure_aad_workload_identity.go index 49b982a1ce4..f9bbcf31046 100644 --- a/pkg/scalers/azure/azure_aad_workload_identity.go +++ b/pkg/scalers/azure/azure_aad_workload_identity.go @@ -19,11 +19,12 @@ package azure import ( "context" "fmt" - "github.com/Azure/azure-sdk-for-go/sdk/azidentity" - "github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential" "os" "strconv" "strings" + + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential" ) // Azure AD Workload Identity Webhook will inject the following environment variables. diff --git a/pkg/scalers/azure/azure_app_insights.go b/pkg/scalers/azure/azure_app_insights.go index f139bcdd8e8..cc9a4a1797d 100644 --- a/pkg/scalers/azure/azure_app_insights.go +++ b/pkg/scalers/azure/azure_app_insights.go @@ -58,19 +58,6 @@ func toISO8601(time string) (string, error) { return fmt.Sprintf("PT%02dH%02dM", hours, minutes), nil } -//func getAuthConfig(ctx context.Context, info AppInsightsInfo, podIdentity kedav1alpha1.AuthPodIdentity) auth.AuthorizerConfig { -// switch podIdentity.Provider { -// case "", kedav1alpha1.PodIdentityProviderNone: -// config := auth.NewClientCredentialsConfig(info.ClientID, info.ClientPassword, info.TenantID) -// config.Resource = info.AppInsightsResourceURL -// config.AADEndpoint = info.ActiveDirectoryEndpoint -// return config -// case kedav1alpha1.PodIdentityProviderAzureWorkload: -// return NewAzureADWorkloadIdentityConfig(ctx, podIdentity.GetIdentityID(), podIdentity.GetIdentityTenantID(), podIdentity.GetIdentityAuthorityHost(), info.AppInsightsResourceURL) -// } -// return nil -//} - func extractAppInsightValue(info AppInsightsInfo, metric ApplicationInsightsMetric) (float64, error) { if _, ok := metric.Value[info.MetricID]; !ok { return -1, fmt.Errorf("metric named %s not found in app insights response", info.MetricID) @@ -108,13 +95,23 @@ func queryParamsForAppInsightsRequest(info AppInsightsInfo) (map[string]interfac return queryParams, nil } -// GetAzureAppInsightsMetricValue returns the value of an Azure App Insights metric, rounded to the nearest int -func GetAzureAppInsightsMetricValue(ctx context.Context, info AppInsightsInfo, podIdentity kedav1alpha1.AuthPodIdentity, ignoreNullValues bool) (float64, error) { +func getAuthConfig(ctx context.Context, info AppInsightsInfo, podIdentity kedav1alpha1.AuthPodIdentity) (AADToken, error) { + token := AADToken{} + var err error + + switch podIdentity.Provider { + case "", kedav1alpha1.PodIdentityProviderNone: + token, err = GetAzureADWorkloadIdentityToken(ctx, info.ClientID, info.TenantID, info.ActiveDirectoryEndpoint, info.AppInsightsResourceURL) + case kedav1alpha1.PodIdentityProviderAzureWorkload: + token, err = GetAzureADWorkloadIdentityToken(ctx, podIdentity.GetIdentityID(), podIdentity.GetIdentityTenantID(), podIdentity.GetIdentityAuthorityHost(), info.AppInsightsResourceURL) + } - //config := getAuthConfig(ctx, info, podIdentity) + return token, err +} - token, err := GetAzureADWorkloadIdentityToken(ctx, info.ClientID, info.TenantID, "", info.AppInsightsResourceURL) - //MSAL get Token here instead of the config +// GetAzureAppInsightsMetricValue returns the value of an Azure App Insights metric, rounded to the nearest int +func GetAzureAppInsightsMetricValue(ctx context.Context, info AppInsightsInfo, podIdentity kedav1alpha1.AuthPodIdentity, ignoreNullValues bool) (float64, error) { + token, err := getAuthConfig(ctx, info, podIdentity) if err != nil { return -1, err } diff --git a/pkg/scalers/azure/azure_app_insights_test.go b/pkg/scalers/azure/azure_app_insights_test.go index 1c470e38d77..db03c60b9b1 100644 --- a/pkg/scalers/azure/azure_app_insights_test.go +++ b/pkg/scalers/azure/azure_app_insights_test.go @@ -1,10 +1,7 @@ package azure import ( - "context" "testing" - - kedav1alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1" ) type testExtractAzAppInsightsTestData struct { @@ -67,39 +64,6 @@ func TestAzGetAzureAppInsightsMetricValue(t *testing.T) { } } -type testAppInsightsAuthConfigTestData struct { - testName string - config string - info AppInsightsInfo - podIdentity kedav1alpha1.PodIdentityProvider -} - -const ( - msiConfig = "msiConfig" - clientCredentialsConfig = "clientCredentialsConfig" - workloadIdentityConfig = "workloadIdentityConfig" -) - -var testAppInsightsAuthConfigData = []testAppInsightsAuthConfigTestData{ - {"client credentials", clientCredentialsConfig, AppInsightsInfo{ClientID: "1234", ClientPassword: "pw", TenantID: "5678"}, ""}, - {"client credentials - pod id none", clientCredentialsConfig, AppInsightsInfo{ClientID: "1234", ClientPassword: "pw", TenantID: "5678"}, kedav1alpha1.PodIdentityProviderNone}, - {"azure workload identity", workloadIdentityConfig, AppInsightsInfo{}, kedav1alpha1.PodIdentityProviderAzureWorkload}, -} - -func TestAzAppInfoGetToken(t *testing.T) { - for _, testData := range testAppInsightsAuthConfigData { - authToken, err := GetAzureADWorkloadIdentityToken(context.TODO(), testData.info.ClientID, testData.info.TenantID, "", testData.info.AppInsightsResourceURL) - - if err != nil { - t.Errorf("Test %v; Expected success but got error: %v", testData.testName, err) - } - if authToken.AccessToken == "" { - t.Errorf("Test %v; Expected token but got empty token: %v", testData.testName, authToken) - } - t.Logf("Test %v; data: %v, token: %v", testData.testName, testData.info, authToken) - } -} - type toISO8601TestData struct { testName string isError bool diff --git a/pkg/scalers/rabbitmq_scaler.go b/pkg/scalers/rabbitmq_scaler.go index 43a62a404ee..40a477b2266 100644 --- a/pkg/scalers/rabbitmq_scaler.go +++ b/pkg/scalers/rabbitmq_scaler.go @@ -12,13 +12,13 @@ import ( "strings" "time" + "github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential" "github.com/go-logr/logr" amqp "github.com/rabbitmq/amqp091-go" v2 "k8s.io/api/autoscaling/v2" "k8s.io/metrics/pkg/apis/external_metrics" "github.com/kedacore/keda/v2/apis/keda/v1alpha1" - "github.com/kedacore/keda/v2/pkg/scalers/azure" "github.com/kedacore/keda/v2/pkg/scalers/scalersconfig" kedautil "github.com/kedacore/keda/v2/pkg/util" ) @@ -64,7 +64,7 @@ type rabbitMQScaler struct { connection *amqp.Connection channel *amqp.Channel httpClient *http.Client - azureOAuth *azure.ADWorkloadIdentityTokenProvider + azureOAuth *confidential.Client logger logr.Logger } @@ -409,15 +409,32 @@ func getJSON(ctx context.Context, s *rabbitMQScaler, url string) (queueInfo, err if s.metadata.WorkloadIdentityResource != "" { if s.azureOAuth == nil { - s.azureOAuth = azure.NewAzureADWorkloadIdentityTokenProvider(ctx, s.metadata.workloadIdentityClientID, s.metadata.workloadIdentityTenantID, s.metadata.workloadIdentityAuthorityHost, s.metadata.WorkloadIdentityResource) + cred, err := confidential.NewCredFromSecret(s.metadata.workloadIdentityClientID) + if err != nil { + return result, err + } + + client, err := confidential.New( + fmt.Sprintf("%s%s/oauth2/token", s.metadata.workloadIdentityAuthorityHost, s.metadata.workloadIdentityTenantID), + s.metadata.workloadIdentityClientID, + cred, + ) + if err != nil { + return result, err + } + + s.azureOAuth = &client } - err = s.azureOAuth.Refresh() + token, err := s.azureOAuth.AcquireTokenSilent(ctx, []string{s.metadata.WorkloadIdentityResource}) if err != nil { - return result, err + token, err = s.azureOAuth.AcquireTokenByCredential(ctx, []string{s.metadata.WorkloadIdentityResource}) + if err != nil { + return result, err + } } - request.Header.Set("Authorization", "Bearer "+s.azureOAuth.OAuthToken()) + request.Header.Set("Authorization", "Bearer "+token.AccessToken) } r, err := s.httpClient.Do(request) diff --git a/vendor/github.com/Azure/azure-amqp-common-go/v4/auth/token.go b/vendor/github.com/Azure/azure-amqp-common-go/v4/auth/token.go deleted file mode 100644 index ea7e5da11fb..00000000000 --- a/vendor/github.com/Azure/azure-amqp-common-go/v4/auth/token.go +++ /dev/null @@ -1,58 +0,0 @@ -// Package auth provides an abstraction over claims-based security for Azure Event Hub and Service Bus. -package auth - -// MIT License -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE - -const ( - // CBSTokenTypeJWT is the type of token to be used for JWTs. For example Azure Active Directory tokens. - CBSTokenTypeJWT TokenType = "jwt" - // CBSTokenTypeSAS is the type of token to be used for SAS tokens. - CBSTokenTypeSAS TokenType = "servicebus.windows.net:sastoken" -) - -type ( - // TokenType represents types of tokens known for claims-based auth - TokenType string - - // Token contains all of the information to negotiate authentication - Token struct { - // TokenType is the type of CBS token - TokenType TokenType - Token string - Expiry string - } - - // TokenProvider abstracts the fetching of authentication tokens - TokenProvider interface { - GetToken(uri string) (*Token, error) - } -) - -// NewToken constructs a new auth token -func NewToken(tokenType TokenType, token, expiry string) *Token { - return &Token{ - TokenType: tokenType, - Token: token, - Expiry: expiry, - } -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/cli/token.go b/vendor/github.com/Azure/go-autorest/autorest/azure/cli/token.go deleted file mode 100644 index 486619111c6..00000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/azure/cli/token.go +++ /dev/null @@ -1,227 +0,0 @@ -package cli - -// Copyright 2017 Microsoft Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import ( - "bytes" - "encoding/json" - "fmt" - "os" - "os/exec" - "path/filepath" - "regexp" - "runtime" - "strconv" - "time" - - "github.com/Azure/go-autorest/autorest/adal" - "github.com/Azure/go-autorest/autorest/date" - "github.com/mitchellh/go-homedir" -) - -// Token represents an AccessToken from the Azure CLI -type Token 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"` -} - -const accessTokensJSON = "accessTokens.json" - -// ToADALToken converts an Azure CLI `Token`` to an `adal.Token`` -func (t Token) ToADALToken() (converted adal.Token, err error) { - tokenExpirationDate, err := ParseExpirationDate(t.ExpiresOn) - if err != nil { - err = fmt.Errorf("Error parsing Token Expiration Date %q: %+v", t.ExpiresOn, err) - return - } - - difference := tokenExpirationDate.Sub(date.UnixEpoch()) - - converted = adal.Token{ - AccessToken: t.AccessToken, - Type: t.TokenType, - ExpiresIn: "3600", - ExpiresOn: json.Number(strconv.Itoa(int(difference.Seconds()))), - RefreshToken: t.RefreshToken, - Resource: t.Resource, - } - return -} - -// AccessTokensPath returns the path where access tokens are stored from the Azure CLI -// TODO(#199): add unit test. -func AccessTokensPath() (string, error) { - // Azure-CLI allows user to customize the path of access tokens through environment variable. - if accessTokenPath := os.Getenv("AZURE_ACCESS_TOKEN_FILE"); accessTokenPath != "" { - return accessTokenPath, nil - } - - // Azure-CLI allows user to customize the path to Azure config directory through environment variable. - if cfgDir := configDir(); cfgDir != "" { - return filepath.Join(cfgDir, accessTokensJSON), nil - } - - // Fallback logic to default path on non-cloud-shell environment. - // TODO(#200): remove the dependency on hard-coding path. - return homedir.Expand("~/.azure/" + accessTokensJSON) -} - -// ParseExpirationDate parses either a Azure CLI or CloudShell date into a time object -func ParseExpirationDate(input string) (*time.Time, error) { - // CloudShell (and potentially the Azure CLI in future) - expirationDate, cloudShellErr := time.Parse(time.RFC3339, input) - if cloudShellErr != nil { - // Azure CLI (Python) e.g. 2017-08-31 19:48:57.998857 (plus the local timezone) - const cliFormat = "2006-01-02 15:04:05.999999" - expirationDate, cliErr := time.ParseInLocation(cliFormat, input, time.Local) - if cliErr == nil { - return &expirationDate, nil - } - - return nil, fmt.Errorf("Error parsing expiration date %q.\n\nCloudShell Error: \n%+v\n\nCLI Error:\n%+v", input, cloudShellErr, cliErr) - } - - return &expirationDate, nil -} - -// LoadTokens restores a set of Token objects from a file located at 'path'. -func LoadTokens(path string) ([]Token, error) { - file, err := os.Open(path) - if err != nil { - return nil, fmt.Errorf("failed to open file (%s) while loading token: %v", path, err) - } - defer file.Close() - - var tokens []Token - - dec := json.NewDecoder(file) - if err = dec.Decode(&tokens); err != nil { - return nil, fmt.Errorf("failed to decode contents of file (%s) into a `cli.Token` representation: %v", path, err) - } - - return tokens, nil -} - -// GetTokenFromCLI gets a token using Azure CLI 2.0 for local development scenarios. -func GetTokenFromCLI(resource string) (*Token, error) { - return GetTokenFromCLIWithParams(GetAccessTokenParams{Resource: resource}) -} - -// GetAccessTokenParams is the parameter struct of GetTokenFromCLIWithParams -type GetAccessTokenParams struct { - Resource string - ResourceType string - Subscription string - Tenant string -} - -// GetTokenFromCLIWithParams gets a token using Azure CLI 2.0 for local development scenarios. -func GetTokenFromCLIWithParams(params GetAccessTokenParams) (*Token, error) { - cliCmd := GetAzureCLICommand() - - cliCmd.Args = append(cliCmd.Args, "account", "get-access-token", "-o", "json") - if params.Resource != "" { - if err := validateParameter(params.Resource); err != nil { - return nil, err - } - cliCmd.Args = append(cliCmd.Args, "--resource", params.Resource) - } - if params.ResourceType != "" { - if err := validateParameter(params.ResourceType); err != nil { - return nil, err - } - cliCmd.Args = append(cliCmd.Args, "--resource-type", params.ResourceType) - } - if params.Subscription != "" { - if err := validateParameter(params.Subscription); err != nil { - return nil, err - } - cliCmd.Args = append(cliCmd.Args, "--subscription", params.Subscription) - } - if params.Tenant != "" { - if err := validateParameter(params.Tenant); err != nil { - return nil, err - } - cliCmd.Args = append(cliCmd.Args, "--tenant", params.Tenant) - } - - var stderr bytes.Buffer - cliCmd.Stderr = &stderr - - output, err := cliCmd.Output() - if err != nil { - if stderr.Len() > 0 { - return nil, fmt.Errorf("Invoking Azure CLI failed with the following error: %s", stderr.String()) - } - - return nil, fmt.Errorf("Invoking Azure CLI failed with the following error: %s", err.Error()) - } - - tokenResponse := Token{} - err = json.Unmarshal(output, &tokenResponse) - if err != nil { - return nil, err - } - - return &tokenResponse, err -} - -func validateParameter(param string) error { - // Validate parameters, since it gets sent as a command line argument to Azure CLI - const invalidResourceErrorTemplate = "Parameter %s is not in expected format. Only alphanumeric characters, [dot], [colon], [hyphen], and [forward slash] are allowed." - match, err := regexp.MatchString("^[0-9a-zA-Z-.:/]+$", param) - if err != nil { - return err - } - if !match { - return fmt.Errorf(invalidResourceErrorTemplate, param) - } - return nil -} - -// GetAzureCLICommand can be used to run arbitrary Azure CLI command -func GetAzureCLICommand() *exec.Cmd { - // This is the path that a developer can set to tell this class what the install path for Azure CLI is. - const azureCLIPath = "AzureCLIPath" - - // The default install paths are used to find Azure CLI. This is for security, so that any path in the calling program's Path environment is not used to execute Azure CLI. - azureCLIDefaultPathWindows := fmt.Sprintf("%s\\Microsoft SDKs\\Azure\\CLI2\\wbin; %s\\Microsoft SDKs\\Azure\\CLI2\\wbin", os.Getenv("ProgramFiles(x86)"), os.Getenv("ProgramFiles")) - - // Default path for non-Windows. - const azureCLIDefaultPath = "/bin:/sbin:/usr/bin:/usr/local/bin" - - // Execute Azure CLI to get token - var cliCmd *exec.Cmd - if runtime.GOOS == "windows" { - cliCmd = exec.Command(fmt.Sprintf("%s\\system32\\cmd.exe", os.Getenv("windir"))) - cliCmd.Env = os.Environ() - cliCmd.Env = append(cliCmd.Env, fmt.Sprintf("PATH=%s;%s", os.Getenv(azureCLIPath), azureCLIDefaultPathWindows)) - cliCmd.Args = append(cliCmd.Args, "/c", "az") - } else { - cliCmd = exec.Command("az") - cliCmd.Env = os.Environ() - cliCmd.Env = append(cliCmd.Env, fmt.Sprintf("PATH=%s:%s", os.Getenv(azureCLIPath), azureCLIDefaultPath)) - } - - return cliCmd -} diff --git a/vendor/github.com/dimchansky/utfbom/.gitignore b/vendor/github.com/dimchansky/utfbom/.gitignore deleted file mode 100644 index d7ec5cebb98..00000000000 --- a/vendor/github.com/dimchansky/utfbom/.gitignore +++ /dev/null @@ -1,37 +0,0 @@ -# Binaries for programs and plugins -*.exe -*.dll -*.so -*.dylib -*.o -*.a - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.prof - -# Test binary, build with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out - -# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 -.glide/ - -# Gogland -.idea/ \ No newline at end of file diff --git a/vendor/github.com/dimchansky/utfbom/.travis.yml b/vendor/github.com/dimchansky/utfbom/.travis.yml deleted file mode 100644 index 19312ee35fc..00000000000 --- a/vendor/github.com/dimchansky/utfbom/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -language: go -sudo: false - -go: - - 1.10.x - - 1.11.x - - 1.12.x - - 1.13.x - - 1.14.x - - 1.15.x - -cache: - directories: - - $HOME/.cache/go-build - - $HOME/gopath/pkg/mod - -env: - global: - - GO111MODULE=on - -before_install: - - go get github.com/mattn/goveralls - - go get golang.org/x/tools/cmd/cover - - go get golang.org/x/tools/cmd/goimports - - go get golang.org/x/lint/golint -script: - - gofiles=$(find ./ -name '*.go') && [ -z "$gofiles" ] || unformatted=$(goimports -l $gofiles) && [ -z "$unformatted" ] || (echo >&2 "Go files must be formatted with gofmt. Following files has problem:\n $unformatted" && false) - - golint ./... # This won't break the build, just show warnings - - $HOME/gopath/bin/goveralls -service=travis-ci diff --git a/vendor/github.com/dimchansky/utfbom/LICENSE b/vendor/github.com/dimchansky/utfbom/LICENSE deleted file mode 100644 index 6279cb87f43..00000000000 --- a/vendor/github.com/dimchansky/utfbom/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright (c) 2018-2020, Dmitrij Koniajev (dimchansky@gmail.com) - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/dimchansky/utfbom/README.md b/vendor/github.com/dimchansky/utfbom/README.md deleted file mode 100644 index 8ece280089a..00000000000 --- a/vendor/github.com/dimchansky/utfbom/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# utfbom [![Godoc](https://godoc.org/github.com/dimchansky/utfbom?status.png)](https://godoc.org/github.com/dimchansky/utfbom) [![License](https://img.shields.io/:license-apache-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![Build Status](https://travis-ci.org/dimchansky/utfbom.svg?branch=master)](https://travis-ci.org/dimchansky/utfbom) [![Go Report Card](https://goreportcard.com/badge/github.com/dimchansky/utfbom)](https://goreportcard.com/report/github.com/dimchansky/utfbom) [![Coverage Status](https://coveralls.io/repos/github/dimchansky/utfbom/badge.svg?branch=master)](https://coveralls.io/github/dimchansky/utfbom?branch=master) - -The package utfbom implements the detection of the BOM (Unicode Byte Order Mark) and removing as necessary. It can also return the encoding detected by the BOM. - -## Installation - - go get -u github.com/dimchansky/utfbom - -## Example - -```go -package main - -import ( - "bytes" - "fmt" - "io/ioutil" - - "github.com/dimchansky/utfbom" -) - -func main() { - trySkip([]byte("\xEF\xBB\xBFhello")) - trySkip([]byte("hello")) -} - -func trySkip(byteData []byte) { - fmt.Println("Input:", byteData) - - // just skip BOM - output, err := ioutil.ReadAll(utfbom.SkipOnly(bytes.NewReader(byteData))) - if err != nil { - fmt.Println(err) - return - } - fmt.Println("ReadAll with BOM skipping", output) - - // skip BOM and detect encoding - sr, enc := utfbom.Skip(bytes.NewReader(byteData)) - fmt.Printf("Detected encoding: %s\n", enc) - output, err = ioutil.ReadAll(sr) - if err != nil { - fmt.Println(err) - return - } - fmt.Println("ReadAll with BOM detection and skipping", output) - fmt.Println() -} -``` - -Output: - -``` -$ go run main.go -Input: [239 187 191 104 101 108 108 111] -ReadAll with BOM skipping [104 101 108 108 111] -Detected encoding: UTF8 -ReadAll with BOM detection and skipping [104 101 108 108 111] - -Input: [104 101 108 108 111] -ReadAll with BOM skipping [104 101 108 108 111] -Detected encoding: Unknown -ReadAll with BOM detection and skipping [104 101 108 108 111] -``` - - diff --git a/vendor/github.com/dimchansky/utfbom/utfbom.go b/vendor/github.com/dimchansky/utfbom/utfbom.go deleted file mode 100644 index 77a303e564b..00000000000 --- a/vendor/github.com/dimchansky/utfbom/utfbom.go +++ /dev/null @@ -1,192 +0,0 @@ -// Package utfbom implements the detection of the BOM (Unicode Byte Order Mark) and removing as necessary. -// It wraps an io.Reader object, creating another object (Reader) that also implements the io.Reader -// interface but provides automatic BOM checking and removing as necessary. -package utfbom - -import ( - "errors" - "io" -) - -// Encoding is type alias for detected UTF encoding. -type Encoding int - -// Constants to identify detected UTF encodings. -const ( - // Unknown encoding, returned when no BOM was detected - Unknown Encoding = iota - - // UTF8, BOM bytes: EF BB BF - UTF8 - - // UTF-16, big-endian, BOM bytes: FE FF - UTF16BigEndian - - // UTF-16, little-endian, BOM bytes: FF FE - UTF16LittleEndian - - // UTF-32, big-endian, BOM bytes: 00 00 FE FF - UTF32BigEndian - - // UTF-32, little-endian, BOM bytes: FF FE 00 00 - UTF32LittleEndian -) - -// String returns a user-friendly string representation of the encoding. Satisfies fmt.Stringer interface. -func (e Encoding) String() string { - switch e { - case UTF8: - return "UTF8" - case UTF16BigEndian: - return "UTF16BigEndian" - case UTF16LittleEndian: - return "UTF16LittleEndian" - case UTF32BigEndian: - return "UTF32BigEndian" - case UTF32LittleEndian: - return "UTF32LittleEndian" - default: - return "Unknown" - } -} - -const maxConsecutiveEmptyReads = 100 - -// Skip creates Reader which automatically detects BOM (Unicode Byte Order Mark) and removes it as necessary. -// It also returns the encoding detected by the BOM. -// If the detected encoding is not needed, you can call the SkipOnly function. -func Skip(rd io.Reader) (*Reader, Encoding) { - // Is it already a Reader? - b, ok := rd.(*Reader) - if ok { - return b, Unknown - } - - enc, left, err := detectUtf(rd) - return &Reader{ - rd: rd, - buf: left, - err: err, - }, enc -} - -// SkipOnly creates Reader which automatically detects BOM (Unicode Byte Order Mark) and removes it as necessary. -func SkipOnly(rd io.Reader) *Reader { - r, _ := Skip(rd) - return r -} - -// Reader implements automatic BOM (Unicode Byte Order Mark) checking and -// removing as necessary for an io.Reader object. -type Reader struct { - rd io.Reader // reader provided by the client - buf []byte // buffered data - err error // last error -} - -// Read is an implementation of io.Reader interface. -// The bytes are taken from the underlying Reader, but it checks for BOMs, removing them as necessary. -func (r *Reader) Read(p []byte) (n int, err error) { - if len(p) == 0 { - return 0, nil - } - - if r.buf == nil { - if r.err != nil { - return 0, r.readErr() - } - - return r.rd.Read(p) - } - - // copy as much as we can - n = copy(p, r.buf) - r.buf = nilIfEmpty(r.buf[n:]) - return n, nil -} - -func (r *Reader) readErr() error { - err := r.err - r.err = nil - return err -} - -var errNegativeRead = errors.New("utfbom: reader returned negative count from Read") - -func detectUtf(rd io.Reader) (enc Encoding, buf []byte, err error) { - buf, err = readBOM(rd) - - if len(buf) >= 4 { - if isUTF32BigEndianBOM4(buf) { - return UTF32BigEndian, nilIfEmpty(buf[4:]), err - } - if isUTF32LittleEndianBOM4(buf) { - return UTF32LittleEndian, nilIfEmpty(buf[4:]), err - } - } - - if len(buf) > 2 && isUTF8BOM3(buf) { - return UTF8, nilIfEmpty(buf[3:]), err - } - - if (err != nil && err != io.EOF) || (len(buf) < 2) { - return Unknown, nilIfEmpty(buf), err - } - - if isUTF16BigEndianBOM2(buf) { - return UTF16BigEndian, nilIfEmpty(buf[2:]), err - } - if isUTF16LittleEndianBOM2(buf) { - return UTF16LittleEndian, nilIfEmpty(buf[2:]), err - } - - return Unknown, nilIfEmpty(buf), err -} - -func readBOM(rd io.Reader) (buf []byte, err error) { - const maxBOMSize = 4 - var bom [maxBOMSize]byte // used to read BOM - - // read as many bytes as possible - for nEmpty, n := 0, 0; err == nil && len(buf) < maxBOMSize; buf = bom[:len(buf)+n] { - if n, err = rd.Read(bom[len(buf):]); n < 0 { - panic(errNegativeRead) - } - if n > 0 { - nEmpty = 0 - } else { - nEmpty++ - if nEmpty >= maxConsecutiveEmptyReads { - err = io.ErrNoProgress - } - } - } - return -} - -func isUTF32BigEndianBOM4(buf []byte) bool { - return buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0xFE && buf[3] == 0xFF -} - -func isUTF32LittleEndianBOM4(buf []byte) bool { - return buf[0] == 0xFF && buf[1] == 0xFE && buf[2] == 0x00 && buf[3] == 0x00 -} - -func isUTF8BOM3(buf []byte) bool { - return buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF -} - -func isUTF16BigEndianBOM2(buf []byte) bool { - return buf[0] == 0xFE && buf[1] == 0xFF -} - -func isUTF16LittleEndianBOM2(buf []byte) bool { - return buf[0] == 0xFF && buf[1] == 0xFE -} - -func nilIfEmpty(buf []byte) (res []byte) { - if len(buf) > 0 { - res = buf - } - return -}