Skip to content

Commit 89ba607

Browse files
author
Sergey Kostrukov
authored
AzureMonitor: strongly-typed AzureCredentials and correct resolution of auth type and cloud (grafana#36284)
1 parent 719e78f commit 89ba607

15 files changed

+741
-396
lines changed

pkg/api/pluginproxy/ds_auth_provider.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,7 @@ func getTokenProvider(ctx context.Context, cfg *setting.Cfg, ds *models.DataSour
9292
if tokenAuth == nil {
9393
return nil, fmt.Errorf("'tokenAuth' not configured for authentication type '%s'", authType)
9494
}
95-
provider := newAzureAccessTokenProvider(ctx, cfg, tokenAuth)
96-
return provider, nil
95+
return newAzureAccessTokenProvider(ctx, cfg, tokenAuth)
9796

9897
case "gce":
9998
if jwtTokenAuth == nil {

pkg/api/pluginproxy/token_provider_azure.go

+38-4
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,58 @@ package pluginproxy
22

33
import (
44
"context"
5+
"strings"
56

67
"github.com/grafana/grafana/pkg/plugins"
78
"github.com/grafana/grafana/pkg/setting"
9+
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/azcredentials"
810
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/aztokenprovider"
911
)
1012

1113
type azureAccessTokenProvider struct {
1214
ctx context.Context
1315
tokenProvider aztokenprovider.AzureTokenProvider
16+
scopes []string
1417
}
1518

16-
func newAzureAccessTokenProvider(ctx context.Context, cfg *setting.Cfg, authParams *plugins.JwtTokenAuth) *azureAccessTokenProvider {
19+
func newAzureAccessTokenProvider(ctx context.Context, cfg *setting.Cfg, authParams *plugins.JwtTokenAuth) (*azureAccessTokenProvider, error) {
20+
credentials := getAzureCredentials(cfg, authParams)
21+
tokenProvider, err := aztokenprovider.NewAzureAccessTokenProvider(cfg, credentials)
22+
if err != nil {
23+
return nil, err
24+
}
1725
return &azureAccessTokenProvider{
1826
ctx: ctx,
19-
tokenProvider: aztokenprovider.NewAzureAccessTokenProvider(cfg, authParams),
20-
}
27+
tokenProvider: tokenProvider,
28+
scopes: authParams.Scopes,
29+
}, nil
2130
}
2231

2332
func (provider *azureAccessTokenProvider) GetAccessToken() (string, error) {
24-
return provider.tokenProvider.GetAccessToken(provider.ctx)
33+
return provider.tokenProvider.GetAccessToken(provider.ctx, provider.scopes)
34+
}
35+
36+
func getAzureCredentials(cfg *setting.Cfg, authParams *plugins.JwtTokenAuth) azcredentials.AzureCredentials {
37+
authType := strings.ToLower(authParams.Params["azure_auth_type"])
38+
clientId := authParams.Params["client_id"]
39+
40+
// Type of authentication being determined by the following logic:
41+
// * If authType is set to 'msi' then user explicitly selected the managed identity authentication
42+
// * If authType isn't set but other fields are configured then it's a datasource which was configured
43+
// before managed identities where introduced, therefore use client secret authentication
44+
// * If authType and other fields aren't set then it means the datasource never been configured
45+
// and managed identity is the default authentication choice as long as managed identities are enabled
46+
isManagedIdentity := authType == "msi" || (authType == "" && clientId == "" && cfg.Azure.ManagedIdentityEnabled)
47+
48+
if isManagedIdentity {
49+
return &azcredentials.AzureManagedIdentityCredentials{}
50+
} else {
51+
return &azcredentials.AzureClientSecretCredentials{
52+
AzureCloud: authParams.Params["azure_cloud"],
53+
Authority: authParams.Url,
54+
TenantId: authParams.Params["tenant_id"],
55+
ClientId: authParams.Params["client_id"],
56+
ClientSecret: authParams.Params["client_secret"],
57+
}
58+
}
2559
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package azcredentials
2+
3+
const (
4+
AzureAuthManagedIdentity = "msi"
5+
AzureAuthClientSecret = "clientsecret"
6+
)
7+
8+
type AzureCredentials interface {
9+
AzureAuthType() string
10+
}
11+
12+
type AzureManagedIdentityCredentials struct {
13+
ClientId string
14+
}
15+
16+
type AzureClientSecretCredentials struct {
17+
AzureCloud string
18+
Authority string
19+
TenantId string
20+
ClientId string
21+
ClientSecret string
22+
}
23+
24+
func (credentials *AzureManagedIdentityCredentials) AzureAuthType() string {
25+
return AzureAuthManagedIdentity
26+
}
27+
28+
func (credentials *AzureClientSecretCredentials) AzureAuthType() string {
29+
return AzureAuthClientSecret
30+
}

pkg/tsdb/azuremonitor/aztokenprovider/middleware.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ import (
99

1010
const authenticationMiddlewareName = "AzureAuthentication"
1111

12-
func AuthMiddleware(tokenProvider AzureTokenProvider) httpclient.Middleware {
12+
func AuthMiddleware(tokenProvider AzureTokenProvider, scopes []string) httpclient.Middleware {
1313
return httpclient.NamedMiddlewareFunc(authenticationMiddlewareName, func(opts httpclient.Options, next http.RoundTripper) http.RoundTripper {
1414
return httpclient.RoundTripperFunc(func(req *http.Request) (*http.Response, error) {
15-
token, err := tokenProvider.GetAccessToken(req.Context())
15+
token, err := tokenProvider.GetAccessToken(req.Context(), scopes)
1616
if err != nil {
1717
return nil, fmt.Errorf("failed to retrieve Azure access token: %w", err)
1818
}

pkg/tsdb/azuremonitor/aztokenprovider/token_cache.go

+16-16
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ type AccessToken struct {
1919
ExpiresOn time.Time
2020
}
2121

22-
type TokenCredential interface {
22+
type TokenRetriever interface {
2323
GetCacheKey() string
2424
Init() error
2525
GetAccessToken(ctx context.Context, scopes []string) (*AccessToken, error)
2626
}
2727

2828
type ConcurrentTokenCache interface {
29-
GetAccessToken(ctx context.Context, credential TokenCredential, scopes []string) (string, error)
29+
GetAccessToken(ctx context.Context, tokenRetriever TokenRetriever, scopes []string) (string, error)
3030
}
3131

3232
func NewConcurrentTokenCache() ConcurrentTokenCache {
@@ -37,35 +37,35 @@ type tokenCacheImpl struct {
3737
cache sync.Map // of *credentialCacheEntry
3838
}
3939
type credentialCacheEntry struct {
40-
credential TokenCredential
40+
retriever TokenRetriever
4141

4242
credInit uint32
4343
credMutex sync.Mutex
4444
cache sync.Map // of *scopesCacheEntry
4545
}
4646

4747
type scopesCacheEntry struct {
48-
credential TokenCredential
49-
scopes []string
48+
retriever TokenRetriever
49+
scopes []string
5050

5151
cond *sync.Cond
5252
refreshing bool
5353
accessToken *AccessToken
5454
}
5555

56-
func (c *tokenCacheImpl) GetAccessToken(ctx context.Context, credential TokenCredential, scopes []string) (string, error) {
57-
return c.getEntryFor(credential).getAccessToken(ctx, scopes)
56+
func (c *tokenCacheImpl) GetAccessToken(ctx context.Context, tokenRetriever TokenRetriever, scopes []string) (string, error) {
57+
return c.getEntryFor(tokenRetriever).getAccessToken(ctx, scopes)
5858
}
5959

60-
func (c *tokenCacheImpl) getEntryFor(credential TokenCredential) *credentialCacheEntry {
60+
func (c *tokenCacheImpl) getEntryFor(credential TokenRetriever) *credentialCacheEntry {
6161
var entry interface{}
6262
var ok bool
6363

6464
key := credential.GetCacheKey()
6565

6666
if entry, ok = c.cache.Load(key); !ok {
6767
entry, _ = c.cache.LoadOrStore(key, &credentialCacheEntry{
68-
credential: credential,
68+
retriever: credential,
6969
})
7070
}
7171

@@ -87,8 +87,8 @@ func (c *credentialCacheEntry) ensureInitialized() error {
8787
defer c.credMutex.Unlock()
8888

8989
if c.credInit == 0 {
90-
// Initialize credential
91-
err := c.credential.Init()
90+
// Initialize retriever
91+
err := c.retriever.Init()
9292
if err != nil {
9393
return err
9494
}
@@ -108,9 +108,9 @@ func (c *credentialCacheEntry) getEntryFor(scopes []string) *scopesCacheEntry {
108108

109109
if entry, ok = c.cache.Load(key); !ok {
110110
entry, _ = c.cache.LoadOrStore(key, &scopesCacheEntry{
111-
credential: c.credential,
112-
scopes: scopes,
113-
cond: sync.NewCond(&sync.Mutex{}),
111+
retriever: c.retriever,
112+
scopes: scopes,
113+
cond: sync.NewCond(&sync.Mutex{}),
114114
})
115115
}
116116

@@ -155,7 +155,7 @@ func (c *scopesCacheEntry) getAccessToken(ctx context.Context) (string, error) {
155155
func (c *scopesCacheEntry) refreshAccessToken(ctx context.Context) (*AccessToken, error) {
156156
var accessToken *AccessToken
157157

158-
// Safeguarding from panic caused by credential implementation
158+
// Safeguarding from panic caused by retriever implementation
159159
defer func() {
160160
c.cond.L.Lock()
161161

@@ -169,7 +169,7 @@ func (c *scopesCacheEntry) refreshAccessToken(ctx context.Context) (*AccessToken
169169
c.cond.L.Unlock()
170170
}()
171171

172-
token, err := c.credential.GetAccessToken(ctx, c.scopes)
172+
token, err := c.retriever.GetAccessToken(ctx, c.scopes)
173173
if err != nil {
174174
return nil, err
175175
}

0 commit comments

Comments
 (0)