-
Notifications
You must be signed in to change notification settings - Fork 84
Expand file tree
/
Copy pathads_windows.go
More file actions
106 lines (92 loc) · 3.22 KB
/
ads_windows.go
File metadata and controls
106 lines (92 loc) · 3.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
package open
import (
"fmt"
"github.com/microsoft/go-sqlcmd/cmd/modern/sqlconfig"
"github.com/microsoft/go-sqlcmd/internal/cmdparser"
"github.com/microsoft/go-sqlcmd/internal/credman"
"github.com/microsoft/go-sqlcmd/internal/localizer"
"github.com/microsoft/go-sqlcmd/internal/secret"
)
// Type Ads is used to implement the "open ads" which launches Azure
// Data Studio and establishes a connection to the SQL Server for the current
// context
type Ads struct {
cmdparser.Cmd
credential credman.Credential
}
// On Windows, the process blocks until the user exits ADS, let user know they can
// Ctrl+C here.
func (c *Ads) displayPreLaunchInfo() {
output := c.Output()
output.Info(localizer.Sprintf("Launching Azure Data Studio..."))
}
// persistCredentialForAds stores a SQL password in the Windows Credential Manager
// for the given hostname and endpoint.
func (c *Ads) persistCredentialForAds(
hostname string,
endpoint sqlconfig.Endpoint,
user *sqlconfig.User,
) {
// Create the target name that ADS will look for
targetName := c.adsKey(
fmt.Sprintf("%s,%#v", hostname, rune(endpoint.Port)),
"", // The default database is set on the user login
"SqlLogin",
user.BasicAuth.Username)
// Store the SQL password in the Windows Credential Manager with the
// generated target name
c.credential = credman.Credential{
TargetName: targetName,
CredentialBlob: secret.DecodeAsUtf16(
user.BasicAuth.Password, user.BasicAuth.PasswordEncryption),
UserName: user.BasicAuth.Username,
Persist: credman.PersistSession,
}
c.removePreviousCredential()
c.writeCredential()
}
// adsKey returns the credential target name for the given instance, database,
// authentication type, and user.
func (c *Ads) adsKey(instance, database, authType, user string) string {
return fmt.Sprintf(
"Microsoft.SqlTools|"+
"itemtype:Profile|"+
"id:providerName:MSSQL|"+
"applicationName:azdata|"+
"authenticationType:%s|"+
"database:%s|"+
"server:%s|"+
"user:%s",
authType, database, instance, user)
}
// removePreviousCredential removes any previously stored credentials with
// the same target name as the current instance's credential.
func (c *Ads) removePreviousCredential() {
credentials, err := credman.EnumerateCredentials("", true)
if err != nil {
// ERROR_NOT_FOUND (element not found) is expected when no
// credentials exist yet. Any other error is non-fatal here
// since we're only trying to clean up a previous entry.
return
}
for _, cred := range credentials {
if cred.TargetName == c.credential.TargetName {
err = credman.DeleteCredential(cred, credman.CredTypeGeneric)
c.CheckErr(err)
break
}
}
}
// writeCredential stores the current instance's credential in the Windows Credential Manager
func (c *Ads) writeCredential() {
output := c.Output()
err := credman.WriteCredential(&c.credential, credman.CredTypeGeneric)
if err != nil {
output.FatalErrorWithHints(
err,
[]string{localizer.Sprintf("A 'Not enough memory resources are available' error can be caused by too many credentials already stored in Windows Credential Manager")},
localizer.Sprintf("Failed to write credential to Windows Credential Manager"))
}
}