forked from denisenkom/go-mssqldb
-
Notifications
You must be signed in to change notification settings - Fork 68
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Export auth interface to allow external implementations e.g. krb5 - V…
…1-Candidate (#15) * Export auth interface to allow external implementations The original driver auth interface and implementations have been moved into their own packages and exported. There are two implementations within the driver, NTML and Windows kerberos. As before the default on windows is the winsspi package and on, on Linux, NTLM. The consuming application can now override this at runtime by calling mssql.SetAuthProvider(authProvider) This allows the application to provide a custom implementation of the auth.Provider interface. auth.Provider is in turn responsible for creating an instance of auth.Auth for the required authentication mechanism. windows, NTML etc. * remove unused function and merge master changes * add golang.org/x/crypto/md4 to appveyor.yml * add unit tests for SetAuthProvider and getAuth * Rename Auth to IntegratedAuthenticator * change package references * modular authentication and runtime registration * fix windows sspi * change test to use DeepEqual instead of struct equality * whitespace change to assess appveyor issue * fix linting issues * bring the kerberos authenticator into the driver * Revert "bring the kerberos authenticator into the driver" This reverts commit 6437077.
- Loading branch information
1 parent
574b528
commit ba5a4a0
Showing
19 changed files
with
651 additions
and
218 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// +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" | ||
// nolint importing the winsspi package causes it to be registered as an available authentication provider | ||
_ "github.com/microsoft/go-mssqldb/integratedauth/winsspi" | ||
) | ||
|
||
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" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:[email protected]?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 [email protected];password=somesecret;" + appid, | ||
expected: &azureFedAuthConfig{ | ||
adalWorkflow: mssql.FedAuthADALWorkflowPassword, | ||
user: "[email protected]", | ||
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:[email protected]?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 [email protected];password=somesecret;" + appid, | ||
expected: &azureFedAuthConfig{ | ||
adalWorkflow: mssql.FedAuthADALWorkflowPassword, | ||
user: "[email protected]", | ||
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) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
Oops, something went wrong.