github.com/coreos/mantle@v0.13.0/auth/azure.go (about)

     1  // Copyright 2016 CoreOS, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package auth
    16  
    17  import (
    18  	"bytes"
    19  	"encoding/json"
    20  	"fmt"
    21  	"io/ioutil"
    22  	"os"
    23  	"os/user"
    24  	"path/filepath"
    25  
    26  	"golang.org/x/text/encoding/unicode"
    27  	"golang.org/x/text/transform"
    28  
    29  	"github.com/coreos/mantle/platform"
    30  )
    31  
    32  const (
    33  	AzureAuthPath    = ".azure/credentials.json"
    34  	AzureProfilePath = ".azure/azureProfile.json"
    35  )
    36  
    37  // A version of the Options struct from platform/api/azure that only
    38  // contains the ASM values. Otherwise there's a cyclical depdendence
    39  // because platform/api/azure has to import auth to have access to
    40  // the ReadAzureProfile function.
    41  type Options struct {
    42  	*platform.Options
    43  
    44  	SubscriptionName string
    45  	SubscriptionID   string
    46  
    47  	// Azure API endpoint. If unset, the Azure SDK default will be used.
    48  	ManagementURL         string
    49  	ManagementCertificate []byte
    50  
    51  	// Azure Storage API endpoint suffix. If unset, the Azure SDK default will be used.
    52  	StorageEndpointSuffix string
    53  }
    54  
    55  type AzureEnvironment struct {
    56  	ActiveDirectoryEndpointURL                        string `json:"activeDirectoryEndpointUrl"`
    57  	ActiveDirectoryGraphAPIVersion                    string `json:"activeDirectoryGraphApiVersion"`
    58  	ActiveDirectoryGraphResourceID                    string `json:"activeDirectoryGraphResourceId"`
    59  	ActiveDirectoryResourceID                         string `json:"activeDirectoryResourceId"`
    60  	AzureDataLakeAnalyticsCatalogAndJobEndpointSuffix string `json:"azureDataLakeAnalyticsCatalogAndJobEndpointSuffix"`
    61  	AzureDataLakeStoreFileSystemEndpointSuffix        string `json:"azureDataLakeStoreFileSystemEndpointSuffix"`
    62  	GalleryEndpointURL                                string `json:"galleryEndpointUrl"`
    63  	KeyVaultDNSSuffix                                 string `json:"keyVaultDnsSuffix"`
    64  	ManagementEndpointURL                             string `json:"managementEndpointUrl"`
    65  	Name                                              string `json:"name"`
    66  	PortalURL                                         string `json:"portalUrl"`
    67  	PublishingProfileURL                              string `json:"publishingProfileUrl"`
    68  	ResourceManagerEndpointURL                        string `json:"resourceManagerEndpointUrl"`
    69  	SqlManagementEndpointURL                          string `json:"sqlManagementEndpointUrl"`
    70  	SqlServerHostnameSuffix                           string `json:"sqlServerHostnameSuffix"`
    71  	StorageEndpointSuffix                             string `json:"storageEndpointSuffix"`
    72  }
    73  
    74  type AzureManagementCertificate struct {
    75  	Cert string `json:"cert"`
    76  	Key  string `json:"key"`
    77  }
    78  
    79  type AzureSubscription struct {
    80  	EnvironmentName       string                     `json:"environmentName"`
    81  	ID                    string                     `json:"id"`
    82  	IsDefault             bool                       `json:"isDefault"`
    83  	ManagementCertificate AzureManagementCertificate `json:"managementCertificate"`
    84  	ManagementEndpointURL string                     `json:"managementEndpointUrl"`
    85  	Name                  string                     `json:"name"`
    86  	RegisteredProviders   []string                   `json:"registeredProviders"`
    87  	State                 string                     `json:"state"`
    88  }
    89  
    90  // AzureProfile represents a parsed Azure Profile Configuration File.
    91  type AzureProfile struct {
    92  	Environments  []AzureEnvironment  `json:"environments"`
    93  	Subscriptions []AzureSubscription `json:"subscriptions"`
    94  }
    95  
    96  // AsOptions converts all subscriptions into a slice of Options.
    97  // If there is an environment with a name matching the subscription, that environment's storage endpoint will be copied to the options.
    98  func (ap *AzureProfile) AsOptions() []Options {
    99  	var o []Options
   100  
   101  	for _, sub := range ap.Subscriptions {
   102  		newo := Options{
   103  			SubscriptionName:      sub.Name,
   104  			SubscriptionID:        sub.ID,
   105  			ManagementURL:         sub.ManagementEndpointURL,
   106  			ManagementCertificate: bytes.Join([][]byte{[]byte(sub.ManagementCertificate.Key), []byte(sub.ManagementCertificate.Cert)}, []byte("\n")),
   107  		}
   108  
   109  		// find the storage endpoint for the subscription
   110  		for _, e := range ap.Environments {
   111  			if e.Name == sub.EnvironmentName {
   112  				newo.StorageEndpointSuffix = e.StorageEndpointSuffix
   113  				break
   114  			}
   115  		}
   116  
   117  		o = append(o, newo)
   118  	}
   119  
   120  	return o
   121  }
   122  
   123  // SubscriptionOptions returns the name subscription in the Azure profile as a Options struct.
   124  // If the subscription name is "", the first subscription is returned.
   125  // If there are no subscriptions or the named subscription is not found, SubscriptionOptions returns nil.
   126  func (ap *AzureProfile) SubscriptionOptions(name string) *Options {
   127  	opts := ap.AsOptions()
   128  
   129  	if len(opts) == 0 {
   130  		return nil
   131  	}
   132  
   133  	if name == "" {
   134  		return &opts[0]
   135  	} else {
   136  		for _, o := range ap.AsOptions() {
   137  			if o.SubscriptionName == name {
   138  				return &o
   139  			}
   140  		}
   141  	}
   142  
   143  	return nil
   144  }
   145  
   146  // ReadAzureProfile decodes an Azure Profile, as created by the Azure Cross-platform CLI.
   147  //
   148  // If path is empty, $HOME/.azure/azureProfile.json is read.
   149  func ReadAzureProfile(path string) (*AzureProfile, error) {
   150  	if path == "" {
   151  		user, err := user.Current()
   152  		if err != nil {
   153  			return nil, err
   154  		}
   155  
   156  		path = filepath.Join(user.HomeDir, AzureProfilePath)
   157  	}
   158  
   159  	contents, err := DecodeBOMFile(path)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  
   164  	var ap AzureProfile
   165  	if err := json.Unmarshal(contents, &ap); err != nil {
   166  		return nil, err
   167  	}
   168  
   169  	if len(ap.Subscriptions) == 0 {
   170  		return nil, fmt.Errorf("Azure profile %q contains no subscriptions", path)
   171  	}
   172  
   173  	return &ap, nil
   174  }
   175  
   176  func DecodeBOMFile(path string) ([]byte, error) {
   177  	f, err := os.Open(path)
   178  	if err != nil {
   179  		return nil, err
   180  	}
   181  	defer f.Close()
   182  	decoder := unicode.UTF8.NewDecoder()
   183  	reader := transform.NewReader(f, unicode.BOMOverride(decoder))
   184  	return ioutil.ReadAll(reader)
   185  }