2022-09-05 03:06:08 -04:00
|
|
|
/*
|
|
|
|
Copyright (c) Edgeless Systems GmbH
|
|
|
|
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
*/
|
|
|
|
|
2022-06-07 10:27:55 -04:00
|
|
|
package azureshared
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net/url"
|
2023-07-31 04:53:05 -04:00
|
|
|
"regexp"
|
2023-04-03 09:01:25 -04:00
|
|
|
"strings"
|
2022-06-07 10:27:55 -04:00
|
|
|
)
|
|
|
|
|
2023-07-31 04:53:05 -04:00
|
|
|
var (
|
|
|
|
subscriptionPattern = regexp.MustCompile(`subscriptions/([^/]+)/`)
|
|
|
|
rgPattern = regexp.MustCompile(`resourceGroups/([^/]+)/`)
|
|
|
|
)
|
|
|
|
|
2023-04-03 09:01:25 -04:00
|
|
|
// ApplicationCredentials is a set of Azure API credentials.
|
|
|
|
// It can contain a client secret and carries the preferred authentication method.
|
2022-06-07 10:27:55 -04:00
|
|
|
// It is the equivalent of a service account key in other cloud providers.
|
|
|
|
type ApplicationCredentials struct {
|
2023-07-31 04:53:05 -04:00
|
|
|
SubscriptionID string
|
|
|
|
ResourceGroup string
|
2023-04-03 09:01:25 -04:00
|
|
|
TenantID string
|
|
|
|
AppClientID string
|
|
|
|
ClientSecretValue string
|
|
|
|
Location string
|
|
|
|
UamiResourceID string
|
|
|
|
PreferredAuthMethod AuthMethod
|
2022-06-07 10:27:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// ApplicationCredentialsFromURI converts a cloudServiceAccountURI into Azure ApplicationCredentials.
|
|
|
|
func ApplicationCredentialsFromURI(cloudServiceAccountURI string) (ApplicationCredentials, error) {
|
|
|
|
uri, err := url.Parse(cloudServiceAccountURI)
|
|
|
|
if err != nil {
|
|
|
|
return ApplicationCredentials{}, err
|
|
|
|
}
|
|
|
|
if uri.Scheme != "serviceaccount" {
|
|
|
|
return ApplicationCredentials{}, fmt.Errorf("invalid service account URI: invalid scheme: %s", uri.Scheme)
|
|
|
|
}
|
|
|
|
if uri.Host != "azure" {
|
|
|
|
return ApplicationCredentials{}, fmt.Errorf("invalid service account URI: invalid host: %s", uri.Host)
|
|
|
|
}
|
|
|
|
query := uri.Query()
|
2023-07-31 04:53:05 -04:00
|
|
|
|
|
|
|
subscriptionID := getFirstMatchOrEmpty(subscriptionPattern, query.Get("uami_resource_id"))
|
|
|
|
resourceGroup := getFirstMatchOrEmpty(rgPattern, query.Get("uami_resource_id"))
|
|
|
|
|
2023-04-03 09:01:25 -04:00
|
|
|
preferredAuthMethod := FromString(query.Get("preferred_auth_method"))
|
2022-06-07 10:27:55 -04:00
|
|
|
return ApplicationCredentials{
|
2023-07-31 04:53:05 -04:00
|
|
|
SubscriptionID: subscriptionID,
|
|
|
|
ResourceGroup: resourceGroup,
|
2023-04-03 09:01:25 -04:00
|
|
|
TenantID: query.Get("tenant_id"),
|
|
|
|
AppClientID: query.Get("client_id"),
|
|
|
|
ClientSecretValue: query.Get("client_secret"),
|
|
|
|
Location: query.Get("location"),
|
|
|
|
UamiResourceID: query.Get("uami_resource_id"),
|
|
|
|
PreferredAuthMethod: preferredAuthMethod,
|
2022-06-07 10:27:55 -04:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2023-07-31 04:53:05 -04:00
|
|
|
func getFirstMatchOrEmpty(pattern *regexp.Regexp, str string) string {
|
|
|
|
subscriptionMatches := pattern.FindStringSubmatch(str)
|
|
|
|
var subscriptionID string
|
|
|
|
if len(subscriptionMatches) > 1 {
|
|
|
|
subscriptionID = subscriptionMatches[1]
|
|
|
|
}
|
|
|
|
return subscriptionID
|
|
|
|
}
|
|
|
|
|
2022-06-07 10:27:55 -04:00
|
|
|
// ToCloudServiceAccountURI converts the ApplicationCredentials into a cloud service account URI.
|
|
|
|
func (c ApplicationCredentials) ToCloudServiceAccountURI() string {
|
|
|
|
query := url.Values{}
|
|
|
|
query.Add("tenant_id", c.TenantID)
|
|
|
|
query.Add("location", c.Location)
|
2023-04-03 09:01:25 -04:00
|
|
|
if c.AppClientID != "" {
|
|
|
|
query.Add("client_id", c.AppClientID)
|
|
|
|
}
|
|
|
|
if c.ClientSecretValue != "" {
|
|
|
|
query.Add("client_secret", c.ClientSecretValue)
|
|
|
|
}
|
|
|
|
if c.UamiResourceID != "" {
|
|
|
|
query.Add("uami_resource_id", c.UamiResourceID)
|
|
|
|
}
|
|
|
|
if c.PreferredAuthMethod != AuthMethodUnknown {
|
|
|
|
query.Add("preferred_auth_method", c.PreferredAuthMethod.String())
|
|
|
|
}
|
2022-06-07 10:27:55 -04:00
|
|
|
uri := url.URL{
|
|
|
|
Scheme: "serviceaccount",
|
|
|
|
Host: "azure",
|
|
|
|
RawQuery: query.Encode(),
|
|
|
|
}
|
|
|
|
return uri.String()
|
|
|
|
}
|
2023-04-03 09:01:25 -04:00
|
|
|
|
|
|
|
//go:generate stringer -type=AuthMethod -trimprefix=AuthMethod
|
|
|
|
|
|
|
|
// AuthMethod is the authentication method used for the Azure API.
|
|
|
|
type AuthMethod uint32
|
|
|
|
|
|
|
|
// FromString converts a string into an AuthMethod.
|
|
|
|
func FromString(s string) AuthMethod {
|
|
|
|
switch strings.ToLower(s) {
|
|
|
|
case strings.ToLower(AuthMethodServicePrincipal.String()):
|
|
|
|
return AuthMethodServicePrincipal
|
|
|
|
case strings.ToLower(AuthMethodUserAssignedIdentity.String()):
|
|
|
|
return AuthMethodUserAssignedIdentity
|
|
|
|
default:
|
|
|
|
return AuthMethodUnknown
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
// AuthMethodUnknown is default value for AuthMethod.
|
|
|
|
AuthMethodUnknown AuthMethod = iota
|
|
|
|
// AuthMethodServicePrincipal uses a client ID and secret.
|
|
|
|
AuthMethodServicePrincipal
|
|
|
|
// AuthMethodUserAssignedIdentity uses a user assigned identity.
|
|
|
|
AuthMethodUserAssignedIdentity
|
|
|
|
)
|