-
Notifications
You must be signed in to change notification settings - Fork 86
/
serviceprincipaltoken.go
111 lines (99 loc) · 3.36 KB
/
serviceprincipaltoken.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
package token
import (
"context"
"encoding/json"
"errors"
"fmt"
"strconv"
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
"github.com/Azure/go-autorest/autorest/adal"
)
const (
certificate = "CERTIFICATE"
privateKey = "PRIVATE KEY"
)
type servicePrincipalToken struct {
clientID string
clientSecret string
clientCert string
clientCertPassword string
resourceID string
tenantID string
cloud cloud.Configuration
popClaims map[string]string
}
func newServicePrincipalTokenProvider(
cloud cloud.Configuration,
clientID,
clientSecret,
clientCert,
clientCertPassword,
resourceID,
tenantID string,
popClaims map[string]string,
) (TokenProvider, error) {
if clientID == "" {
return nil, errors.New("clientID cannot be empty")
}
if clientSecret == "" && clientCert == "" {
return nil, errors.New("both clientSecret and clientcert cannot be empty. One must be specified")
}
if clientSecret != "" && clientCert != "" {
return nil, errors.New("client secret and client certificate cannot be set at the same time. Only one can be specified")
}
if resourceID == "" {
return nil, errors.New("resourceID cannot be empty")
}
if tenantID == "" {
return nil, errors.New("tenantID cannot be empty")
}
return &servicePrincipalToken{
clientID: clientID,
clientSecret: clientSecret,
clientCert: clientCert,
clientCertPassword: clientCertPassword,
resourceID: resourceID,
tenantID: tenantID,
cloud: cloud,
popClaims: popClaims,
}, nil
}
// Token fetches an azcore.AccessToken from the Azure SDK and converts it to an adal.Token for use with kubelogin.
func (p *servicePrincipalToken) Token(ctx context.Context) (adal.Token, error) {
return p.TokenWithOptions(ctx, nil)
}
func (p *servicePrincipalToken) TokenWithOptions(ctx context.Context, options *azcore.ClientOptions) (adal.Token, error) {
emptyToken := adal.Token{}
var accessToken string
var expirationTimeUnix int64
var err error
scopes := []string{p.resourceID + defaultScope}
// Request a new Azure token provider for service principal
if p.clientSecret != "" {
accessToken, expirationTimeUnix, err = p.getTokenWithClientSecret(ctx, scopes, options)
if err != nil {
return emptyToken, fmt.Errorf("failed to create service principal token using secret: %w", err)
}
} else if p.clientCert != "" {
accessToken, expirationTimeUnix, err = p.getTokenWithClientCert(ctx, scopes, options)
if err != nil {
return emptyToken, fmt.Errorf("failed to create service principal token using certificate: %w", err)
}
} else {
return emptyToken, errors.New("service principal token requires either client secret or certificate")
}
if accessToken == "" {
return emptyToken, errors.New("unexpectedly got empty access token")
}
// azurecore.AccessTokens have ExpiresOn as Time.Time. We need to convert it to JSON.Number
// by fetching the time in seconds since the Unix epoch via Unix() and then converting to a
// JSON.Number via formatting as a string using a base-10 int64 conversion.
expiresOn := json.Number(strconv.FormatInt(expirationTimeUnix, 10))
// Re-wrap the azurecore.AccessToken into an adal.Token
return adal.Token{
AccessToken: accessToken,
ExpiresOn: expiresOn,
Resource: p.resourceID,
}, nil
}