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  }