-
Notifications
You must be signed in to change notification settings - Fork 43
/
auth_method_azure_cli_token.go
159 lines (124 loc) · 4.91 KB
/
auth_method_azure_cli_token.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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package authentication
import (
"bytes"
"encoding/json"
"fmt"
"os/exec"
"strings"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/adal"
"github.com/Azure/go-autorest/autorest/azure/cli"
"github.com/hashicorp/go-multierror"
)
type azureCliTokenAuth struct {
profile *azureCLIProfile
servicePrincipalAuthDocsLink string
}
func (a azureCliTokenAuth) build(b Builder) (authMethod, error) {
auth := azureCliTokenAuth{
profile: &azureCLIProfile{
clientId: b.ClientID,
environment: b.Environment,
subscriptionId: b.SubscriptionID,
tenantId: b.TenantID,
},
servicePrincipalAuthDocsLink: b.ClientSecretDocsLink,
}
profilePath, err := cli.ProfilePath()
if err != nil {
return nil, fmt.Errorf("Error loading the Profile Path from the Azure CLI: %+v", err)
}
profile, err := cli.LoadProfile(profilePath)
if err != nil {
return nil, fmt.Errorf("Azure CLI Authorization Profile was not found. Please ensure the Azure CLI is installed and then log-in with `az login`.")
}
auth.profile.profile = profile
// Authenticating as a Service Principal doesn't return all of the information we need for authentication purposes
// as such Service Principal authentication is supported using the specific auth method
if authenticatedAsAUser := auth.profile.verifyAuthenticatedAsAUser(); !authenticatedAsAUser {
return nil, fmt.Errorf(`Authenticating using the Azure CLI is only supported as a User (not a Service Principal).
To authenticate to Azure using a Service Principal, you can use the separate 'Authenticate using a Service Principal'
auth method - instructions for which can be found here: %s
Alternatively you can authenticate using the Azure CLI by using a User Account.`, auth.servicePrincipalAuthDocsLink)
}
err = auth.profile.populateFields()
if err != nil {
return nil, fmt.Errorf("Error retrieving the Profile from the Azure CLI: %s Please re-authenticate using `az login`.", err)
}
err = auth.profile.populateClientId()
if err != nil {
return nil, fmt.Errorf("Error populating Client ID from the Azure CLI: %+v", err)
}
return auth, nil
}
func (a azureCliTokenAuth) isApplicable(b Builder) bool {
return b.SupportsAzureCliToken
}
func (a azureCliTokenAuth) getAuthorizationToken(sender autorest.Sender, oauthConfig *adal.OAuthConfig, endpoint string) (*autorest.BearerAuthorizer, error) {
// the Azure CLI appears to cache these, so to maintain compatibility with the interface this method is intentionally not on the pointer
token, err := obtainAuthorizationToken(endpoint, a.profile.subscriptionId)
if err != nil {
return nil, fmt.Errorf("Error obtaining Authorization Token from the Azure CLI: %s", err)
}
adalToken, err := token.ToADALToken()
if err != nil {
return nil, fmt.Errorf("Error converting Authorization Token to an ADAL Token: %s", err)
}
spt, err := adal.NewServicePrincipalTokenFromManualToken(*oauthConfig, a.profile.clientId, endpoint, adalToken)
if err != nil {
return nil, err
}
auth := autorest.NewBearerAuthorizer(spt)
return auth, nil
}
func (a azureCliTokenAuth) name() string {
return "Obtaining a token from the Azure CLI"
}
func (a azureCliTokenAuth) populateConfig(c *Config) error {
c.ClientID = a.profile.clientId
c.Environment = a.profile.environment
c.SubscriptionID = a.profile.subscriptionId
c.TenantID = a.profile.tenantId
return nil
}
func (a azureCliTokenAuth) validate() error {
var err *multierror.Error
errorMessageFmt := "A %s was not found in your Azure CLI Credentials.\n\nPlease login to the Azure CLI again via `az login`"
if a.profile == nil {
return fmt.Errorf("Azure CLI Profile is nil - this is an internal error and should be reported.")
}
if a.profile.clientId == "" {
err = multierror.Append(err, fmt.Errorf(errorMessageFmt, "Client ID"))
}
if a.profile.subscriptionId == "" {
err = multierror.Append(err, fmt.Errorf(errorMessageFmt, "Subscription ID"))
}
if a.profile.tenantId == "" {
err = multierror.Append(err, fmt.Errorf(errorMessageFmt, "Tenant ID"))
}
return err.ErrorOrNil()
}
func obtainAuthorizationToken(endpoint string, subscriptionId string) (*cli.Token, error) {
var stderr bytes.Buffer
var stdout bytes.Buffer
cmd := exec.Command("az", "account", "get-access-token", "--resource", endpoint, "--subscription", subscriptionId, "-o=json")
cmd.Stderr = &stderr
cmd.Stdout = &stdout
if err := cmd.Start(); err != nil {
return nil, fmt.Errorf("Error launching Azure CLI: %+v", err)
}
if err := cmd.Wait(); err != nil {
return nil, fmt.Errorf("Error waiting for the Azure CLI: %+v", err)
}
stdOutStr := stdout.String()
stdErrStr := stderr.String()
if stdErrStr != "" {
return nil, fmt.Errorf("Error retrieving access token from Azure CLI: %s", strings.TrimSpace(stdErrStr))
}
var token *cli.Token
err := json.Unmarshal([]byte(stdOutStr), &token)
if err != nil {
return nil, fmt.Errorf("Error unmarshaling Access Token from the Azure CLI: %s", err)
}
return token, nil
}