constellation/internal/cloud/azureshared/appcredentials.go

124 lines
3.8 KiB
Go
Raw Normal View History

/*
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"
"regexp"
"strings"
2022-06-07 10:27:55 -04:00
)
var (
subscriptionPattern = regexp.MustCompile(`subscriptions/([^/]+)/`)
rgPattern = regexp.MustCompile(`resourceGroups/([^/]+)/`)
)
// 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 {
SubscriptionID string
ResourceGroup string
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()
subscriptionID := getFirstMatchOrEmpty(subscriptionPattern, query.Get("uami_resource_id"))
resourceGroup := getFirstMatchOrEmpty(rgPattern, query.Get("uami_resource_id"))
preferredAuthMethod := FromString(query.Get("preferred_auth_method"))
2022-06-07 10:27:55 -04:00
return ApplicationCredentials{
SubscriptionID: subscriptionID,
ResourceGroup: resourceGroup,
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
}
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)
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()
}
//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
)