diff --git a/mongo/options/clientoptions.go b/mongo/options/clientoptions.go index 6cfb3dc2f1..bfde3efd64 100644 --- a/mongo/options/clientoptions.go +++ b/mongo/options/clientoptions.go @@ -68,7 +68,7 @@ type ContextDialer interface { // Credential can be used to provide authentication options when configuring a Client. // // AuthMechanism: the mechanism to use for authentication. Supported values include "SCRAM-SHA-256", "SCRAM-SHA-1", -// "MONGODB-CR", "PLAIN", "GSSAPI", "MONGODB-X509", and "MONGODB-AWS". This can also be set through the "authMechanism" +// "PLAIN", "GSSAPI", "MONGODB-X509", and "MONGODB-AWS". This can also be set through the "authMechanism" // URI option. (e.g. "authMechanism=PLAIN"). For more information, see // https://www.mongodb.com/docs/manual/core/authentication-mechanisms/. // diff --git a/x/mongo/driver/auth/auth.go b/x/mongo/driver/auth/auth.go index 843715dc17..bfc7bbdb9f 100644 --- a/x/mongo/driver/auth/auth.go +++ b/x/mongo/driver/auth/auth.go @@ -11,6 +11,7 @@ import ( "errors" "fmt" "net/http" + "strings" "go.mongodb.org/mongo-driver/v2/mongo/address" "go.mongodb.org/mongo-driver/v2/x/mongo/driver" @@ -31,7 +32,6 @@ func init() { RegisterAuthenticatorFactory("", newDefaultAuthenticator) RegisterAuthenticatorFactory(SCRAMSHA1, newScramSHA1Authenticator) RegisterAuthenticatorFactory(SCRAMSHA256, newScramSHA256Authenticator) - RegisterAuthenticatorFactory(MONGODBCR, newMongoDBCRAuthenticator) RegisterAuthenticatorFactory(PLAIN, newPlainAuthenticator) RegisterAuthenticatorFactory(GSSAPI, newGSSAPIAuthenticator) RegisterAuthenticatorFactory(MongoDBX509, newMongoDBX509Authenticator) @@ -41,6 +41,12 @@ func init() { // CreateAuthenticator creates an authenticator. func CreateAuthenticator(name string, cred *Cred, httpClient *http.Client) (Authenticator, error) { + // Return a custom error to indicate why auth mechanism "MONGODB-CR" is + // missing, even though it was previously available. + if strings.EqualFold(name, "MONGODB-CR") { + return nil, errors.New(`auth mechanism "MONGODB-CR" is no longer available in any supported version of MongoDB`) + } + if f, ok := authFactories[name]; ok { return f(cred, httpClient) } diff --git a/x/mongo/driver/auth/auth_test.go b/x/mongo/driver/auth/auth_test.go index 2edf872a4d..ff368ac380 100644 --- a/x/mongo/driver/auth/auth_test.go +++ b/x/mongo/driver/auth/auth_test.go @@ -8,6 +8,7 @@ package auth_test import ( "context" + "errors" "fmt" "net/http" "testing" @@ -29,11 +30,12 @@ func TestCreateAuthenticator(t *testing.T) { name string source string auth auth.Authenticator + err error }{ {name: "", auth: &auth.DefaultAuthenticator{}}, {name: "SCRAM-SHA-1", auth: &auth.ScramAuthenticator{}}, {name: "SCRAM-SHA-256", auth: &auth.ScramAuthenticator{}}, - {name: "MONGODB-CR", auth: &auth.MongoDBCRAuthenticator{}}, + {name: "MONGODB-CR", err: errors.New(`auth mechanism "MONGODB-CR" is no longer available in any supported version of MongoDB`)}, {name: "PLAIN", auth: &auth.PlainAuthenticator{}}, {name: "MONGODB-X509", auth: &auth.MongoDBX509Authenticator{}}, } @@ -47,6 +49,10 @@ func TestCreateAuthenticator(t *testing.T) { } a, err := auth.CreateAuthenticator(test.name, cred, &http.Client{}) + if test.err != nil { + require.EqualError(t, err, test.err.Error()) + return + } require.NoError(t, err) require.IsType(t, test.auth, a) }) diff --git a/x/mongo/driver/auth/default.go b/x/mongo/driver/auth/default.go index 6982ccc012..0033379dfb 100644 --- a/x/mongo/driver/auth/default.go +++ b/x/mongo/driver/auth/default.go @@ -32,8 +32,8 @@ func newDefaultAuthenticator(cred *Cred, httpClient *http.Client) (Authenticator }, nil } -// DefaultAuthenticator uses SCRAM-SHA-1 or MONGODB-CR depending -// on the server version. +// DefaultAuthenticator uses SCRAM-SHA-1 or SCRAM-SHA-256, depending on the +// server's SASL supported mechanisms. type DefaultAuthenticator struct { Cred *Cred @@ -53,18 +53,20 @@ func (a *DefaultAuthenticator) CreateSpeculativeConversation() (SpeculativeConve // Auth authenticates the connection. func (a *DefaultAuthenticator) Auth(ctx context.Context, cfg *driver.AuthConfig) error { - var actual Authenticator - var err error - - switch chooseAuthMechanism(cfg) { - case SCRAMSHA256: - actual, err = newScramSHA256Authenticator(a.Cred, a.httpClient) - case SCRAMSHA1: - actual, err = newScramSHA1Authenticator(a.Cred, a.httpClient) - default: - actual, err = newMongoDBCRAuthenticator(a.Cred, a.httpClient) - } + actual, err := func() (Authenticator, error) { + // If a server provides a list of supported mechanisms, we choose + // SCRAM-SHA-256 if it exists or else MUST use SCRAM-SHA-1. + // Otherwise, we decide based on what is supported. + if saslSupportedMechs := cfg.HandshakeInfo.SaslSupportedMechs; saslSupportedMechs != nil { + for _, v := range saslSupportedMechs { + if v == SCRAMSHA256 { + return newScramSHA256Authenticator(a.Cred, a.httpClient) + } + } + } + return newScramSHA1Authenticator(a.Cred, a.httpClient) + }() if err != nil { return newAuthError("error creating authenticator", err) } @@ -76,18 +78,3 @@ func (a *DefaultAuthenticator) Auth(ctx context.Context, cfg *driver.AuthConfig) func (a *DefaultAuthenticator) Reauth(_ context.Context, _ *driver.AuthConfig) error { return newAuthError("DefaultAuthenticator does not support reauthentication", nil) } - -// If a server provides a list of supported mechanisms, we choose -// SCRAM-SHA-256 if it exists or else MUST use SCRAM-SHA-1. -// Otherwise, we decide based on what is supported. -func chooseAuthMechanism(cfg *driver.AuthConfig) string { - if saslSupportedMechs := cfg.HandshakeInfo.SaslSupportedMechs; saslSupportedMechs != nil { - for _, v := range saslSupportedMechs { - if v == SCRAMSHA256 { - return v - } - } - } - - return SCRAMSHA1 -} diff --git a/x/mongo/driver/auth/mongodbcr.go b/x/mongo/driver/auth/mongodbcr.go deleted file mode 100644 index f8c1466df7..0000000000 --- a/x/mongo/driver/auth/mongodbcr.go +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (C) MongoDB, Inc. 2017-present. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - -package auth - -import ( - "context" - "fmt" - "io" - "net/http" - - // Ignore gosec warning "Blocklisted import crypto/md5: weak cryptographic primitive". We need - // to use MD5 here to implement the MONGODB-CR specification. - /* #nosec G501 */ - "crypto/md5" - - "go.mongodb.org/mongo-driver/v2/bson" - "go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore" - "go.mongodb.org/mongo-driver/v2/x/mongo/driver" - "go.mongodb.org/mongo-driver/v2/x/mongo/driver/operation" -) - -// MONGODBCR is the mechanism name for MONGODB-CR. -// -// The MONGODB-CR authentication mechanism is deprecated in MongoDB 3.6 and removed in -// MongoDB 4.0. -const MONGODBCR = "MONGODB-CR" - -func newMongoDBCRAuthenticator(cred *Cred, _ *http.Client) (Authenticator, error) { - source := cred.Source - if source == "" { - source = "admin" - } - return &MongoDBCRAuthenticator{ - DB: source, - Username: cred.Username, - Password: cred.Password, - }, nil -} - -// MongoDBCRAuthenticator uses the MONGODB-CR algorithm to authenticate a connection. -// -// The MONGODB-CR authentication mechanism is deprecated in MongoDB 3.6 and removed in -// MongoDB 4.0. -type MongoDBCRAuthenticator struct { - DB string - Username string - Password string -} - -// Auth authenticates the connection. -// -// The MONGODB-CR authentication mechanism is deprecated in MongoDB 3.6 and removed in -// MongoDB 4.0. -func (a *MongoDBCRAuthenticator) Auth(ctx context.Context, cfg *driver.AuthConfig) error { - - db := a.DB - if db == "" { - db = defaultAuthDB - } - - doc := bsoncore.BuildDocumentFromElements(nil, bsoncore.AppendInt32Element(nil, "getnonce", 1)) - cmd := operation.NewCommand(doc). - Database(db). - Deployment(driver.SingleConnectionDeployment{C: cfg.Connection}). - ClusterClock(cfg.ClusterClock). - ServerAPI(cfg.ServerAPI) - err := cmd.Execute(ctx) - if err != nil { - return newError(err, MONGODBCR) - } - rdr := cmd.Result() - - var getNonceResult struct { - Nonce string `bson:"nonce"` - } - - err = bson.Unmarshal(rdr, &getNonceResult) - if err != nil { - return newAuthError("unmarshal error", err) - } - - doc = bsoncore.BuildDocumentFromElements(nil, - bsoncore.AppendInt32Element(nil, "authenticate", 1), - bsoncore.AppendStringElement(nil, "user", a.Username), - bsoncore.AppendStringElement(nil, "nonce", getNonceResult.Nonce), - bsoncore.AppendStringElement(nil, "key", a.createKey(getNonceResult.Nonce)), - ) - cmd = operation.NewCommand(doc). - Database(db). - Deployment(driver.SingleConnectionDeployment{C: cfg.Connection}). - ClusterClock(cfg.ClusterClock). - ServerAPI(cfg.ServerAPI) - err = cmd.Execute(ctx) - if err != nil { - return newError(err, MONGODBCR) - } - - return nil -} - -// Reauth reauthenticates the connection. -func (a *MongoDBCRAuthenticator) Reauth(_ context.Context, _ *driver.AuthConfig) error { - return newAuthError("MONGODB-CR does not support reauthentication", nil) -} - -func (a *MongoDBCRAuthenticator) createKey(nonce string) string { - // Ignore gosec warning "Use of weak cryptographic primitive". We need to use MD5 here to - // implement the MONGODB-CR specification. - /* #nosec G401 */ - h := md5.New() - - _, _ = io.WriteString(h, nonce) - _, _ = io.WriteString(h, a.Username) - _, _ = io.WriteString(h, mongoPasswordDigest(a.Username, a.Password)) - return fmt.Sprintf("%x", h.Sum(nil)) -} diff --git a/x/mongo/driver/auth/mongodbcr_test.go b/x/mongo/driver/auth/mongodbcr_test.go deleted file mode 100644 index 0d6a4d0da1..0000000000 --- a/x/mongo/driver/auth/mongodbcr_test.go +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (C) MongoDB, Inc. 2017-present. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - -package auth_test - -import ( - "context" - "testing" - - "strings" - - "go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore" - "go.mongodb.org/mongo-driver/v2/x/mongo/driver" - "go.mongodb.org/mongo-driver/v2/x/mongo/driver/auth" - "go.mongodb.org/mongo-driver/v2/x/mongo/driver/description" - "go.mongodb.org/mongo-driver/v2/x/mongo/driver/drivertest" - "go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet" -) - -func TestMongoDBCRAuthenticator_Fails(t *testing.T) { - t.Parallel() - - authenticator := auth.MongoDBCRAuthenticator{ - DB: "source", - Username: "user", - Password: "pencil", - } - - resps := make(chan []byte, 2) - writeReplies(resps, bsoncore.BuildDocumentFromElements(nil, - bsoncore.AppendInt32Element(nil, "ok", 1), - bsoncore.AppendStringElement(nil, "nonce", "2375531c32080ae8"), - ), bsoncore.BuildDocumentFromElements(nil, - bsoncore.AppendInt32Element(nil, "ok", 0), - )) - - desc := description.Server{ - WireVersion: &description.VersionRange{ - Max: 6, - }, - } - c := &drivertest.ChannelConn{ - Written: make(chan []byte, 2), - ReadResp: resps, - Desc: desc, - } - - mnetconn := mnet.NewConnection(c) - - err := authenticator.Auth(context.Background(), &driver.AuthConfig{Connection: mnetconn}) - if err == nil { - t.Fatalf("expected an error but got none") - } - - errPrefix := "unable to authenticate using mechanism \"MONGODB-CR\"" - if !strings.HasPrefix(err.Error(), errPrefix) { - t.Fatalf("expected an err starting with \"%s\" but got \"%s\"", errPrefix, err) - } -} - -func TestMongoDBCRAuthenticator_Succeeds(t *testing.T) { - t.Parallel() - - authenticator := auth.MongoDBCRAuthenticator{ - DB: "source", - Username: "user", - Password: "pencil", - } - - resps := make(chan []byte, 2) - writeReplies(resps, bsoncore.BuildDocumentFromElements(nil, - bsoncore.AppendInt32Element(nil, "ok", 1), - bsoncore.AppendStringElement(nil, "nonce", "2375531c32080ae8"), - ), bsoncore.BuildDocumentFromElements(nil, - bsoncore.AppendInt32Element(nil, "ok", 1), - )) - - desc := description.Server{ - WireVersion: &description.VersionRange{ - Max: 6, - }, - } - c := &drivertest.ChannelConn{ - Written: make(chan []byte, 2), - ReadResp: resps, - Desc: desc, - } - - mnetconn := mnet.NewConnection(c) - - err := authenticator.Auth(context.Background(), &driver.AuthConfig{Connection: mnetconn}) - if err != nil { - t.Fatalf("expected no error but got \"%s\"", err) - } - - if len(c.Written) != 2 { - t.Fatalf("expected 2 messages to be sent but had %d", len(c.Written)) - } - - want := bsoncore.BuildDocumentFromElements(nil, bsoncore.AppendInt32Element(nil, "getnonce", 1)) - compareResponses(t, <-c.Written, want, "source") - - expectedAuthenticateDoc := bsoncore.BuildDocumentFromElements(nil, - bsoncore.AppendInt32Element(nil, "authenticate", 1), - bsoncore.AppendStringElement(nil, "user", "user"), - bsoncore.AppendStringElement(nil, "nonce", "2375531c32080ae8"), - bsoncore.AppendStringElement(nil, "key", "21742f26431831d5cfca035a08c5bdf6"), - ) - compareResponses(t, <-c.Written, expectedAuthenticateDoc, "source") -} - -func writeReplies(c chan []byte, docs ...bsoncore.Document) { - for _, doc := range docs { - reply := drivertest.MakeReply(doc) - c <- reply - } -} diff --git a/x/mongo/driver/auth/plain_test.go b/x/mongo/driver/auth/plain_test.go index 251769caf7..7075b19b1e 100644 --- a/x/mongo/driver/auth/plain_test.go +++ b/x/mongo/driver/auth/plain_test.go @@ -197,3 +197,10 @@ func TestPlainAuthenticator_SucceedsBoolean(t *testing.T) { ) compareResponses(t, <-c.Written, expectedCmd, "$external") } + +func writeReplies(c chan []byte, docs ...bsoncore.Document) { + for _, doc := range docs { + reply := drivertest.MakeReply(doc) + c <- reply + } +} diff --git a/x/mongo/driver/connstring/connstring.go b/x/mongo/driver/connstring/connstring.go index ece143a1e4..8edad674b7 100644 --- a/x/mongo/driver/connstring/connstring.go +++ b/x/mongo/driver/connstring/connstring.go @@ -297,8 +297,6 @@ func (u *ConnString) setDefaultAuthParams(dbName string) error { } else if u.AuthSource != "$external" { return fmt.Errorf("auth source must be $external") } - case "mongodb-cr": - fallthrough case "scram-sha-1": fallthrough case "scram-sha-256": @@ -696,16 +694,6 @@ func (u *ConnString) addOptions(connectionArgPairs []string) error { func (u *ConnString) validateAuth() error { switch strings.ToLower(u.AuthMechanism) { - case "mongodb-cr": - if u.Username == "" { - return fmt.Errorf("username required for MONGO-CR") - } - if u.Password == "" { - return fmt.Errorf("password required for MONGO-CR") - } - if u.AuthMechanismProperties != nil { - return fmt.Errorf("MONGO-CR cannot have mechanism properties") - } case "mongodb-x509": if u.Password != "" { return fmt.Errorf("password cannot be specified for MONGO-X509") diff --git a/x/mongo/driver/connstring/connstring_test.go b/x/mongo/driver/connstring/connstring_test.go index b203114f3e..209b429e27 100644 --- a/x/mongo/driver/connstring/connstring_test.go +++ b/x/mongo/driver/connstring/connstring_test.go @@ -49,7 +49,6 @@ func TestAuthMechanism(t *testing.T) { }{ {s: "authMechanism=scram-sha-1", expected: "scram-sha-1"}, {s: "authMechanism=scram-sha-256", expected: "scram-sha-256"}, - {s: "authMechanism=mongodb-CR", expected: "mongodb-CR"}, {s: "authMechanism=plain", expected: "plain"}, }