...

Source file src/github.com/Azure/go-autorest/autorest/azure/auth/auth.go

Documentation: github.com/Azure/go-autorest/autorest/azure/auth

     1  package auth
     2  
     3  // Copyright 2017 Microsoft Corporation
     4  //
     5  //  Licensed under the Apache License, Version 2.0 (the "License");
     6  //  you may not use this file except in compliance with the License.
     7  //  You may obtain a copy of the License at
     8  //
     9  //      http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  //  Unless required by applicable law or agreed to in writing, software
    12  //  distributed under the License is distributed on an "AS IS" BASIS,
    13  //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  //  See the License for the specific language governing permissions and
    15  //  limitations under the License.
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"encoding/binary"
    21  	"encoding/json"
    22  	"errors"
    23  	"fmt"
    24  	"io/ioutil"
    25  	"log"
    26  	"os"
    27  	"strings"
    28  	"unicode/utf16"
    29  
    30  	"github.com/Azure/go-autorest/autorest"
    31  	"github.com/Azure/go-autorest/autorest/adal"
    32  	"github.com/Azure/go-autorest/autorest/azure"
    33  	"github.com/Azure/go-autorest/autorest/azure/cli"
    34  	"github.com/Azure/go-autorest/logger"
    35  	"github.com/dimchansky/utfbom"
    36  )
    37  
    38  // The possible keys in the Values map.
    39  const (
    40  	SubscriptionID          = "AZURE_SUBSCRIPTION_ID"
    41  	TenantID                = "AZURE_TENANT_ID"
    42  	AuxiliaryTenantIDs      = "AZURE_AUXILIARY_TENANT_IDS"
    43  	ClientID                = "AZURE_CLIENT_ID"
    44  	ClientSecret            = "AZURE_CLIENT_SECRET"
    45  	CertificatePath         = "AZURE_CERTIFICATE_PATH"
    46  	CertificatePassword     = "AZURE_CERTIFICATE_PASSWORD"
    47  	Username                = "AZURE_USERNAME"
    48  	Password                = "AZURE_PASSWORD"
    49  	EnvironmentName         = "AZURE_ENVIRONMENT"
    50  	Resource                = "AZURE_AD_RESOURCE"
    51  	ActiveDirectoryEndpoint = "ActiveDirectoryEndpoint"
    52  	ResourceManagerEndpoint = "ResourceManagerEndpoint"
    53  	GraphResourceID         = "GraphResourceID"
    54  	SQLManagementEndpoint   = "SQLManagementEndpoint"
    55  	GalleryEndpoint         = "GalleryEndpoint"
    56  	ManagementEndpoint      = "ManagementEndpoint"
    57  )
    58  
    59  // NewAuthorizerFromEnvironment creates an Authorizer configured from environment variables in the order:
    60  // 1. Client credentials
    61  // 2. Client certificate
    62  // 3. Username password
    63  // 4. MSI
    64  func NewAuthorizerFromEnvironment() (autorest.Authorizer, error) {
    65  	logger.Instance.Writeln(logger.LogInfo, "NewAuthorizerFromEnvironment() determining authentication mechanism")
    66  	settings, err := GetSettingsFromEnvironment()
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  	return settings.GetAuthorizer()
    71  }
    72  
    73  // NewAuthorizerFromEnvironmentWithResource creates an Authorizer configured from environment variables in the order:
    74  // 1. Client credentials
    75  // 2. Client certificate
    76  // 3. Username password
    77  // 4. MSI
    78  func NewAuthorizerFromEnvironmentWithResource(resource string) (autorest.Authorizer, error) {
    79  	logger.Instance.Writeln(logger.LogInfo, "NewAuthorizerFromEnvironmentWithResource() determining authentication mechanism")
    80  	settings, err := GetSettingsFromEnvironment()
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  	settings.Values[Resource] = resource
    85  	return settings.GetAuthorizer()
    86  }
    87  
    88  // EnvironmentSettings contains the available authentication settings.
    89  type EnvironmentSettings struct {
    90  	Values      map[string]string
    91  	Environment azure.Environment
    92  }
    93  
    94  // GetSettingsFromEnvironment returns the available authentication settings from the environment.
    95  func GetSettingsFromEnvironment() (s EnvironmentSettings, err error) {
    96  	s = EnvironmentSettings{
    97  		Values: map[string]string{},
    98  	}
    99  	s.setValue(SubscriptionID)
   100  	s.setValue(TenantID)
   101  	s.setValue(AuxiliaryTenantIDs)
   102  	s.setValue(ClientID)
   103  	s.setValue(ClientSecret)
   104  	s.setValue(CertificatePath)
   105  	s.setValue(CertificatePassword)
   106  	s.setValue(Username)
   107  	s.setValue(Password)
   108  	s.setValue(EnvironmentName)
   109  	s.setValue(Resource)
   110  	if v := s.Values[EnvironmentName]; v == "" {
   111  		s.Environment = azure.PublicCloud
   112  	} else {
   113  		s.Environment, err = azure.EnvironmentFromName(v)
   114  	}
   115  	if s.Values[Resource] == "" {
   116  		s.Values[Resource] = s.Environment.ResourceManagerEndpoint
   117  	}
   118  	return
   119  }
   120  
   121  // GetSubscriptionID returns the available subscription ID or an empty string.
   122  func (settings EnvironmentSettings) GetSubscriptionID() string {
   123  	return settings.Values[SubscriptionID]
   124  }
   125  
   126  // adds the specified environment variable value to the Values map if it exists
   127  func (settings EnvironmentSettings) setValue(key string) {
   128  	if v := os.Getenv(key); v != "" {
   129  		logger.Instance.Writef(logger.LogInfo, "GetSettingsFromEnvironment() found environment var %s\n", key)
   130  		settings.Values[key] = v
   131  	}
   132  }
   133  
   134  // helper to return client and tenant IDs
   135  func (settings EnvironmentSettings) getClientAndTenant() (string, string) {
   136  	clientID := settings.Values[ClientID]
   137  	tenantID := settings.Values[TenantID]
   138  	return clientID, tenantID
   139  }
   140  
   141  // GetClientCredentials creates a config object from the available client credentials.
   142  // An error is returned if no client credentials are available.
   143  func (settings EnvironmentSettings) GetClientCredentials() (ClientCredentialsConfig, error) {
   144  	secret := settings.Values[ClientSecret]
   145  	if secret == "" {
   146  		logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetClientCredentials() missing client secret")
   147  		return ClientCredentialsConfig{}, errors.New("missing client secret")
   148  	}
   149  	clientID, tenantID := settings.getClientAndTenant()
   150  	config := NewClientCredentialsConfig(clientID, secret, tenantID)
   151  	config.AADEndpoint = settings.Environment.ActiveDirectoryEndpoint
   152  	config.Resource = settings.Values[Resource]
   153  	if auxTenants, ok := settings.Values[AuxiliaryTenantIDs]; ok {
   154  		config.AuxTenants = strings.Split(auxTenants, ";")
   155  		for i := range config.AuxTenants {
   156  			config.AuxTenants[i] = strings.TrimSpace(config.AuxTenants[i])
   157  		}
   158  	}
   159  	return config, nil
   160  }
   161  
   162  // GetClientCertificate creates a config object from the available certificate credentials.
   163  // An error is returned if no certificate credentials are available.
   164  func (settings EnvironmentSettings) GetClientCertificate() (ClientCertificateConfig, error) {
   165  	certPath := settings.Values[CertificatePath]
   166  	if certPath == "" {
   167  		logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetClientCertificate() missing certificate path")
   168  		return ClientCertificateConfig{}, errors.New("missing certificate path")
   169  	}
   170  	certPwd := settings.Values[CertificatePassword]
   171  	clientID, tenantID := settings.getClientAndTenant()
   172  	config := NewClientCertificateConfig(certPath, certPwd, clientID, tenantID)
   173  	config.AADEndpoint = settings.Environment.ActiveDirectoryEndpoint
   174  	config.Resource = settings.Values[Resource]
   175  	return config, nil
   176  }
   177  
   178  // GetUsernamePassword creates a config object from the available username/password credentials.
   179  // An error is returned if no username/password credentials are available.
   180  func (settings EnvironmentSettings) GetUsernamePassword() (UsernamePasswordConfig, error) {
   181  	username := settings.Values[Username]
   182  	password := settings.Values[Password]
   183  	if username == "" || password == "" {
   184  		logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetUsernamePassword() missing username and/or password")
   185  		return UsernamePasswordConfig{}, errors.New("missing username/password")
   186  	}
   187  	clientID, tenantID := settings.getClientAndTenant()
   188  	config := NewUsernamePasswordConfig(username, password, clientID, tenantID)
   189  	config.AADEndpoint = settings.Environment.ActiveDirectoryEndpoint
   190  	config.Resource = settings.Values[Resource]
   191  	return config, nil
   192  }
   193  
   194  // GetMSI creates a MSI config object from the available client ID.
   195  func (settings EnvironmentSettings) GetMSI() MSIConfig {
   196  	config := NewMSIConfig()
   197  	config.Resource = settings.Values[Resource]
   198  	config.ClientID = settings.Values[ClientID]
   199  	return config
   200  }
   201  
   202  // GetDeviceFlow creates a device-flow config object from the available client and tenant IDs.
   203  func (settings EnvironmentSettings) GetDeviceFlow() DeviceFlowConfig {
   204  	clientID, tenantID := settings.getClientAndTenant()
   205  	config := NewDeviceFlowConfig(clientID, tenantID)
   206  	config.AADEndpoint = settings.Environment.ActiveDirectoryEndpoint
   207  	config.Resource = settings.Values[Resource]
   208  	return config
   209  }
   210  
   211  // GetAuthorizer creates an Authorizer configured from environment variables in the order:
   212  // 1. Client credentials
   213  // 2. Client certificate
   214  // 3. Username password
   215  // 4. MSI
   216  func (settings EnvironmentSettings) GetAuthorizer() (autorest.Authorizer, error) {
   217  	//1.Client Credentials
   218  	if c, e := settings.GetClientCredentials(); e == nil {
   219  		logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetAuthorizer() using client secret credentials")
   220  		return c.Authorizer()
   221  	}
   222  
   223  	//2. Client Certificate
   224  	if c, e := settings.GetClientCertificate(); e == nil {
   225  		logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetAuthorizer() using client certificate credentials")
   226  		return c.Authorizer()
   227  	}
   228  
   229  	//3. Username Password
   230  	if c, e := settings.GetUsernamePassword(); e == nil {
   231  		logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetAuthorizer() using user name/password credentials")
   232  		return c.Authorizer()
   233  	}
   234  
   235  	// 4. MSI
   236  	if !adal.MSIAvailable(context.Background(), nil) {
   237  		return nil, errors.New("MSI not available")
   238  	}
   239  	logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetAuthorizer() using MSI authentication")
   240  	return settings.GetMSI().Authorizer()
   241  }
   242  
   243  // NewAuthorizerFromFile creates an Authorizer configured from a configuration file in the following order.
   244  // 1. Client credentials
   245  // 2. Client certificate
   246  // The path to the configuration file must be specified in the AZURE_AUTH_LOCATION environment variable.
   247  // resourceBaseURI - used to determine the resource type
   248  func NewAuthorizerFromFile(resourceBaseURI string) (autorest.Authorizer, error) {
   249  	settings, err := GetSettingsFromFile()
   250  	if err != nil {
   251  		return nil, err
   252  	}
   253  	return settings.GetAuthorizer(resourceBaseURI)
   254  }
   255  
   256  // GetAuthorizer create an Authorizer in the following order.
   257  // 1. Client credentials
   258  // 2. Client certificate
   259  // resourceBaseURI - used to determine the resource type
   260  func (settings FileSettings) GetAuthorizer(resourceBaseURI string) (autorest.Authorizer, error) {
   261  	if resourceBaseURI == "" {
   262  		resourceBaseURI = azure.PublicCloud.ServiceManagementEndpoint
   263  	}
   264  	if a, err := settings.ClientCredentialsAuthorizer(resourceBaseURI); err == nil {
   265  		return a, err
   266  	}
   267  	if a, err := settings.ClientCertificateAuthorizer(resourceBaseURI); err == nil {
   268  		return a, err
   269  	}
   270  	return nil, errors.New("auth file missing client and certificate credentials")
   271  }
   272  
   273  // NewAuthorizerFromFileWithResource creates an Authorizer configured from a configuration file in the following order.
   274  // 1. Client credentials
   275  // 2. Client certificate
   276  // The path to the configuration file must be specified in the AZURE_AUTH_LOCATION environment variable.
   277  func NewAuthorizerFromFileWithResource(resource string) (autorest.Authorizer, error) {
   278  	s, err := GetSettingsFromFile()
   279  	if err != nil {
   280  		return nil, err
   281  	}
   282  	if a, err := s.ClientCredentialsAuthorizerWithResource(resource); err == nil {
   283  		return a, err
   284  	}
   285  	if a, err := s.ClientCertificateAuthorizerWithResource(resource); err == nil {
   286  		return a, err
   287  	}
   288  	return nil, errors.New("auth file missing client and certificate credentials")
   289  }
   290  
   291  // NewAuthorizerFromCLI creates an Authorizer configured from Azure CLI 2.0 for local development scenarios.
   292  func NewAuthorizerFromCLI() (autorest.Authorizer, error) {
   293  	settings, err := GetSettingsFromEnvironment()
   294  	if err != nil {
   295  		return nil, err
   296  	}
   297  
   298  	if settings.Values[Resource] == "" {
   299  		settings.Values[Resource] = settings.Environment.ResourceManagerEndpoint
   300  	}
   301  
   302  	return NewAuthorizerFromCLIWithResource(settings.Values[Resource])
   303  }
   304  
   305  // NewAuthorizerFromCLIWithResource creates an Authorizer configured from Azure CLI 2.0 for local development scenarios.
   306  func NewAuthorizerFromCLIWithResource(resource string) (autorest.Authorizer, error) {
   307  	token, err := cli.GetTokenFromCLI(resource)
   308  	if err != nil {
   309  		return nil, err
   310  	}
   311  
   312  	adalToken, err := token.ToADALToken()
   313  	if err != nil {
   314  		return nil, err
   315  	}
   316  
   317  	return autorest.NewBearerAuthorizer(&adalToken), nil
   318  }
   319  
   320  // GetSettingsFromFile returns the available authentication settings from an Azure CLI authentication file.
   321  func GetSettingsFromFile() (FileSettings, error) {
   322  	s := FileSettings{}
   323  	fileLocation := os.Getenv("AZURE_AUTH_LOCATION")
   324  	if fileLocation == "" {
   325  		return s, errors.New("environment variable AZURE_AUTH_LOCATION is not set")
   326  	}
   327  
   328  	contents, err := ioutil.ReadFile(fileLocation)
   329  	if err != nil {
   330  		return s, err
   331  	}
   332  
   333  	// Auth file might be encoded
   334  	decoded, err := decode(contents)
   335  	if err != nil {
   336  		return s, err
   337  	}
   338  
   339  	authFile := map[string]interface{}{}
   340  	err = json.Unmarshal(decoded, &authFile)
   341  	if err != nil {
   342  		return s, err
   343  	}
   344  
   345  	s.Values = map[string]string{}
   346  	s.setKeyValue(ClientID, authFile["clientId"])
   347  	s.setKeyValue(ClientSecret, authFile["clientSecret"])
   348  	s.setKeyValue(CertificatePath, authFile["clientCertificate"])
   349  	s.setKeyValue(CertificatePassword, authFile["clientCertificatePassword"])
   350  	s.setKeyValue(SubscriptionID, authFile["subscriptionId"])
   351  	s.setKeyValue(TenantID, authFile["tenantId"])
   352  	s.setKeyValue(ActiveDirectoryEndpoint, authFile["activeDirectoryEndpointUrl"])
   353  	s.setKeyValue(ResourceManagerEndpoint, authFile["resourceManagerEndpointUrl"])
   354  	s.setKeyValue(GraphResourceID, authFile["activeDirectoryGraphResourceId"])
   355  	s.setKeyValue(SQLManagementEndpoint, authFile["sqlManagementEndpointUrl"])
   356  	s.setKeyValue(GalleryEndpoint, authFile["galleryEndpointUrl"])
   357  	s.setKeyValue(ManagementEndpoint, authFile["managementEndpointUrl"])
   358  	return s, nil
   359  }
   360  
   361  // FileSettings contains the available authentication settings.
   362  type FileSettings struct {
   363  	Values map[string]string
   364  }
   365  
   366  // GetSubscriptionID returns the available subscription ID or an empty string.
   367  func (settings FileSettings) GetSubscriptionID() string {
   368  	return settings.Values[SubscriptionID]
   369  }
   370  
   371  // adds the specified value to the Values map if it isn't nil
   372  func (settings FileSettings) setKeyValue(key string, val interface{}) {
   373  	if val != nil {
   374  		settings.Values[key] = val.(string)
   375  	}
   376  }
   377  
   378  // returns the specified AAD endpoint or the public cloud endpoint if unspecified
   379  func (settings FileSettings) getAADEndpoint() string {
   380  	if v, ok := settings.Values[ActiveDirectoryEndpoint]; ok {
   381  		return v
   382  	}
   383  	return azure.PublicCloud.ActiveDirectoryEndpoint
   384  }
   385  
   386  // ServicePrincipalTokenFromClientCredentials creates a ServicePrincipalToken from the available client credentials.
   387  func (settings FileSettings) ServicePrincipalTokenFromClientCredentials(baseURI string) (*adal.ServicePrincipalToken, error) {
   388  	resource, err := settings.getResourceForToken(baseURI)
   389  	if err != nil {
   390  		return nil, err
   391  	}
   392  	return settings.ServicePrincipalTokenFromClientCredentialsWithResource(resource)
   393  }
   394  
   395  // ClientCredentialsAuthorizer creates an authorizer from the available client credentials.
   396  func (settings FileSettings) ClientCredentialsAuthorizer(baseURI string) (autorest.Authorizer, error) {
   397  	resource, err := settings.getResourceForToken(baseURI)
   398  	if err != nil {
   399  		return nil, err
   400  	}
   401  	return settings.ClientCredentialsAuthorizerWithResource(resource)
   402  }
   403  
   404  // ServicePrincipalTokenFromClientCredentialsWithResource creates a ServicePrincipalToken
   405  // from the available client credentials and the specified resource.
   406  func (settings FileSettings) ServicePrincipalTokenFromClientCredentialsWithResource(resource string) (*adal.ServicePrincipalToken, error) {
   407  	if _, ok := settings.Values[ClientSecret]; !ok {
   408  		return nil, errors.New("missing client secret")
   409  	}
   410  	config, err := adal.NewOAuthConfig(settings.getAADEndpoint(), settings.Values[TenantID])
   411  	if err != nil {
   412  		return nil, err
   413  	}
   414  	return adal.NewServicePrincipalToken(*config, settings.Values[ClientID], settings.Values[ClientSecret], resource)
   415  }
   416  
   417  func (settings FileSettings) clientCertificateConfigWithResource(resource string) (ClientCertificateConfig, error) {
   418  	if _, ok := settings.Values[CertificatePath]; !ok {
   419  		return ClientCertificateConfig{}, errors.New("missing certificate path")
   420  	}
   421  	cfg := NewClientCertificateConfig(settings.Values[CertificatePath], settings.Values[CertificatePassword], settings.Values[ClientID], settings.Values[TenantID])
   422  	cfg.AADEndpoint = settings.getAADEndpoint()
   423  	cfg.Resource = resource
   424  	return cfg, nil
   425  }
   426  
   427  // ClientCredentialsAuthorizerWithResource creates an authorizer from the available client credentials and the specified resource.
   428  func (settings FileSettings) ClientCredentialsAuthorizerWithResource(resource string) (autorest.Authorizer, error) {
   429  	spToken, err := settings.ServicePrincipalTokenFromClientCredentialsWithResource(resource)
   430  	if err != nil {
   431  		return nil, err
   432  	}
   433  	return autorest.NewBearerAuthorizer(spToken), nil
   434  }
   435  
   436  // ServicePrincipalTokenFromClientCertificate creates a ServicePrincipalToken from the available certificate credentials.
   437  func (settings FileSettings) ServicePrincipalTokenFromClientCertificate(baseURI string) (*adal.ServicePrincipalToken, error) {
   438  	resource, err := settings.getResourceForToken(baseURI)
   439  	if err != nil {
   440  		return nil, err
   441  	}
   442  	return settings.ServicePrincipalTokenFromClientCertificateWithResource(resource)
   443  }
   444  
   445  // ClientCertificateAuthorizer creates an authorizer from the available certificate credentials.
   446  func (settings FileSettings) ClientCertificateAuthorizer(baseURI string) (autorest.Authorizer, error) {
   447  	resource, err := settings.getResourceForToken(baseURI)
   448  	if err != nil {
   449  		return nil, err
   450  	}
   451  	return settings.ClientCertificateAuthorizerWithResource(resource)
   452  }
   453  
   454  // ServicePrincipalTokenFromClientCertificateWithResource creates a ServicePrincipalToken from the available certificate credentials.
   455  func (settings FileSettings) ServicePrincipalTokenFromClientCertificateWithResource(resource string) (*adal.ServicePrincipalToken, error) {
   456  	cfg, err := settings.clientCertificateConfigWithResource(resource)
   457  	if err != nil {
   458  		return nil, err
   459  	}
   460  	return cfg.ServicePrincipalToken()
   461  }
   462  
   463  // ClientCertificateAuthorizerWithResource creates an authorizer from the available certificate credentials and the specified resource.
   464  func (settings FileSettings) ClientCertificateAuthorizerWithResource(resource string) (autorest.Authorizer, error) {
   465  	cfg, err := settings.clientCertificateConfigWithResource(resource)
   466  	if err != nil {
   467  		return nil, err
   468  	}
   469  	return cfg.Authorizer()
   470  }
   471  
   472  func decode(b []byte) ([]byte, error) {
   473  	reader, enc := utfbom.Skip(bytes.NewReader(b))
   474  
   475  	switch enc {
   476  	case utfbom.UTF16LittleEndian:
   477  		u16 := make([]uint16, (len(b)/2)-1)
   478  		err := binary.Read(reader, binary.LittleEndian, &u16)
   479  		if err != nil {
   480  			return nil, err
   481  		}
   482  		return []byte(string(utf16.Decode(u16))), nil
   483  	case utfbom.UTF16BigEndian:
   484  		u16 := make([]uint16, (len(b)/2)-1)
   485  		err := binary.Read(reader, binary.BigEndian, &u16)
   486  		if err != nil {
   487  			return nil, err
   488  		}
   489  		return []byte(string(utf16.Decode(u16))), nil
   490  	}
   491  	return ioutil.ReadAll(reader)
   492  }
   493  
   494  func (settings FileSettings) getResourceForToken(baseURI string) (string, error) {
   495  	// Compare default base URI from the SDK to the endpoints from the public cloud
   496  	// Base URI and token resource are the same string. This func finds the authentication
   497  	// file field that matches the SDK base URI. The SDK defines the public cloud
   498  	// endpoint as its default base URI
   499  	if !strings.HasSuffix(baseURI, "/") {
   500  		baseURI += "/"
   501  	}
   502  	switch baseURI {
   503  	case azure.PublicCloud.ServiceManagementEndpoint:
   504  		return settings.Values[ManagementEndpoint], nil
   505  	case azure.PublicCloud.ResourceManagerEndpoint:
   506  		return settings.Values[ResourceManagerEndpoint], nil
   507  	case azure.PublicCloud.ActiveDirectoryEndpoint:
   508  		return settings.Values[ActiveDirectoryEndpoint], nil
   509  	case azure.PublicCloud.GalleryEndpoint:
   510  		return settings.Values[GalleryEndpoint], nil
   511  	case azure.PublicCloud.GraphEndpoint:
   512  		return settings.Values[GraphResourceID], nil
   513  	}
   514  	return "", fmt.Errorf("auth: base URI not found in endpoints")
   515  }
   516  
   517  // NewClientCredentialsConfig creates an AuthorizerConfig object configured to obtain an Authorizer through Client Credentials.
   518  // Defaults to Public Cloud and Resource Manager Endpoint.
   519  func NewClientCredentialsConfig(clientID string, clientSecret string, tenantID string) ClientCredentialsConfig {
   520  	return ClientCredentialsConfig{
   521  		ClientID:     clientID,
   522  		ClientSecret: clientSecret,
   523  		TenantID:     tenantID,
   524  		Resource:     azure.PublicCloud.ResourceManagerEndpoint,
   525  		AADEndpoint:  azure.PublicCloud.ActiveDirectoryEndpoint,
   526  	}
   527  }
   528  
   529  // NewClientCertificateConfig creates a ClientCertificateConfig object configured to obtain an Authorizer through client certificate.
   530  // Defaults to Public Cloud and Resource Manager Endpoint.
   531  func NewClientCertificateConfig(certificatePath string, certificatePassword string, clientID string, tenantID string) ClientCertificateConfig {
   532  	return ClientCertificateConfig{
   533  		CertificatePath:     certificatePath,
   534  		CertificatePassword: certificatePassword,
   535  		ClientID:            clientID,
   536  		TenantID:            tenantID,
   537  		Resource:            azure.PublicCloud.ResourceManagerEndpoint,
   538  		AADEndpoint:         azure.PublicCloud.ActiveDirectoryEndpoint,
   539  	}
   540  }
   541  
   542  // NewUsernamePasswordConfig creates an UsernamePasswordConfig object configured to obtain an Authorizer through username and password.
   543  // Defaults to Public Cloud and Resource Manager Endpoint.
   544  func NewUsernamePasswordConfig(username string, password string, clientID string, tenantID string) UsernamePasswordConfig {
   545  	return UsernamePasswordConfig{
   546  		Username:    username,
   547  		Password:    password,
   548  		ClientID:    clientID,
   549  		TenantID:    tenantID,
   550  		Resource:    azure.PublicCloud.ResourceManagerEndpoint,
   551  		AADEndpoint: azure.PublicCloud.ActiveDirectoryEndpoint,
   552  	}
   553  }
   554  
   555  // NewMSIConfig creates an MSIConfig object configured to obtain an Authorizer through MSI.
   556  func NewMSIConfig() MSIConfig {
   557  	return MSIConfig{
   558  		Resource: azure.PublicCloud.ResourceManagerEndpoint,
   559  	}
   560  }
   561  
   562  // NewDeviceFlowConfig creates a DeviceFlowConfig object configured to obtain an Authorizer through device flow.
   563  // Defaults to Public Cloud and Resource Manager Endpoint.
   564  func NewDeviceFlowConfig(clientID string, tenantID string) DeviceFlowConfig {
   565  	return DeviceFlowConfig{
   566  		ClientID:    clientID,
   567  		TenantID:    tenantID,
   568  		Resource:    azure.PublicCloud.ResourceManagerEndpoint,
   569  		AADEndpoint: azure.PublicCloud.ActiveDirectoryEndpoint,
   570  	}
   571  }
   572  
   573  // AuthorizerConfig provides an authorizer from the configuration provided.
   574  type AuthorizerConfig interface {
   575  	Authorizer() (autorest.Authorizer, error)
   576  }
   577  
   578  // ClientCredentialsConfig provides the options to get a bearer authorizer from client credentials.
   579  type ClientCredentialsConfig struct {
   580  	ClientID     string
   581  	ClientSecret string
   582  	TenantID     string
   583  	AuxTenants   []string
   584  	AADEndpoint  string
   585  	Resource     string
   586  }
   587  
   588  // ServicePrincipalToken creates a ServicePrincipalToken from client credentials.
   589  func (ccc ClientCredentialsConfig) ServicePrincipalToken() (*adal.ServicePrincipalToken, error) {
   590  	oauthConfig, err := adal.NewOAuthConfig(ccc.AADEndpoint, ccc.TenantID)
   591  	if err != nil {
   592  		return nil, err
   593  	}
   594  	return adal.NewServicePrincipalToken(*oauthConfig, ccc.ClientID, ccc.ClientSecret, ccc.Resource)
   595  }
   596  
   597  // MultiTenantServicePrincipalToken creates a MultiTenantServicePrincipalToken from client credentials.
   598  func (ccc ClientCredentialsConfig) MultiTenantServicePrincipalToken() (*adal.MultiTenantServicePrincipalToken, error) {
   599  	oauthConfig, err := adal.NewMultiTenantOAuthConfig(ccc.AADEndpoint, ccc.TenantID, ccc.AuxTenants, adal.OAuthOptions{})
   600  	if err != nil {
   601  		return nil, err
   602  	}
   603  	return adal.NewMultiTenantServicePrincipalToken(oauthConfig, ccc.ClientID, ccc.ClientSecret, ccc.Resource)
   604  }
   605  
   606  // Authorizer gets the authorizer from client credentials.
   607  func (ccc ClientCredentialsConfig) Authorizer() (autorest.Authorizer, error) {
   608  	if len(ccc.AuxTenants) == 0 {
   609  		spToken, err := ccc.ServicePrincipalToken()
   610  		if err != nil {
   611  			return nil, fmt.Errorf("failed to get SPT from client credentials: %v", err)
   612  		}
   613  		return autorest.NewBearerAuthorizer(spToken), nil
   614  	}
   615  	mtSPT, err := ccc.MultiTenantServicePrincipalToken()
   616  	if err != nil {
   617  		return nil, fmt.Errorf("failed to get multitenant SPT from client credentials: %v", err)
   618  	}
   619  	return autorest.NewMultiTenantServicePrincipalTokenAuthorizer(mtSPT), nil
   620  }
   621  
   622  // ClientCertificateConfig provides the options to get a bearer authorizer from a client certificate.
   623  type ClientCertificateConfig struct {
   624  	ClientID            string
   625  	CertificatePath     string
   626  	CertificatePassword string
   627  	TenantID            string
   628  	AuxTenants          []string
   629  	AADEndpoint         string
   630  	Resource            string
   631  }
   632  
   633  // ServicePrincipalToken creates a ServicePrincipalToken from client certificate.
   634  func (ccc ClientCertificateConfig) ServicePrincipalToken() (*adal.ServicePrincipalToken, error) {
   635  	oauthConfig, err := adal.NewOAuthConfig(ccc.AADEndpoint, ccc.TenantID)
   636  	if err != nil {
   637  		return nil, err
   638  	}
   639  	certData, err := ioutil.ReadFile(ccc.CertificatePath)
   640  	if err != nil {
   641  		return nil, fmt.Errorf("failed to read the certificate file (%s): %v", ccc.CertificatePath, err)
   642  	}
   643  	certificate, rsaPrivateKey, err := adal.DecodePfxCertificateData(certData, ccc.CertificatePassword)
   644  	if err != nil {
   645  		return nil, fmt.Errorf("failed to decode pkcs12 certificate while creating spt: %v", err)
   646  	}
   647  	return adal.NewServicePrincipalTokenFromCertificate(*oauthConfig, ccc.ClientID, certificate, rsaPrivateKey, ccc.Resource)
   648  }
   649  
   650  // MultiTenantServicePrincipalToken creates a MultiTenantServicePrincipalToken from client certificate.
   651  func (ccc ClientCertificateConfig) MultiTenantServicePrincipalToken() (*adal.MultiTenantServicePrincipalToken, error) {
   652  	oauthConfig, err := adal.NewMultiTenantOAuthConfig(ccc.AADEndpoint, ccc.TenantID, ccc.AuxTenants, adal.OAuthOptions{})
   653  	if err != nil {
   654  		return nil, err
   655  	}
   656  	certData, err := ioutil.ReadFile(ccc.CertificatePath)
   657  	if err != nil {
   658  		return nil, fmt.Errorf("failed to read the certificate file (%s): %v", ccc.CertificatePath, err)
   659  	}
   660  	certificate, rsaPrivateKey, err := adal.DecodePfxCertificateData(certData, ccc.CertificatePassword)
   661  	if err != nil {
   662  		return nil, fmt.Errorf("failed to decode pkcs12 certificate while creating spt: %v", err)
   663  	}
   664  	return adal.NewMultiTenantServicePrincipalTokenFromCertificate(oauthConfig, ccc.ClientID, certificate, rsaPrivateKey, ccc.Resource)
   665  }
   666  
   667  // Authorizer gets an authorizer object from client certificate.
   668  func (ccc ClientCertificateConfig) Authorizer() (autorest.Authorizer, error) {
   669  	if len(ccc.AuxTenants) == 0 {
   670  		spToken, err := ccc.ServicePrincipalToken()
   671  		if err != nil {
   672  			return nil, fmt.Errorf("failed to get oauth token from certificate auth: %v", err)
   673  		}
   674  		return autorest.NewBearerAuthorizer(spToken), nil
   675  	}
   676  	mtSPT, err := ccc.MultiTenantServicePrincipalToken()
   677  	if err != nil {
   678  		return nil, fmt.Errorf("failed to get multitenant SPT from certificate auth: %v", err)
   679  	}
   680  	return autorest.NewMultiTenantServicePrincipalTokenAuthorizer(mtSPT), nil
   681  }
   682  
   683  // DeviceFlowConfig provides the options to get a bearer authorizer using device flow authentication.
   684  type DeviceFlowConfig struct {
   685  	ClientID    string
   686  	TenantID    string
   687  	AADEndpoint string
   688  	Resource    string
   689  }
   690  
   691  // Authorizer gets the authorizer from device flow.
   692  func (dfc DeviceFlowConfig) Authorizer() (autorest.Authorizer, error) {
   693  	spToken, err := dfc.ServicePrincipalToken()
   694  	if err != nil {
   695  		return nil, fmt.Errorf("failed to get oauth token from device flow: %v", err)
   696  	}
   697  	return autorest.NewBearerAuthorizer(spToken), nil
   698  }
   699  
   700  // ServicePrincipalToken gets the service principal token from device flow.
   701  func (dfc DeviceFlowConfig) ServicePrincipalToken() (*adal.ServicePrincipalToken, error) {
   702  	oauthConfig, err := adal.NewOAuthConfig(dfc.AADEndpoint, dfc.TenantID)
   703  	if err != nil {
   704  		return nil, err
   705  	}
   706  	oauthClient := &autorest.Client{}
   707  	deviceCode, err := adal.InitiateDeviceAuth(oauthClient, *oauthConfig, dfc.ClientID, dfc.Resource)
   708  	if err != nil {
   709  		return nil, fmt.Errorf("failed to start device auth flow: %s", err)
   710  	}
   711  	log.Println(*deviceCode.Message)
   712  	token, err := adal.WaitForUserCompletion(oauthClient, deviceCode)
   713  	if err != nil {
   714  		return nil, fmt.Errorf("failed to finish device auth flow: %s", err)
   715  	}
   716  	return adal.NewServicePrincipalTokenFromManualToken(*oauthConfig, dfc.ClientID, dfc.Resource, *token)
   717  }
   718  
   719  // UsernamePasswordConfig provides the options to get a bearer authorizer from a username and a password.
   720  type UsernamePasswordConfig struct {
   721  	ClientID    string
   722  	Username    string
   723  	Password    string
   724  	TenantID    string
   725  	AADEndpoint string
   726  	Resource    string
   727  }
   728  
   729  // ServicePrincipalToken creates a ServicePrincipalToken from username and password.
   730  func (ups UsernamePasswordConfig) ServicePrincipalToken() (*adal.ServicePrincipalToken, error) {
   731  	oauthConfig, err := adal.NewOAuthConfig(ups.AADEndpoint, ups.TenantID)
   732  	if err != nil {
   733  		return nil, err
   734  	}
   735  	return adal.NewServicePrincipalTokenFromUsernamePassword(*oauthConfig, ups.ClientID, ups.Username, ups.Password, ups.Resource)
   736  }
   737  
   738  // Authorizer gets the authorizer from a username and a password.
   739  func (ups UsernamePasswordConfig) Authorizer() (autorest.Authorizer, error) {
   740  	spToken, err := ups.ServicePrincipalToken()
   741  	if err != nil {
   742  		return nil, fmt.Errorf("failed to get oauth token from username and password auth: %v", err)
   743  	}
   744  	return autorest.NewBearerAuthorizer(spToken), nil
   745  }
   746  
   747  // MSIConfig provides the options to get a bearer authorizer through MSI.
   748  type MSIConfig struct {
   749  	Resource string
   750  	ClientID string
   751  }
   752  
   753  // ServicePrincipalToken creates a ServicePrincipalToken from MSI.
   754  func (mc MSIConfig) ServicePrincipalToken() (*adal.ServicePrincipalToken, error) {
   755  	spToken, err := adal.NewServicePrincipalTokenFromManagedIdentity(mc.Resource, &adal.ManagedIdentityOptions{
   756  		ClientID: mc.ClientID,
   757  	})
   758  	if err != nil {
   759  		return nil, fmt.Errorf("failed to get oauth token from MSI: %v", err)
   760  	}
   761  	return spToken, nil
   762  }
   763  
   764  // Authorizer gets the authorizer from MSI.
   765  func (mc MSIConfig) Authorizer() (autorest.Authorizer, error) {
   766  	spToken, err := mc.ServicePrincipalToken()
   767  	if err != nil {
   768  		return nil, err
   769  	}
   770  
   771  	return autorest.NewBearerAuthorizer(spToken), nil
   772  }
   773  

View as plain text