github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/provider/azure/auth.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package azure 5 6 import ( 7 "context" 8 "net/http" 9 "sync" 10 11 "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2016-06-01/subscriptions" 12 "github.com/Azure/go-autorest/autorest" 13 "github.com/Azure/go-autorest/autorest/adal" 14 "github.com/juju/errors" 15 16 "github.com/juju/juju/environs" 17 "github.com/juju/juju/provider/azure/internal/azureauth" 18 "github.com/juju/juju/provider/azure/internal/useragent" 19 ) 20 21 // cloudSpecAuth is an implementation of autorest.Authorizer. 22 type cloudSpecAuth struct { 23 cloud environs.CloudSpec 24 sender autorest.Sender 25 mu sync.Mutex 26 token *adal.ServicePrincipalToken 27 } 28 29 // WithAuthorization is part of the autorest.Authorizer interface. 30 func (c *cloudSpecAuth) WithAuthorization() autorest.PrepareDecorator { 31 return func(p autorest.Preparer) autorest.Preparer { 32 return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) { 33 r, err := p.Prepare(r) 34 if err != nil { 35 return nil, err 36 } 37 token, err := c.getToken() 38 if err != nil { 39 return nil, err 40 } 41 authorizer := autorest.NewBearerAuthorizer(token) 42 return autorest.CreatePreparer(authorizer.WithAuthorization()).Prepare(r) 43 }) 44 } 45 } 46 47 func (c *cloudSpecAuth) refresh() error { 48 token, err := c.getToken() 49 if err != nil { 50 return err 51 } 52 return token.Refresh() 53 } 54 55 func (c *cloudSpecAuth) getToken() (*adal.ServicePrincipalToken, error) { 56 c.mu.Lock() 57 defer c.mu.Unlock() 58 if c.token != nil { 59 return c.token, nil 60 } 61 token, err := AuthToken(c.cloud, c.sender) 62 if err != nil { 63 return nil, errors.Trace(err) 64 } 65 c.token = token 66 return c.token, nil 67 } 68 69 // AuthToken returns a service principal token, suitable for authorizing 70 // Resource Manager API requests, based on the supplied CloudSpec. 71 func AuthToken(cloud environs.CloudSpec, sender autorest.Sender) (*adal.ServicePrincipalToken, error) { 72 if authType := cloud.Credential.AuthType(); authType != clientCredentialsAuthType { 73 // We currently only support a single auth-type for 74 // non-interactive authentication. Interactive auth 75 // is used only to generate a service-principal. 76 return nil, errors.NotSupportedf("auth-type %q", authType) 77 } 78 79 resourceId, err := azureauth.ResourceManagerResourceId(cloud.StorageEndpoint) 80 if err != nil { 81 return nil, errors.Trace(err) 82 } 83 84 credAttrs := cloud.Credential.Attributes() 85 subscriptionId := credAttrs[credAttrSubscriptionId] 86 appId := credAttrs[credAttrAppId] 87 appPassword := credAttrs[credAttrAppPassword] 88 client := subscriptions.Client{subscriptions.NewWithBaseURI(cloud.Endpoint)} 89 useragent.UpdateClient(&client.Client) 90 client.Sender = sender 91 sdkCtx := context.Background() 92 oauthConfig, _, err := azureauth.OAuthConfig(sdkCtx, client, cloud.Endpoint, subscriptionId) 93 if err != nil { 94 return nil, errors.Trace(err) 95 } 96 97 token, err := adal.NewServicePrincipalToken( 98 *oauthConfig, 99 appId, 100 appPassword, 101 resourceId, 102 ) 103 if err != nil { 104 return nil, errors.Annotate(err, "constructing service principal token") 105 } 106 tokenClient := autorest.NewClientWithUserAgent("") 107 useragent.UpdateClient(&tokenClient) 108 tokenClient.Sender = sender 109 token.SetSender(&tokenClient) 110 return token, nil 111 }