Skip to content

Commit

Permalink
Merge branch 'main' into kerberos_auth
Browse files Browse the repository at this point in the history
  • Loading branch information
chandan jain authored and chandan jain committed Aug 30, 2022
2 parents d92f2a8 + ba5a4a0 commit fe3924f
Show file tree
Hide file tree
Showing 21 changed files with 637 additions and 304 deletions.
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ install:
- go env
- go get -u github.com/golang-sql/civil
- go get -u github.com/golang-sql/sqlexp

- go get -u golang.org/x/crypto/md4
build_script:
- go build

Expand Down
15 changes: 15 additions & 0 deletions auth_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// +build !windows

package mssql

import (
"github.com/microsoft/go-mssqldb/integratedauth"
// nolint importing the ntlm package causes it to be registered as an available authentication provider
_ "github.com/microsoft/go-mssqldb/integratedauth/ntlm"
)

func init() {
// we set the default authentication provider name here, rather than within each imported package,
// to force a known default. Go will order execution of init() calls but it is better to be explicit.
integratedauth.DefaultProviderName = "ntlm"
}
20 changes: 13 additions & 7 deletions auth_windows.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
// +build windows

package mssql

import "github.com/microsoft/go-mssqldb/msdsn"
import (
"github.com/microsoft/go-mssqldb/integratedauth"

func getAuthN(user, password, serverSPN, workstation string, _ map[string]interface{}) (auth auth, authOk bool) {
auth, authOk = getAuth(user, password, serverSPN, workstation)
return
}
// nolint importing the ntlm package causes it to be registered as an available authentication provider
_ "github.com/microsoft/go-mssqldb/integratedauth/ntlm"
// nolint importing the winsspi package causes it to be registered as an available authentication provider
_ "github.com/microsoft/go-mssqldb/integratedauth/winsspi"
)

