go.mod: update osbuild/images to v0.156.0
tag v0.155.0 Tagger: imagebuilder-bot <imagebuilder-bots+imagebuilder-bot@redhat.com> Changes with 0.155.0 ---------------- * Fedora 43: add shadow-utils when LockRoot is enabled, update cloud-init service name (osbuild/images#1618) * Author: Achilleas Koutsou, Reviewers: Gianluca Zuccarelli, Michael Vogt * Update osbuild dependency commit ID to latest (osbuild/images#1609) * Author: SchutzBot, Reviewers: Achilleas Koutsou, Simon de Vlieger, Tomáš Hozza * Update snapshots to 20250626 (osbuild/images#1623) * Author: SchutzBot, Reviewers: Achilleas Koutsou, Simon de Vlieger * distro/rhel9: xz compress azure-cvm image type [HMS-8587] (osbuild/images#1620) * Author: Achilleas Koutsou, Reviewers: Simon de Vlieger, Tomáš Hozza * distro/rhel: introduce new image type: Azure SAP Apps [HMS-8738] (osbuild/images#1612) * Author: Achilleas Koutsou, Reviewers: Simon de Vlieger, Tomáš Hozza * distro/rhel: move ansible-core to sap_extras_pkgset (osbuild/images#1624) * Author: Achilleas Koutsou, Reviewers: Brian C. Lane, Tomáš Hozza * github/create-tag: allow passing the version when run manually (osbuild/images#1621) * Author: Achilleas Koutsou, Reviewers: Lukáš Zapletal, Tomáš Hozza * rhel9: move image-config into pure YAML (HMS-8593) (osbuild/images#1616) * Author: Michael Vogt, Reviewers: Achilleas Koutsou, Simon de Vlieger * test: split manifest checksums into separate files (osbuild/images#1625) * Author: Achilleas Koutsou, Reviewers: Simon de Vlieger, Tomáš Hozza — Somewhere on the Internet, 2025-06-30 --- tag v0.156.0 Tagger: imagebuilder-bot <imagebuilder-bots+imagebuilder-bot@redhat.com> Changes with 0.156.0 ---------------- * Many: delete repositories for EOL distributions (HMS-7044) (osbuild/images#1607) * Author: Tomáš Hozza, Reviewers: Michael Vogt, Simon de Vlieger * RHSM/facts: add 'image-builder CLI' API type (osbuild/images#1640) * Author: Tomáš Hozza, Reviewers: Brian C. Lane, Simon de Vlieger * Update dependencies 2025-06-29 (osbuild/images#1628) * Author: SchutzBot, Reviewers: Simon de Vlieger, Tomáš Hozza * Update osbuild dependency commit ID to latest (osbuild/images#1627) * Author: SchutzBot, Reviewers: Simon de Vlieger, Tomáš Hozza * [RFC] image: drop `InstallWeakDeps` from image.DiskImage (osbuild/images#1642) * Author: Michael Vogt, Reviewers: Brian C. Lane, Simon de Vlieger, Tomáš Hozza * build(deps): bump the go-deps group across 1 directory with 3 updates (osbuild/images#1632) * Author: dependabot[bot], Reviewers: SchutzBot, Tomáš Hozza * distro/rhel10: xz compress azure-cvm image type (osbuild/images#1638) * Author: Achilleas Koutsou, Reviewers: Brian C. Lane, Simon de Vlieger * distro: cleanup/refactor distro/{defs,generic} (HMS-8744) (osbuild/images#1570) * Author: Michael Vogt, Reviewers: Simon de Vlieger, Tomáš Hozza * distro: remove some hardcoded values from generic/images.go (osbuild/images#1636) * Author: Michael Vogt, Reviewers: Simon de Vlieger, Tomáš Hozza * distro: small tweaks for the YAML based imagetypes (osbuild/images#1622) * Author: Michael Vogt, Reviewers: Brian C. Lane, Simon de Vlieger * fedora/wsl: packages and locale (osbuild/images#1635) * Author: Simon de Vlieger, Reviewers: Michael Vogt, Tomáš Hozza * image/many: make compression more generic (osbuild/images#1634) * Author: Simon de Vlieger, Reviewers: Brian C. Lane, Michael Vogt * manifest: handle content template name with spaces (osbuild/images#1641) * Author: Bryttanie, Reviewers: Brian C. Lane, Michael Vogt, Tomáš Hozza * many: implement gzip (osbuild/images#1633) * Author: Simon de Vlieger, Reviewers: Michael Vogt, Tomáš Hozza * rhel/azure: set GRUB_TERMINAL based on architecture [RHEL-91383] (osbuild/images#1626) * Author: Achilleas Koutsou, Reviewers: Simon de Vlieger, Tomáš Hozza — Somewhere on the Internet, 2025-07-07 ---
This commit is contained in:
parent
60c5f10af8
commit
3fd7092db5
1486 changed files with 124742 additions and 82516 deletions
|
|
@ -65,6 +65,13 @@ type AuthenticationScheme = authority.AuthenticationScheme
|
|||
|
||||
type Account = shared.Account
|
||||
|
||||
type TokenSource = base.TokenSource
|
||||
|
||||
const (
|
||||
TokenSourceIdentityProvider = base.TokenSourceIdentityProvider
|
||||
TokenSourceCache = base.TokenSourceCache
|
||||
)
|
||||
|
||||
// CertFromPEM converts a PEM file (.pem or .key) for use with [NewCredFromCert]. The file
|
||||
// must contain the public certificate and the private key. If a PEM block is encrypted and
|
||||
// password is not an empty string, it attempts to decrypt the PEM blocks using the password.
|
||||
|
|
@ -305,7 +312,9 @@ func WithInstanceDiscovery(enabled bool) Option {
|
|||
// If an invalid region name is provided, the non-regional endpoint MIGHT be used or the token request MIGHT fail.
|
||||
func WithAzureRegion(val string) Option {
|
||||
return func(o *clientOptions) {
|
||||
o.azureRegion = val
|
||||
if val != "" {
|
||||
o.azureRegion = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -429,6 +438,7 @@ func WithClaims(claims string) interface {
|
|||
AcquireByAuthCodeOption
|
||||
AcquireByCredentialOption
|
||||
AcquireOnBehalfOfOption
|
||||
AcquireByUsernamePasswordOption
|
||||
AcquireSilentOption
|
||||
AuthCodeURLOption
|
||||
options.CallOption
|
||||
|
|
@ -437,6 +447,7 @@ func WithClaims(claims string) interface {
|
|||
AcquireByAuthCodeOption
|
||||
AcquireByCredentialOption
|
||||
AcquireOnBehalfOfOption
|
||||
AcquireByUsernamePasswordOption
|
||||
AcquireSilentOption
|
||||
AuthCodeURLOption
|
||||
options.CallOption
|
||||
|
|
@ -450,6 +461,8 @@ func WithClaims(claims string) interface {
|
|||
t.claims = claims
|
||||
case *acquireTokenOnBehalfOfOptions:
|
||||
t.claims = claims
|
||||
case *acquireTokenByUsernamePasswordOptions:
|
||||
t.claims = claims
|
||||
case *acquireTokenSilentOptions:
|
||||
t.claims = claims
|
||||
case *authCodeURLOptions:
|
||||
|
|
@ -496,6 +509,7 @@ func WithTenantID(tenantID string) interface {
|
|||
AcquireByAuthCodeOption
|
||||
AcquireByCredentialOption
|
||||
AcquireOnBehalfOfOption
|
||||
AcquireByUsernamePasswordOption
|
||||
AcquireSilentOption
|
||||
AuthCodeURLOption
|
||||
options.CallOption
|
||||
|
|
@ -504,6 +518,7 @@ func WithTenantID(tenantID string) interface {
|
|||
AcquireByAuthCodeOption
|
||||
AcquireByCredentialOption
|
||||
AcquireOnBehalfOfOption
|
||||
AcquireByUsernamePasswordOption
|
||||
AcquireSilentOption
|
||||
AuthCodeURLOption
|
||||
options.CallOption
|
||||
|
|
@ -517,6 +532,8 @@ func WithTenantID(tenantID string) interface {
|
|||
t.tenantID = tenantID
|
||||
case *acquireTokenOnBehalfOfOptions:
|
||||
t.tenantID = tenantID
|
||||
case *acquireTokenByUsernamePasswordOptions:
|
||||
t.tenantID = tenantID
|
||||
case *acquireTokenSilentOptions:
|
||||
t.tenantID = tenantID
|
||||
case *authCodeURLOptions:
|
||||
|
|
@ -592,6 +609,46 @@ func (cca Client) AcquireTokenSilent(ctx context.Context, scopes []string, opts
|
|||
return cca.base.AcquireTokenSilent(ctx, silentParameters)
|
||||
}
|
||||
|
||||
// acquireTokenByUsernamePasswordOptions contains optional configuration for AcquireTokenByUsernamePassword
|
||||
type acquireTokenByUsernamePasswordOptions struct {
|
||||
claims, tenantID string
|
||||
authnScheme AuthenticationScheme
|
||||
}
|
||||
|
||||
// AcquireByUsernamePasswordOption is implemented by options for AcquireTokenByUsernamePassword
|
||||
type AcquireByUsernamePasswordOption interface {
|
||||
acquireByUsernamePasswordOption()
|
||||
}
|
||||
|
||||
// AcquireTokenByUsernamePassword acquires a security token from the authority, via Username/Password Authentication.
|
||||
// NOTE: this flow is NOT recommended.
|
||||
//
|
||||
// Options: [WithClaims], [WithTenantID]
|
||||
func (cca Client) AcquireTokenByUsernamePassword(ctx context.Context, scopes []string, username, password string, opts ...AcquireByUsernamePasswordOption) (AuthResult, error) {
|
||||
o := acquireTokenByUsernamePasswordOptions{}
|
||||
if err := options.ApplyOptions(&o, opts); err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
authParams, err := cca.base.AuthParams.WithTenant(o.tenantID)
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
authParams.Scopes = scopes
|
||||
authParams.AuthorizationType = authority.ATUsernamePassword
|
||||
authParams.Claims = o.claims
|
||||
authParams.Username = username
|
||||
authParams.Password = password
|
||||
if o.authnScheme != nil {
|
||||
authParams.AuthnScheme = o.authnScheme
|
||||
}
|
||||
|
||||
token, err := cca.base.Token.UsernamePassword(ctx, authParams)
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
return cca.base.AuthResultFromToken(ctx, authParams, token)
|
||||
}
|
||||
|
||||
// acquireTokenByAuthCodeOptions contains the optional parameters used to acquire an access token using the authorization code flow.
|
||||
type acquireTokenByAuthCodeOptions struct {
|
||||
challenge, claims, tenantID string
|
||||
|
|
@ -683,7 +740,7 @@ func (cca Client) AcquireTokenByCredential(ctx context.Context, scopes []string,
|
|||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
return cca.base.AuthResultFromToken(ctx, authParams, token, true)
|
||||
return cca.base.AuthResultFromToken(ctx, authParams, token)
|
||||
}
|
||||
|
||||
// acquireTokenOnBehalfOfOptions contains optional configuration for AcquireTokenOnBehalfOf
|
||||
|
|
|
|||
|
|
@ -64,11 +64,20 @@ type CallErr struct {
|
|||
Err error
|
||||
}
|
||||
|
||||
type InvalidJsonErr struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
// Errors implements error.Error().
|
||||
func (e CallErr) Error() string {
|
||||
return e.Err.Error()
|
||||
}
|
||||
|
||||
// Errors implements error.Error().
|
||||
func (e InvalidJsonErr) Error() string {
|
||||
return e.Err.Error()
|
||||
}
|
||||
|
||||
// Verbose prints a versbose error message with the request or response.
|
||||
func (e CallErr) Verbose() string {
|
||||
e.Resp.Request = nil // This brings in a bunch of TLS crap we don't need
|
||||
|
|
|
|||
|
|
@ -5,16 +5,17 @@ package base
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/cache"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/internal/storage"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/errors"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/storage"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/accesstokens"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/authority"
|
||||
|
|
@ -94,6 +95,7 @@ type AuthResult struct {
|
|||
|
||||
// AuthResultMetadata which contains meta data for the AuthResult
|
||||
type AuthResultMetadata struct {
|
||||
RefreshOn time.Time
|
||||
TokenSource TokenSource
|
||||
}
|
||||
|
||||
|
|
@ -101,9 +103,8 @@ type TokenSource int
|
|||
|
||||
// These are all the types of token flows.
|
||||
const (
|
||||
SourceUnknown TokenSource = 0
|
||||
IdentityProvider TokenSource = 1
|
||||
Cache TokenSource = 2
|
||||
TokenSourceIdentityProvider TokenSource = 0
|
||||
TokenSourceCache TokenSource = 1
|
||||
)
|
||||
|
||||
// AuthResultFromStorage creates an AuthResult from a storage token response (which is generated from the cache).
|
||||
|
|
@ -111,7 +112,6 @@ func AuthResultFromStorage(storageTokenResponse storage.TokenResponse) (AuthResu
|
|||
if err := storageTokenResponse.AccessToken.Validate(); err != nil {
|
||||
return AuthResult{}, fmt.Errorf("problem with access token in StorageTokenResponse: %w", err)
|
||||
}
|
||||
|
||||
account := storageTokenResponse.Account
|
||||
accessToken := storageTokenResponse.AccessToken.Secret
|
||||
grantedScopes := strings.Split(storageTokenResponse.AccessToken.Scopes, scopeSeparator)
|
||||
|
|
@ -132,7 +132,8 @@ func AuthResultFromStorage(storageTokenResponse storage.TokenResponse) (AuthResu
|
|||
GrantedScopes: grantedScopes,
|
||||
DeclinedScopes: nil,
|
||||
Metadata: AuthResultMetadata{
|
||||
TokenSource: Cache,
|
||||
TokenSource: TokenSourceCache,
|
||||
RefreshOn: storageTokenResponse.AccessToken.RefreshOn.T,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -146,10 +147,11 @@ func NewAuthResult(tokenResponse accesstokens.TokenResponse, account shared.Acco
|
|||
Account: account,
|
||||
IDToken: tokenResponse.IDToken,
|
||||
AccessToken: tokenResponse.AccessToken,
|
||||
ExpiresOn: tokenResponse.ExpiresOn.T,
|
||||
ExpiresOn: tokenResponse.ExpiresOn,
|
||||
GrantedScopes: tokenResponse.GrantedScopes.Slice,
|
||||
Metadata: AuthResultMetadata{
|
||||
TokenSource: IdentityProvider,
|
||||
TokenSource: TokenSourceIdentityProvider,
|
||||
RefreshOn: tokenResponse.RefreshOn.T,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -165,6 +167,8 @@ type Client struct {
|
|||
AuthParams authority.AuthParams // DO NOT EVER MAKE THIS A POINTER! See "Note" in New().
|
||||
cacheAccessor cache.ExportReplace
|
||||
cacheAccessorMu *sync.RWMutex
|
||||
canRefresh map[string]*atomic.Value
|
||||
canRefreshMu *sync.Mutex
|
||||
}
|
||||
|
||||
// Option is an optional argument to the New constructor.
|
||||
|
|
@ -241,6 +245,8 @@ func New(clientID string, authorityURI string, token *oauth.Client, options ...O
|
|||
cacheAccessorMu: &sync.RWMutex{},
|
||||
manager: storage.New(token),
|
||||
pmanager: storage.NewPartitionedManager(token),
|
||||
canRefresh: make(map[string]*atomic.Value),
|
||||
canRefreshMu: &sync.Mutex{},
|
||||
}
|
||||
for _, o := range options {
|
||||
if err = o(&client); err != nil {
|
||||
|
|
@ -345,6 +351,28 @@ func (b Client) AcquireTokenSilent(ctx context.Context, silent AcquireTokenSilen
|
|||
if silent.Claims == "" {
|
||||
ar, err = AuthResultFromStorage(storageTokenResponse)
|
||||
if err == nil {
|
||||
if rt := storageTokenResponse.AccessToken.RefreshOn.T; !rt.IsZero() && Now().After(rt) {
|
||||
b.canRefreshMu.Lock()
|
||||
refreshValue, ok := b.canRefresh[tenant]
|
||||
if !ok {
|
||||
refreshValue = &atomic.Value{}
|
||||
refreshValue.Store(false)
|
||||
b.canRefresh[tenant] = refreshValue
|
||||
}
|
||||
b.canRefreshMu.Unlock()
|
||||
if refreshValue.CompareAndSwap(false, true) {
|
||||
defer refreshValue.Store(false)
|
||||
// Added a check to see if the token is still same because there is a chance
|
||||
// that the token is already refreshed by another thread.
|
||||
// If the token is not same, we don't need to refresh it.
|
||||
// Which means it refreshed.
|
||||
if str, err := m.Read(ctx, authParams); err == nil && str.AccessToken.Secret == ar.AccessToken {
|
||||
if tr, er := b.Token.Credential(ctx, authParams, silent.Credential); er == nil {
|
||||
return b.AuthResultFromToken(ctx, authParams, tr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ar.AccessToken, err = authParams.AuthnScheme.FormatAccessToken(ar.AccessToken)
|
||||
return ar, err
|
||||
}
|
||||
|
|
@ -362,7 +390,7 @@ func (b Client) AcquireTokenSilent(ctx context.Context, silent AcquireTokenSilen
|
|||
if err != nil {
|
||||
return ar, err
|
||||
}
|
||||
return b.AuthResultFromToken(ctx, authParams, token, true)
|
||||
return b.AuthResultFromToken(ctx, authParams, token)
|
||||
}
|
||||
|
||||
func (b Client) AcquireTokenByAuthCode(ctx context.Context, authCodeParams AcquireTokenAuthCodeParameters) (AuthResult, error) {
|
||||
|
|
@ -391,7 +419,7 @@ func (b Client) AcquireTokenByAuthCode(ctx context.Context, authCodeParams Acqui
|
|||
return AuthResult{}, err
|
||||
}
|
||||
|
||||
return b.AuthResultFromToken(ctx, authParams, token, true)
|
||||
return b.AuthResultFromToken(ctx, authParams, token)
|
||||
}
|
||||
|
||||
// AcquireTokenOnBehalfOf acquires a security token for an app using middle tier apps access token.
|
||||
|
|
@ -420,15 +448,12 @@ func (b Client) AcquireTokenOnBehalfOf(ctx context.Context, onBehalfOfParams Acq
|
|||
authParams.UserAssertion = onBehalfOfParams.UserAssertion
|
||||
token, err := b.Token.OnBehalfOf(ctx, authParams, onBehalfOfParams.Credential)
|
||||
if err == nil {
|
||||
ar, err = b.AuthResultFromToken(ctx, authParams, token, true)
|
||||
ar, err = b.AuthResultFromToken(ctx, authParams, token)
|
||||
}
|
||||
return ar, err
|
||||
}
|
||||
|
||||
func (b Client) AuthResultFromToken(ctx context.Context, authParams authority.AuthParams, token accesstokens.TokenResponse, cacheWrite bool) (AuthResult, error) {
|
||||
if !cacheWrite {
|
||||
return NewAuthResult(token, shared.Account{})
|
||||
}
|
||||
func (b Client) AuthResultFromToken(ctx context.Context, authParams authority.AuthParams, token accesstokens.TokenResponse) (AuthResult, error) {
|
||||
var m manager = b.manager
|
||||
if authParams.AuthorizationType == authority.ATOnBehalfOf {
|
||||
m = b.pmanager
|
||||
|
|
@ -458,6 +483,10 @@ func (b Client) AuthResultFromToken(ctx context.Context, authParams authority.Au
|
|||
return ar, err
|
||||
}
|
||||
|
||||
// This function wraps time.Now() and is used for refreshing the application
|
||||
// was created to test the function against refreshin
|
||||
var Now = time.Now
|
||||
|
||||
func (b Client) AllAccounts(ctx context.Context) ([]shared.Account, error) {
|
||||
if b.cacheAccessor != nil {
|
||||
b.cacheAccessorMu.RLock()
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ type AccessToken struct {
|
|||
ClientID string `json:"client_id,omitempty"`
|
||||
Secret string `json:"secret,omitempty"`
|
||||
Scopes string `json:"target,omitempty"`
|
||||
RefreshOn internalTime.Unix `json:"refresh_on,omitempty"`
|
||||
ExpiresOn internalTime.Unix `json:"expires_on,omitempty"`
|
||||
ExtendedExpiresOn internalTime.Unix `json:"extended_expires_on,omitempty"`
|
||||
CachedAt internalTime.Unix `json:"cached_at,omitempty"`
|
||||
|
|
@ -83,7 +84,7 @@ type AccessToken struct {
|
|||
}
|
||||
|
||||
// NewAccessToken is the constructor for AccessToken.
|
||||
func NewAccessToken(homeID, env, realm, clientID string, cachedAt, expiresOn, extendedExpiresOn time.Time, scopes, token, tokenType, authnSchemeKeyID string) AccessToken {
|
||||
func NewAccessToken(homeID, env, realm, clientID string, cachedAt, refreshOn, expiresOn, extendedExpiresOn time.Time, scopes, token, tokenType, authnSchemeKeyID string) AccessToken {
|
||||
return AccessToken{
|
||||
HomeAccountID: homeID,
|
||||
Environment: env,
|
||||
|
|
@ -93,6 +94,7 @@ func NewAccessToken(homeID, env, realm, clientID string, cachedAt, expiresOn, ex
|
|||
Secret: token,
|
||||
Scopes: scopes,
|
||||
CachedAt: internalTime.Unix{T: cachedAt.UTC()},
|
||||
RefreshOn: internalTime.Unix{T: refreshOn.UTC()},
|
||||
ExpiresOn: internalTime.Unix{T: expiresOn.UTC()},
|
||||
ExtendedExpiresOn: internalTime.Unix{T: extendedExpiresOn.UTC()},
|
||||
TokenType: tokenType,
|
||||
|
|
@ -102,8 +104,9 @@ func NewAccessToken(homeID, env, realm, clientID string, cachedAt, expiresOn, ex
|
|||
|
||||
// Key outputs the key that can be used to uniquely look up this entry in a map.
|
||||
func (a AccessToken) Key() string {
|
||||
ks := []string{a.HomeAccountID, a.Environment, a.CredentialType, a.ClientID, a.Realm, a.Scopes}
|
||||
key := strings.Join(
|
||||
[]string{a.HomeAccountID, a.Environment, a.CredentialType, a.ClientID, a.Realm, a.Scopes},
|
||||
ks,
|
||||
shared.CacheKeySeparator,
|
||||
)
|
||||
// add token type to key for new access tokens types. skip for bearer token type to
|
||||
|
|
@ -114,7 +114,8 @@ func (m *PartitionedManager) Write(authParameters authority.AuthParams, tokenRes
|
|||
realm,
|
||||
clientID,
|
||||
cachedAt,
|
||||
tokenResponse.ExpiresOn.T,
|
||||
tokenResponse.RefreshOn.T,
|
||||
tokenResponse.ExpiresOn,
|
||||
tokenResponse.ExtExpiresOn.T,
|
||||
target,
|
||||
tokenResponse.AccessToken,
|
||||
|
|
@ -173,6 +173,7 @@ func (m *Manager) Write(authParameters authority.AuthParams, tokenResponse acces
|
|||
environment := authParameters.AuthorityInfo.Host
|
||||
realm := authParameters.AuthorityInfo.Tenant
|
||||
clientID := authParameters.ClientID
|
||||
|
||||
target := strings.Join(tokenResponse.GrantedScopes.Slice, scopeSeparator)
|
||||
cachedAt := time.Now()
|
||||
authnSchemeKeyID := authParameters.AuthnScheme.KeyID()
|
||||
|
|
@ -193,7 +194,8 @@ func (m *Manager) Write(authParameters authority.AuthParams, tokenResponse acces
|
|||
realm,
|
||||
clientID,
|
||||
cachedAt,
|
||||
tokenResponse.ExpiresOn.T,
|
||||
tokenResponse.RefreshOn.T,
|
||||
tokenResponse.ExpiresOn,
|
||||
tokenResponse.ExtExpiresOn.T,
|
||||
target,
|
||||
tokenResponse.AccessToken,
|
||||
|
|
@ -265,6 +267,9 @@ func (m *Manager) aadMetadataFromCache(ctx context.Context, authorityInfo author
|
|||
}
|
||||
|
||||
func (m *Manager) aadMetadata(ctx context.Context, authorityInfo authority.Info) (authority.InstanceDiscoveryMetadata, error) {
|
||||
if m.requests == nil {
|
||||
return authority.InstanceDiscoveryMetadata{}, fmt.Errorf("httpclient in oauth instance for fetching metadata is nil")
|
||||
}
|
||||
m.aadCacheMu.Lock()
|
||||
defer m.aadCacheMu.Unlock()
|
||||
discoveryResponse, err := m.requests.AADInstanceDiscovery(ctx, authorityInfo)
|
||||
|
|
@ -459,6 +464,7 @@ func (m *Manager) readAccount(homeAccountID string, envAliases []string, realm s
|
|||
|
||||
func (m *Manager) writeAccount(account shared.Account) error {
|
||||
key := account.Key()
|
||||
|
||||
m.contractMu.Lock()
|
||||
defer m.contractMu.Unlock()
|
||||
m.contract.Accounts[key] = account
|
||||
|
|
@ -31,4 +31,6 @@ type TokenProviderResult struct {
|
|||
AccessToken string
|
||||
// ExpiresInSeconds is the lifetime of the token in seconds
|
||||
ExpiresInSeconds int
|
||||
// RefreshInSeconds indicates the suggested time to refresh the token, if any
|
||||
RefreshInSeconds int
|
||||
}
|
||||
|
|
|
|||
|
|
@ -146,7 +146,8 @@ func (s *Server) handler(w http.ResponseWriter, r *http.Request) {
|
|||
// Note: It is a little weird we handle some errors by not going to the failPage. If they all should,
|
||||
// change this to s.error() and make s.error() write the failPage instead of an error code.
|
||||
_, _ = w.Write([]byte(fmt.Sprintf(failPage, headerErr, desc)))
|
||||
s.putResult(Result{Err: fmt.Errorf(desc)})
|
||||
s.putResult(Result{Err: fmt.Errorf("%s", desc)})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ func (t *Client) Credential(ctx context.Context, authParams authority.AuthParams
|
|||
Scopes: scopes,
|
||||
TenantID: authParams.AuthorityInfo.Tenant,
|
||||
}
|
||||
tr, err := cred.TokenProvider(ctx, params)
|
||||
pr, err := cred.TokenProvider(ctx, params)
|
||||
if err != nil {
|
||||
if len(scopes) == 0 {
|
||||
err = fmt.Errorf("token request had an empty authority.AuthParams.Scopes, which may cause the following error: %w", err)
|
||||
|
|
@ -119,14 +119,18 @@ func (t *Client) Credential(ctx context.Context, authParams authority.AuthParams
|
|||
}
|
||||
return accesstokens.TokenResponse{}, err
|
||||
}
|
||||
return accesstokens.TokenResponse{
|
||||
TokenType: authParams.AuthnScheme.AccessTokenType(),
|
||||
AccessToken: tr.AccessToken,
|
||||
ExpiresOn: internalTime.DurationTime{
|
||||
T: now.Add(time.Duration(tr.ExpiresInSeconds) * time.Second),
|
||||
},
|
||||
tr := accesstokens.TokenResponse{
|
||||
TokenType: authParams.AuthnScheme.AccessTokenType(),
|
||||
AccessToken: pr.AccessToken,
|
||||
ExpiresOn: now.Add(time.Duration(pr.ExpiresInSeconds) * time.Second),
|
||||
GrantedScopes: accesstokens.Scopes{Slice: authParams.Scopes},
|
||||
}, nil
|
||||
}
|
||||
if pr.RefreshInSeconds > 0 {
|
||||
tr.RefreshOn = internalTime.DurationTime{
|
||||
T: now.Add(time.Duration(pr.RefreshInSeconds) * time.Second),
|
||||
}
|
||||
}
|
||||
return tr, nil
|
||||
}
|
||||
|
||||
if err := t.resolveEndpoint(ctx, &authParams, ""); err != nil {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import (
|
|||
|
||||
/* #nosec */
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
|
|
@ -68,7 +69,7 @@ type DeviceCodeResponse struct {
|
|||
|
||||
UserCode string `json:"user_code"`
|
||||
DeviceCode string `json:"device_code"`
|
||||
VerificationURL string `json:"verification_url"`
|
||||
VerificationURL string `json:"verification_uri"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
Interval int `json:"interval"`
|
||||
Message string `json:"message"`
|
||||
|
|
@ -112,19 +113,31 @@ func (c *Credential) JWT(ctx context.Context, authParams authority.AuthParams) (
|
|||
}
|
||||
return c.AssertionCallback(ctx, options)
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{
|
||||
claims := jwt.MapClaims{
|
||||
"aud": authParams.Endpoints.TokenEndpoint,
|
||||
"exp": json.Number(strconv.FormatInt(time.Now().Add(10*time.Minute).Unix(), 10)),
|
||||
"iss": authParams.ClientID,
|
||||
"jti": uuid.New().String(),
|
||||
"nbf": json.Number(strconv.FormatInt(time.Now().Unix(), 10)),
|
||||
"sub": authParams.ClientID,
|
||||
})
|
||||
}
|
||||
|
||||
isADFSorDSTS := authParams.AuthorityInfo.AuthorityType == authority.ADFS ||
|
||||
authParams.AuthorityInfo.AuthorityType == authority.DSTS
|
||||
|
||||
var signingMethod jwt.SigningMethod = jwt.SigningMethodPS256
|
||||
thumbprintKey := "x5t#S256"
|
||||
|
||||
if isADFSorDSTS {
|
||||
signingMethod = jwt.SigningMethodRS256
|
||||
thumbprintKey = "x5t"
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(signingMethod, claims)
|
||||
token.Header = map[string]interface{}{
|
||||
"alg": "RS256",
|
||||
"typ": "JWT",
|
||||
"x5t": base64.StdEncoding.EncodeToString(thumbprint(c.Cert)),
|
||||
"alg": signingMethod.Alg(),
|
||||
"typ": "JWT",
|
||||
thumbprintKey: base64.StdEncoding.EncodeToString(thumbprint(c.Cert, signingMethod.Alg())),
|
||||
}
|
||||
|
||||
if authParams.SendX5C {
|
||||
|
|
@ -133,17 +146,23 @@ func (c *Credential) JWT(ctx context.Context, authParams authority.AuthParams) (
|
|||
|
||||
assertion, err := token.SignedString(c.Key)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to sign a JWT token using private key: %w", err)
|
||||
return "", fmt.Errorf("unable to sign JWT token: %w", err)
|
||||
}
|
||||
|
||||
return assertion, nil
|
||||
}
|
||||
|
||||
// thumbprint runs the asn1.Der bytes through sha1 for use in the x5t parameter of JWT.
|
||||
// https://tools.ietf.org/html/rfc7517#section-4.8
|
||||
func thumbprint(cert *x509.Certificate) []byte {
|
||||
/* #nosec */
|
||||
a := sha1.Sum(cert.Raw)
|
||||
return a[:]
|
||||
func thumbprint(cert *x509.Certificate, alg string) []byte {
|
||||
switch alg {
|
||||
case jwt.SigningMethodRS256.Name: // identity providers like ADFS don't support SHA256 assertions, so need to support this
|
||||
hash := sha1.Sum(cert.Raw) /* #nosec */
|
||||
return hash[:]
|
||||
default:
|
||||
hash := sha256.Sum256(cert.Raw)
|
||||
return hash[:]
|
||||
}
|
||||
}
|
||||
|
||||
// Client represents the REST calls to get tokens from token generator backends.
|
||||
|
|
@ -262,11 +281,7 @@ func (c Client) FromClientSecret(ctx context.Context, authParameters authority.A
|
|||
qv.Set(clientID, authParameters.ClientID)
|
||||
addScopeQueryParam(qv, authParameters)
|
||||
|
||||
token, err := c.doTokenResp(ctx, authParameters, qv)
|
||||
if err != nil {
|
||||
return token, fmt.Errorf("FromClientSecret(): %w", err)
|
||||
}
|
||||
return token, nil
|
||||
return c.doTokenResp(ctx, authParameters, qv)
|
||||
}
|
||||
|
||||
func (c Client) FromAssertion(ctx context.Context, authParameters authority.AuthParams, assertion string) (TokenResponse, error) {
|
||||
|
|
@ -281,11 +296,7 @@ func (c Client) FromAssertion(ctx context.Context, authParameters authority.Auth
|
|||
qv.Set(clientInfo, clientInfoVal)
|
||||
addScopeQueryParam(qv, authParameters)
|
||||
|
||||
token, err := c.doTokenResp(ctx, authParameters, qv)
|
||||
if err != nil {
|
||||
return token, fmt.Errorf("FromAssertion(): %w", err)
|
||||
}
|
||||
return token, nil
|
||||
return c.doTokenResp(ctx, authParameters, qv)
|
||||
}
|
||||
|
||||
func (c Client) FromUserAssertionClientSecret(ctx context.Context, authParameters authority.AuthParams, userAssertion string, clientSecret string) (TokenResponse, error) {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
|
@ -173,14 +174,75 @@ type TokenResponse struct {
|
|||
FamilyID string `json:"foci"`
|
||||
IDToken IDToken `json:"id_token"`
|
||||
ClientInfo ClientInfo `json:"client_info"`
|
||||
ExpiresOn internalTime.DurationTime `json:"expires_in"`
|
||||
RefreshOn internalTime.DurationTime `json:"refresh_in,omitempty"`
|
||||
ExpiresOn time.Time `json:"-"`
|
||||
ExtExpiresOn internalTime.DurationTime `json:"ext_expires_in"`
|
||||
GrantedScopes Scopes `json:"scope"`
|
||||
DeclinedScopes []string // This is derived
|
||||
|
||||
AdditionalFields map[string]interface{}
|
||||
scopesComputed bool
|
||||
}
|
||||
|
||||
scopesComputed bool
|
||||
func (tr *TokenResponse) UnmarshalJSON(data []byte) error {
|
||||
type Alias TokenResponse
|
||||
aux := &struct {
|
||||
ExpiresIn internalTime.DurationTime `json:"expires_in,omitempty"`
|
||||
ExpiresOn any `json:"expires_on,omitempty"`
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(tr),
|
||||
}
|
||||
|
||||
// Unmarshal the JSON data into the aux struct
|
||||
if err := json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Function to parse different date formats
|
||||
// This is a workaround for the issue described here:
|
||||
// https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/4963
|
||||
parseExpiresOn := func(expiresOn string) (time.Time, error) {
|
||||
var formats = []string{
|
||||
"01/02/2006 15:04:05", // MM/dd/yyyy HH:mm:ss
|
||||
"2006-01-02 15:04:05", // yyyy-MM-dd HH:mm:ss
|
||||
time.RFC3339Nano, // ISO 8601 (with nanosecond precision)
|
||||
}
|
||||
|
||||
for _, format := range formats {
|
||||
if t, err := time.Parse(format, expiresOn); err == nil {
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
return time.Time{}, fmt.Errorf("invalid ExpiresOn format: %s", expiresOn)
|
||||
}
|
||||
|
||||
if expiresOnStr, ok := aux.ExpiresOn.(string); ok {
|
||||
if ts, err := strconv.ParseInt(expiresOnStr, 10, 64); err == nil {
|
||||
tr.ExpiresOn = time.Unix(ts, 0)
|
||||
return nil
|
||||
}
|
||||
if expiresOnStr != "" {
|
||||
if t, err := parseExpiresOn(expiresOnStr); err != nil {
|
||||
return err
|
||||
} else {
|
||||
tr.ExpiresOn = t
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if ExpiresOn is a number (Unix timestamp or ISO 8601)
|
||||
if expiresOnNum, ok := aux.ExpiresOn.(float64); ok {
|
||||
tr.ExpiresOn = time.Unix(int64(expiresOnNum), 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
if !aux.ExpiresIn.T.IsZero() {
|
||||
tr.ExpiresOn = aux.ExpiresIn.T
|
||||
return nil
|
||||
}
|
||||
return errors.New("expires_in and expires_on are both missing or invalid")
|
||||
}
|
||||
|
||||
// ComputeScope computes the final scopes based on what was granted by the server and
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ func (c *Client) JSONCall(ctx context.Context, endpoint string, headers http.Hea
|
|||
|
||||
if resp != nil {
|
||||
if err := unmarshal(data, resp); err != nil {
|
||||
return fmt.Errorf("json decode error: %w\njson message bytes were: %s", err, string(data))
|
||||
return errors.InvalidJsonErr{Err: fmt.Errorf("json decode error: %w\njson message bytes were: %s", err, string(data))}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
@ -221,7 +221,7 @@ func (c *Client) URLFormCall(ctx context.Context, endpoint string, qv url.Values
|
|||
}
|
||||
if resp != nil {
|
||||
if err := unmarshal(data, resp); err != nil {
|
||||
return fmt.Errorf("json decode error: %w\nraw message was: %s", err, string(data))
|
||||
return errors.InvalidJsonErr{Err: fmt.Errorf("json decode error: %w\nraw message was: %s", err, string(data))}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@
|
|||
package version
|
||||
|
||||
// Version is the version of this client package that is communicated to the server.
|
||||
const Version = "1.2.0"
|
||||
const Version = "1.4.2"
|
||||
|
|
|
|||
28
vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/managedidentity/azure_ml.go
generated
vendored
Normal file
28
vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/managedidentity/azure_ml.go
generated
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package managedidentity
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
func createAzureMLAuthRequest(ctx context.Context, id ID, resource string) (*http.Request, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, os.Getenv(msiEndpointEnvVar), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("secret", os.Getenv(msiSecretEnvVar))
|
||||
q := req.URL.Query()
|
||||
q.Set(apiVersionQueryParameterName, azureMLAPIVersion)
|
||||
q.Set(resourceQueryParameterName, resource)
|
||||
q.Set("clientid", os.Getenv("DEFAULT_IDENTITY_CLIENT_ID"))
|
||||
if cid, ok := id.(UserAssignedClientID); ok {
|
||||
q.Set("clientid", string(cid))
|
||||
}
|
||||
req.URL.RawQuery = q.Encode()
|
||||
return req, nil
|
||||
}
|
||||
37
vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/managedidentity/cloud_shell.go
generated
vendored
Normal file
37
vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/managedidentity/cloud_shell.go
generated
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package managedidentity
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func createCloudShellAuthRequest(ctx context.Context, resource string) (*http.Request, error) {
|
||||
msiEndpoint := os.Getenv(msiEndpointEnvVar)
|
||||
msiEndpointParsed, err := url.Parse(msiEndpoint)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't parse %q: %s", msiEndpoint, err)
|
||||
}
|
||||
|
||||
data := url.Values{}
|
||||
data.Set(resourceQueryParameterName, resource)
|
||||
msiDataEncoded := data.Encode()
|
||||
body := io.NopCloser(strings.NewReader(msiDataEncoded))
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, msiEndpointParsed.String(), body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating http request %s", err)
|
||||
}
|
||||
|
||||
req.Header.Set(metaHTTPHeaderName, "true")
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
return req, nil
|
||||
}
|
||||
717
vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/managedidentity/managedidentity.go
generated
vendored
Normal file
717
vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/managedidentity/managedidentity.go
generated
vendored
Normal file
|
|
@ -0,0 +1,717 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
/*
|
||||
Package managedidentity provides a client for retrieval of Managed Identity applications.
|
||||
The Managed Identity Client is used to acquire a token for managed identity assigned to
|
||||
an azure resource such as Azure function, app service, virtual machine, etc. to acquire a token
|
||||
without using credentials.
|
||||
*/
|
||||
package managedidentity
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/errors"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/storage"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/accesstokens"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/authority"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/shared"
|
||||
)
|
||||
|
||||
// AuthResult contains the results of one token acquisition operation.
|
||||
// For details see https://aka.ms/msal-net-authenticationresult
|
||||
type AuthResult = base.AuthResult
|
||||
|
||||
type TokenSource = base.TokenSource
|
||||
|
||||
const (
|
||||
TokenSourceIdentityProvider = base.TokenSourceIdentityProvider
|
||||
TokenSourceCache = base.TokenSourceCache
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultToIMDS indicates that the source is defaulted to IMDS when no environment variables are set.
|
||||
DefaultToIMDS Source = "DefaultToIMDS"
|
||||
AzureArc Source = "AzureArc"
|
||||
ServiceFabric Source = "ServiceFabric"
|
||||
CloudShell Source = "CloudShell"
|
||||
AzureML Source = "AzureML"
|
||||
AppService Source = "AppService"
|
||||
|
||||
// General request query parameter names
|
||||
metaHTTPHeaderName = "Metadata"
|
||||
apiVersionQueryParameterName = "api-version"
|
||||
resourceQueryParameterName = "resource"
|
||||
wwwAuthenticateHeaderName = "www-authenticate"
|
||||
|
||||
// UAMI query parameter name
|
||||
miQueryParameterClientId = "client_id"
|
||||
miQueryParameterObjectId = "object_id"
|
||||
miQueryParameterPrincipalId = "principal_id"
|
||||
miQueryParameterResourceIdIMDS = "msi_res_id"
|
||||
miQueryParameterResourceId = "mi_res_id"
|
||||
|
||||
// IMDS
|
||||
imdsDefaultEndpoint = "http://169.254.169.254/metadata/identity/oauth2/token"
|
||||
imdsAPIVersion = "2018-02-01"
|
||||
systemAssignedManagedIdentity = "system_assigned_managed_identity"
|
||||
|
||||
// Azure Arc
|
||||
azureArcEndpoint = "http://127.0.0.1:40342/metadata/identity/oauth2/token"
|
||||
azureArcAPIVersion = "2020-06-01"
|
||||
azureArcFileExtension = ".key"
|
||||
azureArcMaxFileSizeBytes int64 = 4096
|
||||
linuxTokenPath = "/var/opt/azcmagent/tokens" // #nosec G101
|
||||
linuxHimdsPath = "/opt/azcmagent/bin/himds"
|
||||
azureConnectedMachine = "AzureConnectedMachineAgent"
|
||||
himdsExecutableName = "himds.exe"
|
||||
tokenName = "Tokens"
|
||||
|
||||
// App Service
|
||||
appServiceAPIVersion = "2019-08-01"
|
||||
|
||||
// AzureML
|
||||
azureMLAPIVersion = "2017-09-01"
|
||||
// Service Fabric
|
||||
serviceFabricAPIVersion = "2019-07-01-preview"
|
||||
|
||||
// Environment Variables
|
||||
identityEndpointEnvVar = "IDENTITY_ENDPOINT"
|
||||
identityHeaderEnvVar = "IDENTITY_HEADER"
|
||||
azurePodIdentityAuthorityHostEnvVar = "AZURE_POD_IDENTITY_AUTHORITY_HOST"
|
||||
imdsEndVar = "IMDS_ENDPOINT"
|
||||
msiEndpointEnvVar = "MSI_ENDPOINT"
|
||||
msiSecretEnvVar = "MSI_SECRET"
|
||||
identityServerThumbprintEnvVar = "IDENTITY_SERVER_THUMBPRINT"
|
||||
|
||||
defaultRetryCount = 3
|
||||
)
|
||||
|
||||
var retryCodesForIMDS = []int{
|
||||
http.StatusNotFound, // 404
|
||||
http.StatusGone, // 410
|
||||
http.StatusTooManyRequests, // 429
|
||||
http.StatusInternalServerError, // 500
|
||||
http.StatusNotImplemented, // 501
|
||||
http.StatusBadGateway, // 502
|
||||
http.StatusServiceUnavailable, // 503
|
||||
http.StatusGatewayTimeout, // 504
|
||||
http.StatusHTTPVersionNotSupported, // 505
|
||||
http.StatusVariantAlsoNegotiates, // 506
|
||||
http.StatusInsufficientStorage, // 507
|
||||
http.StatusLoopDetected, // 508
|
||||
http.StatusNotExtended, // 510
|
||||
http.StatusNetworkAuthenticationRequired, // 511
|
||||
}
|
||||
|
||||
var retryStatusCodes = []int{
|
||||
http.StatusRequestTimeout, // 408
|
||||
http.StatusTooManyRequests, // 429
|
||||
http.StatusInternalServerError, // 500
|
||||
http.StatusBadGateway, // 502
|
||||
http.StatusServiceUnavailable, // 503
|
||||
http.StatusGatewayTimeout, // 504
|
||||
}
|
||||
|
||||
var getAzureArcPlatformPath = func(platform string) string {
|
||||
switch platform {
|
||||
case "windows":
|
||||
return filepath.Join(os.Getenv("ProgramData"), azureConnectedMachine, tokenName)
|
||||
case "linux":
|
||||
return linuxTokenPath
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
var getAzureArcHimdsFilePath = func(platform string) string {
|
||||
switch platform {
|
||||
case "windows":
|
||||
return filepath.Join(os.Getenv("ProgramData"), azureConnectedMachine, himdsExecutableName)
|
||||
case "linux":
|
||||
return linuxHimdsPath
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
type Source string
|
||||
|
||||
type ID interface {
|
||||
value() string
|
||||
}
|
||||
|
||||
type systemAssignedValue string // its private for a reason to make the input consistent.
|
||||
type UserAssignedClientID string
|
||||
type UserAssignedObjectID string
|
||||
type UserAssignedResourceID string
|
||||
|
||||
func (s systemAssignedValue) value() string { return string(s) }
|
||||
func (c UserAssignedClientID) value() string { return string(c) }
|
||||
func (o UserAssignedObjectID) value() string { return string(o) }
|
||||
func (r UserAssignedResourceID) value() string { return string(r) }
|
||||
func SystemAssigned() ID {
|
||||
return systemAssignedValue(systemAssignedManagedIdentity)
|
||||
}
|
||||
|
||||
// cache never uses the client because instance discovery is always disabled.
|
||||
var cacheManager *storage.Manager = storage.New(nil)
|
||||
|
||||
type Client struct {
|
||||
httpClient ops.HTTPClient
|
||||
miType ID
|
||||
source Source
|
||||
authParams authority.AuthParams
|
||||
retryPolicyEnabled bool
|
||||
canRefresh *atomic.Value
|
||||
}
|
||||
|
||||
type AcquireTokenOptions struct {
|
||||
claims string
|
||||
}
|
||||
|
||||
type ClientOption func(*Client)
|
||||
|
||||
type AcquireTokenOption func(o *AcquireTokenOptions)
|
||||
|
||||
// WithClaims sets additional claims to request for the token, such as those required by token revocation or conditional access policies.
|
||||
// Use this option when Azure AD returned a claims challenge for a prior request. The argument must be decoded.
|
||||
func WithClaims(claims string) AcquireTokenOption {
|
||||
return func(o *AcquireTokenOptions) {
|
||||
o.claims = claims
|
||||
}
|
||||
}
|
||||
|
||||
// WithHTTPClient allows for a custom HTTP client to be set.
|
||||
func WithHTTPClient(httpClient ops.HTTPClient) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.httpClient = httpClient
|
||||
}
|
||||
}
|
||||
|
||||
func WithRetryPolicyDisabled() ClientOption {
|
||||
return func(c *Client) {
|
||||
c.retryPolicyEnabled = false
|
||||
}
|
||||
}
|
||||
|
||||
// Client to be used to acquire tokens for managed identity.
|
||||
// ID: [SystemAssigned], [UserAssignedClientID], [UserAssignedResourceID], [UserAssignedObjectID]
|
||||
//
|
||||
// Options: [WithHTTPClient]
|
||||
func New(id ID, options ...ClientOption) (Client, error) {
|
||||
source, err := GetSource()
|
||||
if err != nil {
|
||||
return Client{}, err
|
||||
}
|
||||
|
||||
// Check for user-assigned restrictions based on the source
|
||||
switch source {
|
||||
case AzureArc:
|
||||
switch id.(type) {
|
||||
case UserAssignedClientID, UserAssignedResourceID, UserAssignedObjectID:
|
||||
return Client{}, errors.New("Azure Arc doesn't support user-assigned managed identities")
|
||||
}
|
||||
case AzureML:
|
||||
switch id.(type) {
|
||||
case UserAssignedObjectID, UserAssignedResourceID:
|
||||
return Client{}, errors.New("Azure ML supports specifying a user-assigned managed identity by client ID only")
|
||||
}
|
||||
case CloudShell:
|
||||
switch id.(type) {
|
||||
case UserAssignedClientID, UserAssignedResourceID, UserAssignedObjectID:
|
||||
return Client{}, errors.New("Cloud Shell doesn't support user-assigned managed identities")
|
||||
}
|
||||
case ServiceFabric:
|
||||
switch id.(type) {
|
||||
case UserAssignedClientID, UserAssignedResourceID, UserAssignedObjectID:
|
||||
return Client{}, errors.New("Service Fabric API doesn't support specifying a user-assigned identity. The identity is determined by cluster resource configuration. See https://aka.ms/servicefabricmi")
|
||||
}
|
||||
}
|
||||
|
||||
switch t := id.(type) {
|
||||
case UserAssignedClientID:
|
||||
if len(string(t)) == 0 {
|
||||
return Client{}, fmt.Errorf("empty %T", t)
|
||||
}
|
||||
case UserAssignedResourceID:
|
||||
if len(string(t)) == 0 {
|
||||
return Client{}, fmt.Errorf("empty %T", t)
|
||||
}
|
||||
case UserAssignedObjectID:
|
||||
if len(string(t)) == 0 {
|
||||
return Client{}, fmt.Errorf("empty %T", t)
|
||||
}
|
||||
case systemAssignedValue:
|
||||
default:
|
||||
return Client{}, fmt.Errorf("unsupported type %T", id)
|
||||
}
|
||||
zero := atomic.Value{}
|
||||
zero.Store(false)
|
||||
client := Client{
|
||||
miType: id,
|
||||
httpClient: shared.DefaultClient,
|
||||
retryPolicyEnabled: true,
|
||||
source: source,
|
||||
canRefresh: &zero,
|
||||
}
|
||||
for _, option := range options {
|
||||
option(&client)
|
||||
}
|
||||
fakeAuthInfo, err := authority.NewInfoFromAuthorityURI("https://login.microsoftonline.com/managed_identity", false, true)
|
||||
if err != nil {
|
||||
return Client{}, err
|
||||
}
|
||||
client.authParams = authority.NewAuthParams(client.miType.value(), fakeAuthInfo)
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// GetSource detects and returns the managed identity source available on the environment.
|
||||
func GetSource() (Source, error) {
|
||||
identityEndpoint := os.Getenv(identityEndpointEnvVar)
|
||||
identityHeader := os.Getenv(identityHeaderEnvVar)
|
||||
identityServerThumbprint := os.Getenv(identityServerThumbprintEnvVar)
|
||||
msiEndpoint := os.Getenv(msiEndpointEnvVar)
|
||||
msiSecret := os.Getenv(msiSecretEnvVar)
|
||||
imdsEndpoint := os.Getenv(imdsEndVar)
|
||||
|
||||
if identityEndpoint != "" && identityHeader != "" {
|
||||
if identityServerThumbprint != "" {
|
||||
return ServiceFabric, nil
|
||||
}
|
||||
return AppService, nil
|
||||
} else if msiEndpoint != "" {
|
||||
if msiSecret != "" {
|
||||
return AzureML, nil
|
||||
} else {
|
||||
return CloudShell, nil
|
||||
}
|
||||
} else if isAzureArcEnvironment(identityEndpoint, imdsEndpoint) {
|
||||
return AzureArc, nil
|
||||
}
|
||||
|
||||
return DefaultToIMDS, nil
|
||||
}
|
||||
|
||||
// This function wraps time.Now() and is used for refreshing the application
|
||||
// was created to test the function against refreshin
|
||||
var now = time.Now
|
||||
|
||||
// Acquires tokens from the configured managed identity on an azure resource.
|
||||
//
|
||||
// Resource: scopes application is requesting access to
|
||||
// Options: [WithClaims]
|
||||
func (c Client) AcquireToken(ctx context.Context, resource string, options ...AcquireTokenOption) (AuthResult, error) {
|
||||
resource = strings.TrimSuffix(resource, "/.default")
|
||||
o := AcquireTokenOptions{}
|
||||
for _, option := range options {
|
||||
option(&o)
|
||||
}
|
||||
c.authParams.Scopes = []string{resource}
|
||||
|
||||
// ignore cached access tokens when given claims
|
||||
if o.claims == "" {
|
||||
stResp, err := cacheManager.Read(ctx, c.authParams)
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
ar, err := base.AuthResultFromStorage(stResp)
|
||||
if err == nil {
|
||||
if !stResp.AccessToken.RefreshOn.T.IsZero() && !stResp.AccessToken.RefreshOn.T.After(now()) && c.canRefresh.CompareAndSwap(false, true) {
|
||||
defer c.canRefresh.Store(false)
|
||||
if tr, er := c.getToken(ctx, resource); er == nil {
|
||||
return tr, nil
|
||||
}
|
||||
}
|
||||
ar.AccessToken, err = c.authParams.AuthnScheme.FormatAccessToken(ar.AccessToken)
|
||||
return ar, err
|
||||
}
|
||||
}
|
||||
return c.getToken(ctx, resource)
|
||||
}
|
||||
|
||||
func (c Client) getToken(ctx context.Context, resource string) (AuthResult, error) {
|
||||
switch c.source {
|
||||
case AzureArc:
|
||||
return c.acquireTokenForAzureArc(ctx, resource)
|
||||
case AzureML:
|
||||
return c.acquireTokenForAzureML(ctx, resource)
|
||||
case CloudShell:
|
||||
return c.acquireTokenForCloudShell(ctx, resource)
|
||||
case DefaultToIMDS:
|
||||
return c.acquireTokenForIMDS(ctx, resource)
|
||||
case AppService:
|
||||
return c.acquireTokenForAppService(ctx, resource)
|
||||
case ServiceFabric:
|
||||
return c.acquireTokenForServiceFabric(ctx, resource)
|
||||
default:
|
||||
return AuthResult{}, fmt.Errorf("unsupported source %q", c.source)
|
||||
}
|
||||
}
|
||||
|
||||
func (c Client) acquireTokenForAppService(ctx context.Context, resource string) (AuthResult, error) {
|
||||
req, err := createAppServiceAuthRequest(ctx, c.miType, resource)
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
tokenResponse, err := c.getTokenForRequest(req, resource)
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
return authResultFromToken(c.authParams, tokenResponse)
|
||||
}
|
||||
|
||||
func (c Client) acquireTokenForIMDS(ctx context.Context, resource string) (AuthResult, error) {
|
||||
req, err := createIMDSAuthRequest(ctx, c.miType, resource)
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
tokenResponse, err := c.getTokenForRequest(req, resource)
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
return authResultFromToken(c.authParams, tokenResponse)
|
||||
}
|
||||
|
||||
func (c Client) acquireTokenForCloudShell(ctx context.Context, resource string) (AuthResult, error) {
|
||||
req, err := createCloudShellAuthRequest(ctx, resource)
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
tokenResponse, err := c.getTokenForRequest(req, resource)
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
return authResultFromToken(c.authParams, tokenResponse)
|
||||
}
|
||||
|
||||
func (c Client) acquireTokenForAzureML(ctx context.Context, resource string) (AuthResult, error) {
|
||||
req, err := createAzureMLAuthRequest(ctx, c.miType, resource)
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
tokenResponse, err := c.getTokenForRequest(req, resource)
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
return authResultFromToken(c.authParams, tokenResponse)
|
||||
}
|
||||
|
||||
func (c Client) acquireTokenForServiceFabric(ctx context.Context, resource string) (AuthResult, error) {
|
||||
req, err := createServiceFabricAuthRequest(ctx, resource)
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
tokenResponse, err := c.getTokenForRequest(req, resource)
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
return authResultFromToken(c.authParams, tokenResponse)
|
||||
}
|
||||
|
||||
func (c Client) acquireTokenForAzureArc(ctx context.Context, resource string) (AuthResult, error) {
|
||||
req, err := createAzureArcAuthRequest(ctx, resource, "")
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
|
||||
response, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
if response.StatusCode != http.StatusUnauthorized {
|
||||
return AuthResult{}, fmt.Errorf("expected a 401 response, received %d", response.StatusCode)
|
||||
}
|
||||
|
||||
secret, err := c.getAzureArcSecretKey(response, runtime.GOOS)
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
|
||||
secondRequest, err := createAzureArcAuthRequest(ctx, resource, string(secret))
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
|
||||
tokenResponse, err := c.getTokenForRequest(secondRequest, resource)
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
return authResultFromToken(c.authParams, tokenResponse)
|
||||
}
|
||||
|
||||
func authResultFromToken(authParams authority.AuthParams, token accesstokens.TokenResponse) (AuthResult, error) {
|
||||
if cacheManager == nil {
|
||||
return AuthResult{}, errors.New("cache instance is nil")
|
||||
}
|
||||
account, err := cacheManager.Write(authParams, token)
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
// if refreshOn is not set, set it to half of the time until expiry if expiry is more than 2 hours away
|
||||
if token.RefreshOn.T.IsZero() {
|
||||
if lifetime := time.Until(token.ExpiresOn); lifetime > 2*time.Hour {
|
||||
token.RefreshOn.T = time.Now().Add(lifetime / 2)
|
||||
}
|
||||
}
|
||||
ar, err := base.NewAuthResult(token, account)
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
ar.AccessToken, err = authParams.AuthnScheme.FormatAccessToken(ar.AccessToken)
|
||||
return ar, err
|
||||
}
|
||||
|
||||
// contains checks if the element is present in the list.
|
||||
func contains[T comparable](list []T, element T) bool {
|
||||
for _, v := range list {
|
||||
if v == element {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// retry performs an HTTP request with retries based on the provided options.
|
||||
func (c Client) retry(maxRetries int, req *http.Request) (*http.Response, error) {
|
||||
var resp *http.Response
|
||||
var err error
|
||||
for attempt := 0; attempt < maxRetries; attempt++ {
|
||||
tryCtx, tryCancel := context.WithTimeout(req.Context(), time.Minute)
|
||||
defer tryCancel()
|
||||
if resp != nil && resp.Body != nil {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
}
|
||||
cloneReq := req.Clone(tryCtx)
|
||||
resp, err = c.httpClient.Do(cloneReq)
|
||||
retrylist := retryStatusCodes
|
||||
if c.source == DefaultToIMDS {
|
||||
retrylist = retryCodesForIMDS
|
||||
}
|
||||
if err == nil && !contains(retrylist, resp.StatusCode) {
|
||||
return resp, nil
|
||||
}
|
||||
select {
|
||||
case <-time.After(time.Second):
|
||||
case <-req.Context().Done():
|
||||
err = req.Context().Err()
|
||||
return resp, err
|
||||
}
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (c Client) getTokenForRequest(req *http.Request, resource string) (accesstokens.TokenResponse, error) {
|
||||
r := accesstokens.TokenResponse{}
|
||||
var resp *http.Response
|
||||
var err error
|
||||
|
||||
if c.retryPolicyEnabled {
|
||||
resp, err = c.retry(defaultRetryCount, req)
|
||||
} else {
|
||||
resp, err = c.httpClient.Do(req)
|
||||
}
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
responseBytes, err := io.ReadAll(resp.Body)
|
||||
defer resp.Body.Close()
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK, http.StatusAccepted:
|
||||
default:
|
||||
sd := strings.TrimSpace(string(responseBytes))
|
||||
if sd != "" {
|
||||
return r, errors.CallErr{
|
||||
Req: req,
|
||||
Resp: resp,
|
||||
Err: fmt.Errorf("http call(%s)(%s) error: reply status code was %d:\n%s",
|
||||
req.URL.String(),
|
||||
req.Method,
|
||||
resp.StatusCode,
|
||||
sd),
|
||||
}
|
||||
}
|
||||
return r, errors.CallErr{
|
||||
Req: req,
|
||||
Resp: resp,
|
||||
Err: fmt.Errorf("http call(%s)(%s) error: reply status code was %d", req.URL.String(), req.Method, resp.StatusCode),
|
||||
}
|
||||
}
|
||||
|
||||
err = json.Unmarshal(responseBytes, &r)
|
||||
if err != nil {
|
||||
return r, errors.InvalidJsonErr{
|
||||
Err: fmt.Errorf("error parsing the json error: %s", err),
|
||||
}
|
||||
}
|
||||
r.GrantedScopes.Slice = append(r.GrantedScopes.Slice, resource)
|
||||
|
||||
return r, err
|
||||
}
|
||||
|
||||
func createAppServiceAuthRequest(ctx context.Context, id ID, resource string) (*http.Request, error) {
|
||||
identityEndpoint := os.Getenv(identityEndpointEnvVar)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, identityEndpoint, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("X-IDENTITY-HEADER", os.Getenv(identityHeaderEnvVar))
|
||||
q := req.URL.Query()
|
||||
q.Set("api-version", appServiceAPIVersion)
|
||||
q.Set("resource", resource)
|
||||
switch t := id.(type) {
|
||||
case UserAssignedClientID:
|
||||
q.Set(miQueryParameterClientId, string(t))
|
||||
case UserAssignedResourceID:
|
||||
q.Set(miQueryParameterResourceId, string(t))
|
||||
case UserAssignedObjectID:
|
||||
q.Set(miQueryParameterObjectId, string(t))
|
||||
case systemAssignedValue:
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported type %T", id)
|
||||
}
|
||||
req.URL.RawQuery = q.Encode()
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func createIMDSAuthRequest(ctx context.Context, id ID, resource string) (*http.Request, error) {
|
||||
msiEndpoint, err := url.Parse(imdsDefaultEndpoint)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't parse %q: %s", imdsDefaultEndpoint, err)
|
||||
}
|
||||
msiParameters := msiEndpoint.Query()
|
||||
msiParameters.Set(apiVersionQueryParameterName, imdsAPIVersion)
|
||||
msiParameters.Set(resourceQueryParameterName, resource)
|
||||
|
||||
switch t := id.(type) {
|
||||
case UserAssignedClientID:
|
||||
msiParameters.Set(miQueryParameterClientId, string(t))
|
||||
case UserAssignedResourceID:
|
||||
msiParameters.Set(miQueryParameterResourceIdIMDS, string(t))
|
||||
case UserAssignedObjectID:
|
||||
msiParameters.Set(miQueryParameterObjectId, string(t))
|
||||
case systemAssignedValue: // not adding anything
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported type %T", id)
|
||||
}
|
||||
|
||||
msiEndpoint.RawQuery = msiParameters.Encode()
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, msiEndpoint.String(), nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating http request %s", err)
|
||||
}
|
||||
req.Header.Set(metaHTTPHeaderName, "true")
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func createAzureArcAuthRequest(ctx context.Context, resource string, key string) (*http.Request, error) {
|
||||
identityEndpoint := os.Getenv(identityEndpointEnvVar)
|
||||
if identityEndpoint == "" {
|
||||
identityEndpoint = azureArcEndpoint
|
||||
}
|
||||
msiEndpoint, parseErr := url.Parse(identityEndpoint)
|
||||
|
||||
if parseErr != nil {
|
||||
return nil, fmt.Errorf("couldn't parse %q: %s", identityEndpoint, parseErr)
|
||||
}
|
||||
|
||||
msiParameters := msiEndpoint.Query()
|
||||
msiParameters.Set(apiVersionQueryParameterName, azureArcAPIVersion)
|
||||
msiParameters.Set(resourceQueryParameterName, resource)
|
||||
|
||||
msiEndpoint.RawQuery = msiParameters.Encode()
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, msiEndpoint.String(), nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating http request %s", err)
|
||||
}
|
||||
req.Header.Set(metaHTTPHeaderName, "true")
|
||||
|
||||
if key != "" {
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Basic %s", key))
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func isAzureArcEnvironment(identityEndpoint, imdsEndpoint string) bool {
|
||||
if identityEndpoint != "" && imdsEndpoint != "" {
|
||||
return true
|
||||
}
|
||||
himdsFilePath := getAzureArcHimdsFilePath(runtime.GOOS)
|
||||
if himdsFilePath != "" {
|
||||
if _, err := os.Stat(himdsFilePath); err == nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Client) getAzureArcSecretKey(response *http.Response, platform string) (string, error) {
|
||||
wwwAuthenticateHeader := response.Header.Get(wwwAuthenticateHeaderName)
|
||||
|
||||
if len(wwwAuthenticateHeader) == 0 {
|
||||
return "", errors.New("response has no www-authenticate header")
|
||||
}
|
||||
|
||||
// check if the platform is supported
|
||||
expectedSecretFilePath := getAzureArcPlatformPath(platform)
|
||||
if expectedSecretFilePath == "" {
|
||||
return "", errors.New("platform not supported, expected linux or windows")
|
||||
}
|
||||
|
||||
parts := strings.Split(wwwAuthenticateHeader, "Basic realm=")
|
||||
if len(parts) < 2 {
|
||||
return "", fmt.Errorf("basic realm= not found in the string, instead found: %s", wwwAuthenticateHeader)
|
||||
}
|
||||
|
||||
secretFilePath := parts
|
||||
|
||||
// check that the file in the file path is a .key file
|
||||
fileName := filepath.Base(secretFilePath[1])
|
||||
if !strings.HasSuffix(fileName, azureArcFileExtension) {
|
||||
return "", fmt.Errorf("invalid file extension, expected %s, got %s", azureArcFileExtension, filepath.Ext(fileName))
|
||||
}
|
||||
|
||||
// check that file path from header matches the expected file path for the platform
|
||||
if expectedSecretFilePath != filepath.Dir(secretFilePath[1]) {
|
||||
return "", fmt.Errorf("invalid file path, expected %s, got %s", expectedSecretFilePath, filepath.Dir(secretFilePath[1]))
|
||||
}
|
||||
|
||||
fileInfo, err := os.Stat(secretFilePath[1])
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get metadata for %s due to error: %s", secretFilePath[1], err)
|
||||
}
|
||||
|
||||
// Throw an error if the secret file's size is greater than 4096 bytes
|
||||
if s := fileInfo.Size(); s > azureArcMaxFileSizeBytes {
|
||||
return "", fmt.Errorf("invalid secret file size, expected %d, file size was %d", azureArcMaxFileSizeBytes, s)
|
||||
}
|
||||
|
||||
// Attempt to read the contents of the secret file
|
||||
secret, err := os.ReadFile(secretFilePath[1])
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read %q due to error: %s", secretFilePath[1], err)
|
||||
}
|
||||
|
||||
return string(secret), nil
|
||||
}
|
||||
25
vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/managedidentity/servicefabric.go
generated
vendored
Normal file
25
vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/managedidentity/servicefabric.go
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
package managedidentity
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
func createServiceFabricAuthRequest(ctx context.Context, resource string) (*http.Request, error) {
|
||||
identityEndpoint := os.Getenv(identityEndpointEnvVar)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, identityEndpoint, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Accept", "application/json")
|
||||
req.Header.Set("Secret", os.Getenv(identityHeaderEnvVar))
|
||||
q := req.URL.Query()
|
||||
q.Set("api-version", serviceFabricAPIVersion)
|
||||
q.Set("resource", resource)
|
||||
req.URL.RawQuery = q.Encode()
|
||||
return req, nil
|
||||
}
|
||||
13
vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/public/public.go
generated
vendored
13
vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/public/public.go
generated
vendored
|
|
@ -51,6 +51,13 @@ type AuthenticationScheme = authority.AuthenticationScheme
|
|||
|
||||
type Account = shared.Account
|
||||
|
||||
type TokenSource = base.TokenSource
|
||||
|
||||
const (
|
||||
TokenSourceIdentityProvider = base.TokenSourceIdentityProvider
|
||||
TokenSourceCache = base.TokenSourceCache
|
||||
)
|
||||
|
||||
var errNoAccount = errors.New("no account was specified with public.WithSilentAccount(), or the specified account is invalid")
|
||||
|
||||
// clientOptions configures the Client's behavior.
|
||||
|
|
@ -387,7 +394,7 @@ func (pca Client) AcquireTokenByUsernamePassword(ctx context.Context, scopes []s
|
|||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
return pca.base.AuthResultFromToken(ctx, authParams, token, true)
|
||||
return pca.base.AuthResultFromToken(ctx, authParams, token)
|
||||
}
|
||||
|
||||
type DeviceCodeResult = accesstokens.DeviceCodeResult
|
||||
|
|
@ -412,7 +419,7 @@ func (d DeviceCode) AuthenticationResult(ctx context.Context) (AuthResult, error
|
|||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
return d.client.base.AuthResultFromToken(ctx, d.authParams, token, true)
|
||||
return d.client.base.AuthResultFromToken(ctx, d.authParams, token)
|
||||
}
|
||||
|
||||
// acquireTokenByDeviceCodeOptions contains optional configuration for AcquireTokenByDeviceCode
|
||||
|
|
@ -687,7 +694,7 @@ func (pca Client) AcquireTokenInteractive(ctx context.Context, scopes []string,
|
|||
return AuthResult{}, err
|
||||
}
|
||||
|
||||
return pca.base.AuthResultFromToken(ctx, authParams, token, true)
|
||||
return pca.base.AuthResultFromToken(ctx, authParams, token)
|
||||
}
|
||||
|
||||
type interactiveAuthResult struct {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue