github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/provider/openstack/credentials.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package openstack
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  	"regexp"
    11  	"strconv"
    12  
    13  	"github.com/juju/errors"
    14  	"github.com/juju/utils"
    15  	"gopkg.in/goose.v2/identity"
    16  	"gopkg.in/ini.v1"
    17  
    18  	"github.com/juju/juju/cloud"
    19  	"github.com/juju/juju/environs"
    20  )
    21  
    22  const (
    23  	CredAttrTenantName        = "tenant-name"
    24  	CredAttrTenantID          = "tenant-id"
    25  	CredAttrUserName          = "username"
    26  	CredAttrPassword          = "password"
    27  	CredAttrDomainName        = "domain-name"
    28  	CredAttrProjectDomainName = "project-domain-name"
    29  	CredAttrUserDomainName    = "user-domain-name"
    30  	CredAttrAccessKey         = "access-key"
    31  	CredAttrSecretKey         = "secret-key"
    32  	CredAttrVersion           = "version"
    33  )
    34  
    35  type OpenstackCredentials struct{}
    36  
    37  // CredentialSchemas is part of the environs.ProviderCredentials interface.
    38  func (OpenstackCredentials) CredentialSchemas() map[cloud.AuthType]cloud.CredentialSchema {
    39  	return map[cloud.AuthType]cloud.CredentialSchema{
    40  		cloud.UserPassAuthType: {
    41  			{
    42  				CredAttrUserName, cloud.CredentialAttr{Description: "The username to authenticate with."},
    43  			}, {
    44  				CredAttrPassword, cloud.CredentialAttr{
    45  					Description: "The password for the specified username.",
    46  					Hidden:      true,
    47  				},
    48  			}, {
    49  				CredAttrTenantName, cloud.CredentialAttr{
    50  					Description: "The OpenStack tenant name.",
    51  					Optional:    true,
    52  				},
    53  			}, {
    54  				CredAttrTenantID, cloud.CredentialAttr{
    55  					Description: "The Openstack tenant ID",
    56  					Optional:    true,
    57  				},
    58  			}, {
    59  				CredAttrVersion, cloud.CredentialAttr{
    60  					Description: "The Openstack identity version",
    61  					Optional:    true,
    62  				},
    63  			}, {
    64  				CredAttrDomainName, cloud.CredentialAttr{
    65  					Description: "The OpenStack domain name.",
    66  					Optional:    true,
    67  				},
    68  			}, {
    69  				CredAttrProjectDomainName, cloud.CredentialAttr{
    70  					Description: "The OpenStack project domain name.",
    71  					Optional:    true,
    72  				},
    73  			}, {
    74  				CredAttrUserDomainName, cloud.CredentialAttr{
    75  					Description: "The OpenStack user domain name.",
    76  					Optional:    true,
    77  				},
    78  			},
    79  		},
    80  		cloud.AccessKeyAuthType: {
    81  			{
    82  				CredAttrAccessKey, cloud.CredentialAttr{Description: "The access key to authenticate with."},
    83  			}, {
    84  				CredAttrSecretKey, cloud.CredentialAttr{
    85  					Description: "The secret key to authenticate with.",
    86  					Hidden:      true,
    87  				},
    88  			}, {
    89  				CredAttrTenantName, cloud.CredentialAttr{
    90  					Description: "The OpenStack tenant name.",
    91  					Optional:    true,
    92  				},
    93  			}, {
    94  				CredAttrTenantID, cloud.CredentialAttr{
    95  					Description: "The Openstack tenant ID",
    96  					Optional:    true,
    97  				},
    98  			}, {
    99  				CredAttrVersion, cloud.CredentialAttr{
   100  					Description: "The Openstack identity version",
   101  					Optional:    true,
   102  				},
   103  			},
   104  		},
   105  	}
   106  }
   107  
   108  // DetectCredentials is part of the environs.ProviderCredentials interface.
   109  func (c OpenstackCredentials) DetectCredentials() (*cloud.CloudCredential, error) {
   110  	result := cloud.CloudCredential{
   111  		AuthCredentials: make(map[string]cloud.Credential),
   112  	}
   113  
   114  	// Try just using environment variables
   115  	creds, user, region, err := c.detectCredential()
   116  	if err == nil {
   117  		result.DefaultRegion = region
   118  		result.AuthCredentials[user] = *creds
   119  	}
   120  
   121  	// Now look for .novarc file in home dir.
   122  	novarc := filepath.Join(utils.Home(), ".novarc")
   123  	novaInfo, err := ini.LooseLoad(novarc)
   124  	if err != nil {
   125  		return nil, errors.Annotate(err, "loading novarc file")
   126  	}
   127  	stripExport := regexp.MustCompile(`(?i)^\s*export\s*`)
   128  	keyValues := novaInfo.Section(ini.DEFAULT_SECTION).KeysHash()
   129  	if len(keyValues) > 0 {
   130  		for k, v := range keyValues {
   131  			k = stripExport.ReplaceAllString(k, "")
   132  			os.Setenv(k, v)
   133  		}
   134  		creds, user, region, err := c.detectCredential()
   135  		if err == nil {
   136  			result.DefaultRegion = region
   137  			result.AuthCredentials[user] = *creds
   138  		}
   139  	}
   140  	if len(result.AuthCredentials) == 0 {
   141  		return nil, errors.NotFoundf("openstack credentials")
   142  	}
   143  	return &result, nil
   144  }
   145  
   146  func (c OpenstackCredentials) detectCredential() (*cloud.Credential, string, string, error) {
   147  	creds, err := identity.CredentialsFromEnv()
   148  	if err != nil {
   149  		return nil, "", "", errors.Errorf("failed to retrive cred from env : %v", err)
   150  	}
   151  	if creds.TenantName == "" {
   152  		logger.Debugf("neither OS_TENANT_NAME nor OS_PROJECT_NAME environment variable not set")
   153  	}
   154  	if creds.TenantID == "" {
   155  		logger.Debugf("neither OS_TENANT_ID nor OS_PROJECT_ID environment variable not set")
   156  	}
   157  	if creds.User == "" {
   158  		return nil, "", "", errors.NewNotFound(nil, "neither OS_USERNAME nor OS_ACCESS_KEY environment variable not set")
   159  	}
   160  	if creds.Secrets == "" {
   161  		return nil, "", "", errors.NewNotFound(nil, "neither OS_PASSWORD nor OS_SECRET_KEY environment variable not set")
   162  	}
   163  
   164  	user, err := utils.LocalUsername()
   165  	if err != nil {
   166  		return nil, "", "", errors.Trace(err)
   167  	}
   168  
   169  	var version string
   170  	if creds.Version != 0 {
   171  		version = strconv.Itoa(creds.Version)
   172  	} else {
   173  		version = ""
   174  	}
   175  	// If OS_USERNAME or NOVA_USERNAME is set, assume userpass.
   176  	var credential cloud.Credential
   177  	if os.Getenv("OS_USERNAME") != "" || os.Getenv("NOVA_USERNAME") != "" {
   178  		user = creds.User
   179  		credential = cloud.NewCredential(
   180  			cloud.UserPassAuthType,
   181  			map[string]string{
   182  				CredAttrUserName:          creds.User,
   183  				CredAttrPassword:          creds.Secrets,
   184  				CredAttrTenantName:        creds.TenantName,
   185  				CredAttrTenantID:          creds.TenantID,
   186  				CredAttrUserDomainName:    creds.UserDomain,
   187  				CredAttrProjectDomainName: creds.ProjectDomain,
   188  				CredAttrDomainName:        creds.Domain,
   189  				CredAttrVersion:           version,
   190  			},
   191  		)
   192  	} else {
   193  		credential = cloud.NewCredential(
   194  			cloud.AccessKeyAuthType,
   195  			map[string]string{
   196  				CredAttrAccessKey:  creds.User,
   197  				CredAttrSecretKey:  creds.Secrets,
   198  				CredAttrTenantName: creds.TenantName,
   199  				CredAttrTenantID:   creds.TenantID,
   200  				CredAttrVersion:    version,
   201  			},
   202  		)
   203  	}
   204  	region := creds.Region
   205  	if region == "" {
   206  		region = "<unspecified>"
   207  	}
   208  	credential.Label = fmt.Sprintf("openstack region %q project %q user %q", region, creds.TenantName, user)
   209  	return &credential, user, creds.Region, nil
   210  }
   211  
   212  // FinalizeCredential is part of the environs.ProviderCredentials interface.
   213  func (OpenstackCredentials) FinalizeCredential(_ environs.FinalizeCredentialContext, args environs.FinalizeCredentialParams) (*cloud.Credential, error) {
   214  	return &args.Credential, nil
   215  }