forked from rancher/machine
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tenantid.go
123 lines (106 loc) · 3.78 KB
/
tenantid.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package azureutil
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/docker/machine/drivers/azure/logutil"
"github.com/docker/machine/libmachine/log"
"github.com/Azure/go-autorest/autorest/azure"
)
// loadOrFindTenantID figures out the AAD tenant ID of the subscription by first
// looking at the cache file, if not exists, makes a network call to load it and
// cache it for future use.
func loadOrFindTenantID(env azure.Environment, subscriptionID string) (string, error) {
var tenantID string
log.Debug("Looking up AAD Tenant ID.", logutil.Fields{
"subs": subscriptionID})
// Load from cache
fp := tenantIDPath(subscriptionID)
b, err := ioutil.ReadFile(fp)
if err == nil {
tenantID = strings.TrimSpace(string(b))
log.Debugf("Tenant ID loaded from file: %s", fp)
} else if os.IsNotExist(err) {
log.Debugf("Tenant ID file not found: %s", fp)
} else {
return "", fmt.Errorf("Failed to load tenant ID file: %v", err)
}
// Handle cache miss
if tenantID == "" {
log.Debug("Making API call to find tenant ID")
t, err := findTenantID(env, subscriptionID)
if err != nil {
return "", err
}
tenantID = t
// Cache the result
if err := saveTenantID(fp, tenantID); err != nil {
return "", fmt.Errorf("Failed to save tenant ID: %v", err)
}
log.Debugf("Cached tenant ID to file: %s", fp)
}
log.Debug("Found AAD Tenant ID.", logutil.Fields{
"tenant": tenantID,
"subs": subscriptionID})
return tenantID, nil
}
// findTenantID figures out the AAD tenant ID of the subscription by making an
// unauthenticated request to the Get Subscription Details endpoint and parses
// the value from WWW-Authenticate header.
func findTenantID(env azure.Environment, subscriptionID string) (string, error) {
const hdrKey = "WWW-Authenticate"
c := subscriptionsClient(env.ResourceManagerEndpoint)
// we expect this request to fail (err != nil), but we are only interested
// in headers, so surface the error if the Response is not present (i.e.
// network error etc)
subs, err := c.Get(subscriptionID)
if subs.Response.Response == nil {
return "", fmt.Errorf("Request failed: %v", err)
}
// Expecting 401 StatusUnauthorized here, just read the header
if subs.StatusCode != http.StatusUnauthorized {
return "", fmt.Errorf("Unexpected response from Get Subscription: %v", err)
}
hdr := subs.Header.Get(hdrKey)
if hdr == "" {
return "", fmt.Errorf("Header %v not found in Get Subscription response", hdrKey)
}
// Example value for hdr:
// Bearer authorization_uri="https://login.windows.net/996fe9d1-6171-40aa-945b-4c64b63bf655", error="invalid_token", error_description="The authentication failed because of missing 'Authorization' header."
r := regexp.MustCompile(`authorization_uri=".*/([0-9a-f\-]+)"`)
m := r.FindStringSubmatch(hdr)
if m == nil {
return "", fmt.Errorf("Could not find the tenant ID in header: %s %q", hdrKey, hdr)
}
return m[1], nil
}
// saveTenantID performs an atomic write to the path with given tenantID contents.
func saveTenantID(path string, tenantID string) error {
var perm os.FileMode = 0600
dir := filepath.Dir(path)
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
return fmt.Errorf("Failed to create directory %s: %v", dir, err)
}
f, err := ioutil.TempFile(dir, "tenantid")
if err != nil {
return fmt.Errorf("Failed to create temp file: %v", err)
}
defer f.Close()
fp := f.Name()
if _, err := f.Write([]byte(tenantID)); err != nil {
return fmt.Errorf("Failed to write tenant ID to file: %v", err)
}
f.Close()
// atomic move by rename
if err := os.Rename(fp, path); err != nil {
return fmt.Errorf("Failed to rename file. src=%s dst=%s error=%v", fp, path, err)
}
if err := os.Chmod(path, perm); err != nil {
return fmt.Errorf("Failed to chmod the file %s: %v", path, err)
}
return nil
}