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

Prevent ManagedIdentityCredential mutating GetToken arguments #15331

Merged
merged 2 commits into from
Aug 20, 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 @@ -7,6 +7,7 @@
### Breaking Changes

### Bugs Fixed
* `ManagedIdentityCredential.GetToken` no longer mutates its `opts.Scopes`

### Other Changes

Expand Down
6 changes: 3 additions & 3 deletions sdk/azidentity/managed_identity_credential.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ func (c *ManagedIdentityCredential) GetToken(ctx context.Context, opts azcore.To
addGetTokenFailureLogs("Managed Identity Credential", err, true)
return nil, err
}
// The following code will remove the /.default suffix from any scopes passed into the method since ManagedIdentityCredentials expect a resource string instead of a scope string
opts.Scopes[0] = strings.TrimSuffix(opts.Scopes[0], defaultSuffix)
tk, err := c.client.authenticate(ctx, c.id, opts.Scopes)
// managed identity endpoints require an AADv1 resource (i.e. token audience), not a v2 scope, so we remove "/.default" here
scopes := []string{strings.TrimSuffix(opts.Scopes[0], defaultSuffix)}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's okay to allocate a slice here unconditionally because we'll normally need to trim the suffix but I'm curious, do we prefer to limit such allocations generally?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems totally reasonable here b/c we need to mutate the input. @jhendrixMSFT, Is there design guidance surrounding this, should there be?

Copy link
Member

@jhendrixMSFT jhendrixMSFT Aug 20, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depending on escape analysis, scopes might or might not be allocated on the heap. Given that it's pretty small, I would guess that it's allocated on the stack given that it's passed to callers and doesn't escape the callee, however you'd have to profile to be sure.

Generally speaking, the language is opaque WRT when things are stack vs heap allocated as this can change as the compiler improves. The general guidance is to write the code with the semantics you want, potentially changing it based on profiling (obviously there are exceptions but this is a good place to start).

tk, err := c.client.authenticate(ctx, c.id, scopes)
if err != nil {
addGetTokenFailureLogs("Managed Identity Credential", err, true)
return nil, err
Expand Down
25 changes: 25 additions & 0 deletions sdk/azidentity/managed_identity_credential_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,31 @@ func TestManagedIdentityCredential_GetTokenNilResource(t *testing.T) {
}
}

func TestManagedIdentityCredential_ScopesImmutable(t *testing.T) {
resetEnvironmentVarsForTest()
srv, close := mock.NewServer()
defer close()
srv.AppendResponse(mock.WithBody([]byte(expiresOnIntResp)))
_ = os.Setenv(msiEndpoint, srv.URL())
defer clearEnvVars(msiEndpoint)
options := ManagedIdentityCredentialOptions{
HTTPClient: srv,
}
cred, err := NewManagedIdentityCredential("", &options)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
scope := "https://localhost/.default"
scopes := []string{scope}
_, err = cred.GetToken(context.Background(), azcore.TokenRequestOptions{Scopes: scopes})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if scopes[0] != scope {
t.Fatalf("GetToken shouldn't mutate arguments")
}
}

func TestManagedIdentityCredential_GetTokenMultipleResources(t *testing.T) {
resetEnvironmentVarsForTest()
srv, close := mock.NewServer()
Expand Down