github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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 "net/http" 8 "sync" 9 10 "github.com/Azure/azure-sdk-for-go/arm/resources/subscriptions" 11 "github.com/Azure/go-autorest/autorest" 12 "github.com/Azure/go-autorest/autorest/azure" 13 "github.com/juju/errors" 14 15 "github.com/juju/juju/environs" 16 "github.com/juju/juju/provider/azure/internal/azureauth" 17 ) 18 19 // cloudSpecAuth is an implementation of autorest.Authorizer. 20 type cloudSpecAuth struct { 21 cloud environs.CloudSpec 22 sender autorest.Sender 23 mu sync.Mutex 24 token *azure.ServicePrincipalToken 25 } 26 27 // WithAuthorization is part of the autorest.Authorizer interface. 28 func (c *cloudSpecAuth) WithAuthorization() autorest.PrepareDecorator { 29 return func(p autorest.Preparer) autorest.Preparer { 30 return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) { 31 r, err := p.Prepare(r) 32 if err != nil { 33 return nil, err 34 } 35 token, err := c.getToken() 36 if err != nil { 37 return nil, err 38 } 39 return autorest.CreatePreparer(token.WithAuthorization()).Prepare(r) 40 }) 41 } 42 } 43 44 func (c *cloudSpecAuth) refresh() error { 45 token, err := c.getToken() 46 if err != nil { 47 return err 48 } 49 return token.Refresh() 50 } 51 52 func (c *cloudSpecAuth) getToken() (*azure.ServicePrincipalToken, error) { 53 c.mu.Lock() 54 defer c.mu.Unlock() 55 if c.token != nil { 56 return c.token, nil 57 } 58 token, err := AuthToken(c.cloud, c.sender) 59 if err != nil { 60 return nil, errors.Trace(err) 61 } 62 c.token = token 63 return c.token, nil 64 } 65 66 // AuthToken returns a service principal token, suitable for authorizing 67 // Resource Manager API requests, based on the supplied CloudSpec. 68 func AuthToken(cloud environs.CloudSpec, sender autorest.Sender) (*azure.ServicePrincipalToken, error) { 69 if authType := cloud.Credential.AuthType(); authType != clientCredentialsAuthType { 70 // We currently only support a single auth-type for 71 // non-interactive authentication. Interactive auth 72 // is used only to generate a service-principal. 73 return nil, errors.NotSupportedf("auth-type %q", authType) 74 } 75 76 credAttrs := cloud.Credential.Attributes() 77 subscriptionId := credAttrs[credAttrSubscriptionId] 78 appId := credAttrs[credAttrAppId] 79 appPassword := credAttrs[credAttrAppPassword] 80 client := subscriptions.Client{subscriptions.NewWithBaseURI(cloud.Endpoint)} 81 client.Sender = sender 82 oauthConfig, _, err := azureauth.OAuthConfig(client, cloud.Endpoint, subscriptionId) 83 if err != nil { 84 return nil, errors.Trace(err) 85 } 86 87 resource := azureauth.TokenResource(cloud.Endpoint) 88 token, err := azure.NewServicePrincipalToken( 89 *oauthConfig, 90 appId, 91 appPassword, 92 resource, 93 ) 94 if err != nil { 95 return nil, errors.Annotate(err, "constructing service principal token") 96 } 97 if sender != nil { 98 token.SetSender(sender) 99 } 100 return token, nil 101 }