func getKrbParams(krb msdsn.KerberosConfig) (krbParams map[string]interface{}, err error) {
return
func init() {
// we set the default authentication provider name here, rather than within each imported package,
// to force a known default. Go will order execution of init() calls but it is better to be explicit.
integratedauth.DefaultProviderName = "winsspi"
}
4 changes: 2 additions & 2 deletions azuread/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ type azureFedAuthConfig struct {

// parse returns a config based on an msdsn-style connection string
func parse(dsn string) (*azureFedAuthConfig, error) {
mssqlConfig, params, err := msdsn.Parse(dsn)
mssqlConfig, err := msdsn.Parse(dsn)
if err != nil {
return nil, err
}
Expand All @@ -62,7 +62,7 @@ func parse(dsn string) (*azureFedAuthConfig, error) {
mssqlConfig: mssqlConfig,
}

err = config.validateParameters(params)
err = config.validateParameters(mssqlConfig.Parameters)
if err != nil {
return nil, err
}
Expand Down
256 changes: 128 additions & 128 deletions azuread/configuration_test.go
Original file line number Diff line number Diff line change
@@ -1,128 +1,128 @@
//go:build go1.18
// +build go1.18

package azuread

import (
"testing"

mssql "github.com/microsoft/go-mssqldb"
"github.com/microsoft/go-mssqldb/msdsn"
)

func TestValidateParameters(t *testing.T) {
passphrase := "somesecret"
certificatepath := "/user/cert/cert.pfx"
appid := "applicationclientid=someguid"
certprop := "clientcertpath=" + certificatepath
tests := []struct {
name string
dsn string
expected *azureFedAuthConfig
}{
{
name: "no fed auth configured",
dsn: "server=someserver",
expected: &azureFedAuthConfig{fedAuthLibrary: mssql.FedAuthLibraryReserved},
},
{
name: "application with cert/key",
dsn: `sqlserver://service-principal-id%40tenant-id:somesecret@someserver.database.windows.net?fedauth=ActiveDirectoryApplication&` + certprop + "&" + appid,
expected: &azureFedAuthConfig{
fedAuthLibrary: mssql.FedAuthLibraryADAL,
clientID: "service-principal-id",
tenantID: "tenant-id",
certificatePath: certificatepath,
clientSecret: passphrase,
adalWorkflow: mssql.FedAuthADALWorkflowPassword,
fedAuthWorkflow: ActiveDirectoryApplication,
applicationClientID: "someguid",
},
},
{
name: "application with cert/key missing tenant id",
dsn: "server=someserver.database.windows.net;fedauth=ActiveDirectoryApplication;user id=service-principal-id;password=somesecret;" + certprop + ";" + appid,
expected: &azureFedAuthConfig{
fedAuthLibrary: mssql.FedAuthLibraryADAL,
clientID: "service-principal-id",
certificatePath: certificatepath,
clientSecret: passphrase,
adalWorkflow: mssql.FedAuthADALWorkflowPassword,
fedAuthWorkflow: ActiveDirectoryApplication,
applicationClientID: "someguid",
},
},
{
name: "application with secret",
dsn: "server=someserver.database.windows.net;fedauth=ActiveDirectoryServicePrincipal;user id=service-principal-id@tenant-id;password=somesecret;",
expected: &azureFedAuthConfig{
clientID: "service-principal-id",
tenantID: "tenant-id",
clientSecret: passphrase,
adalWorkflow: mssql.FedAuthADALWorkflowPassword,
fedAuthWorkflow: ActiveDirectoryServicePrincipal,
},
},
{
name: "user with password",
dsn: "server=someserver.database.windows.net;fedauth=ActiveDirectoryPassword;user id=azure-ad-user@example.com;password=somesecret;" + appid,
expected: &azureFedAuthConfig{
adalWorkflow: mssql.FedAuthADALWorkflowPassword,
user: "azure-ad-user@example.com",
password: passphrase,
applicationClientID: "someguid",
fedAuthWorkflow: ActiveDirectoryPassword,
},
},
{
name: "managed identity without client id",
dsn: "server=someserver.database.windows.net;fedauth=ActiveDirectoryMSI",
expected: &azureFedAuthConfig{
adalWorkflow: mssql.FedAuthADALWorkflowMSI,
fedAuthWorkflow: ActiveDirectoryMSI,
},
},
{
name: "managed identity with client id",
dsn: "server=someserver.database.windows.net;fedauth=ActiveDirectoryManagedIdentity;user id=identity-client-id",
expected: &azureFedAuthConfig{
adalWorkflow: mssql.FedAuthADALWorkflowMSI,
clientID: "identity-client-id",
fedAuthWorkflow: ActiveDirectoryManagedIdentity,
},
},
{
name: "managed identity with resource id",
dsn: "server=someserver.database.windows.net;fedauth=ActiveDirectoryManagedIdentity;resource id=/subscriptions/{guid}/resourceGroups/{resource-group-name}/{resource-provider-namespace}/{resource-type}/{resource-name}",
expected: &azureFedAuthConfig{
adalWorkflow: mssql.FedAuthADALWorkflowMSI,
resourceID: "/subscriptions/{guid}/resourceGroups/{resource-group-name}/{resource-provider-namespace}/{resource-type}/{resource-name}",
fedAuthWorkflow: ActiveDirectoryManagedIdentity,
},
},
}
for _, tst := range tests {
config, err := parse(tst.dsn)
if tst.expected == nil {
if err == nil {
t.Errorf("No error returned when error expected in test case '%s'", tst.name)
}
continue
}
if err != nil {
t.Errorf("Error returned when none expected in test case '%s': %v", tst.name, err)
continue
}
if tst.expected.fedAuthLibrary != mssql.FedAuthLibraryReserved {
if tst.expected.fedAuthLibrary == 0 {
tst.expected.fedAuthLibrary = mssql.FedAuthLibraryADAL
}
}
// mssqlConfig is not idempotent due to pointers in it, plus we aren't testing its correctness here
config.mssqlConfig = msdsn.Config{}
if *config != *tst.expected {
t.Errorf("Captured parameters do not match in test case '%s'. Expected:%+v, Actual:%+v", tst.name, tst.expected, config)
}
}

}
//go:build go1.18
// +build go1.18

package azuread

import (
"reflect"
"testing"

mssql "github.com/microsoft/go-mssqldb"
"github.com/microsoft/go-mssqldb/msdsn"
)

func TestValidateParameters(t *testing.T) {
passphrase := "somesecret"
certificatepath := "/user/cert/cert.pfx"
appid := "applicationclientid=someguid"
certprop := "clientcertpath=" + certificatepath
tests := []struct {
name string
dsn string
expected *azureFedAuthConfig
}{
{
name: "no fed auth configured",
dsn: "server=someserver",
expected: &azureFedAuthConfig{fedAuthLibrary: mssql.FedAuthLibraryReserved},
},
{
name: "application with cert/key",
dsn: `sqlserver://service-principal-id%40tenant-id:somesecret@someserver.database.windows.net?fedauth=ActiveDirectoryApplication&` + certprop + "&" + appid,
expected: &azureFedAuthConfig{
fedAuthLibrary: mssql.FedAuthLibraryADAL,
clientID: "service-principal-id",
tenantID: "tenant-id",
certificatePath: certificatepath,
clientSecret: passphrase,
adalWorkflow: mssql.FedAuthADALWorkflowPassword,
fedAuthWorkflow: ActiveDirectoryApplication,
applicationClientID: "someguid",
},
},
{
name: "application with cert/key missing tenant id",
dsn: "server=someserver.database.windows.net;fedauth=ActiveDirectoryApplication;user id=service-principal-id;password=somesecret;" + certprop + ";" + appid,
expected: &azureFedAuthConfig{
fedAuthLibrary: mssql.FedAuthLibraryADAL,
clientID: "service-principal-id",
certificatePath: certificatepath,
clientSecret: passphrase,
adalWorkflow: mssql.FedAuthADALWorkflowPassword,
fedAuthWorkflow: ActiveDirectoryApplication,
applicationClientID: "someguid",
},
},
{
name: "application with secret",
dsn: "server=someserver.database.windows.net;fedauth=ActiveDirectoryServicePrincipal;user id=service-principal-id@tenant-id;password=somesecret;",
expected: &azureFedAuthConfig{
clientID: "service-principal-id",
tenantID: "tenant-id",
clientSecret: passphrase,
adalWorkflow: mssql.FedAuthADALWorkflowPassword,
fedAuthWorkflow: ActiveDirectoryServicePrincipal,
},
},
{
name: "user with password",
dsn: "server=someserver.database.windows.net;fedauth=ActiveDirectoryPassword;user id=azure-ad-user@example.com;password=somesecret;" + appid,
expected: &azureFedAuthConfig{
adalWorkflow: mssql.FedAuthADALWorkflowPassword,
user: "azure-ad-user@example.com",
password: passphrase,
applicationClientID: "someguid",
fedAuthWorkflow: ActiveDirectoryPassword,
},
},
{
name: "managed identity without client id",
dsn: "server=someserver.database.windows.net;fedauth=ActiveDirectoryMSI",
expected: &azureFedAuthConfig{
adalWorkflow: mssql.FedAuthADALWorkflowMSI,
fedAuthWorkflow: ActiveDirectoryMSI,
},
},
{
name: "managed identity with client id",
dsn: "server=someserver.database.windows.net;fedauth=ActiveDirectoryManagedIdentity;user id=identity-client-id",
expected: &azureFedAuthConfig{
adalWorkflow: mssql.FedAuthADALWorkflowMSI,
clientID: "identity-client-id",
fedAuthWorkflow: ActiveDirectoryManagedIdentity,
},
},
{
name: "managed identity with resource id",
dsn: "server=someserver.database.windows.net;fedauth=ActiveDirectoryManagedIdentity;resource id=/subscriptions/{guid}/resourceGroups/{resource-group-name}/{resource-provider-namespace}/{resource-type}/{resource-name}",
expected: &azureFedAuthConfig{
adalWorkflow: mssql.FedAuthADALWorkflowMSI,
resourceID: "/subscriptions/{guid}/resourceGroups/{resource-group-name}/{resource-provider-namespace}/{resource-type}/{resource-name}",
fedAuthWorkflow: ActiveDirectoryManagedIdentity,
},
},
}
for _, tst := range tests {
config, err := parse(tst.dsn)
if tst.expected == nil {
if err == nil {
t.Errorf("No error returned when error expected in test case '%s'", tst.name)
}
continue
}
if err != nil {
t.Errorf("Error returned when none expected in test case '%s': %v", tst.name, err)
continue
}
if tst.expected.fedAuthLibrary != mssql.FedAuthLibraryReserved {
if tst.expected.fedAuthLibrary == 0 {
tst.expected.fedAuthLibrary = mssql.FedAuthLibraryADAL
}
}
// mssqlConfig is not idempotent due to pointers in it, plus we aren't testing its correctness here
config.mssqlConfig = msdsn.Config{}
if !reflect.DeepEqual(config, tst.expected) {
t.Errorf("Captured parameters do not match in test case '%s'. Expected:%+v, Actual:%+v", tst.name, tst.expected, config)
}
}
}
73 changes: 73 additions & 0 deletions integratedauth/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package integratedauth

import (
"errors"
"fmt"

"github.com/microsoft/go-mssqldb/msdsn"
)

var (
providers map[string]Provider
DefaultProviderName string

ErrProviderCannotBeNil = errors.New("provider cannot be nil")
ErrProviderNameMustBePopulated = errors.New("provider name must be populated")
)

func init() {
providers = make(map[string]Provider)
}

// GetIntegratedAuthenticator calls the authProvider specified in the 'authenticator' connection string parameter, if supplied.
// Otherwise fails back to the DefaultProviderName implementation for the platform.
func GetIntegratedAuthenticator(config msdsn.Config) (IntegratedAuthenticator, error) {
authenticatorName, ok := config.Parameters["authenticator"]
if !ok {
provider, err := getProvider(DefaultProviderName)
if err != nil {
return nil, err
}

p, err := provider.GetIntegratedAuthenticator(config)
// we ignore the error in this case to force a fallback to sqlserver authentication.
// this preserves the original behaviour
if err != nil {
return nil, nil
}

return p, nil
}

provider, err := getProvider(authenticatorName)
if err != nil {
return nil, err
}

return provider.GetIntegratedAuthenticator(config)
}

func getProvider(name string) (Provider, error) {
provider, ok := providers[name]

if !ok {
return nil, fmt.Errorf("provider %v not found", name)
}

return provider, nil
}

// SetIntegratedAuthenticationProvider stores a named authentication provider. It should be called before any connections are created.
func SetIntegratedAuthenticationProvider(providerName string, p Provider) error {
if p == nil {
return ErrProviderCannotBeNil
}

if providerName == "" {
return ErrProviderNameMustBePopulated
}

providers[providerName] = p

return nil
}
Loading

0 comments on commit fe3924f

Please sign in to comment.