-
Notifications
You must be signed in to change notification settings - Fork 1
/
azure.go
100 lines (93 loc) · 3.13 KB
/
azure.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
package pkg
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"strings"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/go-logr/logr"
"github.com/golang-jwt/jwt/v5"
)
type AzureHelper struct {
log logr.Logger
credential *azidentity.DefaultAzureCredential
}
func NewAzureHelper(log logr.Logger) (*AzureHelper, error) {
credential, err := azidentity.NewDefaultAzureCredential(nil)
if err != nil {
log.Error(err, "Failed to create default azure credential")
return nil, err
}
return &AzureHelper{
log: log,
credential: credential,
}, nil
}
func (a *AzureHelper) GetAccessToken(url string) (*jwt.Token, error) {
if strings.Contains(url, "dev.azure.com") {
return a.getAzureDevOpsAccessToken()
} else if strings.Contains(url, "azurecr.io") {
return a.getAzureContainerRegistryAccessToken(url)
} else {
a.log.Info("Cannot match url to scope", "url", url)
return nil, errors.New("cannot match url to scope")
}
}
func (a *AzureHelper) getAzureDevOpsAccessToken() (*jwt.Token, error) {
accessToken, err := a.credential.GetToken(context.Background(), policy.TokenRequestOptions{
Scopes: []string{AzureDevOpsScope},
})
if err != nil {
a.log.Error(err, "Failed to get access token")
return nil, err
}
token, _, err := new(jwt.Parser).ParseUnverified(string(accessToken.Token), jwt.MapClaims{})
if err != nil {
a.log.Error(err, "Failed to parse access token.")
return nil, err
}
return token, nil
}
func (a *AzureHelper) getAzureContainerRegistryAccessToken(acrServiceName string) (*jwt.Token, error) {
ctx := context.Background()
aadToken, err := a.credential.GetToken(ctx, policy.TokenRequestOptions{Scopes: []string{AzureContainerRegistryScope}})
if err != nil {
a.log.Error(err, "Cannot fetch token for ACR scope", "name", acrServiceName)
return nil, err
}
aadTokenJWT, _, err := new(jwt.Parser).ParseUnverified(string(aadToken.Token), jwt.MapClaims{})
if err != nil {
a.log.Error(err, "Failed to parse AAD token.")
return nil, err
}
tenantId := aadTokenJWT.Claims.(jwt.MapClaims)["tid"].(string)
formData := url.Values{
"grant_type": {"access_token"},
"service": {acrServiceName},
"tenant": {tenantId},
"access_token": {aadToken.Token},
}
jsonResponse, err := http.PostForm(fmt.Sprintf("https://%s/oauth2/exchange", acrServiceName), formData)
if err != nil {
a.log.Error(err, "Failed to fetch token from ACR with managed identity", "name", acrServiceName, "payload", formData.Encode())
return nil, err
}
if jsonResponse.StatusCode != 200 {
err = fmt.Errorf("unable to fetch token from ACR. name: %s, payload: %s, status, %s", acrServiceName, formData.Encode(), jsonResponse.Status)
a.log.Error(err, "ACR error")
return nil, err
}
var response map[string]interface{}
json.NewDecoder(jsonResponse.Body).Decode(&response)
a.log.Info("Response from ACR", "response", response)
token, _, err := new(jwt.Parser).ParseUnverified(response["refresh_token"].(string), jwt.MapClaims{})
if err != nil {
a.log.Error(err, "Failed to parse access token.")
return nil, err
}
return token, nil
}