-
Notifications
You must be signed in to change notification settings - Fork 86
/
execCredentialPlugin.go
129 lines (112 loc) · 3.61 KB
/
execCredentialPlugin.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
package token
//go:generate sh -c "mockgen -destination mock_$GOPACKAGE/execCredentialPlugin.go github.com/Azure/kubelogin/pkg/internal/token ExecCredentialPlugin"
import (
"context"
"fmt"
"os"
"time"
"github.com/Azure/go-autorest/autorest/adal"
klog "k8s.io/klog/v2"
)
const (
expirationDelta time.Duration = 60 * time.Second
)
type ExecCredentialPlugin interface {
Do(ctx context.Context) error
}
type execCredentialPlugin struct {
o *Options
tokenCache TokenCache
execCredentialWriter ExecCredentialWriter
provider TokenProvider
disableTokenCache bool
refresher func(adal.OAuthConfig, string, string, string, *adal.Token) (TokenProvider, error)
}
func New(o *Options) (ExecCredentialPlugin, error) {
klog.V(10).Info(o.ToString())
provider, err := NewTokenProvider(o)
if err != nil {
return nil, err
}
disableTokenCache := false
if o.LoginMethod == ServicePrincipalLogin || o.LoginMethod == MSILogin || o.LoginMethod == WorkloadIdentityLogin || o.LoginMethod == AzureCLILogin {
disableTokenCache = true
}
return &execCredentialPlugin{
o: o,
tokenCache: &defaultTokenCache{},
execCredentialWriter: &execCredentialWriter{},
provider: provider,
refresher: newManualToken,
disableTokenCache: disableTokenCache,
}, nil
}
func (p *execCredentialPlugin) Do(ctx context.Context) error {
var (
token adal.Token
err error
)
if !p.disableTokenCache {
// get token from cache
token, err = p.tokenCache.Read(p.o.tokenCacheFile)
if err != nil {
return fmt.Errorf("unable to read from token cache: %s, err: %s", p.o.tokenCacheFile, err)
}
}
// verify resource
targetAudience := p.o.ServerID
if p.o.IsLegacy {
targetAudience = fmt.Sprintf("spn:%s", p.o.ServerID)
}
if token.Resource == targetAudience && !token.IsZero() {
// if not expired, return
if !token.WillExpireIn(expirationDelta) {
klog.V(10).Info("access token is still valid. will return")
return p.execCredentialWriter.Write(token, os.Stdout)
}
// if expired, try refresh when refresh token exists
if token.RefreshToken != "" {
tokenRefreshed := false
klog.V(10).Info("getting refresher")
oAuthConfig, err := getOAuthConfig(p.o.Environment, p.o.TenantID, p.o.IsLegacy)
if err != nil {
return fmt.Errorf("unable to get oAuthConfig: %s", err)
}
refresher, err := p.refresher(*oAuthConfig, p.o.ClientID, p.o.ServerID, p.o.TenantID, &token)
if err != nil {
return fmt.Errorf("failed to get refresher: %s", err)
}
klog.V(5).Info("refresh token")
token, err := refresher.Token(ctx)
// if refresh fails, we will login using token provider
if err != nil {
klog.V(5).Infof("refresh failed, will continue to login: %s", err)
} else {
tokenRefreshed = true
}
if tokenRefreshed {
klog.V(10).Info("token refreshed")
// if refresh succeeds, save token, and return
if err := p.tokenCache.Write(p.o.tokenCacheFile, token); err != nil {
return fmt.Errorf("failed to write to store: %s", err)
}
return p.execCredentialWriter.Write(token, os.Stdout)
}
} else {
klog.V(5).Info("there is no refresh token")
}
}
klog.V(5).Info("acquire new token")
// run the underlying provider
token, err = p.provider.Token(ctx)
if err != nil {
return fmt.Errorf("failed to get token: %s", err)
}
if !p.disableTokenCache {
// save token
if err := p.tokenCache.Write(p.o.tokenCacheFile, token); err != nil {
return fmt.Errorf("unable to write to token cache: %s, err: %s", p.o.tokenCacheFile, err)
}
}
return p.execCredentialWriter.Write(token, os.Stdout)
}