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 AzureCLICredentialOptions.TenantID #15761

Merged
merged 1 commit into from
Oct 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions sdk/azidentity/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@

### Other Changes
* `NewDefaultAzureCredential()` returns `*DefaultAzureCredential` instead of `*ChainedTokenCredential`
* Added `TenantID` field to `DefaultAzureCredentialOptions` and `AzureCLICredentialOptions`

## 0.11.0 (2021-09-08)
### Breaking Changes
Expand Down
17 changes: 12 additions & 5 deletions sdk/azidentity/azure_cli_credential.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@ import (
)

// used by tests to fake invoking the CLI
type azureCLITokenProvider func(ctx context.Context, resource string) ([]byte, error)
type azureCLITokenProvider func(ctx context.Context, resource string, tenantID string) ([]byte, error)

// AzureCLICredentialOptions contains options used to configure the AzureCLICredential
// All zero-value fields will be initialized with their default values.
type AzureCLICredentialOptions struct {
tokenProvider azureCLITokenProvider
// TenantID identifies the tenant the credential should authenticate in.
// Defaults to the CLI's default tenant, which is typically the home tenant of the user logged in to the CLI.
TenantID string
}

// init returns an instance of AzureCLICredentialOptions initialized with default values.
Expand All @@ -38,6 +41,7 @@ func (o *AzureCLICredentialOptions) init() {
// AzureCLICredential enables authentication to Azure Active Directory using the Azure CLI command "az account get-access-token".
type AzureCLICredential struct {
tokenProvider azureCLITokenProvider
tenantID string
}

// NewAzureCLICredential constructs a new AzureCLICredential with the details needed to authenticate against Azure Active Directory
Expand All @@ -50,6 +54,7 @@ func NewAzureCLICredential(options *AzureCLICredentialOptions) (*AzureCLICredent
cp.init()
return &AzureCLICredential{
tokenProvider: cp.tokenProvider,
tenantID: cp.TenantID,
}, nil
}

Expand All @@ -76,16 +81,16 @@ const timeoutCLIRequest = 10000 * time.Millisecond
// ctx: The current request context
// scopes: The scopes for which the token has access
func (c *AzureCLICredential) authenticate(ctx context.Context, resource string) (*azcore.AccessToken, error) {
output, err := c.tokenProvider(ctx, resource)
output, err := c.tokenProvider(ctx, resource, c.tenantID)
if err != nil {
return nil, err
}

return c.createAccessToken(output)
}

func defaultTokenProvider() func(ctx context.Context, resource string) ([]byte, error) {
return func(ctx context.Context, resource string) ([]byte, error) {
func defaultTokenProvider() func(ctx context.Context, resource string, tenantID string) ([]byte, error) {
return func(ctx context.Context, resource string, tenantID string) ([]byte, error) {
// This is the path that a developer can set to tell this class what the install path for Azure CLI is.
const azureCLIPath = "AZURE_CLI_PATH"

Expand Down Expand Up @@ -121,7 +126,9 @@ func defaultTokenProvider() func(ctx context.Context, resource string) ([]byte,
cliCmd.Env = append(cliCmd.Env, fmt.Sprintf("PATH=%s:%s", os.Getenv(azureCLIPath), azureCLIDefaultPath))
}
cliCmd.Args = append(cliCmd.Args, "account", "get-access-token", "-o", "json", "--resource", resource)

if tenantID != "" {
cliCmd.Args = append(cliCmd.Args, "--tenant", tenantID)
}
var stderr bytes.Buffer
cliCmd.Stderr = &stderr

Expand Down
30 changes: 28 additions & 2 deletions sdk/azidentity/azure_cli_credential_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ import (
)

var (
mockCLITokenProviderSuccess = func(ctx context.Context, resource string) ([]byte, error) {
mockCLITokenProviderSuccess = func(ctx context.Context, resource string, tenantID string) ([]byte, error) {
return []byte(" {\"accessToken\":\"mocktoken\" , " +
"\"expiresOn\": \"2007-01-01 01:01:01.079627\"," +
"\"subscription\": \"mocksub\"," +
"\"tenant\": \"mocktenant\"," +
"\"tokenType\": \"mocktype\"}"), nil
}
mockCLITokenProviderFailure = func(ctx context.Context, resource string) ([]byte, error) {
mockCLITokenProviderFailure = func(ctx context.Context, resource string, tenantID string) ([]byte, error) {
return nil, errors.New("provider failure message")
}
)
Expand Down Expand Up @@ -59,6 +59,32 @@ func TestAzureCLICredential_GetTokenInvalidToken(t *testing.T) {
}
}

func TestAzureCLICredential_TenantID(t *testing.T) {
expected := "expected-tenant-id"
called := false
options := AzureCLICredentialOptions{
TenantID: expected,
tokenProvider: func(ctx context.Context, resource, tenantID string) ([]byte, error) {
called = true
if tenantID != expected {
t.Fatal("Unexpected tenant ID: " + tenantID)
}
return mockCLITokenProviderSuccess(ctx, resource, tenantID)
},
}
cred, err := NewAzureCLICredential(&options)
if err != nil {
t.Fatalf("Unable to create credential. Received: %v", err)
}
_, err = cred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{scope}})
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if !called {
t.Fatal("token provider wasn't called")
}
}

func TestBearerPolicy_AzureCLICredential(t *testing.T) {
srv, close := mock.NewTLSServer()
defer close()
Expand Down
5 changes: 4 additions & 1 deletion sdk/azidentity/default_azure_credential.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ type DefaultAzureCredentialOptions struct {
// The host of the Azure Active Directory authority. The default is AzurePublicCloud.
// Leave empty to allow overriding the value from the AZURE_AUTHORITY_HOST environment variable.
AuthorityHost AuthorityHost
// TenantID identifies the tenant the Azure CLI should authenticate in.
// Defaults to the CLI's default tenant, which is typically the home tenant of the user logged in to the CLI.
TenantID string
jhendrixMSFT marked this conversation as resolved.
Show resolved Hide resolved
}

// DefaultAzureCredential is a default credential chain for applications that will be deployed to Azure.
Expand Down Expand Up @@ -62,7 +65,7 @@ func NewDefaultAzureCredential(options *DefaultAzureCredentialOptions) (*Default
errMsg += err.Error()
}

cliCred, err := NewAzureCLICredential(nil)
cliCred, err := NewAzureCLICredential(&AzureCLICredentialOptions{TenantID: options.TenantID})
if err == nil {
creds = append(creds, cliCred)
} else {
Expand Down