Skip to content

Commit d5eb4e7

Browse files
authored
fix: using ActiveDirectoryServicePrincipalAccessToken does not set password in connection URL (#756)
1 parent 91fa9f7 commit d5eb4e7

2 files changed

Lines changed: 183 additions & 2 deletions

File tree

pkg/sqlcmd/connect.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ func (connect ConnectSettings) RequiresPassword() bool {
8888
requiresPassword := connect.sqlAuthentication()
8989
if !requiresPassword {
9090
switch connect.authenticationMethod() {
91-
case azuread.ActiveDirectoryApplication, azuread.ActiveDirectoryPassword, azuread.ActiveDirectoryServicePrincipal:
91+
case azuread.ActiveDirectoryApplication, azuread.ActiveDirectoryPassword, azuread.ActiveDirectoryServicePrincipal, azuread.ActiveDirectoryServicePrincipalAccessToken:
9292
requiresPassword = true
9393
}
9494
}
@@ -125,7 +125,7 @@ func (connect ConnectSettings) ConnectionString() (connectionString string, err
125125
Path: instance,
126126
}
127127

128-
if connect.sqlAuthentication() || connect.authenticationMethod() == azuread.ActiveDirectoryPassword || connect.authenticationMethod() == azuread.ActiveDirectoryServicePrincipal || connect.authenticationMethod() == azuread.ActiveDirectoryApplication {
128+
if connect.sqlAuthentication() || connect.authenticationMethod() == azuread.ActiveDirectoryPassword || connect.authenticationMethod() == azuread.ActiveDirectoryServicePrincipal || connect.authenticationMethod() == azuread.ActiveDirectoryApplication || connect.authenticationMethod() == azuread.ActiveDirectoryServicePrincipalAccessToken {
129129
connectionURL.User = url.UserPassword(connect.UserName, connect.Password)
130130
}
131131
if (connect.authenticationMethod() == azuread.ActiveDirectoryMSI || connect.authenticationMethod() == azuread.ActiveDirectoryManagedIdentity) && connect.UserName != "" {

pkg/sqlcmd/connect_test.go

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
4+
package sqlcmd
5+
6+
import (
7+
"fmt"
8+
"strings"
9+
"testing"
10+
11+
"github.com/google/uuid"
12+
"github.com/microsoft/go-mssqldb/azuread"
13+
"github.com/stretchr/testify/assert"
14+
)
15+
16+
func TestConnectionStringIncludesPasswordForAuthMethods(t *testing.T) {
17+
authMethodsRequiringPassword := []string{
18+
azuread.ActiveDirectoryPassword,
19+
azuread.ActiveDirectoryServicePrincipal,
20+
azuread.ActiveDirectoryApplication,
21+
azuread.ActiveDirectoryServicePrincipalAccessToken,
22+
}
23+
24+
pwd := uuid.New().String()
25+
26+
for _, method := range authMethodsRequiringPassword {
27+
t.Run(method, func(t *testing.T) {
28+
settings := ConnectSettings{
29+
ServerName: "someserver",
30+
AuthenticationMethod: method,
31+
UserName: "myapp@mytenant",
32+
Password: pwd,
33+
}
34+
35+
connectionString, err := settings.ConnectionString()
36+
if assert.NoError(t, err) {
37+
expected := fmt.Sprintf("sqlserver://myapp%%40mytenant:%s@someserver", pwd)
38+
assert.Equal(t, expected, connectionString,
39+
"auth method %q should include user:password in the connection URL", method)
40+
}
41+
})
42+
}
43+
}
44+
45+
func TestConnectionStringExcludesPasswordForNonCredentialAuthMethods(t *testing.T) {
46+
authMethodsWithoutPassword := []string{
47+
azuread.ActiveDirectoryDefault,
48+
azuread.ActiveDirectoryIntegrated,
49+
azuread.ActiveDirectoryInteractive,
50+
azuread.ActiveDirectoryDeviceCode,
51+
azuread.ActiveDirectoryAzCli,
52+
azuread.ActiveDirectoryAzureDeveloperCli,
53+
azuread.ActiveDirectoryAzurePipelines,
54+
azuread.ActiveDirectoryEnvironment,
55+
azuread.ActiveDirectoryWorkloadIdentity,
56+
azuread.ActiveDirectoryClientAssertion,
57+
azuread.ActiveDirectoryOnBehalfOf,
58+
}
59+
60+
pwd := uuid.New().String()
61+
62+
for _, method := range authMethodsWithoutPassword {
63+
t.Run(method, func(t *testing.T) {
64+
settings := ConnectSettings{
65+
ServerName: "someserver",
66+
AuthenticationMethod: method,
67+
UserName: "myapp@mytenant",
68+
Password: pwd,
69+
}
70+
71+
connectionString, err := settings.ConnectionString()
72+
if assert.NoError(t, err) {
73+
assert.False(t, strings.Contains(connectionString, pwd),
74+
"auth method %q should not include password in the connection URL", method)
75+
}
76+
})
77+
}
78+
}
79+
80+
func TestConnectionStringIncludesPasswordForManagedIdentityWithUserName(t *testing.T) {
81+
managedIdentityMethods := []string{
82+
azuread.ActiveDirectoryMSI,
83+
azuread.ActiveDirectoryManagedIdentity,
84+
}
85+
86+
pwd := uuid.New().String()
87+
88+
for _, method := range managedIdentityMethods {
89+
t.Run(method+"_with_username", func(t *testing.T) {
90+
settings := ConnectSettings{
91+
ServerName: "someserver",
92+
AuthenticationMethod: method,
93+
UserName: "myclientid",
94+
Password: pwd,
95+
}
96+
97+
connectionString, err := settings.ConnectionString()
98+
if assert.NoError(t, err) {
99+
expected := fmt.Sprintf("sqlserver://myclientid:%s@someserver", pwd)
100+
assert.Equal(t, expected, connectionString,
101+
"auth method %q with UserName should include user:password in the connection URL", method)
102+
}
103+
})
104+
105+
t.Run(method+"_without_username", func(t *testing.T) {
106+
settings := ConnectSettings{
107+
ServerName: "someserver",
108+
AuthenticationMethod: method,
109+
Password: pwd,
110+
}
111+
112+
connectionString, err := settings.ConnectionString()
113+
if assert.NoError(t, err) {
114+
assert.False(t, strings.Contains(connectionString, pwd),
115+
"auth method %q without UserName should not include password in the connection URL", method)
116+
}
117+
})
118+
}
119+
}
120+
121+
func TestRequiresPassword(t *testing.T) {
122+
methodsThatRequirePassword := []string{
123+
azuread.ActiveDirectoryPassword,
124+
azuread.ActiveDirectoryServicePrincipal,
125+
azuread.ActiveDirectoryApplication,
126+
azuread.ActiveDirectoryServicePrincipalAccessToken,
127+
}
128+
129+
for _, method := range methodsThatRequirePassword {
130+
t.Run(method+"_requires_password", func(t *testing.T) {
131+
settings := ConnectSettings{
132+
AuthenticationMethod: method,
133+
UserName: "someuser",
134+
}
135+
assert.True(t, settings.RequiresPassword(),
136+
"auth method %q should require a password", method)
137+
})
138+
}
139+
140+
methodsThatDontRequirePassword := []string{
141+
azuread.ActiveDirectoryDefault,
142+
azuread.ActiveDirectoryIntegrated,
143+
azuread.ActiveDirectoryInteractive,
144+
azuread.ActiveDirectoryDeviceCode,
145+
azuread.ActiveDirectoryAzCli,
146+
azuread.ActiveDirectoryAzureDeveloperCli,
147+
azuread.ActiveDirectoryAzurePipelines,
148+
azuread.ActiveDirectoryEnvironment,
149+
azuread.ActiveDirectoryWorkloadIdentity,
150+
azuread.ActiveDirectoryClientAssertion,
151+
azuread.ActiveDirectoryOnBehalfOf,
152+
azuread.ActiveDirectoryMSI,
153+
azuread.ActiveDirectoryManagedIdentity,
154+
}
155+
156+
for _, method := range methodsThatDontRequirePassword {
157+
t.Run(method+"_does_not_require_password", func(t *testing.T) {
158+
settings := ConnectSettings{
159+
AuthenticationMethod: method,
160+
UserName: "someuser",
161+
}
162+
assert.False(t, settings.RequiresPassword(),
163+
"auth method %q should not require a password", method)
164+
})
165+
}
166+
}
167+
168+
func TestConnectionStringIncludesPasswordForSqlAuth(t *testing.T) {
169+
pwd := uuid.New().String()
170+
settings := ConnectSettings{
171+
ServerName: "someserver",
172+
UserName: "someuser",
173+
Password: pwd,
174+
}
175+
176+
connectionString, err := settings.ConnectionString()
177+
if assert.NoError(t, err) {
178+
assert.True(t, strings.Contains(connectionString, pwd),
179+
"SQL authentication should include password in the connection URL")
180+
}
181+
}

0 commit comments

Comments
 (0)