Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for authfilecredential #7044

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions sdk/azidentity/auth_file_credential.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package azidentity

import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/url"

"github.com/Azure/azure-sdk-for-go/sdk/azcore"
)

type authFileAccesstoken struct {
ClientId string `json:"clientId"`
ClientSecret string `json:"clientSecret"`
SubscriptionId string `json:"subscriptionId"`
TenantId string `json:"tenantId"`
ActiveDirectoryEndpointUrl string `json:"activeDirectoryEndpointUrl"`
ResourceManagerEndpointUrl string `json:"resourceManagerEndpointUrl"`
ActiveDirectoryGraphResourceId string `json:"activeDirectoryGraphResourceId"`
SqlManagementEndpointUrl string `json:"sqlManagementEndpointUrl"`
GalleryEndpointUrl string `json:"galleryEndpointUrl"`
ManagementEndpointUrl string `json:"managementEndpointUrl"`
}

// Enables authentication to Azure Active Directory using configuration information stored Azure SDK Auth File.
Luyunmt marked this conversation as resolved.
Show resolved Hide resolved
type AuthFileCredential struct {
filePath string
credential azcore.TokenCredential
}

// Creates an instance of the SdkAuthFileCredential class based on information in given SDK Auth file.
Luyunmt marked this conversation as resolved.
Show resolved Hide resolved
func NewAuthFileCredential(filePath string) (*AuthFileCredential, error) {
Luyunmt marked this conversation as resolved.
Show resolved Hide resolved
if filePath == "" {
return nil, fmt.Errorf("The parameter 'filePath' must set a value")
Luyunmt marked this conversation as resolved.
Show resolved Hide resolved
}

return &AuthFileCredential{filePath: filePath}, nil
}

// Obtains a token from the Azure Active Directory service, using the specified client detailed specified in the SDK Auth file.
Luyunmt marked this conversation as resolved.
Show resolved Hide resolved
func (c *AuthFileCredential) GetToken(ctx context.Context, opts azcore.TokenRequestOptions) (*azcore.AccessToken, error) {
_, err := c.ensureCredential()
Luyunmt marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return nil, &AuthenticationFailedError{msg: "Error parsing SDK Auth File: " + err.Error()}
Luyunmt marked this conversation as resolved.
Show resolved Hide resolved
}

return c.credential.GetToken(ctx, opts)
}

// AuthenticationPolicy implements the azcore.Credential interface on AuthFileCredential.
Luyunmt marked this conversation as resolved.
Show resolved Hide resolved
func (c *AuthFileCredential) AuthenticationPolicy(options azcore.AuthenticationPolicyOptions) azcore.Policy {
return newBearerTokenPolicy(c, options)
}

// Ensures that credential information is loaded from the SDK Auth file. This method should be called to initialize.
Luyunmt marked this conversation as resolved.
Show resolved Hide resolved
func (c *AuthFileCredential) ensureCredential() (azcore.TokenCredential, error) {
Luyunmt marked this conversation as resolved.
Show resolved Hide resolved
if c.credential != nil {
Luyunmt marked this conversation as resolved.
Show resolved Hide resolved
return c.credential, nil
}

authData, err := c.parseCredentialsFile(c.filePath)
if err != nil {
return nil, err
}

c.credential, err = c.buildCredentialForCredentialsFile(authData)
if err != nil {
return nil, err
}

return c.credential, nil
}

// Parse credential file from local file path to byte.
func (c *AuthFileCredential) parseCredentialsFile(filePath string) ([]byte, error) {
authData, err := ioutil.ReadFile(filePath)
Luyunmt marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
log.Fatal(err)
Luyunmt marked this conversation as resolved.
Show resolved Hide resolved
}

return authData, nil
}

// Use the SDK auth file info to build a ClientSecretCredential
func (c *AuthFileCredential) buildCredentialForCredentialsFile(authData []byte) (*ClientSecretCredential, error) {
Luyunmt marked this conversation as resolved.
Show resolved Hide resolved
token := &authFileAccesstoken{}

err := json.Unmarshal(authData, token)
if err != nil {
return nil, err
}

clientId := token.ClientId
clientSecret := token.ClientSecret
tenantId := token.TenantId
activeDirectoryEndpointUrl := token.ActiveDirectoryEndpointUrl

if clientId == "" || clientSecret == "" || tenantId == "" || activeDirectoryEndpointUrl == "" {
Luyunmt marked this conversation as resolved.
Show resolved Hide resolved
return nil, fmt.Errorf("Malformed Azure SDK Auth file. The file should contain 'clientId', 'clientSecret', 'tenantId' and 'activeDirectoryEndpointUrl' values.")
}

// Parse string activeDirectoryEndpointUrl to a Url.
AuthorityHost, err := url.Parse(activeDirectoryEndpointUrl)
if err != nil {
return nil, err
Luyunmt marked this conversation as resolved.
Show resolved Hide resolved
}

return NewClientSecretCredential(tenantId, clientId, clientSecret, &TokenCredentialOptions{AuthorityHost: AuthorityHost})
}
33 changes: 33 additions & 0 deletions sdk/azidentity/auth_file_credential_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package azidentity

import (
"context"
"errors"
"testing"

"github.com/Azure/azure-sdk-for-go/sdk/azcore"
)

const (
scopeToResource = "https://storage.azure.com/.default"
)

func TestAuthFileCredential_BadSdkAuthFilePathThrowsDuringGetToken(t *testing.T) {
cred, err := NewAuthFileCredential("Bougs*File*Path")
if err != nil {
t.Fatalf("Expected an empty error but received: %s", err.Error())
}

_, err = cred.GetToken(context.Background(), azcore.TokenRequestOptions{Scopes: []string{scopeToResource}})
if err == nil {
t.Fatalf("Expected an empty error but received: %v", err)
}

var authFailed *AuthenticationFailedError
if !errors.As(err, &authFailed) {
t.Fatalf("Expected: AuthenticationFailedError, Received: %T", err)
}
}