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  }