-
Notifications
You must be signed in to change notification settings - Fork 5k
Expand file tree
/
Copy pathauth.go
More file actions
134 lines (111 loc) · 4.68 KB
/
auth.go
File metadata and controls
134 lines (111 loc) · 4.68 KB
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
// Copyright IBM Corp. 2014, 2025
// SPDX-License-Identifier: MPL-2.0
package clients
import (
"context"
"errors"
"fmt"
"log"
"net/http"
"strings"
"github.com/hashicorp/go-azure-sdk/sdk/auth"
"github.com/hashicorp/go-azure-sdk/sdk/claims"
"github.com/hashicorp/go-azure-sdk/sdk/environments"
"github.com/hashicorp/terraform-provider-azurerm/internal/clients/graph"
"github.com/hashicorp/terraform-provider-azurerm/internal/resourceproviders"
)
type ResourceManagerAccount struct {
Environment environments.Environment
ClientId string
ObjectId string
SubscriptionId string
TenantId string
AuthenticatedAsAServicePrincipal bool
RegisteredResourceProviders resourceproviders.ResourceProviders
}
func NewResourceManagerAccount(ctx context.Context, config auth.Credentials, subscriptionId string, registeredResourceProviders resourceproviders.ResourceProviders) (*ResourceManagerAccount, error) {
authorizer, err := auth.NewAuthorizerFromCredentials(ctx, config, config.Environment.MicrosoftGraph)
if err != nil {
return nil, fmt.Errorf("unable to build authorizer for Microsoft Graph API: %+v", err)
}
// Acquire an access token so we can inspect the claims
token, err := authorizer.Token(ctx, &http.Request{})
if err != nil {
return nil, fmt.Errorf("could not acquire access token to parse claims: %+v", err)
}
tokenClaims, err := claims.ParseClaims(token)
if err != nil {
return nil, fmt.Errorf("parsing claims from access token: %+v", err)
}
authenticatedAsServicePrincipal := !strings.Contains(strings.ToLower(tokenClaims.Scopes), "openid")
clientId := tokenClaims.AppId
if clientId == "" {
log.Printf("[DEBUG] Using user-supplied ClientID because the `appid` claim was missing from the access token")
clientId = config.ClientID
}
objectId := tokenClaims.ObjectId
if objectId == "" {
if authenticatedAsServicePrincipal {
log.Printf("[DEBUG] Querying Microsoft Graph to discover authenticated service principal object ID because the `oid` claim was missing from the access token")
id, err := graph.ServicePrincipalObjectID(ctx, authorizer, config.Environment, config.ClientID)
if err != nil {
return nil, fmt.Errorf("attempting to discover object ID for authenticated service principal with client ID %q: %+v", config.ClientID, err)
}
objectId = *id
} else {
log.Printf("[DEBUG] Querying Microsoft Graph to discover authenticated user principal object ID because the `oid` claim was missing from the access token")
id, err := graph.UserPrincipalObjectID(ctx, authorizer, config.Environment)
if err != nil {
return nil, fmt.Errorf("attempting to discover object ID for authenticated user principal: %+v", err)
}
objectId = *id
}
}
tenantId := tokenClaims.TenantId
if tenantId == "" {
log.Printf("[DEBUG] Using user-supplied TenantID because the `tid` claim was missing from the access token")
tenantId = config.TenantID
}
// Finally, defer to Azure CLI to obtain tenant ID, subscription ID and client ID when not specified and missing from claims
realAuthorizer := authorizer
if cache, ok := authorizer.(*auth.CachedAuthorizer); ok {
realAuthorizer = cache.Source
}
if cli, ok := realAuthorizer.(*auth.AzureCliAuthorizer); ok {
// Use the tenant ID from Azure CLI when otherwise unknown
if tenantId == "" {
if cli.TenantID == "" {
return nil, errors.New("azure-cli could not determine tenant ID to use")
}
tenantId = cli.TenantID
log.Printf("[DEBUG] Using tenant ID from Azure CLI: %q", tenantId)
}
// Use the Azure CLI client ID
if id, ok := config.Environment.MicrosoftAzureCli.AppId(); ok {
clientId = *id
log.Printf("[DEBUG] Using client ID from Azure CLI: %q", clientId)
}
// Use the Azure CLI default subscription ID
if subscriptionId == "" {
subscriptionId = cli.DefaultSubscriptionID
log.Printf("[DEBUG] Using the default subscription ID from Azure CLI: %q", subscriptionId)
}
}
// We'll permit the provider to proceed with an unknown client ID since it only affects a small number of use cases when authenticating as a user
if tenantId == "" {
return nil, errors.New("unable to configure ResourceManagerAccount: tenant ID could not be determined and was not specified")
}
if subscriptionId == "" {
return nil, errors.New("unable to configure ResourceManagerAccount: subscription ID could not be determined and was not specified")
}
account := ResourceManagerAccount{
Environment: config.Environment,
ClientId: clientId,
ObjectId: objectId,
SubscriptionId: subscriptionId,
TenantId: tenantId,
AuthenticatedAsAServicePrincipal: authenticatedAsServicePrincipal,
RegisteredResourceProviders: registeredResourceProviders,
}
return &account, nil
}