diff --git a/go.mod b/go.mod index 5a2afe1d7..35b060855 100644 --- a/go.mod +++ b/go.mod @@ -4,18 +4,16 @@ go 1.19 require ( github.com/Azure/aad-pod-identity v1.8.13 - github.com/Azure/azure-sdk-for-go v65.0.0+incompatible github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.2 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization v1.0.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.1.1 github.com/Azure/go-autorest/autorest v0.11.29 - github.com/Azure/go-autorest/autorest/adal v0.9.23 - github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 github.com/Azure/go-autorest/autorest/to v0.4.0 github.com/AzureAD/microsoft-authentication-library-for-go v0.9.0 github.com/golang/mock v1.6.0 github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 github.com/kelseyhightower/envconfig v1.4.0 - github.com/microsoft/kiota-abstractions-go v0.19.1 github.com/microsoft/kiota-authentication-azure-go v0.6.0 github.com/microsoft/kiota-http-go v0.16.2 github.com/microsoft/kiota-serialization-json-go v0.9.3 @@ -38,12 +36,16 @@ require ( sigs.k8s.io/controller-runtime v0.14.6 ) +require ( + github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect + github.com/microsoft/kiota-abstractions-go v0.19.1 // indirect +) + require ( github.com/Azure/azure-sdk-for-go/sdk/azcore v1.5.0 github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect - github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -51,7 +53,6 @@ require ( github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cjlapao/common-go v0.0.39 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dimchansky/utfbom v1.1.1 // indirect github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect @@ -79,7 +80,6 @@ require ( github.com/microsoft/kiota-serialization-form-go v0.9.1 // indirect github.com/microsoft/kiota-serialization-text-go v0.7.0 // indirect github.com/microsoftgraph/msgraph-sdk-go-core v0.36.1 - github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect diff --git a/go.sum b/go.sum index bc3068d65..82f525840 100644 --- a/go.sum +++ b/go.sum @@ -33,24 +33,26 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/aad-pod-identity v1.8.13 h1:/gUmacA0z7+lsOlGYAYzkGvAB/KOkUe5Pb6qSeiHD0k= github.com/Azure/aad-pod-identity v1.8.13/go.mod h1:uxM/lsPo/abzqdk0rwEm4SqO9pavMz0fCmKpYAj4HL8= -github.com/Azure/azure-sdk-for-go v65.0.0+incompatible h1:HzKLt3kIwMm4KeJYTdx9EbjRYTySD/t8i1Ee/W5EGXw= -github.com/Azure/azure-sdk-for-go v65.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v57.2.0+incompatible h1:zoJapafogLazoyp0x9aQENzNNqxvU6pnGtb2P8/i+HI= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.5.0 h1:xGLAFFd9D3iLGxYiUGPdITSzsFmU1K8VtfuUHWAoN7M= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.5.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.2 h1:uqM+VoHjVH6zdlkLF2b6O0ZANcHoj3rO0PoQ3jglUJA= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.2/go.mod h1:twTKAa1E6hLmSDjLhaCkbTMQKc7p/rNLU40rLxGEOCI= github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization v1.0.0 h1:qtRcg5Y7jNJ4jEzPq4GpWLfTspHdNe2ZK6LjwGcjgmU= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization v1.0.0/go.mod h1:lPneRe3TwsoDRKY4O6YDLXHhEWrD+TIRa8XrV/3/fqw= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2 h1:mLY+pNLjCUeKhgnAJWAKhEUQM+RJQo2H1fuGSw1Ky1E= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 h1:ECsQtyERDVz3NP3kvDOTLvbQhqWp/x9EsGKtb4ogUr8= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.1.1 h1:A+a54F7ygu4ANdV9hYsLMfiHFgjuwIUCG+6opLAvxJE= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.1.1/go.mod h1:ThfyMjs6auYrWPnYJjI3H4H++oVPrz01pizpu8lfl3A= 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.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/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= @@ -58,8 +60,6 @@ github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9A github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= -github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= @@ -97,8 +97,6 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/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.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= @@ -146,7 +144,6 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -278,8 +275,6 @@ github.com/microsoftgraph/msgraph-sdk-go v0.61.0 h1:Xa8KoQ6CS04CnUxAVectu185VLlL github.com/microsoftgraph/msgraph-sdk-go v0.61.0/go.mod h1:vPylvYw8unnEZWmfCBeiwMoLvEeHW2FcWD0LjUnuiC4= github.com/microsoftgraph/msgraph-sdk-go-core v0.36.1 h1:TsEECo911Z/0dB/xxEB852/BX14LnU/GvTyJTuysx48= github.com/microsoftgraph/msgraph-sdk-go-core v0.36.1/go.mod h1:6fRetxmlg2LyRDf/wX8h7JP3cj+2JVhI50RqYJk4CIY= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= diff --git a/pkg/cloud/azureclient.go b/pkg/cloud/azureclient.go index ed335627e..38bcd88f4 100644 --- a/pkg/cloud/azureclient.go +++ b/pkg/cloud/azureclient.go @@ -12,14 +12,12 @@ import ( "time" "github.com/Azure/azure-sdk-for-go/sdk/azcore" + armpolicy "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/policy" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" "github.com/Azure/azure-sdk-for-go/sdk/azidentity" - "github.com/Azure/azure-sdk-for-go/services/preview/authorization/mgmt/2018-01-01-preview/authorization" - "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2016-06-01/subscriptions" - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/adal" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions" "github.com/Azure/go-autorest/autorest/azure" - "github.com/Azure/go-autorest/autorest/azure/cli" - "github.com/microsoft/kiota-abstractions-go/authentication" kiotaauth "github.com/microsoft/kiota-authentication-azure-go" msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go" "github.com/microsoftgraph/msgraph-sdk-go/models" @@ -44,11 +42,11 @@ type Interface interface { GetApplication(ctx context.Context, displayName string) (models.Applicationable, error) // Role assignment methods - CreateRoleAssignment(ctx context.Context, scope, roleName, principalID string) (authorization.RoleAssignment, error) - DeleteRoleAssignment(ctx context.Context, roleAssignmentID string) (authorization.RoleAssignment, error) + CreateRoleAssignment(ctx context.Context, scope, roleName, principalID string) (armauthorization.RoleAssignment, error) + DeleteRoleAssignment(ctx context.Context, roleAssignmentID string) (armauthorization.RoleAssignment, error) // Role definition methods - GetRoleDefinitionIDByName(ctx context.Context, scope, roleName string) (authorization.RoleDefinition, error) + GetRoleDefinitionIDByName(ctx context.Context, scope, roleName string) (armauthorization.RoleDefinition, error) // Federation methods AddFederatedCredential(ctx context.Context, objectID string, fic models.FederatedIdentityCredentialable) error @@ -62,51 +60,22 @@ type AzureClient struct { graphServiceClient *msgraphsdk.GraphServiceClient - roleAssignmentsClient authorization.RoleAssignmentsClient - roleDefinitionsClient authorization.RoleDefinitionsClient + roleAssignmentsClient *armauthorization.RoleAssignmentsClient + roleDefinitionsClient *armauthorization.RoleDefinitionsClient } // NewAzureClientWithCLI creates an AzureClient configured from Azure CLI 2.0 for local development scenarios. -func NewAzureClientWithCLI(env azure.Environment, subscriptionID, tenantID string, client *http.Client) (*AzureClient, error) { - _, _, err := getOAuthConfig(env, tenantID) - if err != nil { - return nil, err - } - - token, err := cli.GetTokenFromCLI(env.ResourceManagerEndpoint) - if err != nil { - return nil, err - } - - adalToken, err := token.ToADALToken() - if err != nil { - return nil, err - } - +func NewAzureClientWithCLI(env azure.Environment, subscriptionID string, client *http.Client) (*AzureClient, error) { cred, err := azidentity.NewAzureCLICredential(nil) if err != nil { return nil, errors.Wrap(err, "failed to create credential") } - auth, err := kiotaauth.NewAzureIdentityAuthenticationProviderWithScopes(cred, []string{getGraphScope(env)}) - if err != nil { - return nil, errors.Wrap(err, "failed to create authentication provider") - } - return getClient(env, subscriptionID, autorest.NewBearerAuthorizer(&adalToken), auth, client) + return getClient(env, subscriptionID, cred, client) } // NewAzureClientWithClientSecret returns an AzureClient via client_id and client_secret func NewAzureClientWithClientSecret(env azure.Environment, subscriptionID, clientID, clientSecret, tenantID string, client *http.Client) (*AzureClient, error) { - oauthConfig, tenantID, err := getOAuthConfig(env, tenantID) - if err != nil { - return nil, err - } - - armSpt, err := adal.NewServicePrincipalToken(*oauthConfig, clientID, clientSecret, env.ServiceManagementEndpoint) - if err != nil { - return nil, err - } - cred, err := azidentity.NewClientSecretCredential(tenantID, clientID, clientSecret, &azidentity.ClientSecretCredentialOptions{ ClientOptions: azcore.ClientOptions{ @@ -116,12 +85,8 @@ func NewAzureClientWithClientSecret(env azure.Environment, subscriptionID, clien if err != nil { return nil, errors.Wrap(err, "failed to create credential") } - auth, err := kiotaauth.NewAzureIdentityAuthenticationProviderWithScopes(cred, []string{getGraphScope(env)}) - if err != nil { - return nil, errors.Wrap(err, "failed to create authentication provider") - } - return getClient(env, subscriptionID, autorest.NewBearerAuthorizer(armSpt), auth, client) + return getClient(env, subscriptionID, cred, client) } // NewAzureClientWithClientCertificateFile returns an AzureClient via client_id and jwt certificate assertion @@ -151,24 +116,10 @@ func NewAzureClientWithClientCertificateFile(env azure.Environment, subscription // NewAzureClientWithClientCertificate returns an AzureClient via client_id and jwt certificate assertion func NewAzureClientWithClientCertificate(env azure.Environment, subscriptionID, clientID, tenantID string, certificate *x509.Certificate, privateKey *rsa.PrivateKey, client *http.Client) (*AzureClient, error) { - oauthConfig, tenantID, err := getOAuthConfig(env, tenantID) - if err != nil { - return nil, err - } - - return newAzureClientWithCertificate(env, oauthConfig, subscriptionID, clientID, tenantID, certificate, privateKey, client) + return newAzureClientWithCertificate(env, subscriptionID, clientID, tenantID, certificate, privateKey, client) } -func getOAuthConfig(env azure.Environment, tenantID string) (*adal.OAuthConfig, string, error) { - oauthConfig, err := adal.NewOAuthConfig(env.ActiveDirectoryEndpoint, tenantID) - if err != nil { - return nil, "", err - } - - return oauthConfig, tenantID, nil -} - -func newAzureClientWithCertificate(env azure.Environment, oauthConfig *adal.OAuthConfig, subscriptionID, clientID, tenantID string, certificate *x509.Certificate, privateKey *rsa.PrivateKey, client *http.Client) (*AzureClient, error) { +func newAzureClientWithCertificate(env azure.Environment, subscriptionID, clientID, tenantID string, certificate *x509.Certificate, privateKey *rsa.PrivateKey, client *http.Client) (*AzureClient, error) { if certificate == nil { return nil, errors.New("certificate should not be nil") } @@ -177,11 +128,6 @@ func newAzureClientWithCertificate(env azure.Environment, oauthConfig *adal.OAut return nil, errors.New("privateKey should not be nil") } - armSpt, err := adal.NewServicePrincipalTokenFromCertificate(*oauthConfig, clientID, certificate, privateKey, env.ServiceManagementEndpoint) - if err != nil { - return nil, err - } - cred, err := azidentity.NewClientCertificateCredential(tenantID, clientID, []*x509.Certificate{certificate}, privateKey, &azidentity.ClientCertificateCredentialOptions{ ClientOptions: azcore.ClientOptions{ @@ -191,46 +137,73 @@ func newAzureClientWithCertificate(env azure.Environment, oauthConfig *adal.OAut if err != nil { return nil, errors.Wrap(err, "failed to create credential") } - auth, err := kiotaauth.NewAzureIdentityAuthenticationProviderWithScopes(cred, []string{getGraphScope(env)}) + + return getClient(env, subscriptionID, cred, client) +} + +func getClient(env azure.Environment, subscriptionID string, credential azcore.TokenCredential, client *http.Client) (*AzureClient, error) { + auth, err := kiotaauth.NewAzureIdentityAuthenticationProviderWithScopes(credential, []string{getGraphScope(env)}) if err != nil { return nil, errors.Wrap(err, "failed to create authentication provider") } - return getClient(env, subscriptionID, autorest.NewBearerAuthorizer(armSpt), auth, client) -} - -func getClient(env azure.Environment, subscriptionID string, armAuthorizer autorest.Authorizer, auth authentication.AuthenticationProvider, client *http.Client) (*AzureClient, error) { adapter, err := msgraphsdk.NewGraphRequestAdapterWithParseNodeFactoryAndSerializationWriterFactoryAndHttpClient(auth, nil, nil, client) if err != nil { return nil, errors.Wrap(err, "failed to create request adapter") } + clientOpts := &armpolicy.ClientOptions{ + ClientOptions: azcore.ClientOptions{ + Transport: client, + }, + } + + roleAssignmentsClient, err := armauthorization.NewRoleAssignmentsClient(subscriptionID, credential, clientOpts) + if err != nil { + return nil, errors.Wrap(err, "failed to create role assignments client") + } + + roleDefinitionsClient, err := armauthorization.NewRoleDefinitionsClient(credential, clientOpts) + if err != nil { + return nil, errors.Wrap(err, "failed to create role definitions client") + } + azClient := &AzureClient{ environment: env, subscriptionID: subscriptionID, graphServiceClient: msgraphsdk.NewGraphServiceClient(adapter), - roleAssignmentsClient: authorization.NewRoleAssignmentsClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID), - roleDefinitionsClient: authorization.NewRoleDefinitionsClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID), + roleAssignmentsClient: roleAssignmentsClient, + roleDefinitionsClient: roleDefinitionsClient, } - azClient.roleAssignmentsClient.Authorizer = armAuthorizer - azClient.roleDefinitionsClient.Authorizer = armAuthorizer + return azClient, nil +} - azClient.roleAssignmentsClient.Sender = client - azClient.roleDefinitionsClient.Sender = client +var _ azcore.TokenCredential = (*dummyCredential)(nil) - return azClient, nil +// dummyCredential is a dummy implementation of azcore.TokenCredential to be used +// when we only need to get the tenantID from a subscriptionID +type dummyCredential struct{} + +func (d *dummyCredential) GetToken(_ context.Context, _ policy.TokenRequestOptions) (azcore.AccessToken, error) { + return azcore.AccessToken{}, nil } -// GetTenantID figures out the AAD tenant ID of the subscription by making an -// unauthenticated request to the Get Subscription Details endpoint and parses -// the value from WWW-Authenticate header. -// TODO this should probably to to the armhelpers library -func GetTenantID(resourceManagerEndpoint string, subscriptionID string) (string, error) { +// GetTenantID returns the tenantID for the given subscriptionID +// The tenantID is parsed from the WWW-Authenticate header of a failed request +func GetTenantID(subscriptionID string, client *http.Client) (string, error) { const hdrKey = "WWW-Authenticate" - c := subscriptions.NewClientWithBaseURI(resourceManagerEndpoint) + clientOpts := &armpolicy.ClientOptions{ + ClientOptions: azcore.ClientOptions{ + Transport: client, + }, + } + subscriptionsClient, err := armsubscriptions.NewClient(&dummyCredential{}, clientOpts) + if err != nil { + return "", errors.Wrap(err, "failed to create subscriptions client") + } mlog.Debug("Resolving tenantID", "subscriptionID", subscriptionID) @@ -239,18 +212,16 @@ func GetTenantID(resourceManagerEndpoint string, subscriptionID string) (string, // network error etc) ctx, cancel := context.WithTimeout(context.Background(), time.Minute*150) defer cancel() - subs, err := c.Get(ctx, subscriptionID) - if subs.Response.Response == nil { - return "", errors.Wrap(err, "Request failed") - } - // Expecting 401 StatusUnauthorized here, just read the header - if subs.StatusCode != http.StatusUnauthorized { - return "", errors.Errorf("Unexpected response from Get Subscription: %v", subs.StatusCode) + _, err = subscriptionsClient.Get(ctx, subscriptionID, &armsubscriptions.ClientGetOptions{}) + var respErr *azcore.ResponseError + if !errors.As(err, &respErr) { + return "", errors.Errorf("unexpected response from get subscription: %v", err) } - hdr := subs.Header.Get(hdrKey) + + hdr := respErr.RawResponse.Header.Get(hdrKey) if hdr == "" { - return "", errors.Errorf("Header %v not found in Get Subscription response", hdrKey) + return "", errors.Errorf("header %q not found in get subscription response", hdrKey) } // Example value for hdr: diff --git a/pkg/cloud/mock_cloud/cloud_mock.go b/pkg/cloud/mock_cloud/cloud_mock.go index bb5b7d7ce..cedb931ce 100644 --- a/pkg/cloud/mock_cloud/cloud_mock.go +++ b/pkg/cloud/mock_cloud/cloud_mock.go @@ -8,7 +8,7 @@ import ( context "context" reflect "reflect" - authorization "github.com/Azure/azure-sdk-for-go/services/preview/authorization/mgmt/2018-01-01-preview/authorization" + armauthorization "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization" gomock "github.com/golang/mock/gomock" models "github.com/microsoftgraph/msgraph-sdk-go/models" ) @@ -66,10 +66,10 @@ func (mr *MockInterfaceMockRecorder) CreateApplication(ctx, displayName interfac } // CreateRoleAssignment mocks base method. -func (m *MockInterface) CreateRoleAssignment(ctx context.Context, scope, roleName, principalID string) (authorization.RoleAssignment, error) { +func (m *MockInterface) CreateRoleAssignment(ctx context.Context, scope, roleName, principalID string) (armauthorization.RoleAssignment, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateRoleAssignment", ctx, scope, roleName, principalID) - ret0, _ := ret[0].(authorization.RoleAssignment) + ret0, _ := ret[0].(armauthorization.RoleAssignment) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -124,10 +124,10 @@ func (mr *MockInterfaceMockRecorder) DeleteFederatedCredential(ctx, objectID, fe } // DeleteRoleAssignment mocks base method. -func (m *MockInterface) DeleteRoleAssignment(ctx context.Context, roleAssignmentID string) (authorization.RoleAssignment, error) { +func (m *MockInterface) DeleteRoleAssignment(ctx context.Context, roleAssignmentID string) (armauthorization.RoleAssignment, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteRoleAssignment", ctx, roleAssignmentID) - ret0, _ := ret[0].(authorization.RoleAssignment) + ret0, _ := ret[0].(armauthorization.RoleAssignment) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -183,10 +183,10 @@ func (mr *MockInterfaceMockRecorder) GetFederatedCredential(ctx, objectID, issue } // GetRoleDefinitionIDByName mocks base method. -func (m *MockInterface) GetRoleDefinitionIDByName(ctx context.Context, scope, roleName string) (authorization.RoleDefinition, error) { +func (m *MockInterface) GetRoleDefinitionIDByName(ctx context.Context, scope, roleName string) (armauthorization.RoleDefinition, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetRoleDefinitionIDByName", ctx, scope, roleName) - ret0, _ := ret[0].(authorization.RoleDefinition) + ret0, _ := ret[0].(armauthorization.RoleDefinition) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/pkg/cloud/roleassignments.go b/pkg/cloud/roleassignments.go index 8b008d465..7038d0a38 100644 --- a/pkg/cloud/roleassignments.go +++ b/pkg/cloud/roleassignments.go @@ -4,7 +4,7 @@ import ( "context" "time" - "github.com/Azure/azure-sdk-for-go/services/preview/authorization/mgmt/2018-01-01-preview/authorization" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization" "github.com/Azure/go-autorest/autorest/to" "github.com/google/uuid" "github.com/pkg/errors" @@ -17,8 +17,8 @@ const ( ) // CreateRoleAssignment creates a role assignment. -func (c *AzureClient) CreateRoleAssignment(ctx context.Context, scope, roleName, principalID string) (authorization.RoleAssignment, error) { - var result authorization.RoleAssignment +func (c *AzureClient) CreateRoleAssignment(ctx context.Context, scope, roleName, principalID string) (armauthorization.RoleAssignment, error) { + var result armauthorization.RoleAssignment roleDefinitionID, err := c.GetRoleDefinitionIDByName(ctx, "", roleName) if err != nil { @@ -29,8 +29,8 @@ func (c *AzureClient) CreateRoleAssignment(ctx context.Context, scope, roleName, "principalID", principalID, "role", roleName, ) - parameters := authorization.RoleAssignmentCreateParameters{ - RoleAssignmentProperties: &authorization.RoleAssignmentProperties{ + parameters := armauthorization.RoleAssignmentCreateParameters{ + Properties: &armauthorization.RoleAssignmentProperties{ RoleDefinitionID: roleDefinitionID.ID, PrincipalID: to.StringPtr(principalID), }, @@ -40,8 +40,8 @@ func (c *AzureClient) CreateRoleAssignment(ctx context.Context, scope, roleName, // Trying to create role assignment immediately after service principal is created // results in "PrincipalNotFound" error. for i := 0; i < roleAssignmentCreateRetryCount; i++ { - if result, err = c.roleAssignmentsClient.Create(ctx, scope, uuid.New().String(), parameters); err == nil { - return result, nil + if resp, err := c.roleAssignmentsClient.Create(ctx, scope, uuid.New().String(), parameters, nil); err == nil { + return resp.RoleAssignment, nil } if IsAlreadyExists(err) { mlog.Warning("Role assignment already exists", "principalID", principalID, "role", roleName) @@ -54,7 +54,11 @@ func (c *AzureClient) CreateRoleAssignment(ctx context.Context, scope, roleName, } // DeleteRoleAssignment deletes a role assignment. -func (c *AzureClient) DeleteRoleAssignment(ctx context.Context, roleAssignmentID string) (authorization.RoleAssignment, error) { +func (c *AzureClient) DeleteRoleAssignment(ctx context.Context, roleAssignmentID string) (armauthorization.RoleAssignment, error) { mlog.Debug("Deleting role assignment", "id", roleAssignmentID) - return c.roleAssignmentsClient.DeleteByID(ctx, roleAssignmentID) + resp, err := c.roleAssignmentsClient.DeleteByID(ctx, roleAssignmentID, nil) + if err != nil { + return armauthorization.RoleAssignment{}, err + } + return resp.RoleAssignment, nil } diff --git a/pkg/cloud/roledefinitions.go b/pkg/cloud/roledefinitions.go index cbeb10084..3f7287c70 100644 --- a/pkg/cloud/roledefinitions.go +++ b/pkg/cloud/roledefinitions.go @@ -4,24 +4,31 @@ import ( "context" "fmt" - "github.com/Azure/azure-sdk-for-go/services/preview/authorization/mgmt/2018-01-01-preview/authorization" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization" "github.com/pkg/errors" "monis.app/mlog" ) // GetRoleDefinitionIDByName returns the role definition ID for the given role name. -func (c *AzureClient) GetRoleDefinitionIDByName(ctx context.Context, scope, roleName string) (authorization.RoleDefinition, error) { +func (c *AzureClient) GetRoleDefinitionIDByName(ctx context.Context, scope, roleName string) (armauthorization.RoleDefinition, error) { mlog.Debug("Get role definition ID", "name", roleName) - roleDefinitionList, err := c.roleDefinitionsClient.List(ctx, scope, getRoleNameFilter(roleName)) - if err != nil { - return authorization.RoleDefinition{}, errors.Wrap(err, "failed to list role definitions") - } - if len(roleDefinitionList.Values()) == 0 { - return authorization.RoleDefinition{}, errors.Errorf("role definition %s not found", roleName) + filter := getRoleNameFilter(roleName) + pager := c.roleDefinitionsClient.NewListPager(scope, &armauthorization.RoleDefinitionsClientListOptions{ + Filter: &filter, + }) + + for pager.More() { + nextResult, err := pager.NextPage(ctx) + if err != nil { + return armauthorization.RoleDefinition{}, errors.Wrap(err, "failed to list role definitions") + } + if len(nextResult.Value) > 0 { + return *nextResult.Value[0], nil + } } - return roleDefinitionList.Values()[0], nil + return armauthorization.RoleDefinition{}, errors.Errorf("role definition %s not found", roleName) } // getRoleNameFilter returns a filter string for the given role name. diff --git a/pkg/cmd/serviceaccount/auth/provider.go b/pkg/cmd/serviceaccount/auth/provider.go index 1d8ad1adf..173e71048 100644 --- a/pkg/cmd/serviceaccount/auth/provider.go +++ b/pkg/cmd/serviceaccount/auth/provider.go @@ -197,13 +197,13 @@ func (a *authArgs) Validate() error { return errors.Wrap(err, "failed to parse --azure-env as a valid target Azure cloud environment") } - if a.tenantID, err = cloud.GetTenantID(env.ResourceManagerEndpoint, a.subscriptionID.String()); err != nil { + if a.tenantID, err = cloud.GetTenantID(a.subscriptionID.String(), a.client); err != nil { return err } switch a.authMethod { case cliAuthMethod: - a.azureClient, err = cloud.NewAzureClientWithCLI(env, a.subscriptionID.String(), a.tenantID, a.client) + a.azureClient, err = cloud.NewAzureClientWithCLI(env, a.subscriptionID.String(), a.client) case clientSecretAuthMethod: a.azureClient, err = cloud.NewAzureClientWithClientSecret(env, a.subscriptionID.String(), a.clientID.String(), a.clientSecret, a.tenantID, a.client) case clientCertificateAuthMethod: diff --git a/pkg/cmd/serviceaccount/auth/provider_test.go b/pkg/cmd/serviceaccount/auth/provider_test.go index 1a5c2a9e9..c70f14fa0 100644 --- a/pkg/cmd/serviceaccount/auth/provider_test.go +++ b/pkg/cmd/serviceaccount/auth/provider_test.go @@ -1,6 +1,7 @@ package auth import ( + "net/http" "testing" "github.com/pkg/errors" @@ -18,6 +19,7 @@ func TestValidateAuthArgs(t *testing.T) { { name: "AuthMethodIsRequired", authArgs: authArgs{ + client: http.DefaultClient, authMethod: "", }, wantErr: errors.New("--auth-method is a required parameter"), @@ -25,6 +27,7 @@ func TestValidateAuthArgs(t *testing.T) { { name: "AlwaysExpectValidClientID", authArgs: authArgs{ + client: http.DefaultClient, rawSubscriptionID: validID, rawClientID: invalidID, clientSecret: "secret", @@ -36,6 +39,7 @@ func TestValidateAuthArgs(t *testing.T) { { name: "AlwaysExpectValidClientID", authArgs: authArgs{ + client: http.DefaultClient, rawSubscriptionID: validID, rawClientID: invalidID, clientSecret: "secret", @@ -47,6 +51,7 @@ func TestValidateAuthArgs(t *testing.T) { { name: "ClientSecretAuthExpectsClientSecret", authArgs: authArgs{ + client: http.DefaultClient, rawSubscriptionID: validID, rawClientID: validID, clientSecret: "", @@ -58,6 +63,7 @@ func TestValidateAuthArgs(t *testing.T) { { name: "ValidClientSecretAuth", authArgs: authArgs{ + client: http.DefaultClient, rawSubscriptionID: validID, rawClientID: validID, clientSecret: "secret", @@ -69,6 +75,7 @@ func TestValidateAuthArgs(t *testing.T) { { name: "ClientCertificateAuthExpectsCertificatePath", authArgs: authArgs{ + client: http.DefaultClient, rawSubscriptionID: validID, rawClientID: validID, certificatePath: "", @@ -81,6 +88,7 @@ func TestValidateAuthArgs(t *testing.T) { { name: "ClientCertificateAuthExpectsPrivateKeyPath", authArgs: authArgs{ + client: http.DefaultClient, rawSubscriptionID: validID, rawClientID: validID, certificatePath: "/a/path", @@ -93,6 +101,7 @@ func TestValidateAuthArgs(t *testing.T) { { name: "ValidClientCertificateAuth", authArgs: authArgs{ + client: http.DefaultClient, rawSubscriptionID: validID, rawClientID: validID, certificatePath: "/a/path", diff --git a/pkg/cmd/serviceaccount/phases/create/roleassignment_test.go b/pkg/cmd/serviceaccount/phases/create/roleassignment_test.go index b314ba1d8..bf32631e1 100644 --- a/pkg/cmd/serviceaccount/phases/create/roleassignment_test.go +++ b/pkg/cmd/serviceaccount/phases/create/roleassignment_test.go @@ -5,7 +5,7 @@ import ( "net/http" "testing" - "github.com/Azure/azure-sdk-for-go/services/preview/authorization/mgmt/2018-01-01-preview/authorization" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization" "github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest/to" "github.com/golang/mock/gomock" @@ -89,7 +89,7 @@ func TestRoleAssignmentRun(t *testing.T) { defer ctrl.Finish() mockAzureClient := mock_cloud.NewMockInterface(ctrl) - mockAzureClient.EXPECT().CreateRoleAssignment(context.Background(), data.azureScope, data.azureRole, data.servicePrincipalObjectID).Return(authorization.RoleAssignment{ + mockAzureClient.EXPECT().CreateRoleAssignment(context.Background(), data.azureScope, data.azureRole, data.servicePrincipalObjectID).Return(armauthorization.RoleAssignment{ ID: to.StringPtr("id"), }, nil) data.azureClient = mockAzureClient @@ -99,7 +99,7 @@ func TestRoleAssignmentRun(t *testing.T) { } // Test for scenario where role assignment already exists - mockAzureClient.EXPECT().CreateRoleAssignment(context.Background(), data.azureScope, data.azureRole, data.servicePrincipalObjectID).Return(authorization.RoleAssignment{ + mockAzureClient.EXPECT().CreateRoleAssignment(context.Background(), data.azureScope, data.azureRole, data.servicePrincipalObjectID).Return(armauthorization.RoleAssignment{ ID: to.StringPtr("id"), }, autorest.DetailedError{StatusCode: http.StatusConflict}) if err := phase.Run(context.Background(), data); err != nil { diff --git a/pkg/cmd/serviceaccount/phases/delete/roleassignment_test.go b/pkg/cmd/serviceaccount/phases/delete/roleassignment_test.go index cfd709e7d..2e2e4b84c 100644 --- a/pkg/cmd/serviceaccount/phases/delete/roleassignment_test.go +++ b/pkg/cmd/serviceaccount/phases/delete/roleassignment_test.go @@ -5,7 +5,7 @@ import ( "net/http" "testing" - authorization "github.com/Azure/azure-sdk-for-go/services/preview/authorization/mgmt/2018-01-01-preview/authorization" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization" "github.com/Azure/go-autorest/autorest" "github.com/golang/mock/gomock" "github.com/pkg/errors" @@ -62,7 +62,7 @@ func TestRoleAssignmentRun(t *testing.T) { defer ctrl.Finish() mockAzureClient := mock_cloud.NewMockInterface(ctrl) - mockAzureClient.EXPECT().DeleteRoleAssignment(gomock.Any(), data.roleAssignmentID).Return(authorization.RoleAssignment{}, nil) + mockAzureClient.EXPECT().DeleteRoleAssignment(gomock.Any(), data.roleAssignmentID).Return(armauthorization.RoleAssignment{}, nil) data.azureClient = mockAzureClient if err := phase.Run(context.Background(), data); err != nil { @@ -70,13 +70,13 @@ func TestRoleAssignmentRun(t *testing.T) { } // Test for scenario where it failed to delete role assignment - mockAzureClient.EXPECT().DeleteRoleAssignment(gomock.Any(), data.roleAssignmentID).Return(authorization.RoleAssignment{}, errors.New("random error")) + mockAzureClient.EXPECT().DeleteRoleAssignment(gomock.Any(), data.roleAssignmentID).Return(armauthorization.RoleAssignment{}, errors.New("random error")) if err := phase.Run(context.Background(), data); err == nil { t.Errorf("expected error but got nil") } // Test for scenario where role assignment is not found - mockAzureClient.EXPECT().DeleteRoleAssignment(gomock.Any(), data.roleAssignmentID).Return(authorization.RoleAssignment{}, autorest.DetailedError{StatusCode: http.StatusNoContent}) + mockAzureClient.EXPECT().DeleteRoleAssignment(gomock.Any(), data.roleAssignmentID).Return(armauthorization.RoleAssignment{}, autorest.DetailedError{StatusCode: http.StatusNoContent}) if err := phase.Run(context.Background(), data); err != nil { t.Errorf("expected no error but got: %s", err.Error()) }