From fbaf68a4f7a559cf7b569c064068e1c350c9e604 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Wed, 14 Feb 2024 12:25:17 +0900 Subject: [PATCH 01/42] get authentication mechanism from db --- internal/handler/authenticate.go | 109 +++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 internal/handler/authenticate.go diff --git a/internal/handler/authenticate.go b/internal/handler/authenticate.go new file mode 100644 index 000000000000..2e8bc343f45e --- /dev/null +++ b/internal/handler/authenticate.go @@ -0,0 +1,109 @@ +// Copyright 2021 FerretDB Inc. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package handler + +import ( + "context" + "errors" + "fmt" + + "github.com/FerretDB/FerretDB/internal/clientconn/conninfo" + "github.com/FerretDB/FerretDB/internal/handler/common" + "github.com/FerretDB/FerretDB/internal/handler/handlererrors" + "github.com/FerretDB/FerretDB/internal/types" + "github.com/FerretDB/FerretDB/internal/util/iterator" + "github.com/FerretDB/FerretDB/internal/util/lazyerrors" + "github.com/FerretDB/FerretDB/internal/util/must" + "github.com/FerretDB/FerretDB/internal/wire" +) + +// authenticate validates the user's credentials if new auth is enabled. +func authenticate(ctx context.Context, msg *wire.OpMsg, h *Handler) error { + if !h.EnableNewAuth { + return nil + } + + adminDB, err := h.b.Database("admin") + if err != nil { + return lazyerrors.Error(err) + } + + usersCol, err := adminDB.Collection("system.users") + if err != nil { + return lazyerrors.Error(err) + } + + document, err := msg.Document() + if err != nil { + return lazyerrors.Error(err) + } + + var dbName string + + if dbName, err = common.GetRequiredParam[string](document, "$db"); err != nil { + return err + } + + username, _ := conninfo.Get(ctx).Auth() + + // NOTE: do we need to handle user who have access to all databases? + filter := must.NotFail(types.NewDocument("_id", dbName+"."+username)) + + // Filter isn't being passed to the query as we are filtering after retrieving all data + // from the database due to limitations of the internal/backends filters. + qr, err := usersCol.Query(ctx, nil) + if err != nil { + return lazyerrors.Error(err) + } + + defer qr.Iter.Close() + + var storedUser *types.Document + + for { + var v *types.Document + _, v, err = qr.Iter.Next() + + if errors.Is(err, iterator.ErrIteratorDone) { + break + } + + if err != nil { + return lazyerrors.Error(err) + } + + var matches bool + + if matches, err = common.FilterDocument(v, filter); err != nil { + return lazyerrors.Error(err) + } + + if matches { + storedUser = v + break + } + } + + if storedUser == nil { + return handlererrors.NewCommandErrorMsg( + handlererrors.ErrUserNotFound, + fmt.Sprintf("User %s@%s not found", username, dbName), + ) + } + + // authenticate user + + return nil +} From f5c6049d9552612d5a1bbf4ce45b2ca220e2885f Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Wed, 14 Feb 2024 16:46:31 +0900 Subject: [PATCH 02/42] add test for conneting without plain username password --- integration/users/connection_test.go | 95 +++++++++++++++++++++++ internal/handler/authenticate.go | 109 --------------------------- internal/handler/msg_saslstart.go | 6 ++ 3 files changed, 101 insertions(+), 109 deletions(-) delete mode 100644 internal/handler/authenticate.go diff --git a/integration/users/connection_test.go b/integration/users/connection_test.go index 056697ffe90a..bc7019caae17 100644 --- a/integration/users/connection_test.go +++ b/integration/users/connection_test.go @@ -16,6 +16,7 @@ package users import ( "context" + "net/url" "testing" "github.com/stretchr/testify/assert" @@ -179,3 +180,97 @@ func TestAuthentication(t *testing.T) { }) } } + +func TestAuthenticationURLEnableNewAuth(t *testing.T) { + t.Parallel() + + s := setup.SetupWithOpts(t, nil) + ctx := s.Ctx + collection := s.Collection + db := collection.Database() + + err := db.RunCommand(ctx, bson.D{ + {"createUser", "scrum-user"}, + {"roles", bson.A{}}, + {"pwd", "correct"}, + {"mechanisms", bson.A{"SCRAM-SHA-256"}}, + }).Err() + require.NoErrorf(t, err, "cannot create user") + + testCases := map[string]struct { //nolint:vet // for readability + username string + password string + updatePassword string // if true, the password will be updated to this one after the user is created. + mechanism string + + err string + userNotFound bool + wrongPassword bool + + failsForFerretDB bool + }{ + "PassAuth": { + username: "scram-user", + password: "correct", + mechanism: "SCRAM-SHA-256", + }, + "FailAuth": { + username: "scram-user", + password: "wrong", + mechanism: "SCRAM-SHA-256", + wrongPassword: true, + failsForFerretDB: true, + err: "AuthenticationFailed", + }, + "NotFoundAuth": { + username: "non-existent", + password: "wrong", + mechanism: "SCRAM-SHA-256", + wrongPassword: true, + failsForFerretDB: true, + err: "AuthenticationFailed", + }, + } + + for name, tc := range testCases { + name, tc := name, tc + t.Run(name, func(t *testing.T) { + t.Parallel() + + credential := options.Credential{ + AuthMechanism: tc.mechanism, + AuthSource: db.Name(), + Username: tc.username, + Password: tc.password, + } + + u, err := url.Parse(s.MongoDBURI) + require.NoError(t, err) + + urlWithoutUser := url.URL{ + Scheme: u.Scheme, + Host: u.Host, + Path: u.Path, + RawQuery: u.Query().Encode(), + } + + opts := options.Client().ApplyURI(urlWithoutUser.String()).SetAuth(credential) + + client, err := mongo.Connect(ctx, opts) + require.NoError(t, err, "cannot connect to MongoDB") + + err = client.Ping(ctx, nil) + + if tc.err != "" { + require.ErrorContains(t, err, tc.err) + return + } + + require.NoError(t, err, "cannot ping MongoDB") + + connCollection := client.Database(db.Name()).Collection(collection.Name()) + _, err = connCollection.InsertOne(ctx, bson.D{{"ping", "pong"}}) + require.NoError(t, err, "cannot insert document") + }) + } +} diff --git a/internal/handler/authenticate.go b/internal/handler/authenticate.go deleted file mode 100644 index 2e8bc343f45e..000000000000 --- a/internal/handler/authenticate.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2021 FerretDB Inc. -// -// 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 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package handler - -import ( - "context" - "errors" - "fmt" - - "github.com/FerretDB/FerretDB/internal/clientconn/conninfo" - "github.com/FerretDB/FerretDB/internal/handler/common" - "github.com/FerretDB/FerretDB/internal/handler/handlererrors" - "github.com/FerretDB/FerretDB/internal/types" - "github.com/FerretDB/FerretDB/internal/util/iterator" - "github.com/FerretDB/FerretDB/internal/util/lazyerrors" - "github.com/FerretDB/FerretDB/internal/util/must" - "github.com/FerretDB/FerretDB/internal/wire" -) - -// authenticate validates the user's credentials if new auth is enabled. -func authenticate(ctx context.Context, msg *wire.OpMsg, h *Handler) error { - if !h.EnableNewAuth { - return nil - } - - adminDB, err := h.b.Database("admin") - if err != nil { - return lazyerrors.Error(err) - } - - usersCol, err := adminDB.Collection("system.users") - if err != nil { - return lazyerrors.Error(err) - } - - document, err := msg.Document() - if err != nil { - return lazyerrors.Error(err) - } - - var dbName string - - if dbName, err = common.GetRequiredParam[string](document, "$db"); err != nil { - return err - } - - username, _ := conninfo.Get(ctx).Auth() - - // NOTE: do we need to handle user who have access to all databases? - filter := must.NotFail(types.NewDocument("_id", dbName+"."+username)) - - // Filter isn't being passed to the query as we are filtering after retrieving all data - // from the database due to limitations of the internal/backends filters. - qr, err := usersCol.Query(ctx, nil) - if err != nil { - return lazyerrors.Error(err) - } - - defer qr.Iter.Close() - - var storedUser *types.Document - - for { - var v *types.Document - _, v, err = qr.Iter.Next() - - if errors.Is(err, iterator.ErrIteratorDone) { - break - } - - if err != nil { - return lazyerrors.Error(err) - } - - var matches bool - - if matches, err = common.FilterDocument(v, filter); err != nil { - return lazyerrors.Error(err) - } - - if matches { - storedUser = v - break - } - } - - if storedUser == nil { - return handlererrors.NewCommandErrorMsg( - handlererrors.ErrUserNotFound, - fmt.Sprintf("User %s@%s not found", username, dbName), - ) - } - - // authenticate user - - return nil -} diff --git a/internal/handler/msg_saslstart.go b/internal/handler/msg_saslstart.go index ab55181e91bc..5a6992a5f316 100644 --- a/internal/handler/msg_saslstart.go +++ b/internal/handler/msg_saslstart.go @@ -52,6 +52,12 @@ func (h *Handler) MsgSASLStart(ctx context.Context, msg *wire.OpMsg) (*wire.OpMs return nil, lazyerrors.Error(err) } + if !h.EnableNewAuth && mechanism != "PLAIN" { + msg := fmt.Sprintf("Unsupported authentication mechanism %q.\n", mechanism) + + "See https://docs.ferretdb.io/security/authentication/ for more details." + return nil, handlererrors.NewCommandErrorMsgWithArgument(handlererrors.ErrAuthenticationFailed, msg, "mechanism") + } + var username, password string switch mechanism { From 88b6316d2a94e88cc64379c99d2ba40947976984 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Thu, 15 Feb 2024 12:48:44 +0900 Subject: [PATCH 03/42] add authentication to sasl start --- integration/users/connection_test.go | 89 +++++++++++---------- internal/handler/msg_saslstart.go | 112 +++++++++++++++++++++++++-- 2 files changed, 151 insertions(+), 50 deletions(-) diff --git a/integration/users/connection_test.go b/integration/users/connection_test.go index bc7019caae17..39fb02d5f9a3 100644 --- a/integration/users/connection_test.go +++ b/integration/users/connection_test.go @@ -16,7 +16,6 @@ package users import ( "context" - "net/url" "testing" "github.com/stretchr/testify/assert" @@ -181,7 +180,7 @@ func TestAuthentication(t *testing.T) { } } -func TestAuthenticationURLEnableNewAuth(t *testing.T) { +func TestAuthenticationEnableNewAuth(t *testing.T) { t.Parallel() s := setup.SetupWithOpts(t, nil) @@ -189,46 +188,60 @@ func TestAuthenticationURLEnableNewAuth(t *testing.T) { collection := s.Collection db := collection.Database() + var res bson.D err := db.RunCommand(ctx, bson.D{ - {"createUser", "scrum-user"}, - {"roles", bson.A{}}, - {"pwd", "correct"}, - {"mechanisms", bson.A{"SCRAM-SHA-256"}}, - }).Err() - require.NoErrorf(t, err, "cannot create user") + {"dropAllUsersFromDatabase", 1}, + }).Decode(&res) + require.NoError(t, err) + + //err = db.RunCommand(ctx, bson.D{ + // {"createUser", "plain-user"}, + // {"roles", bson.A{}}, + // {"pwd", "correct"}, + // {"mechanisms", bson.A{"PLAIN"}}, + //}).Err() + //require.NoErrorf(t, err, "cannot create user") testCases := map[string]struct { //nolint:vet // for readability - username string - password string - updatePassword string // if true, the password will be updated to this one after the user is created. - mechanism string - - err string - userNotFound bool - wrongPassword bool + username string + password string + mechanism string - failsForFerretDB bool + err string }{ - "PassAuth": { + "PLAIN": { + username: "plain-user", + password: "correct", + mechanism: "PLAIN", + }, + "PLAINWrongPassword": { + username: "scram-user", + password: "wrong", + mechanism: "SCRAM-SHA-256", + err: "AuthenticationFailed", + }, + "PLAINWrongUser": { + username: "non-existent", + password: "wrong", + mechanism: "SCRAM-SHA-256", + err: "AuthenticationFailed", + }, + "SHA256": { username: "scram-user", password: "correct", mechanism: "SCRAM-SHA-256", }, - "FailAuth": { - username: "scram-user", - password: "wrong", - mechanism: "SCRAM-SHA-256", - wrongPassword: true, - failsForFerretDB: true, - err: "AuthenticationFailed", + "SHA256WrongUser": { + username: "non-existent", + password: "wrong", + mechanism: "SCRAM-SHA-256", + err: "AuthenticationFailed", }, - "NotFoundAuth": { - username: "non-existent", - password: "wrong", - mechanism: "SCRAM-SHA-256", - wrongPassword: true, - failsForFerretDB: true, - err: "AuthenticationFailed", + "SHA256WrongPassword": { + username: "scram-user", + password: "wrong", + mechanism: "SCRAM-SHA-256", + err: "AuthenticationFailed", }, } @@ -244,17 +257,7 @@ func TestAuthenticationURLEnableNewAuth(t *testing.T) { Password: tc.password, } - u, err := url.Parse(s.MongoDBURI) - require.NoError(t, err) - - urlWithoutUser := url.URL{ - Scheme: u.Scheme, - Host: u.Host, - Path: u.Path, - RawQuery: u.Query().Encode(), - } - - opts := options.Client().ApplyURI(urlWithoutUser.String()).SetAuth(credential) + opts := options.Client().ApplyURI(s.MongoDBURI).SetAuth(credential) client, err := mongo.Connect(ctx, opts) require.NoError(t, err, "cannot connect to MongoDB") diff --git a/internal/handler/msg_saslstart.go b/internal/handler/msg_saslstart.go index 5a6992a5f316..77214b648ca9 100644 --- a/internal/handler/msg_saslstart.go +++ b/internal/handler/msg_saslstart.go @@ -18,14 +18,17 @@ import ( "bytes" "context" "encoding/base64" + "errors" "fmt" "github.com/FerretDB/FerretDB/internal/clientconn/conninfo" "github.com/FerretDB/FerretDB/internal/handler/common" "github.com/FerretDB/FerretDB/internal/handler/handlererrors" "github.com/FerretDB/FerretDB/internal/types" + "github.com/FerretDB/FerretDB/internal/util/iterator" "github.com/FerretDB/FerretDB/internal/util/lazyerrors" "github.com/FerretDB/FerretDB/internal/util/must" + "github.com/FerretDB/FerretDB/internal/util/password" "github.com/FerretDB/FerretDB/internal/wire" ) @@ -52,12 +55,6 @@ func (h *Handler) MsgSASLStart(ctx context.Context, msg *wire.OpMsg) (*wire.OpMs return nil, lazyerrors.Error(err) } - if !h.EnableNewAuth && mechanism != "PLAIN" { - msg := fmt.Sprintf("Unsupported authentication mechanism %q.\n", mechanism) + - "See https://docs.ferretdb.io/security/authentication/ for more details." - return nil, handlererrors.NewCommandErrorMsgWithArgument(handlererrors.ErrAuthenticationFailed, msg, "mechanism") - } - var username, password string switch mechanism { @@ -67,13 +64,20 @@ func (h *Handler) MsgSASLStart(ctx context.Context, msg *wire.OpMsg) (*wire.OpMs return nil, err } + if err = h.authenticate(ctx, msg); err != nil { + return nil, err + } default: msg := fmt.Sprintf("Unsupported authentication mechanism %q.\n", mechanism) + "See https://docs.ferretdb.io/security/authentication/ for more details." return nil, handlererrors.NewCommandErrorMsgWithArgument(handlererrors.ErrAuthenticationFailed, msg, "mechanism") } - conninfo.Get(ctx).SetAuth(username, password) + if h.EnableNewAuth { + conninfo.Get(ctx).BypassBackendAuth = true + } else { + conninfo.Get(ctx).SetAuth(username, password) + } var emptyPayload types.Binary var reply wire.OpMsg @@ -135,3 +139,97 @@ func saslStartPlain(doc *types.Document) (string, string, error) { return string(authcid), string(passwd), nil } + +// authenticate validates the user's credentials in the connection with the credentials in the database. +// If the authentication fails, it returns error. +// +// An exception where database collection contains no user, it succeeds authentication. +func (h *Handler) authenticate(ctx context.Context, msg *wire.OpMsg) error { + if !h.EnableNewAuth { + return nil + } + + adminDB, err := h.b.Database("admin") + if err != nil { + return lazyerrors.Error(err) + } + + usersCol, err := adminDB.Collection("system.users") + if err != nil { + return lazyerrors.Error(err) + } + + document, err := msg.Document() + if err != nil { + return lazyerrors.Error(err) + } + + var dbName string + + if dbName, err = common.GetRequiredParam[string](document, "$db"); err != nil { + return err + } + + username, pwd := conninfo.Get(ctx).Auth() + + // NOTE: how does a user with access to all database look like? + filter := must.NotFail(types.NewDocument("_id", dbName+"."+username)) + + qr, err := usersCol.Query(ctx, nil) + if err != nil { + return lazyerrors.Error(err) + } + + defer qr.Iter.Close() + + var storedUser *types.Document + + var hasUser bool + + for { + var v *types.Document + _, v, err = qr.Iter.Next() + + if errors.Is(err, iterator.ErrIteratorDone) { + break + } + + if err != nil { + return lazyerrors.Error(err) + } + + hasUser = true + + var matches bool + + if matches, err = common.FilterDocument(v, filter); err != nil { + return lazyerrors.Error(err) + } + + if matches { + storedUser = v + break + } + } + + if !hasUser { + // an exception where authentication is skipped until the first user is created. + return nil + } + + credentials := must.NotFail(storedUser.Get("credentials")).(*types.Document) + if !credentials.Has("PLAIN") { + return handlererrors.NewCommandErrorMsgWithArgument( + handlererrors.ErrAuthenticationFailed, + "TODO: wrong authentication mechanism", + "PLAIN", + ) + } + + err = password.PlainVerify(pwd, credentials) + if err != nil { + return lazyerrors.Error(err) + } + + return nil +} From bcfd3503e439f0a413fa85c050d0a82ec7f4b233 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Thu, 15 Feb 2024 12:58:35 +0900 Subject: [PATCH 04/42] add authentication to all handlers except command query --- internal/handler/msg_aggregate.go | 4 ++++ internal/handler/msg_buildinfo.go | 4 ++++ internal/handler/msg_collmod.go | 4 ++++ internal/handler/msg_collstats.go | 4 ++++ internal/handler/msg_compact.go | 4 ++++ internal/handler/msg_connectionstatus.go | 4 ++++ internal/handler/msg_count.go | 4 ++++ internal/handler/msg_create.go | 4 ++++ internal/handler/msg_createindexes.go | 4 ++++ internal/handler/msg_createuser.go | 4 ++++ internal/handler/msg_currentop.go | 4 ++++ internal/handler/msg_datasize.go | 4 ++++ internal/handler/msg_dbstats.go | 4 ++++ internal/handler/msg_debugerror.go | 4 ++++ internal/handler/msg_delete.go | 4 ++++ internal/handler/msg_distinct.go | 4 ++++ internal/handler/msg_drop.go | 4 ++++ internal/handler/msg_dropallusersfromdatabase.go | 4 ++++ internal/handler/msg_dropdatabase.go | 4 ++++ internal/handler/msg_dropindexes.go | 4 ++++ internal/handler/msg_dropuser.go | 4 ++++ internal/handler/msg_explain.go | 4 ++++ internal/handler/msg_find.go | 4 ++++ internal/handler/msg_findandmodify.go | 4 ++++ internal/handler/msg_getcmdlineopts.go | 4 ++++ internal/handler/msg_getfreemonitoringstatus.go | 4 ++++ internal/handler/msg_getlog.go | 4 ++++ internal/handler/msg_getmore.go | 4 ++++ internal/handler/msg_getparameter.go | 4 ++++ internal/handler/msg_hello.go | 4 ++++ internal/handler/msg_hostinfo.go | 4 ++++ internal/handler/msg_insert.go | 4 ++++ internal/handler/msg_ismaster.go | 4 ++++ internal/handler/msg_killcursors.go | 4 ++++ internal/handler/msg_listcollections.go | 4 ++++ internal/handler/msg_listcommands.go | 4 ++++ internal/handler/msg_listdatabases.go | 4 ++++ internal/handler/msg_listindexes.go | 4 ++++ internal/handler/msg_logout.go | 4 ++++ internal/handler/msg_ping.go | 4 ++++ internal/handler/msg_renamecollection.go | 4 ++++ internal/handler/msg_serverstatus.go | 4 ++++ internal/handler/msg_setfreemonitoring.go | 4 ++++ internal/handler/msg_update.go | 4 ++++ internal/handler/msg_updateuser.go | 4 ++++ internal/handler/msg_usersinfo.go | 4 ++++ internal/handler/msg_validate.go | 4 ++++ internal/handler/msg_whatsmyuri.go | 4 ++++ 48 files changed, 192 insertions(+) diff --git a/internal/handler/msg_aggregate.go b/internal/handler/msg_aggregate.go index ba72fcd7ab63..c037fa2a4f67 100644 --- a/internal/handler/msg_aggregate.go +++ b/internal/handler/msg_aggregate.go @@ -42,6 +42,10 @@ import ( // MsgAggregate implements `aggregate` command. func (h *Handler) MsgAggregate(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_buildinfo.go b/internal/handler/msg_buildinfo.go index ba4a3310f6f3..7029d60a3440 100644 --- a/internal/handler/msg_buildinfo.go +++ b/internal/handler/msg_buildinfo.go @@ -27,6 +27,10 @@ import ( // MsgBuildInfo implements `buildInfo` command. func (h *Handler) MsgBuildInfo(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + aggregationStages := types.MakeArray(len(stages.Stages)) for stage := range stages.Stages { aggregationStages.Append(stage) diff --git a/internal/handler/msg_collmod.go b/internal/handler/msg_collmod.go index b6e426974f4a..75972571cd06 100644 --- a/internal/handler/msg_collmod.go +++ b/internal/handler/msg_collmod.go @@ -23,6 +23,10 @@ import ( // MsgCollMod implements `collMod` command. func (h *Handler) MsgCollMod(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + return nil, handlererrors.NewCommandErrorMsg( handlererrors.ErrNotImplemented, "`collMod` command is not implemented yet", diff --git a/internal/handler/msg_collstats.go b/internal/handler/msg_collstats.go index a561ac8e32da..0d0bd33a39ae 100644 --- a/internal/handler/msg_collstats.go +++ b/internal/handler/msg_collstats.go @@ -30,6 +30,10 @@ import ( // MsgCollStats implements `collStats` command. func (h *Handler) MsgCollStats(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_compact.go b/internal/handler/msg_compact.go index d036adbe4e5f..22c45ed1e8c1 100644 --- a/internal/handler/msg_compact.go +++ b/internal/handler/msg_compact.go @@ -30,6 +30,10 @@ import ( // MsgCompact implements `compact` command. func (h *Handler) MsgCompact(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_connectionstatus.go b/internal/handler/msg_connectionstatus.go index 53d750ef4857..a0d35a4cd49e 100644 --- a/internal/handler/msg_connectionstatus.go +++ b/internal/handler/msg_connectionstatus.go @@ -25,6 +25,10 @@ import ( // MsgConnectionStatus implements `connectionStatus` command. func (h *Handler) MsgConnectionStatus(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + users := types.MakeArray(1) if username := conninfo.Get(ctx).Username(); username != "" { diff --git a/internal/handler/msg_count.go b/internal/handler/msg_count.go index 950dc9902134..a0b0bf76d571 100644 --- a/internal/handler/msg_count.go +++ b/internal/handler/msg_count.go @@ -31,6 +31,10 @@ import ( // MsgCount implements `count` command. func (h *Handler) MsgCount(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_create.go b/internal/handler/msg_create.go index ad2c26ecf399..a93b0109d98f 100644 --- a/internal/handler/msg_create.go +++ b/internal/handler/msg_create.go @@ -30,6 +30,10 @@ import ( // MsgCreate implements `create` command. func (h *Handler) MsgCreate(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_createindexes.go b/internal/handler/msg_createindexes.go index 7df27494b5eb..b199a875b200 100644 --- a/internal/handler/msg_createindexes.go +++ b/internal/handler/msg_createindexes.go @@ -34,6 +34,10 @@ import ( // MsgCreateIndexes implements `createIndexes` command. func (h *Handler) MsgCreateIndexes(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_createuser.go b/internal/handler/msg_createuser.go index eff52d07c5bf..4960900da9f2 100644 --- a/internal/handler/msg_createuser.go +++ b/internal/handler/msg_createuser.go @@ -35,6 +35,10 @@ import ( // MsgCreateUser implements `createUser` command. func (h *Handler) MsgCreateUser(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_currentop.go b/internal/handler/msg_currentop.go index 8c0a10935d6b..f0679e7cc74f 100644 --- a/internal/handler/msg_currentop.go +++ b/internal/handler/msg_currentop.go @@ -24,6 +24,10 @@ import ( // MsgCurrentOp implements `currentOp` command. func (h *Handler) MsgCurrentOp(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + var reply wire.OpMsg must.NoError(reply.SetSections(wire.MakeOpMsgSection( must.NotFail(types.NewDocument( diff --git a/internal/handler/msg_datasize.go b/internal/handler/msg_datasize.go index 0de861a79f0a..d5ee3e87a425 100644 --- a/internal/handler/msg_datasize.go +++ b/internal/handler/msg_datasize.go @@ -31,6 +31,10 @@ import ( // MsgDataSize implements `dataSize` command. func (h *Handler) MsgDataSize(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_dbstats.go b/internal/handler/msg_dbstats.go index 8112605f6661..2c9fc757c382 100644 --- a/internal/handler/msg_dbstats.go +++ b/internal/handler/msg_dbstats.go @@ -30,6 +30,10 @@ import ( // MsgDBStats implements `dbStats` command. func (h *Handler) MsgDBStats(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_debugerror.go b/internal/handler/msg_debugerror.go index eeceb9a7803b..2bd226539abc 100644 --- a/internal/handler/msg_debugerror.go +++ b/internal/handler/msg_debugerror.go @@ -30,6 +30,10 @@ import ( // MsgDebugError implements `debugError` command. func (h *Handler) MsgDebugError(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_delete.go b/internal/handler/msg_delete.go index 39e5b44ed6f7..0d9f570b2186 100644 --- a/internal/handler/msg_delete.go +++ b/internal/handler/msg_delete.go @@ -33,6 +33,10 @@ import ( // MsgDelete implements `delete` command. func (h *Handler) MsgDelete(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_distinct.go b/internal/handler/msg_distinct.go index 2e3d08a86a5d..657157e79df5 100644 --- a/internal/handler/msg_distinct.go +++ b/internal/handler/msg_distinct.go @@ -30,6 +30,10 @@ import ( // MsgDistinct implements `distinct` command. func (h *Handler) MsgDistinct(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_drop.go b/internal/handler/msg_drop.go index 9793a64da012..6b5da23ccdc1 100644 --- a/internal/handler/msg_drop.go +++ b/internal/handler/msg_drop.go @@ -29,6 +29,10 @@ import ( // MsgDrop implements `drop` command. func (h *Handler) MsgDrop(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_dropallusersfromdatabase.go b/internal/handler/msg_dropallusersfromdatabase.go index cdcae1a488f3..430b31aacbe6 100644 --- a/internal/handler/msg_dropallusersfromdatabase.go +++ b/internal/handler/msg_dropallusersfromdatabase.go @@ -29,6 +29,10 @@ import ( // MsgDropAllUsersFromDatabase implements `dropAllUsersFromDatabase` command. func (h *Handler) MsgDropAllUsersFromDatabase(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_dropdatabase.go b/internal/handler/msg_dropdatabase.go index b8ca5821b25a..18ddcbc28217 100644 --- a/internal/handler/msg_dropdatabase.go +++ b/internal/handler/msg_dropdatabase.go @@ -27,6 +27,10 @@ import ( // MsgDropDatabase implements `dropDatabase` command. func (h *Handler) MsgDropDatabase(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_dropindexes.go b/internal/handler/msg_dropindexes.go index cf1866dc12ee..52c8ecea0eae 100644 --- a/internal/handler/msg_dropindexes.go +++ b/internal/handler/msg_dropindexes.go @@ -32,6 +32,10 @@ import ( // MsgDropIndexes implements `dropIndexes` command. func (h *Handler) MsgDropIndexes(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_dropuser.go b/internal/handler/msg_dropuser.go index 3464f351fb50..f5fb1164be50 100644 --- a/internal/handler/msg_dropuser.go +++ b/internal/handler/msg_dropuser.go @@ -29,6 +29,10 @@ import ( // MsgDropUser implements `dropUser` command. func (h *Handler) MsgDropUser(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_explain.go b/internal/handler/msg_explain.go index f4a2ff934d65..12f550c51732 100644 --- a/internal/handler/msg_explain.go +++ b/internal/handler/msg_explain.go @@ -34,6 +34,10 @@ import ( // MsgExplain implements `explain` command. func (h *Handler) MsgExplain(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_find.go b/internal/handler/msg_find.go index 4ea5d83a969e..bc38df0a67ea 100644 --- a/internal/handler/msg_find.go +++ b/internal/handler/msg_find.go @@ -37,6 +37,10 @@ import ( // MsgFind implements `find` command. func (h *Handler) MsgFind(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_findandmodify.go b/internal/handler/msg_findandmodify.go index 76cedf69d5c0..72cb5d2d8960 100644 --- a/internal/handler/msg_findandmodify.go +++ b/internal/handler/msg_findandmodify.go @@ -43,6 +43,10 @@ type findAndModifyResult struct { // MsgFindAndModify implements `findAndModify` command. func (h *Handler) MsgFindAndModify(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_getcmdlineopts.go b/internal/handler/msg_getcmdlineopts.go index 79dac633511f..ad8a65c66d2a 100644 --- a/internal/handler/msg_getcmdlineopts.go +++ b/internal/handler/msg_getcmdlineopts.go @@ -24,6 +24,10 @@ import ( // MsgGetCmdLineOpts implements `getCmdLineOpts` command. func (h *Handler) MsgGetCmdLineOpts(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + var reply wire.OpMsg must.NoError(reply.SetSections(wire.MakeOpMsgSection( must.NotFail(types.NewDocument( diff --git a/internal/handler/msg_getfreemonitoringstatus.go b/internal/handler/msg_getfreemonitoringstatus.go index 372cb1615e4b..de2252e41021 100644 --- a/internal/handler/msg_getfreemonitoringstatus.go +++ b/internal/handler/msg_getfreemonitoringstatus.go @@ -24,6 +24,10 @@ import ( // MsgGetFreeMonitoringStatus implements `getFreeMonitoringStatus` command. func (h *Handler) MsgGetFreeMonitoringStatus(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + state := h.StateProvider.Get().TelemetryString() message := "monitoring is " + state diff --git a/internal/handler/msg_getlog.go b/internal/handler/msg_getlog.go index cdbfa192637f..d8f8fe67341f 100644 --- a/internal/handler/msg_getlog.go +++ b/internal/handler/msg_getlog.go @@ -36,6 +36,10 @@ import ( // MsgGetLog implements `getLog` command. func (h *Handler) MsgGetLog(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_getmore.go b/internal/handler/msg_getmore.go index bacf3b3d87c2..ed24f38188bc 100644 --- a/internal/handler/msg_getmore.go +++ b/internal/handler/msg_getmore.go @@ -39,6 +39,10 @@ import ( // MsgGetMore implements `getMore` command. func (h *Handler) MsgGetMore(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_getparameter.go b/internal/handler/msg_getparameter.go index 47180050a34e..3527dd50420f 100644 --- a/internal/handler/msg_getparameter.go +++ b/internal/handler/msg_getparameter.go @@ -30,6 +30,10 @@ import ( // MsgGetParameter implements `getParameter` command. func (h *Handler) MsgGetParameter(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_hello.go b/internal/handler/msg_hello.go index 9845961f024c..d5a386696d01 100644 --- a/internal/handler/msg_hello.go +++ b/internal/handler/msg_hello.go @@ -27,6 +27,10 @@ import ( // MsgHello implements `hello` command. func (h *Handler) MsgHello(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + doc, err := msg.Document() if err != nil { return nil, err diff --git a/internal/handler/msg_hostinfo.go b/internal/handler/msg_hostinfo.go index 976b6c04c6ad..73b34af939ff 100644 --- a/internal/handler/msg_hostinfo.go +++ b/internal/handler/msg_hostinfo.go @@ -32,6 +32,10 @@ import ( // MsgHostInfo implements `hostInfo` command. func (h *Handler) MsgHostInfo(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + now := time.Now().UTC() hostname, err := os.Hostname() diff --git a/internal/handler/msg_insert.go b/internal/handler/msg_insert.go index 7571ce1dc3c7..aeb50bacb1d3 100644 --- a/internal/handler/msg_insert.go +++ b/internal/handler/msg_insert.go @@ -47,6 +47,10 @@ func WriteErrorDocument(we *mongo.WriteError) *types.Document { // MsgInsert implements `insert` command. func (h *Handler) MsgInsert(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_ismaster.go b/internal/handler/msg_ismaster.go index 4e2e0733a590..7c4683584c1c 100644 --- a/internal/handler/msg_ismaster.go +++ b/internal/handler/msg_ismaster.go @@ -25,6 +25,10 @@ import ( // MsgIsMaster implements `isMaster` command. func (h *Handler) MsgIsMaster(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + doc, err := msg.Document() if err != nil { return nil, err diff --git a/internal/handler/msg_killcursors.go b/internal/handler/msg_killcursors.go index a62debd7cc22..748d618fbc88 100644 --- a/internal/handler/msg_killcursors.go +++ b/internal/handler/msg_killcursors.go @@ -32,6 +32,10 @@ import ( // MsgKillCursors implements `killCursors` command. func (h *Handler) MsgKillCursors(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_listcollections.go b/internal/handler/msg_listcollections.go index 3ac39b24ef05..1b94cace2092 100644 --- a/internal/handler/msg_listcollections.go +++ b/internal/handler/msg_listcollections.go @@ -32,6 +32,10 @@ import ( // MsgListCollections implements `listCollections` command. func (h *Handler) MsgListCollections(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_listcommands.go b/internal/handler/msg_listcommands.go index 1aa8a888a6d6..da393d0686ac 100644 --- a/internal/handler/msg_listcommands.go +++ b/internal/handler/msg_listcommands.go @@ -27,6 +27,10 @@ import ( // MsgListCommands implements `listCommands` command. func (h *Handler) MsgListCommands(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + cmdList := must.NotFail(types.NewDocument()) names := maps.Keys(h.Commands()) sort.Strings(names) diff --git a/internal/handler/msg_listdatabases.go b/internal/handler/msg_listdatabases.go index 4946b91d9c06..ca5c6942e717 100644 --- a/internal/handler/msg_listdatabases.go +++ b/internal/handler/msg_listdatabases.go @@ -29,6 +29,10 @@ import ( // MsgListDatabases implements `listDatabases` command. func (h *Handler) MsgListDatabases(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_listindexes.go b/internal/handler/msg_listindexes.go index 6e53f09878d2..72e18ac983ed 100644 --- a/internal/handler/msg_listindexes.go +++ b/internal/handler/msg_listindexes.go @@ -29,6 +29,10 @@ import ( // MsgListIndexes implements `listIndexes` command. func (h *Handler) MsgListIndexes(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_logout.go b/internal/handler/msg_logout.go index 1e40e48a23c5..50a8de882e12 100644 --- a/internal/handler/msg_logout.go +++ b/internal/handler/msg_logout.go @@ -25,6 +25,10 @@ import ( // MsgLogout implements `logout` command. func (h *Handler) MsgLogout(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + conninfo.Get(ctx).SetAuth("", "") var reply wire.OpMsg diff --git a/internal/handler/msg_ping.go b/internal/handler/msg_ping.go index e836f52b8f7d..f7bb0dbb5e6e 100644 --- a/internal/handler/msg_ping.go +++ b/internal/handler/msg_ping.go @@ -29,6 +29,10 @@ import ( // MsgPing implements `ping` command. func (h *Handler) MsgPing(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_renamecollection.go b/internal/handler/msg_renamecollection.go index 02d7eb6f2efc..a7d7cc0bbb27 100644 --- a/internal/handler/msg_renamecollection.go +++ b/internal/handler/msg_renamecollection.go @@ -30,6 +30,10 @@ import ( // MsgRenameCollection implements `renameCollection` command. func (h *Handler) MsgRenameCollection(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + var err error document, err := msg.Document() diff --git a/internal/handler/msg_serverstatus.go b/internal/handler/msg_serverstatus.go index 082cfdc1dfb7..4fd6ca57fe7a 100644 --- a/internal/handler/msg_serverstatus.go +++ b/internal/handler/msg_serverstatus.go @@ -30,6 +30,10 @@ import ( // MsgServerStatus implements `serverStatus` command. func (h *Handler) MsgServerStatus(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + host, err := os.Hostname() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_setfreemonitoring.go b/internal/handler/msg_setfreemonitoring.go index b0f957cc870f..0cc91e6572d3 100644 --- a/internal/handler/msg_setfreemonitoring.go +++ b/internal/handler/msg_setfreemonitoring.go @@ -29,6 +29,10 @@ import ( // MsgSetFreeMonitoring implements `setFreeMonitoring` command. func (h *Handler) MsgSetFreeMonitoring(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_update.go b/internal/handler/msg_update.go index baca03bb3fa9..a7061ec0b29f 100644 --- a/internal/handler/msg_update.go +++ b/internal/handler/msg_update.go @@ -33,6 +33,10 @@ import ( // MsgUpdate implements `update` command. func (h *Handler) MsgUpdate(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_updateuser.go b/internal/handler/msg_updateuser.go index b3b99a69b271..565033f1f1a3 100644 --- a/internal/handler/msg_updateuser.go +++ b/internal/handler/msg_updateuser.go @@ -33,6 +33,10 @@ import ( // MsgUpdateUser implements `updateUser` command. func (h *Handler) MsgUpdateUser(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_usersinfo.go b/internal/handler/msg_usersinfo.go index d0547aad05ce..faf6e4bcf786 100644 --- a/internal/handler/msg_usersinfo.go +++ b/internal/handler/msg_usersinfo.go @@ -30,6 +30,10 @@ import ( // MsgUsersInfo implements `usersInfo` command. func (h *Handler) MsgUsersInfo(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_validate.go b/internal/handler/msg_validate.go index bdb3d3016d3c..7a6d99ba4886 100644 --- a/internal/handler/msg_validate.go +++ b/internal/handler/msg_validate.go @@ -29,6 +29,10 @@ import ( // MsgValidate implements `validate` command. func (h *Handler) MsgValidate(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_whatsmyuri.go b/internal/handler/msg_whatsmyuri.go index 20d7ac680deb..825a27f6a468 100644 --- a/internal/handler/msg_whatsmyuri.go +++ b/internal/handler/msg_whatsmyuri.go @@ -25,6 +25,10 @@ import ( // MsgWhatsMyURI implements `whatsMyURI` command. func (h *Handler) MsgWhatsMyURI(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if err := h.authenticate(ctx, msg); err != nil { + return nil, err + } + var reply wire.OpMsg must.NoError(reply.SetSections(wire.MakeOpMsgSection( must.NotFail(types.NewDocument( From 3699034b3bad98602ce615109edfe9755852164f Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Thu, 15 Feb 2024 15:32:38 +0900 Subject: [PATCH 05/42] move files --- internal/handler/authentication.go | 125 +++++++++++++++++++++++++++++ internal/handler/msg_saslstart.go | 99 +---------------------- 2 files changed, 127 insertions(+), 97 deletions(-) create mode 100644 internal/handler/authentication.go diff --git a/internal/handler/authentication.go b/internal/handler/authentication.go new file mode 100644 index 000000000000..d9ed5f514e0d --- /dev/null +++ b/internal/handler/authentication.go @@ -0,0 +1,125 @@ +// Copyright 2021 FerretDB Inc. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package handler + +import ( + "context" + "errors" + + "github.com/FerretDB/FerretDB/internal/clientconn/conninfo" + "github.com/FerretDB/FerretDB/internal/handler/common" + "github.com/FerretDB/FerretDB/internal/handler/handlererrors" + "github.com/FerretDB/FerretDB/internal/types" + "github.com/FerretDB/FerretDB/internal/util/iterator" + "github.com/FerretDB/FerretDB/internal/util/lazyerrors" + "github.com/FerretDB/FerretDB/internal/util/must" + "github.com/FerretDB/FerretDB/internal/util/password" + "github.com/FerretDB/FerretDB/internal/wire" +) + +// authenticate validates the user's credentials in the connection with the +// credentials in the database. If EnableNewAuth is false, it does nothing. +// +// When admin.systems.user contains no user, the authentication succeeds until +// the first user is created. +func (h *Handler) authenticate(ctx context.Context, msg *wire.OpMsg) error { + if !h.EnableNewAuth { + return nil + } + + adminDB, err := h.b.Database("admin") + if err != nil { + return lazyerrors.Error(err) + } + + usersCol, err := adminDB.Collection("system.users") + if err != nil { + return lazyerrors.Error(err) + } + + document, err := msg.Document() + if err != nil { + return lazyerrors.Error(err) + } + + var dbName string + + if dbName, err = common.GetRequiredParam[string](document, "$db"); err != nil { + return err + } + + username, pwd := conninfo.Get(ctx).Auth() + + // NOTE: how does a user with access to all database look like? + filter := must.NotFail(types.NewDocument("_id", dbName+"."+username)) + + qr, err := usersCol.Query(ctx, nil) + if err != nil { + return lazyerrors.Error(err) + } + + defer qr.Iter.Close() + + var storedUser *types.Document + + var hasUser bool + + for { + var v *types.Document + _, v, err = qr.Iter.Next() + + if errors.Is(err, iterator.ErrIteratorDone) { + break + } + + if err != nil { + return lazyerrors.Error(err) + } + + hasUser = true + + var matches bool + + if matches, err = common.FilterDocument(v, filter); err != nil { + return lazyerrors.Error(err) + } + + if matches { + storedUser = v + break + } + } + + if !hasUser { + // an exception where authentication is skipped until the first user is created. + return nil + } + + credentials := must.NotFail(storedUser.Get("credentials")).(*types.Document) + if !credentials.Has("PLAIN") { + return handlererrors.NewCommandErrorMsgWithArgument( + handlererrors.ErrAuthenticationFailed, + "TODO: wrong authentication mechanism", + "PLAIN", + ) + } + + err = password.PlainVerify(pwd, credentials) + if err != nil { + return lazyerrors.Error(err) + } + + return nil +} diff --git a/internal/handler/msg_saslstart.go b/internal/handler/msg_saslstart.go index 77214b648ca9..3d624935c0aa 100644 --- a/internal/handler/msg_saslstart.go +++ b/internal/handler/msg_saslstart.go @@ -18,17 +18,14 @@ import ( "bytes" "context" "encoding/base64" - "errors" "fmt" "github.com/FerretDB/FerretDB/internal/clientconn/conninfo" "github.com/FerretDB/FerretDB/internal/handler/common" "github.com/FerretDB/FerretDB/internal/handler/handlererrors" "github.com/FerretDB/FerretDB/internal/types" - "github.com/FerretDB/FerretDB/internal/util/iterator" "github.com/FerretDB/FerretDB/internal/util/lazyerrors" "github.com/FerretDB/FerretDB/internal/util/must" - "github.com/FerretDB/FerretDB/internal/util/password" "github.com/FerretDB/FerretDB/internal/wire" ) @@ -74,6 +71,8 @@ func (h *Handler) MsgSASLStart(ctx context.Context, msg *wire.OpMsg) (*wire.OpMs } if h.EnableNewAuth { + // even if the database does not contain the first user yet + // backend authentication is bypassed conninfo.Get(ctx).BypassBackendAuth = true } else { conninfo.Get(ctx).SetAuth(username, password) @@ -139,97 +138,3 @@ func saslStartPlain(doc *types.Document) (string, string, error) { return string(authcid), string(passwd), nil } - -// authenticate validates the user's credentials in the connection with the credentials in the database. -// If the authentication fails, it returns error. -// -// An exception where database collection contains no user, it succeeds authentication. -func (h *Handler) authenticate(ctx context.Context, msg *wire.OpMsg) error { - if !h.EnableNewAuth { - return nil - } - - adminDB, err := h.b.Database("admin") - if err != nil { - return lazyerrors.Error(err) - } - - usersCol, err := adminDB.Collection("system.users") - if err != nil { - return lazyerrors.Error(err) - } - - document, err := msg.Document() - if err != nil { - return lazyerrors.Error(err) - } - - var dbName string - - if dbName, err = common.GetRequiredParam[string](document, "$db"); err != nil { - return err - } - - username, pwd := conninfo.Get(ctx).Auth() - - // NOTE: how does a user with access to all database look like? - filter := must.NotFail(types.NewDocument("_id", dbName+"."+username)) - - qr, err := usersCol.Query(ctx, nil) - if err != nil { - return lazyerrors.Error(err) - } - - defer qr.Iter.Close() - - var storedUser *types.Document - - var hasUser bool - - for { - var v *types.Document - _, v, err = qr.Iter.Next() - - if errors.Is(err, iterator.ErrIteratorDone) { - break - } - - if err != nil { - return lazyerrors.Error(err) - } - - hasUser = true - - var matches bool - - if matches, err = common.FilterDocument(v, filter); err != nil { - return lazyerrors.Error(err) - } - - if matches { - storedUser = v - break - } - } - - if !hasUser { - // an exception where authentication is skipped until the first user is created. - return nil - } - - credentials := must.NotFail(storedUser.Get("credentials")).(*types.Document) - if !credentials.Has("PLAIN") { - return handlererrors.NewCommandErrorMsgWithArgument( - handlererrors.ErrAuthenticationFailed, - "TODO: wrong authentication mechanism", - "PLAIN", - ) - } - - err = password.PlainVerify(pwd, credentials) - if err != nil { - return lazyerrors.Error(err) - } - - return nil -} From 70f018391a8627b11403a84187a32b0340b0b7de Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Thu, 15 Feb 2024 16:32:25 +0900 Subject: [PATCH 06/42] until first user is created new authentication always succeeds --- integration/users/connection_test.go | 80 ++++++++++++++++--- .../{authentication.go => authenticate.go} | 22 ++++- internal/handler/msg_saslstart.go | 7 +- 3 files changed, 92 insertions(+), 17 deletions(-) rename internal/handler/{authentication.go => authenticate.go} (85%) diff --git a/integration/users/connection_test.go b/integration/users/connection_test.go index 39fb02d5f9a3..2f6c38b9136f 100644 --- a/integration/users/connection_test.go +++ b/integration/users/connection_test.go @@ -180,6 +180,68 @@ func TestAuthentication(t *testing.T) { } } +// TestAuthenticationEnableNewAuthNoUser tests that the authentication succeeds +// with any user until the first user is created. +func TestAuthenticationEnableNewAuthNoUser(t *testing.T) { + t.Parallel() + + s := setup.SetupWithOpts(t, nil) + ctx := s.Ctx + collection := s.Collection + db := collection.Database() + + testCases := map[string]struct { //nolint:vet // for readability + username string + password string + mechanism string + + err string + }{ + "PLAIN": { + username: "plain-user", + password: "whatever", + mechanism: "PLAIN", + }, + "PLAINEmpty": { + username: "", + password: "", + mechanism: "PLAIN", + }, + } + + for name, tc := range testCases { + name, tc := name, tc + t.Run(name, func(t *testing.T) { + t.Parallel() + + credential := options.Credential{ + AuthMechanism: tc.mechanism, + AuthSource: db.Name(), + Username: tc.username, + Password: tc.password, + } + + opts := options.Client().ApplyURI(s.MongoDBURI).SetAuth(credential) + + client, err := mongo.Connect(ctx, opts) + require.NoError(t, err, "cannot connect to MongoDB") + + err = client.Ping(ctx, nil) + + if tc.err != "" { + require.ErrorContains(t, err, tc.err) + return + } + + require.NoError(t, err, "cannot ping MongoDB") + + connCollection := client.Database(db.Name()).Collection(collection.Name()) + _, err = connCollection.InsertOne(ctx, bson.D{{"ping", "pong"}}) + require.NoError(t, err, "cannot insert document") + }) + } +} + func TestAuthenticationEnableNewAuth(t *testing.T) { t.Parallel() @@ -188,19 +250,13 @@ func TestAuthenticationEnableNewAuth(t *testing.T) { collection := s.Collection db := collection.Database() - var res bson.D err := db.RunCommand(ctx, bson.D{ - {"dropAllUsersFromDatabase", 1}, - }).Decode(&res) - require.NoError(t, err) - - //err = db.RunCommand(ctx, bson.D{ - // {"createUser", "plain-user"}, - // {"roles", bson.A{}}, - // {"pwd", "correct"}, - // {"mechanisms", bson.A{"PLAIN"}}, - //}).Err() - //require.NoErrorf(t, err, "cannot create user") + {"createUser", "plain-user"}, + {"roles", bson.A{}}, + {"pwd", "correct"}, + {"mechanisms", bson.A{"PLAIN"}}, + }).Err() + require.NoErrorf(t, err, "cannot create user") testCases := map[string]struct { //nolint:vet // for readability username string diff --git a/internal/handler/authentication.go b/internal/handler/authenticate.go similarity index 85% rename from internal/handler/authentication.go rename to internal/handler/authenticate.go index d9ed5f514e0d..efbbfe1a0959 100644 --- a/internal/handler/authentication.go +++ b/internal/handler/authenticate.go @@ -103,12 +103,23 @@ func (h *Handler) authenticate(ctx context.Context, msg *wire.OpMsg) error { } if !hasUser { - // an exception where authentication is skipped until the first user is created. + // If a user connects with any credentials or no credentials at all, + // the authentication succeeds until the first user is created. return nil } + if storedUser == nil { + return handlererrors.NewCommandErrorMsgWithArgument( + handlererrors.ErrAuthenticationFailed, + "User not found", + "PLAIN", + ) + } + credentials := must.NotFail(storedUser.Get("credentials")).(*types.Document) - if !credentials.Has("PLAIN") { + + v, _ := credentials.Get("PLAIN") + if v == nil { return handlererrors.NewCommandErrorMsgWithArgument( handlererrors.ErrAuthenticationFailed, "TODO: wrong authentication mechanism", @@ -116,7 +127,12 @@ func (h *Handler) authenticate(ctx context.Context, msg *wire.OpMsg) error { ) } - err = password.PlainVerify(pwd, credentials) + doc, ok := v.(*types.Document) + if !ok { + return lazyerrors.Errorf("field 'PLAIN' has type %T, expected Document", v) + } + + err = password.PlainVerify(pwd, doc) if err != nil { return lazyerrors.Error(err) } diff --git a/internal/handler/msg_saslstart.go b/internal/handler/msg_saslstart.go index 3d624935c0aa..267dc04cb014 100644 --- a/internal/handler/msg_saslstart.go +++ b/internal/handler/msg_saslstart.go @@ -71,8 +71,11 @@ func (h *Handler) MsgSASLStart(ctx context.Context, msg *wire.OpMsg) (*wire.OpMs } if h.EnableNewAuth { - // even if the database does not contain the first user yet - // backend authentication is bypassed + // If new auth is enabled and the database does not contain any user, + // backend authentication is bypassed. + // + // If a user connects with any credentials or no credentials at all, + // the authentication succeeds until the first user is created. conninfo.Get(ctx).BypassBackendAuth = true } else { conninfo.Get(ctx).SetAuth(username, password) From 470ca146c18279b84f21fbced4c307f281b34d15 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Thu, 15 Feb 2024 17:41:33 +0900 Subject: [PATCH 07/42] is master does not need authentication --- internal/handler/msg_ismaster.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal/handler/msg_ismaster.go b/internal/handler/msg_ismaster.go index 7c4683584c1c..4e2e0733a590 100644 --- a/internal/handler/msg_ismaster.go +++ b/internal/handler/msg_ismaster.go @@ -25,10 +25,6 @@ import ( // MsgIsMaster implements `isMaster` command. func (h *Handler) MsgIsMaster(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { - return nil, err - } - doc, err := msg.Document() if err != nil { return nil, err From 40be66db55391e140b181b3a9f336db360c962e3 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Fri, 16 Feb 2024 10:18:45 +0900 Subject: [PATCH 08/42] handle database without any pool yet --- internal/backends/postgresql/metadata/registry.go | 9 ++++++++- internal/handler/msg_saslstart.go | 10 +++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/internal/backends/postgresql/metadata/registry.go b/internal/backends/postgresql/metadata/registry.go index 3c4980ce8a4e..a86b828a97d2 100644 --- a/internal/backends/postgresql/metadata/registry.go +++ b/internal/backends/postgresql/metadata/registry.go @@ -122,7 +122,14 @@ func (r *Registry) getPool(ctx context.Context) (*pgxpool.Pool, error) { if connInfo.BypassBackendAuth { if p = r.p.GetAny(); p == nil { - return nil, lazyerrors.New("no connection pool") + // no connection pool has been created yet and authentication + // is bypassed, attempt to use credentials to connect + username, password := connInfo.Auth() + + var err error + if p, err = r.p.Get(username, password); err != nil { + return nil, lazyerrors.Error(err) + } } } else { username, password := connInfo.Auth() diff --git a/internal/handler/msg_saslstart.go b/internal/handler/msg_saslstart.go index 267dc04cb014..5fcd91a61761 100644 --- a/internal/handler/msg_saslstart.go +++ b/internal/handler/msg_saslstart.go @@ -60,10 +60,6 @@ func (h *Handler) MsgSASLStart(ctx context.Context, msg *wire.OpMsg) (*wire.OpMs if err != nil { return nil, err } - - if err = h.authenticate(ctx, msg); err != nil { - return nil, err - } default: msg := fmt.Sprintf("Unsupported authentication mechanism %q.\n", mechanism) + "See https://docs.ferretdb.io/security/authentication/ for more details." @@ -73,10 +69,10 @@ func (h *Handler) MsgSASLStart(ctx context.Context, msg *wire.OpMsg) (*wire.OpMs if h.EnableNewAuth { // If new auth is enabled and the database does not contain any user, // backend authentication is bypassed. - // - // If a user connects with any credentials or no credentials at all, - // the authentication succeeds until the first user is created. conninfo.Get(ctx).BypassBackendAuth = true + if err = h.authenticate(ctx, msg); err != nil { + return nil, err + } } else { conninfo.Get(ctx).SetAuth(username, password) } From fd048c69a5da5ac8914e81aa593c97442f325ae8 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Fri, 16 Feb 2024 11:43:03 +0900 Subject: [PATCH 09/42] do not authenticate on some handlers --- internal/handler/msg_buildinfo.go | 4 ---- internal/handler/msg_connectionstatus.go | 4 ---- internal/handler/msg_hello.go | 4 ---- internal/handler/msg_ping.go | 4 ---- internal/handler/msg_saslstart.go | 5 +---- internal/handler/msg_whatsmyuri.go | 4 ---- 6 files changed, 1 insertion(+), 24 deletions(-) diff --git a/internal/handler/msg_buildinfo.go b/internal/handler/msg_buildinfo.go index 7029d60a3440..ba4a3310f6f3 100644 --- a/internal/handler/msg_buildinfo.go +++ b/internal/handler/msg_buildinfo.go @@ -27,10 +27,6 @@ import ( // MsgBuildInfo implements `buildInfo` command. func (h *Handler) MsgBuildInfo(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { - return nil, err - } - aggregationStages := types.MakeArray(len(stages.Stages)) for stage := range stages.Stages { aggregationStages.Append(stage) diff --git a/internal/handler/msg_connectionstatus.go b/internal/handler/msg_connectionstatus.go index a0d35a4cd49e..53d750ef4857 100644 --- a/internal/handler/msg_connectionstatus.go +++ b/internal/handler/msg_connectionstatus.go @@ -25,10 +25,6 @@ import ( // MsgConnectionStatus implements `connectionStatus` command. func (h *Handler) MsgConnectionStatus(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { - return nil, err - } - users := types.MakeArray(1) if username := conninfo.Get(ctx).Username(); username != "" { diff --git a/internal/handler/msg_hello.go b/internal/handler/msg_hello.go index d5a386696d01..9845961f024c 100644 --- a/internal/handler/msg_hello.go +++ b/internal/handler/msg_hello.go @@ -27,10 +27,6 @@ import ( // MsgHello implements `hello` command. func (h *Handler) MsgHello(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { - return nil, err - } - doc, err := msg.Document() if err != nil { return nil, err diff --git a/internal/handler/msg_ping.go b/internal/handler/msg_ping.go index f7bb0dbb5e6e..e836f52b8f7d 100644 --- a/internal/handler/msg_ping.go +++ b/internal/handler/msg_ping.go @@ -29,10 +29,6 @@ import ( // MsgPing implements `ping` command. func (h *Handler) MsgPing(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_saslstart.go b/internal/handler/msg_saslstart.go index 101aecadbfdc..9ff09d4bf0d6 100644 --- a/internal/handler/msg_saslstart.go +++ b/internal/handler/msg_saslstart.go @@ -68,11 +68,8 @@ func (h *Handler) MsgSASLStart(ctx context.Context, msg *wire.OpMsg) (*wire.OpMs if h.EnableNewAuth { // If new auth is enabled and the database does not contain any user, // backend authentication is bypassed. + conninfo.Get(ctx).SetAuth(username, password) conninfo.Get(ctx).SetBypassBackendAuth() - - if err = h.authenticate(ctx, msg); err != nil { - return nil, err - } } else { conninfo.Get(ctx).SetAuth(username, password) } diff --git a/internal/handler/msg_whatsmyuri.go b/internal/handler/msg_whatsmyuri.go index 825a27f6a468..20d7ac680deb 100644 --- a/internal/handler/msg_whatsmyuri.go +++ b/internal/handler/msg_whatsmyuri.go @@ -25,10 +25,6 @@ import ( // MsgWhatsMyURI implements `whatsMyURI` command. func (h *Handler) MsgWhatsMyURI(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { - return nil, err - } - var reply wire.OpMsg must.NoError(reply.SetSections(wire.MakeOpMsgSection( must.NotFail(types.NewDocument( From 4a6b841a29aeb7eab2b90e07e688e797a2470cf3 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Fri, 16 Feb 2024 12:40:51 +0900 Subject: [PATCH 10/42] authentication for sha256 is done by conversation step, so handler checks nothing --- integration/users/connection_test.go | 39 ++++++++++++++++++++++++---- internal/handler/authenticate.go | 19 +++++++++----- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/integration/users/connection_test.go b/integration/users/connection_test.go index 257bd14e7eb8..a3aa7f6696a4 100644 --- a/integration/users/connection_test.go +++ b/integration/users/connection_test.go @@ -252,6 +252,10 @@ func TestAuthenticationEnableNewAuthNoUser(t *testing.T) { for name, tc := range testCases { name, tc := name, tc t.Run(name, func(t *testing.T) { + if tc.mechanism == "PLAIN" { + setup.SkipForMongoDB(t, "PLAIN mechanism is not supported by MongoDB") + } + t.Parallel() credential := options.Credential{ @@ -282,7 +286,7 @@ func TestAuthenticationEnableNewAuthNoUser(t *testing.T) { } } -func TestAuthenticationEnableNewAuth(t *testing.T) { +func TestAuthenticationEnableNewAuthWithUser(t *testing.T) { t.Parallel() s := setup.SetupWithOpts(t, nil) @@ -291,13 +295,34 @@ func TestAuthenticationEnableNewAuth(t *testing.T) { db := collection.Database() err := db.RunCommand(ctx, bson.D{ - {"createUser", "plain-user"}, + {"createUser", "sha256-user"}, {"roles", bson.A{}}, {"pwd", "correct"}, - {"mechanisms", bson.A{"PLAIN"}}, + {"mechanisms", bson.A{"SCRAM-SHA-256"}}, }).Err() require.NoErrorf(t, err, "cannot create user") + if !setup.IsMongoDB(t) { + // one user has been created so authentication is required now, + // use that created user to authenticate + opts := options.Client().ApplyURI(s.MongoDBURI).SetAuth(options.Credential{ + AuthMechanism: "SCRAM-SHA-256", + AuthSource: db.Name(), + Username: "sha256-user", + Password: "correct", + }) + + client, err := mongo.Connect(ctx, opts) + require.NoError(t, err, "cannot connect to MongoDB") + err = client.Database(db.Name()).RunCommand(ctx, bson.D{ + {"createUser", "plain-user"}, + {"roles", bson.A{}}, + {"pwd", "correct"}, + {"mechanisms", bson.A{"PLAIN"}}, + }).Err() + require.NoErrorf(t, err, "cannot create user") + } + testCases := map[string]struct { //nolint:vet // for readability username string password string @@ -323,7 +348,7 @@ func TestAuthenticationEnableNewAuth(t *testing.T) { err: "AuthenticationFailed", }, "SHA256": { - username: "scram-user", + username: "sha256-user", password: "correct", mechanism: "SCRAM-SHA-256", }, @@ -334,7 +359,7 @@ func TestAuthenticationEnableNewAuth(t *testing.T) { err: "AuthenticationFailed", }, "SHA256WrongPassword": { - username: "scram-user", + username: "sha256-user", password: "wrong", mechanism: "SCRAM-SHA-256", err: "AuthenticationFailed", @@ -344,6 +369,10 @@ func TestAuthenticationEnableNewAuth(t *testing.T) { for name, tc := range testCases { name, tc := name, tc t.Run(name, func(t *testing.T) { + if tc.mechanism == "PLAIN" { + setup.SkipForMongoDB(t, "PLAIN mechanism is not supported by MongoDB") + } + t.Parallel() credential := options.Credential{ diff --git a/internal/handler/authenticate.go b/internal/handler/authenticate.go index efbbfe1a0959..8defaa91b668 100644 --- a/internal/handler/authenticate.go +++ b/internal/handler/authenticate.go @@ -112,21 +112,28 @@ func (h *Handler) authenticate(ctx context.Context, msg *wire.OpMsg) error { return handlererrors.NewCommandErrorMsgWithArgument( handlererrors.ErrAuthenticationFailed, "User not found", - "PLAIN", + "authenticate", ) } credentials := must.NotFail(storedUser.Get("credentials")).(*types.Document) - v, _ := credentials.Get("PLAIN") - if v == nil { + switch { + case credentials.Has("SCRAM-SHA-256"): + // SCRAM-SHA-256 calls back scramCredentialLookup each time Step is called, + // and that checks the authentication. + return nil + case credentials.Has("PLAIN"): + break + default: return handlererrors.NewCommandErrorMsgWithArgument( handlererrors.ErrAuthenticationFailed, - "TODO: wrong authentication mechanism", - "PLAIN", + "Unknown authentication mechanism", + "authenticate", ) } + v := must.NotFail(credentials.Get("PLAIN")) doc, ok := v.(*types.Document) if !ok { return lazyerrors.Errorf("field 'PLAIN' has type %T, expected Document", v) @@ -134,7 +141,7 @@ func (h *Handler) authenticate(ctx context.Context, msg *wire.OpMsg) error { err = password.PlainVerify(pwd, doc) if err != nil { - return lazyerrors.Error(err) + return lazyerrors.New("invalid password") } return nil From 90de3fadc0a2e2e8851ac3de52fbad0c3ec186c8 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Fri, 16 Feb 2024 12:41:19 +0900 Subject: [PATCH 11/42] Plain credential hashes password --- internal/handler/msg_createuser.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/handler/msg_createuser.go b/internal/handler/msg_createuser.go index 48e80b81431f..68baa8b24907 100644 --- a/internal/handler/msg_createuser.go +++ b/internal/handler/msg_createuser.go @@ -222,7 +222,7 @@ func makeCredentials(mechanisms *types.Array, username, pwd string) (*types.Docu switch v { case "PLAIN": - credentials.Set("PLAIN", must.NotFail(password.PlainHash(username))) + credentials.Set("PLAIN", must.NotFail(password.PlainHash(pwd))) case "SCRAM-SHA-256": hash, err := password.SCRAMSHA256Hash(pwd) if err != nil { From b4b4c574c834b1c8a9807643f8f51d77ae4e21e4 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Fri, 16 Feb 2024 15:27:30 +0900 Subject: [PATCH 12/42] add test for scram sha256 user for empty database --- integration/users/connection_test.go | 38 ++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/integration/users/connection_test.go b/integration/users/connection_test.go index a3aa7f6696a4..3bfea7a67129 100644 --- a/integration/users/connection_test.go +++ b/integration/users/connection_test.go @@ -221,8 +221,10 @@ func TestAuthentication(t *testing.T) { } // TestAuthenticationEnableNewAuthNoUser tests that the authentication succeeds -// with any user until the first user is created. -func TestAuthenticationEnableNewAuthNoUser(t *testing.T) { +// with any PLAIN mechanism user until the first user is created. This is temporary +// until local exception is implemented. +// For SCRAM-SHA-256 mechanism users, authentication fails if the user does not exist. +func TestAuthenticationEnableNewAuthNoUserExists(t *testing.T) { t.Parallel() s := setup.SetupWithOpts(t, nil) @@ -247,6 +249,12 @@ func TestAuthenticationEnableNewAuthNoUser(t *testing.T) { password: "", mechanism: "PLAIN", }, + "SHA256": { + username: "sha256-user", + password: "whatever", + mechanism: "SCRAM-SHA-256", + err: "Authentication failed", + }, } for name, tc := range testCases { @@ -286,7 +294,7 @@ func TestAuthenticationEnableNewAuthNoUser(t *testing.T) { } } -func TestAuthenticationEnableNewAuthWithUser(t *testing.T) { +func TestAuthenticationEnableNewAuthWithExistingUser(t *testing.T) { t.Parallel() s := setup.SetupWithOpts(t, nil) @@ -302,9 +310,25 @@ func TestAuthenticationEnableNewAuthWithUser(t *testing.T) { }).Err() require.NoErrorf(t, err, "cannot create user") + t.Cleanup(func() { + // once the first user has been created use that user for any other action + // until local exception is implemented + opts := options.Client().ApplyURI(s.MongoDBURI).SetAuth(options.Credential{ + AuthMechanism: "SCRAM-SHA-256", + AuthSource: db.Name(), + Username: "sha256-user", + Password: "correct", + }) + + client, err := mongo.Connect(ctx, opts) + require.NoError(t, err, "cannot connect to MongoDB") + + require.NoError(t, client.Database(db.Name()).RunCommand(ctx, bson.D{{"dropUser", "sha256-user"}}).Err()) + }) + if !setup.IsMongoDB(t) { - // one user has been created so authentication is required now, - // use that created user to authenticate + // once the first user has been created use that user for any other action + // until local exception is implemented opts := options.Client().ApplyURI(s.MongoDBURI).SetAuth(options.Credential{ AuthMechanism: "SCRAM-SHA-256", AuthSource: db.Name(), @@ -321,6 +345,10 @@ func TestAuthenticationEnableNewAuthWithUser(t *testing.T) { {"mechanisms", bson.A{"PLAIN"}}, }).Err() require.NoErrorf(t, err, "cannot create user") + + t.Cleanup(func() { + require.NoError(t, client.Database(db.Name()).RunCommand(ctx, bson.D{{"dropUser", "plain-user"}}).Err()) + }) } testCases := map[string]struct { //nolint:vet // for readability From 5dc7ae5d319703f7a1c0a59565f18decf221e100 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Fri, 16 Feb 2024 16:16:25 +0900 Subject: [PATCH 13/42] fix create update and drop user tests --- integration/users/create_user_test.go | 48 +++++++++++++++++++++++++-- integration/users/drop_user_test.go | 6 ++-- integration/users/update_user_test.go | 6 ++-- 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/integration/users/create_user_test.go b/integration/users/create_user_test.go index d0ad8c11b761..d8aae15f178f 100644 --- a/integration/users/create_user_test.go +++ b/integration/users/create_user_test.go @@ -22,6 +22,7 @@ import ( "github.com/stretchr/testify/require" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" "github.com/FerretDB/FerretDB/integration" "github.com/FerretDB/FerretDB/integration/setup" @@ -34,8 +35,10 @@ import ( func TestCreateUser(t *testing.T) { t.Parallel() - ctx, collection := setup.Setup(t) - db := collection.Database() + s := setup.SetupWithOpts(t, nil) + ctx := s.Ctx + + db, _ := createUserTestRunnerUser(t, s) testCases := map[string]struct { //nolint:vet // for readability payload bson.D @@ -302,3 +305,44 @@ func assertSCRAMSHA256Credentials(t testtb.TB, key string, cred *types.Document) assert.NotEmpty(t, must.NotFail(c.Get("serverKey")).(string)) assert.NotEmpty(t, must.NotFail(c.Get("storedKey")).(string)) } + +// createUserTestRunnerUser creates a user with PLAIN mechanism and returns +// the database and collection connection created by that user. +// This gives a user to run user creation tests until local exception is implemented for FerretDB. +// Without this, once the first user is created, the connection to FerretDB fails authentication +// and cannot do any further operations. +func createUserTestRunnerUser(tb *testing.T, s *setup.SetupResult) (*mongo.Database, *mongo.Collection) { + if setup.IsMongoDB(tb) { + return s.Collection.Database(), s.Collection + } + + username, pwd, mechanism := "user-test-runner", "password", "PLAIN" + + err := s.Collection.Database().RunCommand(s.Ctx, bson.D{ + {"createUser", username}, + {"roles", bson.A{}}, + {"pwd", pwd}, + {"mechanisms", bson.A{mechanism}}, + }).Err() + require.NoErrorf(tb, err, "cannot create user") + + // once the first user has been created use that user for any other action + // until local exception is implemented + opts := options.Client().ApplyURI(s.MongoDBURI).SetAuth(options.Credential{ + AuthMechanism: mechanism, + AuthSource: s.Collection.Name(), + Username: username, + Password: pwd, + }) + client, err := mongo.Connect(s.Ctx, opts) + require.NoError(tb, err, "cannot connect to MongoDB") + + db := client.Database(s.Collection.Database().Name()) + collection := db.Collection(s.Collection.Name()) + + tb.Cleanup(func() { + require.NoError(tb, db.RunCommand(s.Ctx, bson.D{{"dropAllUsersFromDatabase", 1}}).Err()) + }) + + return db, collection +} diff --git a/integration/users/drop_user_test.go b/integration/users/drop_user_test.go index a492b33b5b86..ebcb008d2b7e 100644 --- a/integration/users/drop_user_test.go +++ b/integration/users/drop_user_test.go @@ -31,8 +31,10 @@ import ( func TestDropUser(t *testing.T) { t.Parallel() - ctx, collection := setup.Setup(t) - db := collection.Database() + s := setup.SetupWithOpts(t, nil) + ctx := s.Ctx + + db, _ := createUserTestRunnerUser(t, s) err := db.RunCommand(ctx, bson.D{ {"createUser", "a_user"}, diff --git a/integration/users/update_user_test.go b/integration/users/update_user_test.go index d6d7ba1a4961..4795e2a76b78 100644 --- a/integration/users/update_user_test.go +++ b/integration/users/update_user_test.go @@ -32,8 +32,10 @@ import ( func TestUpdateUser(t *testing.T) { t.Parallel() - ctx, collection := setup.Setup(t) - db := collection.Database() + s := setup.SetupWithOpts(t, nil) + ctx := s.Ctx + + db, _ := createUserTestRunnerUser(t, s) testCases := map[string]struct { //nolint:vet // for readability createPayload bson.D From c3583d179efea588e3a7355b5194496c9e96b177 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Fri, 16 Feb 2024 16:30:41 +0900 Subject: [PATCH 14/42] update error and panic --- internal/handler/authenticate.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/handler/authenticate.go b/internal/handler/authenticate.go index 8defaa91b668..6b339a1555f2 100644 --- a/internal/handler/authenticate.go +++ b/internal/handler/authenticate.go @@ -111,7 +111,7 @@ func (h *Handler) authenticate(ctx context.Context, msg *wire.OpMsg) error { if storedUser == nil { return handlererrors.NewCommandErrorMsgWithArgument( handlererrors.ErrAuthenticationFailed, - "User not found", + "Authentication failed", "authenticate", ) } @@ -119,18 +119,14 @@ func (h *Handler) authenticate(ctx context.Context, msg *wire.OpMsg) error { credentials := must.NotFail(storedUser.Get("credentials")).(*types.Document) switch { - case credentials.Has("SCRAM-SHA-256"): - // SCRAM-SHA-256 calls back scramCredentialLookup each time Step is called, + case credentials.Has("SCRAM-SHA-256"), credentials.Has("SCRAM-SHA-1"): + // SCRAM calls back scramCredentialLookup each time Step is called, // and that checks the authentication. return nil case credentials.Has("PLAIN"): break default: - return handlererrors.NewCommandErrorMsgWithArgument( - handlererrors.ErrAuthenticationFailed, - "Unknown authentication mechanism", - "authenticate", - ) + panic("credentials does not contain a known mechanism") } v := must.NotFail(credentials.Get("PLAIN")) @@ -141,7 +137,11 @@ func (h *Handler) authenticate(ctx context.Context, msg *wire.OpMsg) error { err = password.PlainVerify(pwd, doc) if err != nil { - return lazyerrors.New("invalid password") + return handlererrors.NewCommandErrorMsgWithArgument( + handlererrors.ErrAuthenticationFailed, + "Authentication failed", + "authenticate", + ) } return nil From 8d72103ab64eea2d15415c937d4fe73326fa32a5 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Mon, 19 Feb 2024 10:27:19 +0900 Subject: [PATCH 15/42] create pool upon registry creation --- .../backends/postgresql/metadata/registry.go | 30 ++++++++++++++----- internal/handler/msg_saslstart.go | 10 ++----- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/internal/backends/postgresql/metadata/registry.go b/internal/backends/postgresql/metadata/registry.go index fa2f453bb17c..1816796e66b0 100644 --- a/internal/backends/postgresql/metadata/registry.go +++ b/internal/backends/postgresql/metadata/registry.go @@ -18,6 +18,7 @@ import ( "context" "fmt" "hash/fnv" + "net/url" "regexp" "slices" "sort" @@ -86,6 +87,9 @@ type Registry struct { } // NewRegistry creates a registry for PostgreSQL databases with a given base URI. +// +// It gets a pool using the user and password from the base URI, which is later used +// by connections that by passes backend authentication. func NewRegistry(u string, l *zap.Logger, sp *state.Provider) (*Registry, error) { p, err := pool.New(u, l, sp) if err != nil { @@ -97,6 +101,23 @@ func NewRegistry(u string, l *zap.Logger, sp *state.Provider) (*Registry, error) l: l, } + baseURI, err := url.Parse(u) + if err != nil { + return nil, lazyerrors.Error(err) + } + + username := baseURI.User.Username() + pwd, _ := baseURI.User.Password() + + c := conninfo.New() + c.SetAuth(username, pwd) + + ctx := conninfo.Ctx(context.Background(), c) + _, err = r.getPool(ctx) + if err != nil { + return nil, lazyerrors.Error(err) + } + return r, nil } @@ -122,14 +143,7 @@ func (r *Registry) getPool(ctx context.Context) (*pgxpool.Pool, error) { if connInfo.BypassBackendAuth() { if p = r.p.GetAny(); p == nil { - // no connection pool has been created yet and authentication - // is bypassed, attempt to use credentials to connect - username, password := connInfo.Auth() - - var err error - if p, err = r.p.Get(username, password); err != nil { - return nil, lazyerrors.Error(err) - } + return nil, lazyerrors.New("no connection pool") } } else { username, password := connInfo.Auth() diff --git a/internal/handler/msg_saslstart.go b/internal/handler/msg_saslstart.go index 9ff09d4bf0d6..020210c1b710 100644 --- a/internal/handler/msg_saslstart.go +++ b/internal/handler/msg_saslstart.go @@ -65,14 +65,8 @@ func (h *Handler) MsgSASLStart(ctx context.Context, msg *wire.OpMsg) (*wire.OpMs return nil, err } - if h.EnableNewAuth { - // If new auth is enabled and the database does not contain any user, - // backend authentication is bypassed. - conninfo.Get(ctx).SetAuth(username, password) - conninfo.Get(ctx).SetBypassBackendAuth() - } else { - conninfo.Get(ctx).SetAuth(username, password) - } + conninfo.Get(ctx).SetBypassBackendAuth() + conninfo.Get(ctx).SetAuth(username, password) var emptyPayload types.Binary must.NoError(reply.SetSections(wire.MakeOpMsgSection( From f694f4ebf7124361ff1d7492ce3865ee30c861c0 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Mon, 19 Feb 2024 10:41:55 +0900 Subject: [PATCH 16/42] do not authenticate on handler if bypass backend auth is not set --- internal/handler/authenticate.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/handler/authenticate.go b/internal/handler/authenticate.go index 6b339a1555f2..52d548b636b4 100644 --- a/internal/handler/authenticate.go +++ b/internal/handler/authenticate.go @@ -30,7 +30,8 @@ import ( ) // authenticate validates the user's credentials in the connection with the -// credentials in the database. If EnableNewAuth is false, it does nothing. +// credentials in the database. If EnableNewAuth is false or bypass backend auth +// is set false, it succeeds authentication. // // When admin.systems.user contains no user, the authentication succeeds until // the first user is created. @@ -39,6 +40,10 @@ func (h *Handler) authenticate(ctx context.Context, msg *wire.OpMsg) error { return nil } + if !conninfo.Get(ctx).BypassBackendAuth() { + return nil + } + adminDB, err := h.b.Database("admin") if err != nil { return lazyerrors.Error(err) From b08beeac6149b674e029912e5cfb51cd4b91c468 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Mon, 19 Feb 2024 11:20:26 +0900 Subject: [PATCH 17/42] user tests use credentials for test runner --- integration/users/connection_test.go | 45 +++---------------- integration/users/create_user_test.go | 7 ++- .../drop_all_users_from_database_test.go | 5 ++- integration/users/update_user_test.go | 12 ++--- integration/users/usersinfo_test.go | 23 +++++----- internal/handler/authenticate.go | 14 +----- internal/handler/msg_createuser.go | 2 +- internal/handler/msg_dropuser.go | 2 +- internal/handler/msg_usersinfo.go | 2 +- 9 files changed, 35 insertions(+), 77 deletions(-) diff --git a/integration/users/connection_test.go b/integration/users/connection_test.go index 3bfea7a67129..9f5ce5359687 100644 --- a/integration/users/connection_test.go +++ b/integration/users/connection_test.go @@ -35,8 +35,8 @@ func TestAuthentication(t *testing.T) { s := setup.SetupWithOpts(t, nil) ctx := s.Ctx - collection := s.Collection - db := collection.Database() + + db, collection := createUserTestRunnerUser(t, s) testCases := map[string]struct { //nolint:vet // for readability username string @@ -228,9 +228,7 @@ func TestAuthenticationEnableNewAuthNoUserExists(t *testing.T) { t.Parallel() s := setup.SetupWithOpts(t, nil) - ctx := s.Ctx - collection := s.Collection - db := collection.Database() + ctx, collection, db := s.Ctx, s.Collection, s.Collection.Database() testCases := map[string]struct { //nolint:vet // for readability username string @@ -299,8 +297,8 @@ func TestAuthenticationEnableNewAuthWithExistingUser(t *testing.T) { s := setup.SetupWithOpts(t, nil) ctx := s.Ctx - collection := s.Collection - db := collection.Database() + + db, collection := createUserTestRunnerUser(t, s) err := db.RunCommand(ctx, bson.D{ {"createUser", "sha256-user"}, @@ -310,45 +308,14 @@ func TestAuthenticationEnableNewAuthWithExistingUser(t *testing.T) { }).Err() require.NoErrorf(t, err, "cannot create user") - t.Cleanup(func() { - // once the first user has been created use that user for any other action - // until local exception is implemented - opts := options.Client().ApplyURI(s.MongoDBURI).SetAuth(options.Credential{ - AuthMechanism: "SCRAM-SHA-256", - AuthSource: db.Name(), - Username: "sha256-user", - Password: "correct", - }) - - client, err := mongo.Connect(ctx, opts) - require.NoError(t, err, "cannot connect to MongoDB") - - require.NoError(t, client.Database(db.Name()).RunCommand(ctx, bson.D{{"dropUser", "sha256-user"}}).Err()) - }) - if !setup.IsMongoDB(t) { - // once the first user has been created use that user for any other action - // until local exception is implemented - opts := options.Client().ApplyURI(s.MongoDBURI).SetAuth(options.Credential{ - AuthMechanism: "SCRAM-SHA-256", - AuthSource: db.Name(), - Username: "sha256-user", - Password: "correct", - }) - - client, err := mongo.Connect(ctx, opts) - require.NoError(t, err, "cannot connect to MongoDB") - err = client.Database(db.Name()).RunCommand(ctx, bson.D{ + err = db.RunCommand(ctx, bson.D{ {"createUser", "plain-user"}, {"roles", bson.A{}}, {"pwd", "correct"}, {"mechanisms", bson.A{"PLAIN"}}, }).Err() require.NoErrorf(t, err, "cannot create user") - - t.Cleanup(func() { - require.NoError(t, client.Database(db.Name()).RunCommand(ctx, bson.D{{"dropUser", "plain-user"}}).Err()) - }) } testCases := map[string]struct { //nolint:vet // for readability diff --git a/integration/users/create_user_test.go b/integration/users/create_user_test.go index d8aae15f178f..656abbb4e951 100644 --- a/integration/users/create_user_test.go +++ b/integration/users/create_user_test.go @@ -15,7 +15,6 @@ package users import ( - "fmt" "testing" "github.com/stretchr/testify/assert" @@ -267,7 +266,7 @@ func TestCreateUser(t *testing.T) { user.Remove("credentials") expectedRec := integration.ConvertDocument(t, bson.D{ - {"_id", fmt.Sprintf("%s.%s", db.Name(), must.NotFail(payload.Get("createUser")))}, + {"_id", must.NotFail(payload.Get("createUser"))}, {"user", must.NotFail(payload.Get("createUser"))}, {"db", db.Name()}, {"roles", bson.A{}}, @@ -317,8 +316,7 @@ func createUserTestRunnerUser(tb *testing.T, s *setup.SetupResult) (*mongo.Datab } username, pwd, mechanism := "user-test-runner", "password", "PLAIN" - - err := s.Collection.Database().RunCommand(s.Ctx, bson.D{ + err := s.Collection.Database().Client().Database("admin").RunCommand(s.Ctx, bson.D{ {"createUser", username}, {"roles", bson.A{}}, {"pwd", pwd}, @@ -342,6 +340,7 @@ func createUserTestRunnerUser(tb *testing.T, s *setup.SetupResult) (*mongo.Datab tb.Cleanup(func() { require.NoError(tb, db.RunCommand(s.Ctx, bson.D{{"dropAllUsersFromDatabase", 1}}).Err()) + require.NoError(tb, client.Database("admin").RunCommand(s.Ctx, bson.D{{"dropAllUsersFromDatabase", 1}}).Err()) }) return db, collection diff --git a/integration/users/drop_all_users_from_database_test.go b/integration/users/drop_all_users_from_database_test.go index 730743b5235f..1617b5f77f3d 100644 --- a/integration/users/drop_all_users_from_database_test.go +++ b/integration/users/drop_all_users_from_database_test.go @@ -34,8 +34,9 @@ import ( func TestDropAllUsersFromDatabase(t *testing.T) { t.Parallel() - ctx, collection := setup.Setup(t) - db := collection.Database() + s := setup.SetupWithOpts(t, nil) + ctx := s.Ctx + db, collection := createUserTestRunnerUser(t, s) client := collection.Database().Client() quantity := 5 // Add some users to the database. diff --git a/integration/users/update_user_test.go b/integration/users/update_user_test.go index 4795e2a76b78..1acf6131e220 100644 --- a/integration/users/update_user_test.go +++ b/integration/users/update_user_test.go @@ -155,7 +155,7 @@ func TestUpdateUser(t *testing.T) { {"pwd", "donotchange"}, }, expected: bson.D{ - {"_id", "TestUpdateUser.same_password_user"}, + {"_id", "same_password_user"}, {"user", "same_password_user"}, {"db", "TestUpdateUser"}, {"roles", bson.A{}}, @@ -172,7 +172,7 @@ func TestUpdateUser(t *testing.T) { {"pwd", "anewpassword"}, }, expected: bson.D{ - {"_id", "TestUpdateUser.a_user"}, + {"_id", "a_user"}, {"user", "a_user"}, {"db", "TestUpdateUser"}, {"roles", bson.A{}}, @@ -190,7 +190,7 @@ func TestUpdateUser(t *testing.T) { {"pwd", "anewpassword"}, }, expected: bson.D{ - {"_id", "TestUpdateUser.a_user_with_mechanism"}, + {"_id", "a_user_with_mechanism"}, {"user", "a_user_with_mechanism"}, {"db", "TestUpdateUser"}, {"roles", bson.A{}}, @@ -210,7 +210,7 @@ func TestUpdateUser(t *testing.T) { {"mechanisms", bson.A{"SCRAM-SHA-256"}}, }, expected: bson.D{ - {"_id", "TestUpdateUser.a_user_with_scram_mechanism"}, + {"_id", "a_user_with_scram_mechanism"}, {"user", "a_user_with_scram_mechanism"}, {"db", "TestUpdateUser"}, {"roles", bson.A{}}, @@ -246,7 +246,7 @@ func TestUpdateUser(t *testing.T) { {"pwd", "anewpassword"}, }, expected: bson.D{ - {"_id", "TestUpdateUser.a_user_with_no_roles"}, + {"_id", "a_user_with_no_roles"}, {"user", "a_user_with_no_roles"}, {"db", "TestUpdateUser"}, {"roles", bson.A{}}, @@ -264,7 +264,7 @@ func TestUpdateUser(t *testing.T) { {"comment", "test string comment"}, }, expected: bson.D{ - {"_id", "TestUpdateUser.another_user"}, + {"_id", "another_user"}, {"user", "another_user"}, {"db", "TestUpdateUser"}, {"roles", bson.A{}}, diff --git a/integration/users/usersinfo_test.go b/integration/users/usersinfo_test.go index f4cebb4c9e14..0d8f0f212d6e 100644 --- a/integration/users/usersinfo_test.go +++ b/integration/users/usersinfo_test.go @@ -41,8 +41,11 @@ func createUser(username, password string) bson.D { func TestUsersinfo(t *testing.T) { t.Parallel() - ctx, collection := setup.Setup(t) - client := collection.Database().Client() + s := setup.SetupWithOpts(t, nil) + ctx := s.Ctx + + db, _ := createUserTestRunnerUser(t, s) + client := db.Client() dbToUsers := []struct { dbSuffix string @@ -456,14 +459,14 @@ func TestUsersinfo(t *testing.T) { }, }}, hasUser: map[string]struct{}{ - "TestUsersinfo.one": {}, - "TestUsersinfo.two": {}, - "TestUsersinfo_example.a": {}, - "TestUsersinfo_example.b": {}, - "TestUsersinfo_example.c": {}, - "TestUsersinfo_few.i": {}, - "TestUsersinfo_few.j": {}, - "TestUsersinfo_another.singleuser": {}, + "one": {}, + "two": {}, + "a": {}, + "b": {}, + "c": {}, + "i": {}, + "j": {}, + "singleuser": {}, }, }, } diff --git a/internal/handler/authenticate.go b/internal/handler/authenticate.go index 52d548b636b4..7f3cec21dbb3 100644 --- a/internal/handler/authenticate.go +++ b/internal/handler/authenticate.go @@ -54,21 +54,9 @@ func (h *Handler) authenticate(ctx context.Context, msg *wire.OpMsg) error { return lazyerrors.Error(err) } - document, err := msg.Document() - if err != nil { - return lazyerrors.Error(err) - } - - var dbName string - - if dbName, err = common.GetRequiredParam[string](document, "$db"); err != nil { - return err - } - username, pwd := conninfo.Get(ctx).Auth() - // NOTE: how does a user with access to all database look like? - filter := must.NotFail(types.NewDocument("_id", dbName+"."+username)) + filter := must.NotFail(types.NewDocument("_id", username)) qr, err := usersCol.Query(ctx, nil) if err != nil { diff --git a/internal/handler/msg_createuser.go b/internal/handler/msg_createuser.go index 68baa8b24907..d25bc5d5299c 100644 --- a/internal/handler/msg_createuser.go +++ b/internal/handler/msg_createuser.go @@ -145,7 +145,7 @@ func (h *Handler) MsgCreateUser(ctx context.Context, msg *wire.OpMsg) (*wire.OpM id := uuid.New() saved := must.NotFail(types.NewDocument( - "_id", dbName+"."+username, + "_id", username, "credentials", credentials, "user", username, "db", dbName, diff --git a/internal/handler/msg_dropuser.go b/internal/handler/msg_dropuser.go index f5fb1164be50..e525acb5a4a6 100644 --- a/internal/handler/msg_dropuser.go +++ b/internal/handler/msg_dropuser.go @@ -61,7 +61,7 @@ func (h *Handler) MsgDropUser(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg } res, err := users.DeleteAll(ctx, &backends.DeleteAllParams{ - IDs: []any{dbName + "." + username}, + IDs: []any{username}, }) if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_usersinfo.go b/internal/handler/msg_usersinfo.go index e5b3cbe81541..bca452d18f6f 100644 --- a/internal/handler/msg_usersinfo.go +++ b/internal/handler/msg_usersinfo.go @@ -281,7 +281,7 @@ func usersInfoFilter(allDBs, singleDB bool, dbName string, pairs []usersInfoPair ps := []any{} for _, p := range pairs { - ps = append(ps, p.db+"."+p.username) + ps = append(ps, p.username) } ids, err := types.NewArray(ps...) From 428662875974b480e6759e639152a4aa4859ef2b Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Mon, 19 Feb 2024 11:29:06 +0900 Subject: [PATCH 18/42] lint --- internal/backends/postgresql/metadata/registry.go | 2 +- internal/handler/authenticate.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/backends/postgresql/metadata/registry.go b/internal/backends/postgresql/metadata/registry.go index 1816796e66b0..de596949621b 100644 --- a/internal/backends/postgresql/metadata/registry.go +++ b/internal/backends/postgresql/metadata/registry.go @@ -111,8 +111,8 @@ func NewRegistry(u string, l *zap.Logger, sp *state.Provider) (*Registry, error) c := conninfo.New() c.SetAuth(username, pwd) - ctx := conninfo.Ctx(context.Background(), c) + _, err = r.getPool(ctx) if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/authenticate.go b/internal/handler/authenticate.go index 7f3cec21dbb3..576318f04b72 100644 --- a/internal/handler/authenticate.go +++ b/internal/handler/authenticate.go @@ -123,6 +123,7 @@ func (h *Handler) authenticate(ctx context.Context, msg *wire.OpMsg) error { } v := must.NotFail(credentials.Get("PLAIN")) + doc, ok := v.(*types.Document) if !ok { return lazyerrors.Errorf("field 'PLAIN' has type %T, expected Document", v) From 287b72881620f8a33af683c9ca5deef64a4553a2 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Mon, 19 Feb 2024 11:53:48 +0900 Subject: [PATCH 19/42] Revert "user tests use credentials for test runner" This reverts commit b08beeac6149b674e029912e5cfb51cd4b91c468. --- integration/users/connection_test.go | 45 ++++++++++++++++--- integration/users/create_user_test.go | 7 +-- .../drop_all_users_from_database_test.go | 5 +-- integration/users/update_user_test.go | 12 ++--- integration/users/usersinfo_test.go | 23 +++++----- internal/handler/authenticate.go | 14 +++++- internal/handler/msg_createuser.go | 2 +- internal/handler/msg_dropuser.go | 2 +- internal/handler/msg_usersinfo.go | 2 +- 9 files changed, 77 insertions(+), 35 deletions(-) diff --git a/integration/users/connection_test.go b/integration/users/connection_test.go index 9f5ce5359687..3bfea7a67129 100644 --- a/integration/users/connection_test.go +++ b/integration/users/connection_test.go @@ -35,8 +35,8 @@ func TestAuthentication(t *testing.T) { s := setup.SetupWithOpts(t, nil) ctx := s.Ctx - - db, collection := createUserTestRunnerUser(t, s) + collection := s.Collection + db := collection.Database() testCases := map[string]struct { //nolint:vet // for readability username string @@ -228,7 +228,9 @@ func TestAuthenticationEnableNewAuthNoUserExists(t *testing.T) { t.Parallel() s := setup.SetupWithOpts(t, nil) - ctx, collection, db := s.Ctx, s.Collection, s.Collection.Database() + ctx := s.Ctx + collection := s.Collection + db := collection.Database() testCases := map[string]struct { //nolint:vet // for readability username string @@ -297,8 +299,8 @@ func TestAuthenticationEnableNewAuthWithExistingUser(t *testing.T) { s := setup.SetupWithOpts(t, nil) ctx := s.Ctx - - db, collection := createUserTestRunnerUser(t, s) + collection := s.Collection + db := collection.Database() err := db.RunCommand(ctx, bson.D{ {"createUser", "sha256-user"}, @@ -308,14 +310,45 @@ func TestAuthenticationEnableNewAuthWithExistingUser(t *testing.T) { }).Err() require.NoErrorf(t, err, "cannot create user") + t.Cleanup(func() { + // once the first user has been created use that user for any other action + // until local exception is implemented + opts := options.Client().ApplyURI(s.MongoDBURI).SetAuth(options.Credential{ + AuthMechanism: "SCRAM-SHA-256", + AuthSource: db.Name(), + Username: "sha256-user", + Password: "correct", + }) + + client, err := mongo.Connect(ctx, opts) + require.NoError(t, err, "cannot connect to MongoDB") + + require.NoError(t, client.Database(db.Name()).RunCommand(ctx, bson.D{{"dropUser", "sha256-user"}}).Err()) + }) + if !setup.IsMongoDB(t) { - err = db.RunCommand(ctx, bson.D{ + // once the first user has been created use that user for any other action + // until local exception is implemented + opts := options.Client().ApplyURI(s.MongoDBURI).SetAuth(options.Credential{ + AuthMechanism: "SCRAM-SHA-256", + AuthSource: db.Name(), + Username: "sha256-user", + Password: "correct", + }) + + client, err := mongo.Connect(ctx, opts) + require.NoError(t, err, "cannot connect to MongoDB") + err = client.Database(db.Name()).RunCommand(ctx, bson.D{ {"createUser", "plain-user"}, {"roles", bson.A{}}, {"pwd", "correct"}, {"mechanisms", bson.A{"PLAIN"}}, }).Err() require.NoErrorf(t, err, "cannot create user") + + t.Cleanup(func() { + require.NoError(t, client.Database(db.Name()).RunCommand(ctx, bson.D{{"dropUser", "plain-user"}}).Err()) + }) } testCases := map[string]struct { //nolint:vet // for readability diff --git a/integration/users/create_user_test.go b/integration/users/create_user_test.go index 656abbb4e951..d8aae15f178f 100644 --- a/integration/users/create_user_test.go +++ b/integration/users/create_user_test.go @@ -15,6 +15,7 @@ package users import ( + "fmt" "testing" "github.com/stretchr/testify/assert" @@ -266,7 +267,7 @@ func TestCreateUser(t *testing.T) { user.Remove("credentials") expectedRec := integration.ConvertDocument(t, bson.D{ - {"_id", must.NotFail(payload.Get("createUser"))}, + {"_id", fmt.Sprintf("%s.%s", db.Name(), must.NotFail(payload.Get("createUser")))}, {"user", must.NotFail(payload.Get("createUser"))}, {"db", db.Name()}, {"roles", bson.A{}}, @@ -316,7 +317,8 @@ func createUserTestRunnerUser(tb *testing.T, s *setup.SetupResult) (*mongo.Datab } username, pwd, mechanism := "user-test-runner", "password", "PLAIN" - err := s.Collection.Database().Client().Database("admin").RunCommand(s.Ctx, bson.D{ + + err := s.Collection.Database().RunCommand(s.Ctx, bson.D{ {"createUser", username}, {"roles", bson.A{}}, {"pwd", pwd}, @@ -340,7 +342,6 @@ func createUserTestRunnerUser(tb *testing.T, s *setup.SetupResult) (*mongo.Datab tb.Cleanup(func() { require.NoError(tb, db.RunCommand(s.Ctx, bson.D{{"dropAllUsersFromDatabase", 1}}).Err()) - require.NoError(tb, client.Database("admin").RunCommand(s.Ctx, bson.D{{"dropAllUsersFromDatabase", 1}}).Err()) }) return db, collection diff --git a/integration/users/drop_all_users_from_database_test.go b/integration/users/drop_all_users_from_database_test.go index 1617b5f77f3d..730743b5235f 100644 --- a/integration/users/drop_all_users_from_database_test.go +++ b/integration/users/drop_all_users_from_database_test.go @@ -34,9 +34,8 @@ import ( func TestDropAllUsersFromDatabase(t *testing.T) { t.Parallel() - s := setup.SetupWithOpts(t, nil) - ctx := s.Ctx - db, collection := createUserTestRunnerUser(t, s) + ctx, collection := setup.Setup(t) + db := collection.Database() client := collection.Database().Client() quantity := 5 // Add some users to the database. diff --git a/integration/users/update_user_test.go b/integration/users/update_user_test.go index 1acf6131e220..4795e2a76b78 100644 --- a/integration/users/update_user_test.go +++ b/integration/users/update_user_test.go @@ -155,7 +155,7 @@ func TestUpdateUser(t *testing.T) { {"pwd", "donotchange"}, }, expected: bson.D{ - {"_id", "same_password_user"}, + {"_id", "TestUpdateUser.same_password_user"}, {"user", "same_password_user"}, {"db", "TestUpdateUser"}, {"roles", bson.A{}}, @@ -172,7 +172,7 @@ func TestUpdateUser(t *testing.T) { {"pwd", "anewpassword"}, }, expected: bson.D{ - {"_id", "a_user"}, + {"_id", "TestUpdateUser.a_user"}, {"user", "a_user"}, {"db", "TestUpdateUser"}, {"roles", bson.A{}}, @@ -190,7 +190,7 @@ func TestUpdateUser(t *testing.T) { {"pwd", "anewpassword"}, }, expected: bson.D{ - {"_id", "a_user_with_mechanism"}, + {"_id", "TestUpdateUser.a_user_with_mechanism"}, {"user", "a_user_with_mechanism"}, {"db", "TestUpdateUser"}, {"roles", bson.A{}}, @@ -210,7 +210,7 @@ func TestUpdateUser(t *testing.T) { {"mechanisms", bson.A{"SCRAM-SHA-256"}}, }, expected: bson.D{ - {"_id", "a_user_with_scram_mechanism"}, + {"_id", "TestUpdateUser.a_user_with_scram_mechanism"}, {"user", "a_user_with_scram_mechanism"}, {"db", "TestUpdateUser"}, {"roles", bson.A{}}, @@ -246,7 +246,7 @@ func TestUpdateUser(t *testing.T) { {"pwd", "anewpassword"}, }, expected: bson.D{ - {"_id", "a_user_with_no_roles"}, + {"_id", "TestUpdateUser.a_user_with_no_roles"}, {"user", "a_user_with_no_roles"}, {"db", "TestUpdateUser"}, {"roles", bson.A{}}, @@ -264,7 +264,7 @@ func TestUpdateUser(t *testing.T) { {"comment", "test string comment"}, }, expected: bson.D{ - {"_id", "another_user"}, + {"_id", "TestUpdateUser.another_user"}, {"user", "another_user"}, {"db", "TestUpdateUser"}, {"roles", bson.A{}}, diff --git a/integration/users/usersinfo_test.go b/integration/users/usersinfo_test.go index 0d8f0f212d6e..f4cebb4c9e14 100644 --- a/integration/users/usersinfo_test.go +++ b/integration/users/usersinfo_test.go @@ -41,11 +41,8 @@ func createUser(username, password string) bson.D { func TestUsersinfo(t *testing.T) { t.Parallel() - s := setup.SetupWithOpts(t, nil) - ctx := s.Ctx - - db, _ := createUserTestRunnerUser(t, s) - client := db.Client() + ctx, collection := setup.Setup(t) + client := collection.Database().Client() dbToUsers := []struct { dbSuffix string @@ -459,14 +456,14 @@ func TestUsersinfo(t *testing.T) { }, }}, hasUser: map[string]struct{}{ - "one": {}, - "two": {}, - "a": {}, - "b": {}, - "c": {}, - "i": {}, - "j": {}, - "singleuser": {}, + "TestUsersinfo.one": {}, + "TestUsersinfo.two": {}, + "TestUsersinfo_example.a": {}, + "TestUsersinfo_example.b": {}, + "TestUsersinfo_example.c": {}, + "TestUsersinfo_few.i": {}, + "TestUsersinfo_few.j": {}, + "TestUsersinfo_another.singleuser": {}, }, }, } diff --git a/internal/handler/authenticate.go b/internal/handler/authenticate.go index 576318f04b72..c96e579e2517 100644 --- a/internal/handler/authenticate.go +++ b/internal/handler/authenticate.go @@ -54,9 +54,21 @@ func (h *Handler) authenticate(ctx context.Context, msg *wire.OpMsg) error { return lazyerrors.Error(err) } + document, err := msg.Document() + if err != nil { + return lazyerrors.Error(err) + } + + var dbName string + + if dbName, err = common.GetRequiredParam[string](document, "$db"); err != nil { + return err + } + username, pwd := conninfo.Get(ctx).Auth() - filter := must.NotFail(types.NewDocument("_id", username)) + // NOTE: how does a user with access to all database look like? + filter := must.NotFail(types.NewDocument("_id", dbName+"."+username)) qr, err := usersCol.Query(ctx, nil) if err != nil { diff --git a/internal/handler/msg_createuser.go b/internal/handler/msg_createuser.go index d25bc5d5299c..68baa8b24907 100644 --- a/internal/handler/msg_createuser.go +++ b/internal/handler/msg_createuser.go @@ -145,7 +145,7 @@ func (h *Handler) MsgCreateUser(ctx context.Context, msg *wire.OpMsg) (*wire.OpM id := uuid.New() saved := must.NotFail(types.NewDocument( - "_id", username, + "_id", dbName+"."+username, "credentials", credentials, "user", username, "db", dbName, diff --git a/internal/handler/msg_dropuser.go b/internal/handler/msg_dropuser.go index e525acb5a4a6..f5fb1164be50 100644 --- a/internal/handler/msg_dropuser.go +++ b/internal/handler/msg_dropuser.go @@ -61,7 +61,7 @@ func (h *Handler) MsgDropUser(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg } res, err := users.DeleteAll(ctx, &backends.DeleteAllParams{ - IDs: []any{username}, + IDs: []any{dbName + "." + username}, }) if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_usersinfo.go b/internal/handler/msg_usersinfo.go index bca452d18f6f..e5b3cbe81541 100644 --- a/internal/handler/msg_usersinfo.go +++ b/internal/handler/msg_usersinfo.go @@ -281,7 +281,7 @@ func usersInfoFilter(allDBs, singleDB bool, dbName string, pairs []usersInfoPair ps := []any{} for _, p := range pairs { - ps = append(ps, p.username) + ps = append(ps, p.db+"."+p.username) } ids, err := types.NewArray(ps...) From 950e56793dbea36adf9c9d0087a71bcbb065c0d3 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Mon, 19 Feb 2024 12:25:13 +0900 Subject: [PATCH 20/42] authentication checks user instead of db.user --- .../users/drop_all_users_from_database_test.go | 10 ++++++++-- integration/users/usersinfo_test.go | 6 ++++-- internal/handler/authenticate.go | 14 +------------- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/integration/users/drop_all_users_from_database_test.go b/integration/users/drop_all_users_from_database_test.go index 730743b5235f..040f46373580 100644 --- a/integration/users/drop_all_users_from_database_test.go +++ b/integration/users/drop_all_users_from_database_test.go @@ -34,8 +34,9 @@ import ( func TestDropAllUsersFromDatabase(t *testing.T) { t.Parallel() - ctx, collection := setup.Setup(t) - db := collection.Database() + s := setup.SetupWithOpts(t, nil) + ctx := s.Ctx + db, collection := createUserTestRunnerUser(t, s) client := collection.Database().Client() quantity := 5 // Add some users to the database. @@ -52,6 +53,11 @@ func TestDropAllUsersFromDatabase(t *testing.T) { // So this call should remove zero users as the database doesn't exist. The next one, "quantity" users. assertDropAllUsersFromDatabase(t, ctx, client.Database(t.Name()+"_another_database"), 0) + if !setup.IsMongoDB(t) { + // For non MongoDB, a user created to run the tests is also dropped. + quantity++ + } + assertDropAllUsersFromDatabase(t, ctx, db, quantity) // Run for the second time to check if it still succeeds when there aren't any users remaining, diff --git a/integration/users/usersinfo_test.go b/integration/users/usersinfo_test.go index f4cebb4c9e14..65f9c0ccdfb8 100644 --- a/integration/users/usersinfo_test.go +++ b/integration/users/usersinfo_test.go @@ -41,8 +41,10 @@ func createUser(username, password string) bson.D { func TestUsersinfo(t *testing.T) { t.Parallel() - ctx, collection := setup.Setup(t) - client := collection.Database().Client() + s := setup.SetupWithOpts(t, nil) + ctx := s.Ctx + db, _ := createUserTestRunnerUser(t, s) + client := db.Client() dbToUsers := []struct { dbSuffix string diff --git a/internal/handler/authenticate.go b/internal/handler/authenticate.go index c96e579e2517..c5a1b879b95e 100644 --- a/internal/handler/authenticate.go +++ b/internal/handler/authenticate.go @@ -54,21 +54,9 @@ func (h *Handler) authenticate(ctx context.Context, msg *wire.OpMsg) error { return lazyerrors.Error(err) } - document, err := msg.Document() - if err != nil { - return lazyerrors.Error(err) - } - - var dbName string - - if dbName, err = common.GetRequiredParam[string](document, "$db"); err != nil { - return err - } - username, pwd := conninfo.Get(ctx).Auth() - // NOTE: how does a user with access to all database look like? - filter := must.NotFail(types.NewDocument("_id", dbName+"."+username)) + filter := must.NotFail(types.NewDocument("user", username)) qr, err := usersCol.Query(ctx, nil) if err != nil { From a9be0e245fe3159d3b6b631717104cb63598e2e9 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Mon, 19 Feb 2024 12:57:27 +0900 Subject: [PATCH 21/42] backend fallback --- integration/users/connection_test.go | 28 ++++++++++++------- integration/users/create_user_test.go | 4 +-- .../drop_all_users_from_database_test.go | 7 +---- internal/clientconn/conninfo/conn_info.go | 8 ++++++ internal/handler/authenticate.go | 7 +++-- 5 files changed, 34 insertions(+), 20 deletions(-) diff --git a/integration/users/connection_test.go b/integration/users/connection_test.go index 3bfea7a67129..5835875555b7 100644 --- a/integration/users/connection_test.go +++ b/integration/users/connection_test.go @@ -221,8 +221,8 @@ func TestAuthentication(t *testing.T) { } // TestAuthenticationEnableNewAuthNoUser tests that the authentication succeeds -// with any PLAIN mechanism user until the first user is created. This is temporary -// until local exception is implemented. +// with PLAIN mechanism user when there are no users in the database. +// It uses backend to authenticate the user such case. // For SCRAM-SHA-256 mechanism users, authentication fails if the user does not exist. func TestAuthenticationEnableNewAuthNoUserExists(t *testing.T) { t.Parallel() @@ -237,23 +237,25 @@ func TestAuthenticationEnableNewAuthNoUserExists(t *testing.T) { password string mechanism string - err string + pingErr string + insertErr string }{ - "PLAIN": { + "PLAINNonExistingUser": { username: "plain-user", password: "whatever", mechanism: "PLAIN", + insertErr: `role "plain-user" does not exist`, }, - "PLAINEmpty": { - username: "", - password: "", + "PLAINBackendUser": { + username: "username", + password: "password", mechanism: "PLAIN", }, "SHA256": { username: "sha256-user", password: "whatever", mechanism: "SCRAM-SHA-256", - err: "Authentication failed", + pingErr: "Authentication failed", }, } @@ -280,8 +282,8 @@ func TestAuthenticationEnableNewAuthNoUserExists(t *testing.T) { err = client.Ping(ctx, nil) - if tc.err != "" { - require.ErrorContains(t, err, tc.err) + if tc.pingErr != "" { + require.ErrorContains(t, err, tc.pingErr) return } @@ -289,6 +291,12 @@ func TestAuthenticationEnableNewAuthNoUserExists(t *testing.T) { connCollection := client.Database(db.Name()).Collection(collection.Name()) _, err = connCollection.InsertOne(ctx, bson.D{{"ping", "pong"}}) + + if tc.insertErr != "" { + require.ErrorContains(t, err, tc.insertErr) + return + } + require.NoError(t, err, "cannot insert document") }) } diff --git a/integration/users/create_user_test.go b/integration/users/create_user_test.go index d8aae15f178f..9c16bde70bb9 100644 --- a/integration/users/create_user_test.go +++ b/integration/users/create_user_test.go @@ -318,7 +318,7 @@ func createUserTestRunnerUser(tb *testing.T, s *setup.SetupResult) (*mongo.Datab username, pwd, mechanism := "user-test-runner", "password", "PLAIN" - err := s.Collection.Database().RunCommand(s.Ctx, bson.D{ + err := s.Collection.Database().Client().Database("admin").RunCommand(s.Ctx, bson.D{ {"createUser", username}, {"roles", bson.A{}}, {"pwd", pwd}, @@ -341,7 +341,7 @@ func createUserTestRunnerUser(tb *testing.T, s *setup.SetupResult) (*mongo.Datab collection := db.Collection(s.Collection.Name()) tb.Cleanup(func() { - require.NoError(tb, db.RunCommand(s.Ctx, bson.D{{"dropAllUsersFromDatabase", 1}}).Err()) + _ = db.RunCommand(s.Ctx, bson.D{{"dropAllUsersFromDatabase", 1}}) }) return db, collection diff --git a/integration/users/drop_all_users_from_database_test.go b/integration/users/drop_all_users_from_database_test.go index 040f46373580..7cda9cb62b25 100644 --- a/integration/users/drop_all_users_from_database_test.go +++ b/integration/users/drop_all_users_from_database_test.go @@ -53,16 +53,11 @@ func TestDropAllUsersFromDatabase(t *testing.T) { // So this call should remove zero users as the database doesn't exist. The next one, "quantity" users. assertDropAllUsersFromDatabase(t, ctx, client.Database(t.Name()+"_another_database"), 0) - if !setup.IsMongoDB(t) { - // For non MongoDB, a user created to run the tests is also dropped. - quantity++ - } - assertDropAllUsersFromDatabase(t, ctx, db, quantity) // Run for the second time to check if it still succeeds when there aren't any users remaining, // instead of returning an error. - assertDropAllUsersFromDatabase(t, ctx, db, 0) + assertDropAllUsersFromDatabase(t, ctx, s.Collection.Database(), 0) } func assertDropAllUsersFromDatabase(t *testing.T, ctx context.Context, db *mongo.Database, quantity int) { diff --git a/internal/clientconn/conninfo/conn_info.go b/internal/clientconn/conninfo/conn_info.go index 2c9c2e339b48..2666dd00dc66 100644 --- a/internal/clientconn/conninfo/conn_info.go +++ b/internal/clientconn/conninfo/conn_info.go @@ -120,6 +120,14 @@ func (connInfo *ConnInfo) SetBypassBackendAuth() { connInfo.bypassBackendAuth = true } +// UnsetBypassBackendAuth marks the connection as requiring backend authentication. +func (connInfo *ConnInfo) UnsetBypassBackendAuth() { + connInfo.rw.Lock() + defer connInfo.rw.Unlock() + + connInfo.bypassBackendAuth = false +} + // BypassBackendAuth returns whether the connection requires backend authentication. func (connInfo *ConnInfo) BypassBackendAuth() bool { connInfo.rw.RLock() diff --git a/internal/handler/authenticate.go b/internal/handler/authenticate.go index c5a1b879b95e..7773a77c83ba 100644 --- a/internal/handler/authenticate.go +++ b/internal/handler/authenticate.go @@ -96,8 +96,11 @@ func (h *Handler) authenticate(ctx context.Context, msg *wire.OpMsg) error { } if !hasUser { - // If a user connects with any credentials or no credentials at all, - // the authentication succeeds until the first user is created. + // There is no user in the database, let backend check the authentication. + // Do not want unauthenticated users accessing the database, while there need + // to be a way to access the database until local exception is implemented. + conninfo.Get(ctx).UnsetBypassBackendAuth() + return nil } From a4b56995b73f70243a2a9c4bd4c46d93324d033a Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Mon, 19 Feb 2024 13:39:27 +0900 Subject: [PATCH 22/42] cleanup --- integration/users/create_user_test.go | 13 +++++++------ internal/backends/postgresql/metadata/registry.go | 1 - internal/handler/authenticate.go | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/integration/users/create_user_test.go b/integration/users/create_user_test.go index 9c16bde70bb9..dc02aa4946bf 100644 --- a/integration/users/create_user_test.go +++ b/integration/users/create_user_test.go @@ -307,10 +307,12 @@ func assertSCRAMSHA256Credentials(t testtb.TB, key string, cred *types.Document) } // createUserTestRunnerUser creates a user with PLAIN mechanism and returns -// the database and collection connection created by that user. -// This gives a user to run user creation tests until local exception is implemented for FerretDB. -// Without this, once the first user is created, the connection to FerretDB fails authentication -// and cannot do any further operations. +// the database and collection connection of that user. It registers cleanup +// of deleting all users of that database. +// For mongoDB it does nothing. +// +// Without this, once the first user is created, the authentication fails +// and further tests fails. func createUserTestRunnerUser(tb *testing.T, s *setup.SetupResult) (*mongo.Database, *mongo.Collection) { if setup.IsMongoDB(tb) { return s.Collection.Database(), s.Collection @@ -326,8 +328,7 @@ func createUserTestRunnerUser(tb *testing.T, s *setup.SetupResult) (*mongo.Datab }).Err() require.NoErrorf(tb, err, "cannot create user") - // once the first user has been created use that user for any other action - // until local exception is implemented + // once the first user has been created use that user for running test opts := options.Client().ApplyURI(s.MongoDBURI).SetAuth(options.Credential{ AuthMechanism: mechanism, AuthSource: s.Collection.Name(), diff --git a/internal/backends/postgresql/metadata/registry.go b/internal/backends/postgresql/metadata/registry.go index de596949621b..aa09eee54ffa 100644 --- a/internal/backends/postgresql/metadata/registry.go +++ b/internal/backends/postgresql/metadata/registry.go @@ -18,7 +18,6 @@ import ( "context" "fmt" "hash/fnv" - "net/url" "regexp" "slices" "sort" diff --git a/internal/handler/authenticate.go b/internal/handler/authenticate.go index 7773a77c83ba..fe3e04ecb68d 100644 --- a/internal/handler/authenticate.go +++ b/internal/handler/authenticate.go @@ -33,8 +33,8 @@ import ( // credentials in the database. If EnableNewAuth is false or bypass backend auth // is set false, it succeeds authentication. // -// When admin.systems.user contains no user, the authentication succeeds until -// the first user is created. +// When admin.systems.user contains no user, the authentication delegates +// it to the backend. This may change once local exception is implemented. func (h *Handler) authenticate(ctx context.Context, msg *wire.OpMsg) error { if !h.EnableNewAuth { return nil From daba28aec13b3fe10083fd4db216b911e7325a71 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Mon, 19 Feb 2024 14:50:46 +0900 Subject: [PATCH 23/42] missing import --- internal/backends/postgresql/metadata/registry.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/backends/postgresql/metadata/registry.go b/internal/backends/postgresql/metadata/registry.go index aa09eee54ffa..de596949621b 100644 --- a/internal/backends/postgresql/metadata/registry.go +++ b/internal/backends/postgresql/metadata/registry.go @@ -18,6 +18,7 @@ import ( "context" "fmt" "hash/fnv" + "net/url" "regexp" "slices" "sort" From 5a8014e401b572bd03c992ebb4cabeb1d274b157 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Mon, 19 Feb 2024 15:07:17 +0900 Subject: [PATCH 24/42] revert --- .../backends/postgresql/metadata/registry.go | 24 ++++--------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/internal/backends/postgresql/metadata/registry.go b/internal/backends/postgresql/metadata/registry.go index de596949621b..f653a9578d41 100644 --- a/internal/backends/postgresql/metadata/registry.go +++ b/internal/backends/postgresql/metadata/registry.go @@ -18,7 +18,6 @@ import ( "context" "fmt" "hash/fnv" - "net/url" "regexp" "slices" "sort" @@ -101,23 +100,6 @@ func NewRegistry(u string, l *zap.Logger, sp *state.Provider) (*Registry, error) l: l, } - baseURI, err := url.Parse(u) - if err != nil { - return nil, lazyerrors.Error(err) - } - - username := baseURI.User.Username() - pwd, _ := baseURI.User.Password() - - c := conninfo.New() - c.SetAuth(username, pwd) - ctx := conninfo.Ctx(context.Background(), c) - - _, err = r.getPool(ctx) - if err != nil { - return nil, lazyerrors.Error(err) - } - return r, nil } @@ -143,7 +125,11 @@ func (r *Registry) getPool(ctx context.Context) (*pgxpool.Pool, error) { if connInfo.BypassBackendAuth() { if p = r.p.GetAny(); p == nil { - return nil, lazyerrors.New("no connection pool") + var err error + // pass no authentication info to use credentials from the base URI + if p, err = r.p.Get("", ""); err != nil { + return nil, lazyerrors.Error(err) + } } } else { username, password := connInfo.Auth() From eda4ed1ef81a14db9a8771a9937be08f40e3c8b1 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Mon, 19 Feb 2024 15:32:12 +0900 Subject: [PATCH 25/42] tidy up --- integration/users/connection_test.go | 90 +++++++--------------------- 1 file changed, 22 insertions(+), 68 deletions(-) diff --git a/integration/users/connection_test.go b/integration/users/connection_test.go index 5835875555b7..ea558d991aa4 100644 --- a/integration/users/connection_test.go +++ b/integration/users/connection_test.go @@ -35,8 +35,7 @@ func TestAuthentication(t *testing.T) { s := setup.SetupWithOpts(t, nil) ctx := s.Ctx - collection := s.Collection - db := collection.Database() + db, collection := createUserTestRunnerUser(t, s) testCases := map[string]struct { //nolint:vet // for readability username string @@ -251,7 +250,7 @@ func TestAuthenticationEnableNewAuthNoUserExists(t *testing.T) { password: "password", mechanism: "PLAIN", }, - "SHA256": { + "SHA256NonExistingUser": { username: "sha256-user", password: "whatever", mechanism: "SCRAM-SHA-256", @@ -302,7 +301,9 @@ func TestAuthenticationEnableNewAuthNoUserExists(t *testing.T) { } } -func TestAuthenticationEnableNewAuthWithExistingUser(t *testing.T) { +func TestAuthenticationEnableNewAuthPLAIN(t *testing.T) { + setup.SkipForMongoDB(t, "PLAIN mechanism is not supported by MongoDB") + t.Parallel() s := setup.SetupWithOpts(t, nil) @@ -311,54 +312,27 @@ func TestAuthenticationEnableNewAuthWithExistingUser(t *testing.T) { db := collection.Database() err := db.RunCommand(ctx, bson.D{ - {"createUser", "sha256-user"}, + {"createUser", "plain-user"}, {"roles", bson.A{}}, {"pwd", "correct"}, - {"mechanisms", bson.A{"SCRAM-SHA-256"}}, + {"mechanisms", bson.A{"PLAIN"}}, }).Err() - require.NoErrorf(t, err, "cannot create user") + require.NoError(t, err, "cannot create user") t.Cleanup(func() { - // once the first user has been created use that user for any other action - // until local exception is implemented opts := options.Client().ApplyURI(s.MongoDBURI).SetAuth(options.Credential{ - AuthMechanism: "SCRAM-SHA-256", + AuthMechanism: "PLAIN", AuthSource: db.Name(), - Username: "sha256-user", + Username: "plain-user", Password: "correct", }) client, err := mongo.Connect(ctx, opts) - require.NoError(t, err, "cannot connect to MongoDB") + require.NoError(t, err) - require.NoError(t, client.Database(db.Name()).RunCommand(ctx, bson.D{{"dropUser", "sha256-user"}}).Err()) + require.NoError(t, client.Database(db.Name()).RunCommand(ctx, bson.D{{"dropUser", "plain-user"}}).Err()) }) - if !setup.IsMongoDB(t) { - // once the first user has been created use that user for any other action - // until local exception is implemented - opts := options.Client().ApplyURI(s.MongoDBURI).SetAuth(options.Credential{ - AuthMechanism: "SCRAM-SHA-256", - AuthSource: db.Name(), - Username: "sha256-user", - Password: "correct", - }) - - client, err := mongo.Connect(ctx, opts) - require.NoError(t, err, "cannot connect to MongoDB") - err = client.Database(db.Name()).RunCommand(ctx, bson.D{ - {"createUser", "plain-user"}, - {"roles", bson.A{}}, - {"pwd", "correct"}, - {"mechanisms", bson.A{"PLAIN"}}, - }).Err() - require.NoErrorf(t, err, "cannot create user") - - t.Cleanup(func() { - require.NoError(t, client.Database(db.Name()).RunCommand(ctx, bson.D{{"dropUser", "plain-user"}}).Err()) - }) - } - testCases := map[string]struct { //nolint:vet // for readability username string password string @@ -366,38 +340,21 @@ func TestAuthenticationEnableNewAuthWithExistingUser(t *testing.T) { err string }{ - "PLAIN": { + "Success": { username: "plain-user", password: "correct", mechanism: "PLAIN", }, - "PLAINWrongPassword": { - username: "scram-user", - password: "wrong", - mechanism: "SCRAM-SHA-256", - err: "AuthenticationFailed", - }, - "PLAINWrongUser": { - username: "non-existent", - password: "wrong", - mechanism: "SCRAM-SHA-256", - err: "AuthenticationFailed", - }, - "SHA256": { - username: "sha256-user", - password: "correct", - mechanism: "SCRAM-SHA-256", - }, - "SHA256WrongUser": { - username: "non-existent", + "BadPassword": { + username: "plain-user", password: "wrong", - mechanism: "SCRAM-SHA-256", + mechanism: "PLAIN", err: "AuthenticationFailed", }, - "SHA256WrongPassword": { - username: "sha256-user", - password: "wrong", - mechanism: "SCRAM-SHA-256", + "NonExistentUser": { + username: "not-found-user", + password: "something", + mechanism: "PLAIN", err: "AuthenticationFailed", }, } @@ -423,17 +380,14 @@ func TestAuthenticationEnableNewAuthWithExistingUser(t *testing.T) { client, err := mongo.Connect(ctx, opts) require.NoError(t, err, "cannot connect to MongoDB") - err = client.Ping(ctx, nil) + connCollection := client.Database(db.Name()).Collection(collection.Name()) + _, err = connCollection.InsertOne(ctx, bson.D{{"ping", "pong"}}) if tc.err != "" { require.ErrorContains(t, err, tc.err) return } - require.NoError(t, err, "cannot ping MongoDB") - - connCollection := client.Database(db.Name()).Collection(collection.Name()) - _, err = connCollection.InsertOne(ctx, bson.D{{"ping", "pong"}}) require.NoError(t, err, "cannot insert document") }) } From add453efe97a992be6c379670c6aa5b253410de9 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Mon, 19 Feb 2024 15:39:30 +0900 Subject: [PATCH 26/42] add test for plain mechanism backend user --- integration/users/connection_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/integration/users/connection_test.go b/integration/users/connection_test.go index ea558d991aa4..e5928e42d854 100644 --- a/integration/users/connection_test.go +++ b/integration/users/connection_test.go @@ -357,6 +357,12 @@ func TestAuthenticationEnableNewAuthPLAIN(t *testing.T) { mechanism: "PLAIN", err: "AuthenticationFailed", }, + "PLAINBackendUser": { + username: "username", + password: "password", + mechanism: "PLAIN", + err: "AuthenticationFailed", + }, } for name, tc := range testCases { From 0d786cb0c66b6f4d18eb6009efe5785350473aeb Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Mon, 19 Feb 2024 16:02:38 +0900 Subject: [PATCH 27/42] simplify test user --- integration/users/connection_test.go | 20 ++++---- integration/users/create_user_test.go | 46 ++++++------------- .../drop_all_users_from_database_test.go | 8 ++-- integration/users/drop_user_test.go | 7 ++- integration/users/update_user_test.go | 7 ++- integration/users/usersinfo_test.go | 6 +-- 6 files changed, 35 insertions(+), 59 deletions(-) diff --git a/integration/users/connection_test.go b/integration/users/connection_test.go index e5928e42d854..ba02c809bdda 100644 --- a/integration/users/connection_test.go +++ b/integration/users/connection_test.go @@ -35,7 +35,10 @@ func TestAuthentication(t *testing.T) { s := setup.SetupWithOpts(t, nil) ctx := s.Ctx - db, collection := createUserTestRunnerUser(t, s) + collection := s.Collection + db := collection.Database() + + createTestRunnerUser(t, ctx, s.Collection.Database()) testCases := map[string]struct { //nolint:vet // for readability username string @@ -219,10 +222,9 @@ func TestAuthentication(t *testing.T) { } } -// TestAuthenticationEnableNewAuthNoUser tests that the authentication succeeds -// with PLAIN mechanism user when there are no users in the database. -// It uses backend to authenticate the user such case. -// For SCRAM-SHA-256 mechanism users, authentication fails if the user does not exist. +// TestAuthenticationEnableNewAuthNoUser tests that the backend authentication +// is used when there is no user in the database. This ensures that there is +// some form of authentication even if there is no user. func TestAuthenticationEnableNewAuthNoUserExists(t *testing.T) { t.Parallel() @@ -231,7 +233,7 @@ func TestAuthenticationEnableNewAuthNoUserExists(t *testing.T) { collection := s.Collection db := collection.Database() - testCases := map[string]struct { //nolint:vet // for readability + testCases := map[string]struct { username string password string mechanism string @@ -333,7 +335,7 @@ func TestAuthenticationEnableNewAuthPLAIN(t *testing.T) { require.NoError(t, client.Database(db.Name()).RunCommand(ctx, bson.D{{"dropUser", "plain-user"}}).Err()) }) - testCases := map[string]struct { //nolint:vet // for readability + testCases := map[string]struct { username string password string mechanism string @@ -368,10 +370,6 @@ func TestAuthenticationEnableNewAuthPLAIN(t *testing.T) { for name, tc := range testCases { name, tc := name, tc t.Run(name, func(t *testing.T) { - if tc.mechanism == "PLAIN" { - setup.SkipForMongoDB(t, "PLAIN mechanism is not supported by MongoDB") - } - t.Parallel() credential := options.Credential{ diff --git a/integration/users/create_user_test.go b/integration/users/create_user_test.go index dc02aa4946bf..a465b124f328 100644 --- a/integration/users/create_user_test.go +++ b/integration/users/create_user_test.go @@ -15,6 +15,7 @@ package users import ( + "context" "fmt" "testing" @@ -22,7 +23,6 @@ import ( "github.com/stretchr/testify/require" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" - "go.mongodb.org/mongo-driver/mongo/options" "github.com/FerretDB/FerretDB/integration" "github.com/FerretDB/FerretDB/integration/setup" @@ -35,10 +35,9 @@ import ( func TestCreateUser(t *testing.T) { t.Parallel() - s := setup.SetupWithOpts(t, nil) - ctx := s.Ctx - - db, _ := createUserTestRunnerUser(t, s) + ctx, collection := setup.Setup(t) + db := collection.Database() + createTestRunnerUser(t, ctx, collection.Database()) testCases := map[string]struct { //nolint:vet // for readability payload bson.D @@ -306,44 +305,25 @@ func assertSCRAMSHA256Credentials(t testtb.TB, key string, cred *types.Document) assert.NotEmpty(t, must.NotFail(c.Get("storedKey")).(string)) } -// createUserTestRunnerUser creates a user with PLAIN mechanism and returns -// the database and collection connection of that user. It registers cleanup -// of deleting all users of that database. -// For mongoDB it does nothing. +// createTestRunnerUser creates a user in admin database with PLAIN mechanism +// and returns the test database. It uses username/password pair which is +// the same as database credentials for integration test. This is done to +// avoid the need to reconnect as different credential in tests. // // Without this, once the first user is created, the authentication fails -// and further tests fails. -func createUserTestRunnerUser(tb *testing.T, s *setup.SetupResult) (*mongo.Database, *mongo.Collection) { +// as username/password does not exist in admin.system.users collection. +func createTestRunnerUser(tb *testing.T, ctx context.Context, db *mongo.Database) { if setup.IsMongoDB(tb) { - return s.Collection.Database(), s.Collection + return } - username, pwd, mechanism := "user-test-runner", "password", "PLAIN" + username, pwd, mechanism := "username", "password", "PLAIN" - err := s.Collection.Database().Client().Database("admin").RunCommand(s.Ctx, bson.D{ + err := db.Client().Database("admin").RunCommand(ctx, bson.D{ {"createUser", username}, {"roles", bson.A{}}, {"pwd", pwd}, {"mechanisms", bson.A{mechanism}}, }).Err() require.NoErrorf(tb, err, "cannot create user") - - // once the first user has been created use that user for running test - opts := options.Client().ApplyURI(s.MongoDBURI).SetAuth(options.Credential{ - AuthMechanism: mechanism, - AuthSource: s.Collection.Name(), - Username: username, - Password: pwd, - }) - client, err := mongo.Connect(s.Ctx, opts) - require.NoError(tb, err, "cannot connect to MongoDB") - - db := client.Database(s.Collection.Database().Name()) - collection := db.Collection(s.Collection.Name()) - - tb.Cleanup(func() { - _ = db.RunCommand(s.Ctx, bson.D{{"dropAllUsersFromDatabase", 1}}) - }) - - return db, collection } diff --git a/integration/users/drop_all_users_from_database_test.go b/integration/users/drop_all_users_from_database_test.go index 7cda9cb62b25..9334c3e83f7a 100644 --- a/integration/users/drop_all_users_from_database_test.go +++ b/integration/users/drop_all_users_from_database_test.go @@ -34,9 +34,9 @@ import ( func TestDropAllUsersFromDatabase(t *testing.T) { t.Parallel() - s := setup.SetupWithOpts(t, nil) - ctx := s.Ctx - db, collection := createUserTestRunnerUser(t, s) + ctx, collection := setup.Setup(t) + db := collection.Database() + createTestRunnerUser(t, ctx, db) client := collection.Database().Client() quantity := 5 // Add some users to the database. @@ -57,7 +57,7 @@ func TestDropAllUsersFromDatabase(t *testing.T) { // Run for the second time to check if it still succeeds when there aren't any users remaining, // instead of returning an error. - assertDropAllUsersFromDatabase(t, ctx, s.Collection.Database(), 0) + assertDropAllUsersFromDatabase(t, ctx, db, 0) } func assertDropAllUsersFromDatabase(t *testing.T, ctx context.Context, db *mongo.Database, quantity int) { diff --git a/integration/users/drop_user_test.go b/integration/users/drop_user_test.go index ebcb008d2b7e..116f108974a0 100644 --- a/integration/users/drop_user_test.go +++ b/integration/users/drop_user_test.go @@ -31,10 +31,9 @@ import ( func TestDropUser(t *testing.T) { t.Parallel() - s := setup.SetupWithOpts(t, nil) - ctx := s.Ctx - - db, _ := createUserTestRunnerUser(t, s) + ctx, collection := setup.Setup(t) + db := collection.Database() + createTestRunnerUser(t, ctx, db) err := db.RunCommand(ctx, bson.D{ {"createUser", "a_user"}, diff --git a/integration/users/update_user_test.go b/integration/users/update_user_test.go index 4795e2a76b78..c2fba3ad5707 100644 --- a/integration/users/update_user_test.go +++ b/integration/users/update_user_test.go @@ -32,10 +32,9 @@ import ( func TestUpdateUser(t *testing.T) { t.Parallel() - s := setup.SetupWithOpts(t, nil) - ctx := s.Ctx - - db, _ := createUserTestRunnerUser(t, s) + ctx, collection := setup.Setup(t) + db := collection.Database() + createTestRunnerUser(t, ctx, db) testCases := map[string]struct { //nolint:vet // for readability createPayload bson.D diff --git a/integration/users/usersinfo_test.go b/integration/users/usersinfo_test.go index 65f9c0ccdfb8..41078f2a0b58 100644 --- a/integration/users/usersinfo_test.go +++ b/integration/users/usersinfo_test.go @@ -41,9 +41,9 @@ func createUser(username, password string) bson.D { func TestUsersinfo(t *testing.T) { t.Parallel() - s := setup.SetupWithOpts(t, nil) - ctx := s.Ctx - db, _ := createUserTestRunnerUser(t, s) + ctx, collection := setup.Setup(t) + db := collection.Database() + createTestRunnerUser(t, ctx, db) client := db.Client() dbToUsers := []struct { From 5bdcfc4ec7d80624e5165a8a866a7613231004e3 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Mon, 19 Feb 2024 16:21:44 +0900 Subject: [PATCH 28/42] update comments --- integration/users/connection_test.go | 22 +++---------------- integration/users/create_user_test.go | 7 +++--- integration/users/usersinfo_test.go | 5 ++--- .../backends/postgresql/metadata/registry.go | 5 +---- internal/handler/authenticate.go | 15 +++++++------ 5 files changed, 17 insertions(+), 37 deletions(-) diff --git a/integration/users/connection_test.go b/integration/users/connection_test.go index ba02c809bdda..da3c13968136 100644 --- a/integration/users/connection_test.go +++ b/integration/users/connection_test.go @@ -309,9 +309,7 @@ func TestAuthenticationEnableNewAuthPLAIN(t *testing.T) { t.Parallel() s := setup.SetupWithOpts(t, nil) - ctx := s.Ctx - collection := s.Collection - db := collection.Database() + ctx, cName, db := s.Ctx, s.Collection.Name(), s.Collection.Database() err := db.RunCommand(ctx, bson.D{ {"createUser", "plain-user"}, @@ -321,20 +319,6 @@ func TestAuthenticationEnableNewAuthPLAIN(t *testing.T) { }).Err() require.NoError(t, err, "cannot create user") - t.Cleanup(func() { - opts := options.Client().ApplyURI(s.MongoDBURI).SetAuth(options.Credential{ - AuthMechanism: "PLAIN", - AuthSource: db.Name(), - Username: "plain-user", - Password: "correct", - }) - - client, err := mongo.Connect(ctx, opts) - require.NoError(t, err) - - require.NoError(t, client.Database(db.Name()).RunCommand(ctx, bson.D{{"dropUser", "plain-user"}}).Err()) - }) - testCases := map[string]struct { username string password string @@ -384,8 +368,8 @@ func TestAuthenticationEnableNewAuthPLAIN(t *testing.T) { client, err := mongo.Connect(ctx, opts) require.NoError(t, err, "cannot connect to MongoDB") - connCollection := client.Database(db.Name()).Collection(collection.Name()) - _, err = connCollection.InsertOne(ctx, bson.D{{"ping", "pong"}}) + c := client.Database(db.Name()).Collection(cName) + _, err = c.InsertOne(ctx, bson.D{{"ping", "pong"}}) if tc.err != "" { require.ErrorContains(t, err, tc.err) diff --git a/integration/users/create_user_test.go b/integration/users/create_user_test.go index a465b124f328..c6ae4cc54ec6 100644 --- a/integration/users/create_user_test.go +++ b/integration/users/create_user_test.go @@ -305,10 +305,9 @@ func assertSCRAMSHA256Credentials(t testtb.TB, key string, cred *types.Document) assert.NotEmpty(t, must.NotFail(c.Get("storedKey")).(string)) } -// createTestRunnerUser creates a user in admin database with PLAIN mechanism -// and returns the test database. It uses username/password pair which is -// the same as database credentials for integration test. This is done to -// avoid the need to reconnect as different credential in tests. +// createTestRunnerUser creates a user in admin database with PLAIN mechanism. +// The user uses username/password credential which is the same as the database +// credentials. This is done to avoid the need to reconnect as different credential. // // Without this, once the first user is created, the authentication fails // as username/password does not exist in admin.system.users collection. diff --git a/integration/users/usersinfo_test.go b/integration/users/usersinfo_test.go index 41078f2a0b58..0bcbc34fba43 100644 --- a/integration/users/usersinfo_test.go +++ b/integration/users/usersinfo_test.go @@ -42,9 +42,8 @@ func TestUsersinfo(t *testing.T) { t.Parallel() ctx, collection := setup.Setup(t) - db := collection.Database() - createTestRunnerUser(t, ctx, db) - client := db.Client() + createTestRunnerUser(t, ctx, collection.Database()) + client := collection.Database().Client() dbToUsers := []struct { dbSuffix string diff --git a/internal/backends/postgresql/metadata/registry.go b/internal/backends/postgresql/metadata/registry.go index f653a9578d41..a1bf7f704d97 100644 --- a/internal/backends/postgresql/metadata/registry.go +++ b/internal/backends/postgresql/metadata/registry.go @@ -86,9 +86,6 @@ type Registry struct { } // NewRegistry creates a registry for PostgreSQL databases with a given base URI. -// -// It gets a pool using the user and password from the base URI, which is later used -// by connections that by passes backend authentication. func NewRegistry(u string, l *zap.Logger, sp *state.Provider) (*Registry, error) { p, err := pool.New(u, l, sp) if err != nil { @@ -126,7 +123,7 @@ func (r *Registry) getPool(ctx context.Context) (*pgxpool.Pool, error) { if connInfo.BypassBackendAuth() { if p = r.p.GetAny(); p == nil { var err error - // pass no authentication info to use credentials from the base URI + // use credential from the base URI by passing empty values if p, err = r.p.Get("", ""); err != nil { return nil, lazyerrors.Error(err) } diff --git a/internal/handler/authenticate.go b/internal/handler/authenticate.go index fe3e04ecb68d..d41a04d21cfa 100644 --- a/internal/handler/authenticate.go +++ b/internal/handler/authenticate.go @@ -30,11 +30,11 @@ import ( ) // authenticate validates the user's credentials in the connection with the -// credentials in the database. If EnableNewAuth is false or bypass backend auth -// is set false, it succeeds authentication. +// credentials in admin.systems.user. If EnableNewAuth is false or bypass backend auth +// is set false, it succeeds authentication and let backend handle it. // -// When admin.systems.user contains no user, the authentication delegates -// it to the backend. This may change once local exception is implemented. +// When admin.systems.user contains no user, the authentication is delegated to +// the backend. This may change once local exception is implemented. func (h *Handler) authenticate(ctx context.Context, msg *wire.OpMsg) error { if !h.EnableNewAuth { return nil @@ -96,9 +96,10 @@ func (h *Handler) authenticate(ctx context.Context, msg *wire.OpMsg) error { } if !hasUser { - // There is no user in the database, let backend check the authentication. - // Do not want unauthenticated users accessing the database, while there need - // to be a way to access the database until local exception is implemented. + // There is no user in the database, let the backend check the authentication. + // We do not want unauthenticated users accessing the database, while allowing + // users with valid backend credentials to access the database + // until local exception is implemented. conninfo.Get(ctx).UnsetBypassBackendAuth() return nil From f74532cd60e9ee3fbc9d1ffec8e80102050d21aa Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Mon, 19 Feb 2024 16:33:15 +0900 Subject: [PATCH 29/42] remove unused var --- internal/handler/authenticate.go | 9 ++++----- internal/handler/msg_aggregate.go | 2 +- internal/handler/msg_collmod.go | 2 +- internal/handler/msg_collstats.go | 2 +- internal/handler/msg_compact.go | 2 +- internal/handler/msg_count.go | 2 +- internal/handler/msg_create.go | 2 +- internal/handler/msg_createindexes.go | 2 +- internal/handler/msg_createuser.go | 2 +- internal/handler/msg_currentop.go | 2 +- internal/handler/msg_datasize.go | 2 +- internal/handler/msg_dbstats.go | 2 +- internal/handler/msg_debugerror.go | 2 +- internal/handler/msg_delete.go | 2 +- internal/handler/msg_distinct.go | 2 +- internal/handler/msg_drop.go | 2 +- internal/handler/msg_dropallusersfromdatabase.go | 2 +- internal/handler/msg_dropdatabase.go | 2 +- internal/handler/msg_dropindexes.go | 2 +- internal/handler/msg_dropuser.go | 2 +- internal/handler/msg_explain.go | 2 +- internal/handler/msg_find.go | 2 +- internal/handler/msg_findandmodify.go | 2 +- internal/handler/msg_getcmdlineopts.go | 2 +- internal/handler/msg_getfreemonitoringstatus.go | 2 +- internal/handler/msg_getlog.go | 2 +- internal/handler/msg_getmore.go | 2 +- internal/handler/msg_getparameter.go | 2 +- internal/handler/msg_hostinfo.go | 2 +- internal/handler/msg_insert.go | 2 +- internal/handler/msg_killcursors.go | 2 +- internal/handler/msg_listcollections.go | 2 +- internal/handler/msg_listcommands.go | 2 +- internal/handler/msg_listdatabases.go | 2 +- internal/handler/msg_listindexes.go | 2 +- internal/handler/msg_logout.go | 2 +- internal/handler/msg_renamecollection.go | 2 +- internal/handler/msg_saslstart.go | 5 ++++- internal/handler/msg_serverstatus.go | 2 +- internal/handler/msg_setfreemonitoring.go | 2 +- internal/handler/msg_update.go | 2 +- internal/handler/msg_updateuser.go | 2 +- internal/handler/msg_usersinfo.go | 2 +- internal/handler/msg_validate.go | 2 +- 44 files changed, 50 insertions(+), 48 deletions(-) diff --git a/internal/handler/authenticate.go b/internal/handler/authenticate.go index d41a04d21cfa..eaf230915e50 100644 --- a/internal/handler/authenticate.go +++ b/internal/handler/authenticate.go @@ -26,16 +26,15 @@ import ( "github.com/FerretDB/FerretDB/internal/util/lazyerrors" "github.com/FerretDB/FerretDB/internal/util/must" "github.com/FerretDB/FerretDB/internal/util/password" - "github.com/FerretDB/FerretDB/internal/wire" ) -// authenticate validates the user's credentials in the connection with the -// credentials in admin.systems.user. If EnableNewAuth is false or bypass backend auth -// is set false, it succeeds authentication and let backend handle it. +// authenticate validates users with stored credentials in admin.systems.user. +// If EnableNewAuth is false or bypass backend auth is set false, it succeeds +// authentication and let backend handle it. // // When admin.systems.user contains no user, the authentication is delegated to // the backend. This may change once local exception is implemented. -func (h *Handler) authenticate(ctx context.Context, msg *wire.OpMsg) error { +func (h *Handler) authenticate(ctx context.Context) error { if !h.EnableNewAuth { return nil } diff --git a/internal/handler/msg_aggregate.go b/internal/handler/msg_aggregate.go index c037fa2a4f67..4740e4e76a7e 100644 --- a/internal/handler/msg_aggregate.go +++ b/internal/handler/msg_aggregate.go @@ -42,7 +42,7 @@ import ( // MsgAggregate implements `aggregate` command. func (h *Handler) MsgAggregate(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_collmod.go b/internal/handler/msg_collmod.go index 75972571cd06..374524960d11 100644 --- a/internal/handler/msg_collmod.go +++ b/internal/handler/msg_collmod.go @@ -23,7 +23,7 @@ import ( // MsgCollMod implements `collMod` command. func (h *Handler) MsgCollMod(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_collstats.go b/internal/handler/msg_collstats.go index 0d0bd33a39ae..78456649784f 100644 --- a/internal/handler/msg_collstats.go +++ b/internal/handler/msg_collstats.go @@ -30,7 +30,7 @@ import ( // MsgCollStats implements `collStats` command. func (h *Handler) MsgCollStats(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_compact.go b/internal/handler/msg_compact.go index 22c45ed1e8c1..195d1c38f389 100644 --- a/internal/handler/msg_compact.go +++ b/internal/handler/msg_compact.go @@ -30,7 +30,7 @@ import ( // MsgCompact implements `compact` command. func (h *Handler) MsgCompact(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_count.go b/internal/handler/msg_count.go index a0b0bf76d571..998f7be0b208 100644 --- a/internal/handler/msg_count.go +++ b/internal/handler/msg_count.go @@ -31,7 +31,7 @@ import ( // MsgCount implements `count` command. func (h *Handler) MsgCount(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_create.go b/internal/handler/msg_create.go index a93b0109d98f..ef3856c12ab3 100644 --- a/internal/handler/msg_create.go +++ b/internal/handler/msg_create.go @@ -30,7 +30,7 @@ import ( // MsgCreate implements `create` command. func (h *Handler) MsgCreate(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_createindexes.go b/internal/handler/msg_createindexes.go index b199a875b200..5447846abd9d 100644 --- a/internal/handler/msg_createindexes.go +++ b/internal/handler/msg_createindexes.go @@ -34,7 +34,7 @@ import ( // MsgCreateIndexes implements `createIndexes` command. func (h *Handler) MsgCreateIndexes(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_createuser.go b/internal/handler/msg_createuser.go index 68baa8b24907..d421260a2a69 100644 --- a/internal/handler/msg_createuser.go +++ b/internal/handler/msg_createuser.go @@ -36,7 +36,7 @@ import ( // MsgCreateUser implements `createUser` command. func (h *Handler) MsgCreateUser(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_currentop.go b/internal/handler/msg_currentop.go index f0679e7cc74f..ee00ce38be86 100644 --- a/internal/handler/msg_currentop.go +++ b/internal/handler/msg_currentop.go @@ -24,7 +24,7 @@ import ( // MsgCurrentOp implements `currentOp` command. func (h *Handler) MsgCurrentOp(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_datasize.go b/internal/handler/msg_datasize.go index d5ee3e87a425..69b0d8da5c4a 100644 --- a/internal/handler/msg_datasize.go +++ b/internal/handler/msg_datasize.go @@ -31,7 +31,7 @@ import ( // MsgDataSize implements `dataSize` command. func (h *Handler) MsgDataSize(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_dbstats.go b/internal/handler/msg_dbstats.go index 2c9fc757c382..d6b7216550e9 100644 --- a/internal/handler/msg_dbstats.go +++ b/internal/handler/msg_dbstats.go @@ -30,7 +30,7 @@ import ( // MsgDBStats implements `dbStats` command. func (h *Handler) MsgDBStats(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_debugerror.go b/internal/handler/msg_debugerror.go index 2bd226539abc..1998023563e5 100644 --- a/internal/handler/msg_debugerror.go +++ b/internal/handler/msg_debugerror.go @@ -30,7 +30,7 @@ import ( // MsgDebugError implements `debugError` command. func (h *Handler) MsgDebugError(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_delete.go b/internal/handler/msg_delete.go index 0d9f570b2186..8881bc8f8eca 100644 --- a/internal/handler/msg_delete.go +++ b/internal/handler/msg_delete.go @@ -33,7 +33,7 @@ import ( // MsgDelete implements `delete` command. func (h *Handler) MsgDelete(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_distinct.go b/internal/handler/msg_distinct.go index 657157e79df5..63082cb4c6a6 100644 --- a/internal/handler/msg_distinct.go +++ b/internal/handler/msg_distinct.go @@ -30,7 +30,7 @@ import ( // MsgDistinct implements `distinct` command. func (h *Handler) MsgDistinct(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_drop.go b/internal/handler/msg_drop.go index 6b5da23ccdc1..464be7bba1a0 100644 --- a/internal/handler/msg_drop.go +++ b/internal/handler/msg_drop.go @@ -29,7 +29,7 @@ import ( // MsgDrop implements `drop` command. func (h *Handler) MsgDrop(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_dropallusersfromdatabase.go b/internal/handler/msg_dropallusersfromdatabase.go index 430b31aacbe6..32d0c14977ed 100644 --- a/internal/handler/msg_dropallusersfromdatabase.go +++ b/internal/handler/msg_dropallusersfromdatabase.go @@ -29,7 +29,7 @@ import ( // MsgDropAllUsersFromDatabase implements `dropAllUsersFromDatabase` command. func (h *Handler) MsgDropAllUsersFromDatabase(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_dropdatabase.go b/internal/handler/msg_dropdatabase.go index 18ddcbc28217..750d6e201992 100644 --- a/internal/handler/msg_dropdatabase.go +++ b/internal/handler/msg_dropdatabase.go @@ -27,7 +27,7 @@ import ( // MsgDropDatabase implements `dropDatabase` command. func (h *Handler) MsgDropDatabase(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_dropindexes.go b/internal/handler/msg_dropindexes.go index 52c8ecea0eae..c4629eafbb8f 100644 --- a/internal/handler/msg_dropindexes.go +++ b/internal/handler/msg_dropindexes.go @@ -32,7 +32,7 @@ import ( // MsgDropIndexes implements `dropIndexes` command. func (h *Handler) MsgDropIndexes(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_dropuser.go b/internal/handler/msg_dropuser.go index f5fb1164be50..7e3e83444f2d 100644 --- a/internal/handler/msg_dropuser.go +++ b/internal/handler/msg_dropuser.go @@ -29,7 +29,7 @@ import ( // MsgDropUser implements `dropUser` command. func (h *Handler) MsgDropUser(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_explain.go b/internal/handler/msg_explain.go index 12f550c51732..5a63472d7e81 100644 --- a/internal/handler/msg_explain.go +++ b/internal/handler/msg_explain.go @@ -34,7 +34,7 @@ import ( // MsgExplain implements `explain` command. func (h *Handler) MsgExplain(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_find.go b/internal/handler/msg_find.go index bc38df0a67ea..588f767f3699 100644 --- a/internal/handler/msg_find.go +++ b/internal/handler/msg_find.go @@ -37,7 +37,7 @@ import ( // MsgFind implements `find` command. func (h *Handler) MsgFind(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_findandmodify.go b/internal/handler/msg_findandmodify.go index 72cb5d2d8960..d925cfad8c37 100644 --- a/internal/handler/msg_findandmodify.go +++ b/internal/handler/msg_findandmodify.go @@ -43,7 +43,7 @@ type findAndModifyResult struct { // MsgFindAndModify implements `findAndModify` command. func (h *Handler) MsgFindAndModify(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_getcmdlineopts.go b/internal/handler/msg_getcmdlineopts.go index ad8a65c66d2a..2dce1c98178d 100644 --- a/internal/handler/msg_getcmdlineopts.go +++ b/internal/handler/msg_getcmdlineopts.go @@ -24,7 +24,7 @@ import ( // MsgGetCmdLineOpts implements `getCmdLineOpts` command. func (h *Handler) MsgGetCmdLineOpts(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_getfreemonitoringstatus.go b/internal/handler/msg_getfreemonitoringstatus.go index de2252e41021..f395ff172a65 100644 --- a/internal/handler/msg_getfreemonitoringstatus.go +++ b/internal/handler/msg_getfreemonitoringstatus.go @@ -24,7 +24,7 @@ import ( // MsgGetFreeMonitoringStatus implements `getFreeMonitoringStatus` command. func (h *Handler) MsgGetFreeMonitoringStatus(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_getlog.go b/internal/handler/msg_getlog.go index d8f8fe67341f..e6a8d1b43ef3 100644 --- a/internal/handler/msg_getlog.go +++ b/internal/handler/msg_getlog.go @@ -36,7 +36,7 @@ import ( // MsgGetLog implements `getLog` command. func (h *Handler) MsgGetLog(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_getmore.go b/internal/handler/msg_getmore.go index ed24f38188bc..2e516f29b3e0 100644 --- a/internal/handler/msg_getmore.go +++ b/internal/handler/msg_getmore.go @@ -39,7 +39,7 @@ import ( // MsgGetMore implements `getMore` command. func (h *Handler) MsgGetMore(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_getparameter.go b/internal/handler/msg_getparameter.go index 3527dd50420f..9118fa19eaff 100644 --- a/internal/handler/msg_getparameter.go +++ b/internal/handler/msg_getparameter.go @@ -30,7 +30,7 @@ import ( // MsgGetParameter implements `getParameter` command. func (h *Handler) MsgGetParameter(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_hostinfo.go b/internal/handler/msg_hostinfo.go index 73b34af939ff..7ed66c73ab00 100644 --- a/internal/handler/msg_hostinfo.go +++ b/internal/handler/msg_hostinfo.go @@ -32,7 +32,7 @@ import ( // MsgHostInfo implements `hostInfo` command. func (h *Handler) MsgHostInfo(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_insert.go b/internal/handler/msg_insert.go index aeb50bacb1d3..8100001e0789 100644 --- a/internal/handler/msg_insert.go +++ b/internal/handler/msg_insert.go @@ -47,7 +47,7 @@ func WriteErrorDocument(we *mongo.WriteError) *types.Document { // MsgInsert implements `insert` command. func (h *Handler) MsgInsert(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_killcursors.go b/internal/handler/msg_killcursors.go index 748d618fbc88..bb4bd0cbc8ab 100644 --- a/internal/handler/msg_killcursors.go +++ b/internal/handler/msg_killcursors.go @@ -32,7 +32,7 @@ import ( // MsgKillCursors implements `killCursors` command. func (h *Handler) MsgKillCursors(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_listcollections.go b/internal/handler/msg_listcollections.go index 1b94cace2092..6b4a6ec412e2 100644 --- a/internal/handler/msg_listcollections.go +++ b/internal/handler/msg_listcollections.go @@ -32,7 +32,7 @@ import ( // MsgListCollections implements `listCollections` command. func (h *Handler) MsgListCollections(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_listcommands.go b/internal/handler/msg_listcommands.go index da393d0686ac..3242579d687b 100644 --- a/internal/handler/msg_listcommands.go +++ b/internal/handler/msg_listcommands.go @@ -27,7 +27,7 @@ import ( // MsgListCommands implements `listCommands` command. func (h *Handler) MsgListCommands(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_listdatabases.go b/internal/handler/msg_listdatabases.go index ca5c6942e717..d2ada0a59227 100644 --- a/internal/handler/msg_listdatabases.go +++ b/internal/handler/msg_listdatabases.go @@ -29,7 +29,7 @@ import ( // MsgListDatabases implements `listDatabases` command. func (h *Handler) MsgListDatabases(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_listindexes.go b/internal/handler/msg_listindexes.go index 72e18ac983ed..ad3b99363f22 100644 --- a/internal/handler/msg_listindexes.go +++ b/internal/handler/msg_listindexes.go @@ -29,7 +29,7 @@ import ( // MsgListIndexes implements `listIndexes` command. func (h *Handler) MsgListIndexes(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_logout.go b/internal/handler/msg_logout.go index 50a8de882e12..4c2796d77b0c 100644 --- a/internal/handler/msg_logout.go +++ b/internal/handler/msg_logout.go @@ -25,7 +25,7 @@ import ( // MsgLogout implements `logout` command. func (h *Handler) MsgLogout(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_renamecollection.go b/internal/handler/msg_renamecollection.go index a7d7cc0bbb27..8708e1c7bc35 100644 --- a/internal/handler/msg_renamecollection.go +++ b/internal/handler/msg_renamecollection.go @@ -30,7 +30,7 @@ import ( // MsgRenameCollection implements `renameCollection` command. func (h *Handler) MsgRenameCollection(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_saslstart.go b/internal/handler/msg_saslstart.go index 020210c1b710..6ff0b8262bb4 100644 --- a/internal/handler/msg_saslstart.go +++ b/internal/handler/msg_saslstart.go @@ -65,7 +65,10 @@ func (h *Handler) MsgSASLStart(ctx context.Context, msg *wire.OpMsg) (*wire.OpMs return nil, err } - conninfo.Get(ctx).SetBypassBackendAuth() + if h.EnableNewAuth { + conninfo.Get(ctx).SetBypassBackendAuth() + } + conninfo.Get(ctx).SetAuth(username, password) var emptyPayload types.Binary diff --git a/internal/handler/msg_serverstatus.go b/internal/handler/msg_serverstatus.go index 4fd6ca57fe7a..4d5ffe63bf3f 100644 --- a/internal/handler/msg_serverstatus.go +++ b/internal/handler/msg_serverstatus.go @@ -30,7 +30,7 @@ import ( // MsgServerStatus implements `serverStatus` command. func (h *Handler) MsgServerStatus(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_setfreemonitoring.go b/internal/handler/msg_setfreemonitoring.go index 0cc91e6572d3..49226eb260c6 100644 --- a/internal/handler/msg_setfreemonitoring.go +++ b/internal/handler/msg_setfreemonitoring.go @@ -29,7 +29,7 @@ import ( // MsgSetFreeMonitoring implements `setFreeMonitoring` command. func (h *Handler) MsgSetFreeMonitoring(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_update.go b/internal/handler/msg_update.go index a7061ec0b29f..2056969aed91 100644 --- a/internal/handler/msg_update.go +++ b/internal/handler/msg_update.go @@ -33,7 +33,7 @@ import ( // MsgUpdate implements `update` command. func (h *Handler) MsgUpdate(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_updateuser.go b/internal/handler/msg_updateuser.go index e2ce09742f35..6c471d18799e 100644 --- a/internal/handler/msg_updateuser.go +++ b/internal/handler/msg_updateuser.go @@ -32,7 +32,7 @@ import ( // MsgUpdateUser implements `updateUser` command. func (h *Handler) MsgUpdateUser(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_usersinfo.go b/internal/handler/msg_usersinfo.go index e5b3cbe81541..5eda7aba7016 100644 --- a/internal/handler/msg_usersinfo.go +++ b/internal/handler/msg_usersinfo.go @@ -30,7 +30,7 @@ import ( // MsgUsersInfo implements `usersInfo` command. func (h *Handler) MsgUsersInfo(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } diff --git a/internal/handler/msg_validate.go b/internal/handler/msg_validate.go index 7a6d99ba4886..62b5c7e33c07 100644 --- a/internal/handler/msg_validate.go +++ b/internal/handler/msg_validate.go @@ -29,7 +29,7 @@ import ( // MsgValidate implements `validate` command. func (h *Handler) MsgValidate(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx, msg); err != nil { + if err := h.authenticate(ctx); err != nil { return nil, err } From d407f5f0f774e2542e09c406611a48b1d6e3a06e Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Mon, 19 Feb 2024 17:04:22 +0900 Subject: [PATCH 30/42] sqlite does not have backend auth --- integration/users/connection_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/integration/users/connection_test.go b/integration/users/connection_test.go index da3c13968136..0685dec31542 100644 --- a/integration/users/connection_test.go +++ b/integration/users/connection_test.go @@ -294,7 +294,12 @@ func TestAuthenticationEnableNewAuthNoUserExists(t *testing.T) { _, err = connCollection.InsertOne(ctx, bson.D{{"ping", "pong"}}) if tc.insertErr != "" { + if setup.IsSQLite(t) { + t.Skip("SQLite does not have backend authentication") + } + require.ErrorContains(t, err, tc.insertErr) + return } From 0ab1f56400d533122b1b506ebf66480e38f344dd Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Tue, 20 Feb 2024 12:11:12 +0900 Subject: [PATCH 31/42] use opt out way --- internal/handler/commands.go | 36 +++++++++++++++++++ internal/handler/msg_aggregate.go | 4 --- internal/handler/msg_collmod.go | 4 --- internal/handler/msg_collstats.go | 4 --- internal/handler/msg_compact.go | 4 --- internal/handler/msg_count.go | 4 --- internal/handler/msg_create.go | 4 --- internal/handler/msg_createindexes.go | 4 --- internal/handler/msg_createuser.go | 4 --- internal/handler/msg_currentop.go | 4 --- internal/handler/msg_datasize.go | 4 --- internal/handler/msg_dbstats.go | 4 --- internal/handler/msg_debugerror.go | 4 --- internal/handler/msg_delete.go | 4 --- internal/handler/msg_distinct.go | 4 --- internal/handler/msg_drop.go | 4 --- .../handler/msg_dropallusersfromdatabase.go | 4 --- internal/handler/msg_dropdatabase.go | 4 --- internal/handler/msg_dropindexes.go | 4 --- internal/handler/msg_dropuser.go | 4 --- internal/handler/msg_explain.go | 4 --- internal/handler/msg_find.go | 4 --- internal/handler/msg_findandmodify.go | 4 --- internal/handler/msg_getcmdlineopts.go | 4 --- .../handler/msg_getfreemonitoringstatus.go | 4 --- internal/handler/msg_getlog.go | 4 --- internal/handler/msg_getmore.go | 4 --- internal/handler/msg_getparameter.go | 4 --- internal/handler/msg_hostinfo.go | 4 --- internal/handler/msg_insert.go | 4 --- internal/handler/msg_killcursors.go | 4 --- internal/handler/msg_listcollections.go | 4 --- internal/handler/msg_listcommands.go | 4 --- internal/handler/msg_listdatabases.go | 4 --- internal/handler/msg_listindexes.go | 4 --- internal/handler/msg_logout.go | 4 --- internal/handler/msg_renamecollection.go | 4 --- internal/handler/msg_serverstatus.go | 4 --- internal/handler/msg_setfreemonitoring.go | 4 --- internal/handler/msg_update.go | 4 --- internal/handler/msg_updateuser.go | 4 --- internal/handler/msg_usersinfo.go | 4 --- internal/handler/msg_validate.go | 4 --- 43 files changed, 36 insertions(+), 168 deletions(-) diff --git a/internal/handler/commands.go b/internal/handler/commands.go index fa2c266fa5ef..821c6595acbb 100644 --- a/internal/handler/commands.go +++ b/internal/handler/commands.go @@ -27,6 +27,9 @@ type command struct { // The passed context is canceled when the client disconnects. Handler func(context.Context, *wire.OpMsg) (*wire.OpMsg, error) + // Unsafe indicates that the command does not require authentication. + Unsafe bool + // Help is shown in the `listCommands` command output. // If empty, that command is hidden, but still can be used. Help string @@ -42,10 +45,12 @@ func (h *Handler) initCommands() { }, "buildInfo": { Handler: h.MsgBuildInfo, + Unsafe: true, Help: "Returns a summary of the build information.", }, "buildinfo": { // old lowercase variant Handler: h.MsgBuildInfo, + Unsafe: true, Help: "", // hidden }, "collMod": { @@ -62,6 +67,7 @@ func (h *Handler) initCommands() { }, "connectionStatus": { Handler: h.MsgConnectionStatus, + Unsafe: true, Help: "Returns information about the current connection, " + "specifically the state of authenticated users and their available permissions.", }, @@ -155,6 +161,7 @@ func (h *Handler) initCommands() { }, "hello": { Handler: h.MsgHello, + Unsafe: true, Help: "Returns the role of the FerretDB instance.", }, "hostInfo": { @@ -167,10 +174,12 @@ func (h *Handler) initCommands() { }, "isMaster": { Handler: h.MsgIsMaster, + Unsafe: true, Help: "Returns the role of the FerretDB instance.", }, "ismaster": { // old lowercase variant Handler: h.MsgIsMaster, + Unsafe: true, Help: "", // hidden }, "killCursors": { @@ -199,6 +208,7 @@ func (h *Handler) initCommands() { }, "ping": { Handler: h.MsgPing, + Unsafe: true, Help: "Returns a pong response.", }, "renameCollection": { @@ -207,10 +217,12 @@ func (h *Handler) initCommands() { }, "saslStart": { Handler: h.MsgSASLStart, + Unsafe: true, Help: "", // hidden }, "saslContinue": { Handler: h.MsgSASLContinue, + Unsafe: true, Help: "", // hidden }, "serverStatus": { @@ -231,6 +243,7 @@ func (h *Handler) initCommands() { }, "whatsmyuri": { Handler: h.MsgWhatsMyURI, + Unsafe: true, Help: "Returns peer information.", }, // please keep sorted alphabetically @@ -260,9 +273,32 @@ func (h *Handler) initCommands() { } // please keep sorted alphabetically } + + for name, cmd := range h.commands { + h.commands[name] = command{ + Handler: h.authenticateHandler(cmd), + Unsafe: cmd.Unsafe, + Help: cmd.Help, + } + } } // Commands returns a map of enabled commands. func (h *Handler) Commands() map[string]command { return h.commands } + +// authenticateHandler wraps the command handler with the authentication check. +func (h *Handler) authenticateHandler(cmd command) func(context.Context, *wire.OpMsg) (*wire.OpMsg, error) { + return func(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { + if cmd.Unsafe { + return cmd.Handler(ctx, msg) + } + + if err := h.authenticate(ctx); err != nil { + return nil, err + } + + return cmd.Handler(ctx, msg) + } +} diff --git a/internal/handler/msg_aggregate.go b/internal/handler/msg_aggregate.go index 4740e4e76a7e..ba72fcd7ab63 100644 --- a/internal/handler/msg_aggregate.go +++ b/internal/handler/msg_aggregate.go @@ -42,10 +42,6 @@ import ( // MsgAggregate implements `aggregate` command. func (h *Handler) MsgAggregate(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_collmod.go b/internal/handler/msg_collmod.go index 374524960d11..b6e426974f4a 100644 --- a/internal/handler/msg_collmod.go +++ b/internal/handler/msg_collmod.go @@ -23,10 +23,6 @@ import ( // MsgCollMod implements `collMod` command. func (h *Handler) MsgCollMod(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - return nil, handlererrors.NewCommandErrorMsg( handlererrors.ErrNotImplemented, "`collMod` command is not implemented yet", diff --git a/internal/handler/msg_collstats.go b/internal/handler/msg_collstats.go index 78456649784f..a561ac8e32da 100644 --- a/internal/handler/msg_collstats.go +++ b/internal/handler/msg_collstats.go @@ -30,10 +30,6 @@ import ( // MsgCollStats implements `collStats` command. func (h *Handler) MsgCollStats(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_compact.go b/internal/handler/msg_compact.go index 195d1c38f389..d036adbe4e5f 100644 --- a/internal/handler/msg_compact.go +++ b/internal/handler/msg_compact.go @@ -30,10 +30,6 @@ import ( // MsgCompact implements `compact` command. func (h *Handler) MsgCompact(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_count.go b/internal/handler/msg_count.go index 998f7be0b208..950dc9902134 100644 --- a/internal/handler/msg_count.go +++ b/internal/handler/msg_count.go @@ -31,10 +31,6 @@ import ( // MsgCount implements `count` command. func (h *Handler) MsgCount(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_create.go b/internal/handler/msg_create.go index ef3856c12ab3..ad2c26ecf399 100644 --- a/internal/handler/msg_create.go +++ b/internal/handler/msg_create.go @@ -30,10 +30,6 @@ import ( // MsgCreate implements `create` command. func (h *Handler) MsgCreate(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_createindexes.go b/internal/handler/msg_createindexes.go index 5447846abd9d..7df27494b5eb 100644 --- a/internal/handler/msg_createindexes.go +++ b/internal/handler/msg_createindexes.go @@ -34,10 +34,6 @@ import ( // MsgCreateIndexes implements `createIndexes` command. func (h *Handler) MsgCreateIndexes(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_createuser.go b/internal/handler/msg_createuser.go index 57df0516bcc2..a23f2a30ce22 100644 --- a/internal/handler/msg_createuser.go +++ b/internal/handler/msg_createuser.go @@ -36,10 +36,6 @@ import ( // MsgCreateUser implements `createUser` command. func (h *Handler) MsgCreateUser(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_currentop.go b/internal/handler/msg_currentop.go index ee00ce38be86..8c0a10935d6b 100644 --- a/internal/handler/msg_currentop.go +++ b/internal/handler/msg_currentop.go @@ -24,10 +24,6 @@ import ( // MsgCurrentOp implements `currentOp` command. func (h *Handler) MsgCurrentOp(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - var reply wire.OpMsg must.NoError(reply.SetSections(wire.MakeOpMsgSection( must.NotFail(types.NewDocument( diff --git a/internal/handler/msg_datasize.go b/internal/handler/msg_datasize.go index 69b0d8da5c4a..0de861a79f0a 100644 --- a/internal/handler/msg_datasize.go +++ b/internal/handler/msg_datasize.go @@ -31,10 +31,6 @@ import ( // MsgDataSize implements `dataSize` command. func (h *Handler) MsgDataSize(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_dbstats.go b/internal/handler/msg_dbstats.go index d6b7216550e9..8112605f6661 100644 --- a/internal/handler/msg_dbstats.go +++ b/internal/handler/msg_dbstats.go @@ -30,10 +30,6 @@ import ( // MsgDBStats implements `dbStats` command. func (h *Handler) MsgDBStats(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_debugerror.go b/internal/handler/msg_debugerror.go index 1998023563e5..eeceb9a7803b 100644 --- a/internal/handler/msg_debugerror.go +++ b/internal/handler/msg_debugerror.go @@ -30,10 +30,6 @@ import ( // MsgDebugError implements `debugError` command. func (h *Handler) MsgDebugError(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_delete.go b/internal/handler/msg_delete.go index 8881bc8f8eca..39e5b44ed6f7 100644 --- a/internal/handler/msg_delete.go +++ b/internal/handler/msg_delete.go @@ -33,10 +33,6 @@ import ( // MsgDelete implements `delete` command. func (h *Handler) MsgDelete(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_distinct.go b/internal/handler/msg_distinct.go index 63082cb4c6a6..2e3d08a86a5d 100644 --- a/internal/handler/msg_distinct.go +++ b/internal/handler/msg_distinct.go @@ -30,10 +30,6 @@ import ( // MsgDistinct implements `distinct` command. func (h *Handler) MsgDistinct(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_drop.go b/internal/handler/msg_drop.go index 464be7bba1a0..9793a64da012 100644 --- a/internal/handler/msg_drop.go +++ b/internal/handler/msg_drop.go @@ -29,10 +29,6 @@ import ( // MsgDrop implements `drop` command. func (h *Handler) MsgDrop(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_dropallusersfromdatabase.go b/internal/handler/msg_dropallusersfromdatabase.go index 32d0c14977ed..cdcae1a488f3 100644 --- a/internal/handler/msg_dropallusersfromdatabase.go +++ b/internal/handler/msg_dropallusersfromdatabase.go @@ -29,10 +29,6 @@ import ( // MsgDropAllUsersFromDatabase implements `dropAllUsersFromDatabase` command. func (h *Handler) MsgDropAllUsersFromDatabase(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_dropdatabase.go b/internal/handler/msg_dropdatabase.go index 750d6e201992..b8ca5821b25a 100644 --- a/internal/handler/msg_dropdatabase.go +++ b/internal/handler/msg_dropdatabase.go @@ -27,10 +27,6 @@ import ( // MsgDropDatabase implements `dropDatabase` command. func (h *Handler) MsgDropDatabase(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_dropindexes.go b/internal/handler/msg_dropindexes.go index c4629eafbb8f..cf1866dc12ee 100644 --- a/internal/handler/msg_dropindexes.go +++ b/internal/handler/msg_dropindexes.go @@ -32,10 +32,6 @@ import ( // MsgDropIndexes implements `dropIndexes` command. func (h *Handler) MsgDropIndexes(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_dropuser.go b/internal/handler/msg_dropuser.go index 7e3e83444f2d..3464f351fb50 100644 --- a/internal/handler/msg_dropuser.go +++ b/internal/handler/msg_dropuser.go @@ -29,10 +29,6 @@ import ( // MsgDropUser implements `dropUser` command. func (h *Handler) MsgDropUser(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_explain.go b/internal/handler/msg_explain.go index 5a63472d7e81..f4a2ff934d65 100644 --- a/internal/handler/msg_explain.go +++ b/internal/handler/msg_explain.go @@ -34,10 +34,6 @@ import ( // MsgExplain implements `explain` command. func (h *Handler) MsgExplain(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_find.go b/internal/handler/msg_find.go index 588f767f3699..4ea5d83a969e 100644 --- a/internal/handler/msg_find.go +++ b/internal/handler/msg_find.go @@ -37,10 +37,6 @@ import ( // MsgFind implements `find` command. func (h *Handler) MsgFind(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_findandmodify.go b/internal/handler/msg_findandmodify.go index d925cfad8c37..76cedf69d5c0 100644 --- a/internal/handler/msg_findandmodify.go +++ b/internal/handler/msg_findandmodify.go @@ -43,10 +43,6 @@ type findAndModifyResult struct { // MsgFindAndModify implements `findAndModify` command. func (h *Handler) MsgFindAndModify(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_getcmdlineopts.go b/internal/handler/msg_getcmdlineopts.go index 2dce1c98178d..79dac633511f 100644 --- a/internal/handler/msg_getcmdlineopts.go +++ b/internal/handler/msg_getcmdlineopts.go @@ -24,10 +24,6 @@ import ( // MsgGetCmdLineOpts implements `getCmdLineOpts` command. func (h *Handler) MsgGetCmdLineOpts(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - var reply wire.OpMsg must.NoError(reply.SetSections(wire.MakeOpMsgSection( must.NotFail(types.NewDocument( diff --git a/internal/handler/msg_getfreemonitoringstatus.go b/internal/handler/msg_getfreemonitoringstatus.go index f395ff172a65..372cb1615e4b 100644 --- a/internal/handler/msg_getfreemonitoringstatus.go +++ b/internal/handler/msg_getfreemonitoringstatus.go @@ -24,10 +24,6 @@ import ( // MsgGetFreeMonitoringStatus implements `getFreeMonitoringStatus` command. func (h *Handler) MsgGetFreeMonitoringStatus(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - state := h.StateProvider.Get().TelemetryString() message := "monitoring is " + state diff --git a/internal/handler/msg_getlog.go b/internal/handler/msg_getlog.go index 82ae0628d237..498c9462e9ab 100644 --- a/internal/handler/msg_getlog.go +++ b/internal/handler/msg_getlog.go @@ -36,10 +36,6 @@ import ( // MsgGetLog implements `getLog` command. func (h *Handler) MsgGetLog(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_getmore.go b/internal/handler/msg_getmore.go index 2e516f29b3e0..bacf3b3d87c2 100644 --- a/internal/handler/msg_getmore.go +++ b/internal/handler/msg_getmore.go @@ -39,10 +39,6 @@ import ( // MsgGetMore implements `getMore` command. func (h *Handler) MsgGetMore(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_getparameter.go b/internal/handler/msg_getparameter.go index 9118fa19eaff..47180050a34e 100644 --- a/internal/handler/msg_getparameter.go +++ b/internal/handler/msg_getparameter.go @@ -30,10 +30,6 @@ import ( // MsgGetParameter implements `getParameter` command. func (h *Handler) MsgGetParameter(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_hostinfo.go b/internal/handler/msg_hostinfo.go index 7ed66c73ab00..976b6c04c6ad 100644 --- a/internal/handler/msg_hostinfo.go +++ b/internal/handler/msg_hostinfo.go @@ -32,10 +32,6 @@ import ( // MsgHostInfo implements `hostInfo` command. func (h *Handler) MsgHostInfo(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - now := time.Now().UTC() hostname, err := os.Hostname() diff --git a/internal/handler/msg_insert.go b/internal/handler/msg_insert.go index 8100001e0789..7571ce1dc3c7 100644 --- a/internal/handler/msg_insert.go +++ b/internal/handler/msg_insert.go @@ -47,10 +47,6 @@ func WriteErrorDocument(we *mongo.WriteError) *types.Document { // MsgInsert implements `insert` command. func (h *Handler) MsgInsert(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_killcursors.go b/internal/handler/msg_killcursors.go index bb4bd0cbc8ab..a62debd7cc22 100644 --- a/internal/handler/msg_killcursors.go +++ b/internal/handler/msg_killcursors.go @@ -32,10 +32,6 @@ import ( // MsgKillCursors implements `killCursors` command. func (h *Handler) MsgKillCursors(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_listcollections.go b/internal/handler/msg_listcollections.go index 6b4a6ec412e2..3ac39b24ef05 100644 --- a/internal/handler/msg_listcollections.go +++ b/internal/handler/msg_listcollections.go @@ -32,10 +32,6 @@ import ( // MsgListCollections implements `listCollections` command. func (h *Handler) MsgListCollections(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_listcommands.go b/internal/handler/msg_listcommands.go index 3242579d687b..1aa8a888a6d6 100644 --- a/internal/handler/msg_listcommands.go +++ b/internal/handler/msg_listcommands.go @@ -27,10 +27,6 @@ import ( // MsgListCommands implements `listCommands` command. func (h *Handler) MsgListCommands(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - cmdList := must.NotFail(types.NewDocument()) names := maps.Keys(h.Commands()) sort.Strings(names) diff --git a/internal/handler/msg_listdatabases.go b/internal/handler/msg_listdatabases.go index d2ada0a59227..4946b91d9c06 100644 --- a/internal/handler/msg_listdatabases.go +++ b/internal/handler/msg_listdatabases.go @@ -29,10 +29,6 @@ import ( // MsgListDatabases implements `listDatabases` command. func (h *Handler) MsgListDatabases(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_listindexes.go b/internal/handler/msg_listindexes.go index ad3b99363f22..6e53f09878d2 100644 --- a/internal/handler/msg_listindexes.go +++ b/internal/handler/msg_listindexes.go @@ -29,10 +29,6 @@ import ( // MsgListIndexes implements `listIndexes` command. func (h *Handler) MsgListIndexes(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_logout.go b/internal/handler/msg_logout.go index 4c2796d77b0c..1e40e48a23c5 100644 --- a/internal/handler/msg_logout.go +++ b/internal/handler/msg_logout.go @@ -25,10 +25,6 @@ import ( // MsgLogout implements `logout` command. func (h *Handler) MsgLogout(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - conninfo.Get(ctx).SetAuth("", "") var reply wire.OpMsg diff --git a/internal/handler/msg_renamecollection.go b/internal/handler/msg_renamecollection.go index 8708e1c7bc35..02d7eb6f2efc 100644 --- a/internal/handler/msg_renamecollection.go +++ b/internal/handler/msg_renamecollection.go @@ -30,10 +30,6 @@ import ( // MsgRenameCollection implements `renameCollection` command. func (h *Handler) MsgRenameCollection(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - var err error document, err := msg.Document() diff --git a/internal/handler/msg_serverstatus.go b/internal/handler/msg_serverstatus.go index 4d5ffe63bf3f..082cfdc1dfb7 100644 --- a/internal/handler/msg_serverstatus.go +++ b/internal/handler/msg_serverstatus.go @@ -30,10 +30,6 @@ import ( // MsgServerStatus implements `serverStatus` command. func (h *Handler) MsgServerStatus(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - host, err := os.Hostname() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_setfreemonitoring.go b/internal/handler/msg_setfreemonitoring.go index 49226eb260c6..b0f957cc870f 100644 --- a/internal/handler/msg_setfreemonitoring.go +++ b/internal/handler/msg_setfreemonitoring.go @@ -29,10 +29,6 @@ import ( // MsgSetFreeMonitoring implements `setFreeMonitoring` command. func (h *Handler) MsgSetFreeMonitoring(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_update.go b/internal/handler/msg_update.go index 2056969aed91..baca03bb3fa9 100644 --- a/internal/handler/msg_update.go +++ b/internal/handler/msg_update.go @@ -33,10 +33,6 @@ import ( // MsgUpdate implements `update` command. func (h *Handler) MsgUpdate(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_updateuser.go b/internal/handler/msg_updateuser.go index 6c471d18799e..7b3e62d98740 100644 --- a/internal/handler/msg_updateuser.go +++ b/internal/handler/msg_updateuser.go @@ -32,10 +32,6 @@ import ( // MsgUpdateUser implements `updateUser` command. func (h *Handler) MsgUpdateUser(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_usersinfo.go b/internal/handler/msg_usersinfo.go index 2b40ac27f38c..774c0f262cc7 100644 --- a/internal/handler/msg_usersinfo.go +++ b/internal/handler/msg_usersinfo.go @@ -30,10 +30,6 @@ import ( // MsgUsersInfo implements `usersInfo` command. func (h *Handler) MsgUsersInfo(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) diff --git a/internal/handler/msg_validate.go b/internal/handler/msg_validate.go index 62b5c7e33c07..bdb3d3016d3c 100644 --- a/internal/handler/msg_validate.go +++ b/internal/handler/msg_validate.go @@ -29,10 +29,6 @@ import ( // MsgValidate implements `validate` command. func (h *Handler) MsgValidate(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if err := h.authenticate(ctx); err != nil { - return nil, err - } - document, err := msg.Document() if err != nil { return nil, lazyerrors.Error(err) From 306c10b73ad210f1d5999d2f3be247549e364237 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Tue, 20 Feb 2024 12:11:42 +0900 Subject: [PATCH 32/42] add todo links --- internal/handler/authenticate.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/handler/authenticate.go b/internal/handler/authenticate.go index eaf230915e50..647e81456e56 100644 --- a/internal/handler/authenticate.go +++ b/internal/handler/authenticate.go @@ -34,6 +34,7 @@ import ( // // When admin.systems.user contains no user, the authentication is delegated to // the backend. This may change once local exception is implemented. +// TODO https://github.com/FerretDB/FerretDB/issues/4100 func (h *Handler) authenticate(ctx context.Context) error { if !h.EnableNewAuth { return nil @@ -99,6 +100,7 @@ func (h *Handler) authenticate(ctx context.Context) error { // We do not want unauthenticated users accessing the database, while allowing // users with valid backend credentials to access the database // until local exception is implemented. + // TODO https://github.com/FerretDB/FerretDB/issues/4100 conninfo.Get(ctx).UnsetBypassBackendAuth() return nil From 01281867859162640c130695fd0391d5329d191c Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Tue, 20 Feb 2024 13:13:58 +0900 Subject: [PATCH 33/42] rename reorder --- internal/handler/commands.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/internal/handler/commands.go b/internal/handler/commands.go index 821c6595acbb..7ccd4249012b 100644 --- a/internal/handler/commands.go +++ b/internal/handler/commands.go @@ -22,14 +22,14 @@ import ( // command represents a handler for single command. type command struct { + // Unsafe indicates that the command does not require authentication. + Unsafe bool + // Handler processes this command. // // The passed context is canceled when the client disconnects. Handler func(context.Context, *wire.OpMsg) (*wire.OpMsg, error) - // Unsafe indicates that the command does not require authentication. - Unsafe bool - // Help is shown in the `listCommands` command output. // If empty, that command is hidden, but still can be used. Help string @@ -276,7 +276,7 @@ func (h *Handler) initCommands() { for name, cmd := range h.commands { h.commands[name] = command{ - Handler: h.authenticateHandler(cmd), + Handler: h.authenticateWrapper(cmd), Unsafe: cmd.Unsafe, Help: cmd.Help, } @@ -288,8 +288,9 @@ func (h *Handler) Commands() map[string]command { return h.commands } -// authenticateHandler wraps the command handler with the authentication check. -func (h *Handler) authenticateHandler(cmd command) func(context.Context, *wire.OpMsg) (*wire.OpMsg, error) { +// authenticateWrapper wraps the command handler with the authentication check. +// If Unsafe is true, the command handler is executed without authentication. +func (h *Handler) authenticateWrapper(cmd command) func(context.Context, *wire.OpMsg) (*wire.OpMsg, error) { return func(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { if cmd.Unsafe { return cmd.Handler(ctx, msg) From a0df39febf9f88d768dcf2e74dc979057e407c51 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Tue, 20 Feb 2024 15:17:06 +0900 Subject: [PATCH 34/42] add todo --- internal/clientconn/conninfo/conn_info.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/clientconn/conninfo/conn_info.go b/internal/clientconn/conninfo/conn_info.go index 2666dd00dc66..7828685957e1 100644 --- a/internal/clientconn/conninfo/conn_info.go +++ b/internal/clientconn/conninfo/conn_info.go @@ -121,6 +121,10 @@ func (connInfo *ConnInfo) SetBypassBackendAuth() { } // UnsetBypassBackendAuth marks the connection as requiring backend authentication. +// +// This is a temporary workaround to use backend authentication until local exception +// is implemented. +// TODO https://github.com/FerretDB/FerretDB/issues/4100 func (connInfo *ConnInfo) UnsetBypassBackendAuth() { connInfo.rw.Lock() defer connInfo.rw.Unlock() From 24c393cb18542cd90c2b3f09fc62a6c1f28d6229 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Wed, 21 Feb 2024 17:42:03 +0900 Subject: [PATCH 35/42] address feedback --- integration/users/create_user_test.go | 4 +- internal/handler/authenticate.go | 1 + internal/handler/commands.go | 72 +++++++++++++-------------- 3 files changed, 39 insertions(+), 38 deletions(-) diff --git a/integration/users/create_user_test.go b/integration/users/create_user_test.go index ee3d4ff807db..21ee0fff6d71 100644 --- a/integration/users/create_user_test.go +++ b/integration/users/create_user_test.go @@ -345,12 +345,12 @@ func createTestRunnerUser(tb *testing.T, ctx context.Context, db *mongo.Database return } - username, pwd, mechanism := "username", "password", "PLAIN" + username, password, mechanism := "username", "password", "PLAIN" err := db.Client().Database("admin").RunCommand(ctx, bson.D{ {"createUser", username}, {"roles", bson.A{}}, - {"pwd", pwd}, + {"pwd", password}, {"mechanisms", bson.A{mechanism}}, }).Err() require.NoErrorf(tb, err, "cannot create user") diff --git a/internal/handler/authenticate.go b/internal/handler/authenticate.go index 647e81456e56..0fac5a95bd22 100644 --- a/internal/handler/authenticate.go +++ b/internal/handler/authenticate.go @@ -102,6 +102,7 @@ func (h *Handler) authenticate(ctx context.Context) error { // until local exception is implemented. // TODO https://github.com/FerretDB/FerretDB/issues/4100 conninfo.Get(ctx).UnsetBypassBackendAuth() + h.L.DPanic("backend is used for authentication - no user in admin.system.users collection") return nil } diff --git a/internal/handler/commands.go b/internal/handler/commands.go index 7ccd4249012b..03d6868697de 100644 --- a/internal/handler/commands.go +++ b/internal/handler/commands.go @@ -22,8 +22,8 @@ import ( // command represents a handler for single command. type command struct { - // Unsafe indicates that the command does not require authentication. - Unsafe bool + // anonymous indicates that the command does not require authentication. + anonymous bool // Handler processes this command. // @@ -44,14 +44,14 @@ func (h *Handler) initCommands() { Help: "Returns aggregated data.", }, "buildInfo": { - Handler: h.MsgBuildInfo, - Unsafe: true, - Help: "Returns a summary of the build information.", + Handler: h.MsgBuildInfo, + anonymous: true, + Help: "Returns a summary of the build information.", }, "buildinfo": { // old lowercase variant - Handler: h.MsgBuildInfo, - Unsafe: true, - Help: "", // hidden + Handler: h.MsgBuildInfo, + anonymous: true, + Help: "", // hidden }, "collMod": { Handler: h.MsgCollMod, @@ -66,8 +66,8 @@ func (h *Handler) initCommands() { Help: "Reduces the disk space collection takes and refreshes its statistics.", }, "connectionStatus": { - Handler: h.MsgConnectionStatus, - Unsafe: true, + Handler: h.MsgConnectionStatus, + anonymous: true, Help: "Returns information about the current connection, " + "specifically the state of authenticated users and their available permissions.", }, @@ -160,9 +160,9 @@ func (h *Handler) initCommands() { Help: "Returns the value of the parameter.", }, "hello": { - Handler: h.MsgHello, - Unsafe: true, - Help: "Returns the role of the FerretDB instance.", + Handler: h.MsgHello, + anonymous: true, + Help: "Returns the role of the FerretDB instance.", }, "hostInfo": { Handler: h.MsgHostInfo, @@ -173,14 +173,14 @@ func (h *Handler) initCommands() { Help: "Inserts documents into the database.", }, "isMaster": { - Handler: h.MsgIsMaster, - Unsafe: true, - Help: "Returns the role of the FerretDB instance.", + Handler: h.MsgIsMaster, + anonymous: true, + Help: "Returns the role of the FerretDB instance.", }, "ismaster": { // old lowercase variant - Handler: h.MsgIsMaster, - Unsafe: true, - Help: "", // hidden + Handler: h.MsgIsMaster, + anonymous: true, + Help: "", // hidden }, "killCursors": { Handler: h.MsgKillCursors, @@ -207,23 +207,23 @@ func (h *Handler) initCommands() { Help: "Logs out from the current session.", }, "ping": { - Handler: h.MsgPing, - Unsafe: true, - Help: "Returns a pong response.", + Handler: h.MsgPing, + anonymous: true, + Help: "Returns a pong response.", }, "renameCollection": { Handler: h.MsgRenameCollection, Help: "Changes the name of an existing collection.", }, "saslStart": { - Handler: h.MsgSASLStart, - Unsafe: true, - Help: "", // hidden + Handler: h.MsgSASLStart, + anonymous: true, + Help: "", // hidden }, "saslContinue": { - Handler: h.MsgSASLContinue, - Unsafe: true, - Help: "", // hidden + Handler: h.MsgSASLContinue, + anonymous: true, + Help: "", // hidden }, "serverStatus": { Handler: h.MsgServerStatus, @@ -242,9 +242,9 @@ func (h *Handler) initCommands() { Help: "Validates collection.", }, "whatsmyuri": { - Handler: h.MsgWhatsMyURI, - Unsafe: true, - Help: "Returns peer information.", + Handler: h.MsgWhatsMyURI, + anonymous: true, + Help: "Returns peer information.", }, // please keep sorted alphabetically } @@ -276,9 +276,9 @@ func (h *Handler) initCommands() { for name, cmd := range h.commands { h.commands[name] = command{ - Handler: h.authenticateWrapper(cmd), - Unsafe: cmd.Unsafe, - Help: cmd.Help, + Handler: h.authenticateWrapper(cmd), + anonymous: cmd.anonymous, + Help: cmd.Help, } } } @@ -289,10 +289,10 @@ func (h *Handler) Commands() map[string]command { } // authenticateWrapper wraps the command handler with the authentication check. -// If Unsafe is true, the command handler is executed without authentication. +// If anonymous is true, the command handler is executed without authentication. func (h *Handler) authenticateWrapper(cmd command) func(context.Context, *wire.OpMsg) (*wire.OpMsg, error) { return func(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, error) { - if cmd.Unsafe { + if cmd.anonymous { return cmd.Handler(ctx, msg) } From 73ec82abd6b8962f2cbb4b175166ef81d693e0e1 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Wed, 21 Feb 2024 19:07:41 +0900 Subject: [PATCH 36/42] create user during the setup --- integration/setup/setup.go | 26 +++++++++++++++++++ integration/users/connection_test.go | 14 +++++----- integration/users/create_user_test.go | 24 ----------------- .../drop_all_users_from_database_test.go | 1 - integration/users/drop_user_test.go | 1 - integration/users/update_user_test.go | 1 - integration/users/usersinfo_test.go | 1 - internal/handler/authenticate.go | 2 +- 8 files changed, 33 insertions(+), 37 deletions(-) diff --git a/integration/setup/setup.go b/integration/setup/setup.go index ec3395a59fae..455784a29996 100644 --- a/integration/setup/setup.go +++ b/integration/setup/setup.go @@ -163,6 +163,8 @@ func SetupWithOpts(tb testtb.TB, opts *SetupOpts) *SetupResult { collection := setupCollection(tb, setupCtx, client, opts) + setupUser(tb, setupCtx, client) + level.SetLevel(*logLevelF) return &SetupResult{ @@ -322,3 +324,27 @@ func insertBenchmarkProvider(tb testtb.TB, ctx context.Context, collection *mong return } + +// setupUser creates a user in admin database with PLAIN mechanism. +// The user uses username/password credential which is the same as the database +// credentials. This is done to avoid the need to reconnect as different credential. +// +// Without this, once the first user is created, the authentication fails +// as username/password does not exist in admin.system.users collection. +func setupUser(tb testtb.TB, ctx context.Context, client *mongo.Client) { + tb.Helper() + + if IsMongoDB(tb) { + return + } + + username, password, mechanism := "username", "password", "PLAIN" + + err := client.Database("admin").RunCommand(ctx, bson.D{ + {"createUser", username}, + {"roles", bson.A{}}, + {"pwd", password}, + {"mechanisms", bson.A{mechanism}}, + }).Err() + require.NoErrorf(tb, err, "cannot create user") +} diff --git a/integration/users/connection_test.go b/integration/users/connection_test.go index c7a675e1f4d4..5593c2bdad3c 100644 --- a/integration/users/connection_test.go +++ b/integration/users/connection_test.go @@ -38,8 +38,6 @@ func TestAuthentication(t *testing.T) { collection := s.Collection db := collection.Database() - createTestRunnerUser(t, ctx, s.Collection.Database()) - testCases := map[string]struct { //nolint:vet // for readability username string password string @@ -252,6 +250,12 @@ func TestAuthenticationEnableNewAuthNoUserExists(t *testing.T) { collection := s.Collection db := collection.Database() + // drop the user created in the setup + err := db.Client().Database("admin").RunCommand(ctx, bson.D{ + {"dropUser", "username"}, + }).Err() + require.NoError(t, err, "cannot drop user") + testCases := map[string]struct { username string password string @@ -367,12 +371,6 @@ func TestAuthenticationEnableNewAuthPLAIN(t *testing.T) { mechanism: "PLAIN", err: "AuthenticationFailed", }, - "PLAINBackendUser": { - username: "username", - password: "password", - mechanism: "PLAIN", - err: "AuthenticationFailed", - }, } for name, tc := range testCases { diff --git a/integration/users/create_user_test.go b/integration/users/create_user_test.go index 21ee0fff6d71..63b204ae4f93 100644 --- a/integration/users/create_user_test.go +++ b/integration/users/create_user_test.go @@ -15,7 +15,6 @@ package users import ( - "context" "fmt" "testing" @@ -37,7 +36,6 @@ func TestCreateUser(t *testing.T) { ctx, collection := setup.Setup(t) db := collection.Database() - createTestRunnerUser(t, ctx, collection.Database()) testCases := map[string]struct { //nolint:vet // for readability payload bson.D @@ -333,25 +331,3 @@ func assertSCRAMSHA256Credentials(t testtb.TB, key string, cred *types.Document) assert.NotEmpty(t, must.NotFail(c.Get("serverKey")).(string)) assert.NotEmpty(t, must.NotFail(c.Get("storedKey")).(string)) } - -// createTestRunnerUser creates a user in admin database with PLAIN mechanism. -// The user uses username/password credential which is the same as the database -// credentials. This is done to avoid the need to reconnect as different credential. -// -// Without this, once the first user is created, the authentication fails -// as username/password does not exist in admin.system.users collection. -func createTestRunnerUser(tb *testing.T, ctx context.Context, db *mongo.Database) { - if setup.IsMongoDB(tb) { - return - } - - username, password, mechanism := "username", "password", "PLAIN" - - err := db.Client().Database("admin").RunCommand(ctx, bson.D{ - {"createUser", username}, - {"roles", bson.A{}}, - {"pwd", password}, - {"mechanisms", bson.A{mechanism}}, - }).Err() - require.NoErrorf(tb, err, "cannot create user") -} diff --git a/integration/users/drop_all_users_from_database_test.go b/integration/users/drop_all_users_from_database_test.go index 9334c3e83f7a..730743b5235f 100644 --- a/integration/users/drop_all_users_from_database_test.go +++ b/integration/users/drop_all_users_from_database_test.go @@ -36,7 +36,6 @@ func TestDropAllUsersFromDatabase(t *testing.T) { ctx, collection := setup.Setup(t) db := collection.Database() - createTestRunnerUser(t, ctx, db) client := collection.Database().Client() quantity := 5 // Add some users to the database. diff --git a/integration/users/drop_user_test.go b/integration/users/drop_user_test.go index 116f108974a0..a492b33b5b86 100644 --- a/integration/users/drop_user_test.go +++ b/integration/users/drop_user_test.go @@ -33,7 +33,6 @@ func TestDropUser(t *testing.T) { ctx, collection := setup.Setup(t) db := collection.Database() - createTestRunnerUser(t, ctx, db) err := db.RunCommand(ctx, bson.D{ {"createUser", "a_user"}, diff --git a/integration/users/update_user_test.go b/integration/users/update_user_test.go index c2fba3ad5707..d6d7ba1a4961 100644 --- a/integration/users/update_user_test.go +++ b/integration/users/update_user_test.go @@ -34,7 +34,6 @@ func TestUpdateUser(t *testing.T) { ctx, collection := setup.Setup(t) db := collection.Database() - createTestRunnerUser(t, ctx, db) testCases := map[string]struct { //nolint:vet // for readability createPayload bson.D diff --git a/integration/users/usersinfo_test.go b/integration/users/usersinfo_test.go index b3585694f218..e41b1f8c8ce0 100644 --- a/integration/users/usersinfo_test.go +++ b/integration/users/usersinfo_test.go @@ -42,7 +42,6 @@ func TestUsersinfo(t *testing.T) { t.Parallel() ctx, collection := setup.Setup(t) - createTestRunnerUser(t, ctx, collection.Database()) client := collection.Database().Client() dbToUsers := []struct { diff --git a/internal/handler/authenticate.go b/internal/handler/authenticate.go index 0fac5a95bd22..540a6ec29048 100644 --- a/internal/handler/authenticate.go +++ b/internal/handler/authenticate.go @@ -102,7 +102,7 @@ func (h *Handler) authenticate(ctx context.Context) error { // until local exception is implemented. // TODO https://github.com/FerretDB/FerretDB/issues/4100 conninfo.Get(ctx).UnsetBypassBackendAuth() - h.L.DPanic("backend is used for authentication - no user in admin.system.users collection") + h.L.Error("backend is used for authentication - no user in admin.system.users collection") return nil } From 07c749add594c599cd2ea8c1d40f6ab23ee3bdfd Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Thu, 22 Feb 2024 12:29:45 +0900 Subject: [PATCH 37/42] fix test --- integration/users/connection_test.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/integration/users/connection_test.go b/integration/users/connection_test.go index 5593c2bdad3c..f8d7a001a6db 100644 --- a/integration/users/connection_test.go +++ b/integration/users/connection_test.go @@ -250,11 +250,13 @@ func TestAuthenticationEnableNewAuthNoUserExists(t *testing.T) { collection := s.Collection db := collection.Database() - // drop the user created in the setup - err := db.Client().Database("admin").RunCommand(ctx, bson.D{ - {"dropUser", "username"}, - }).Err() - require.NoError(t, err, "cannot drop user") + if !setup.IsMongoDB(t) { + // drop the user created in the setup + err := db.Client().Database("admin").RunCommand(ctx, bson.D{ + {"dropUser", "username"}, + }).Err() + require.NoError(t, err, "cannot drop user") + } testCases := map[string]struct { username string From 3f95100efcdb459365aac011c9a96cad6f3d0966 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Thu, 22 Feb 2024 16:22:50 +0900 Subject: [PATCH 38/42] update comment add explaination add more mechanisms --- integration/setup/setup.go | 6 +++--- internal/handler/authenticate.go | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/integration/setup/setup.go b/integration/setup/setup.go index 455784a29996..8cbd81b06098 100644 --- a/integration/setup/setup.go +++ b/integration/setup/setup.go @@ -325,7 +325,7 @@ func insertBenchmarkProvider(tb testtb.TB, ctx context.Context, collection *mong return } -// setupUser creates a user in admin database with PLAIN mechanism. +// setupUser creates a user in admin database with supported mechanisms. // The user uses username/password credential which is the same as the database // credentials. This is done to avoid the need to reconnect as different credential. // @@ -338,13 +338,13 @@ func setupUser(tb testtb.TB, ctx context.Context, client *mongo.Client) { return } - username, password, mechanism := "username", "password", "PLAIN" + username, password := "username", "password" err := client.Database("admin").RunCommand(ctx, bson.D{ {"createUser", username}, {"roles", bson.A{}}, {"pwd", password}, - {"mechanisms", bson.A{mechanism}}, + {"mechanisms", bson.A{"PLAIN", "SCRAM-SHA-1", "SCRAM-SHA-256"}}, }).Err() require.NoErrorf(tb, err, "cannot create user") } diff --git a/internal/handler/authenticate.go b/internal/handler/authenticate.go index 540a6ec29048..1333e1470238 100644 --- a/internal/handler/authenticate.go +++ b/internal/handler/authenticate.go @@ -56,6 +56,11 @@ func (h *Handler) authenticate(ctx context.Context) error { username, pwd := conninfo.Get(ctx).Auth() + // For `PLAIN` mechanism $db field is always `$external` upon saslStart. + // For `SCRAM-SHA-1` and `SCRAM-SHA-256` mechanisms $db field contains + // authSource option of the client. + // Since _id field compromises of `dbName.username`, and dbName cannot be + // passed by client for `PLAIN` mechanism, it does not use _id for the filter. filter := must.NotFail(types.NewDocument("user", username)) qr, err := usersCol.Query(ctx, nil) From c4c122439dcedca3504f5c1b16325c04ae3870a8 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Thu, 22 Feb 2024 19:02:59 +0900 Subject: [PATCH 39/42] PLAIN and SHA handles authenticated users the same way --- integration/users/connection_test.go | 8 ++++++++ internal/handler/authenticate.go | 4 ++-- internal/handler/msg_saslstart.go | 14 ++++++-------- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/integration/users/connection_test.go b/integration/users/connection_test.go index f8d7a001a6db..a499ced063ad 100644 --- a/integration/users/connection_test.go +++ b/integration/users/connection_test.go @@ -306,6 +306,10 @@ func TestAuthenticationEnableNewAuthNoUserExists(t *testing.T) { client, err := mongo.Connect(ctx, opts) require.NoError(t, err, "cannot connect to MongoDB") + t.Cleanup(func() { + require.NoError(t, client.Disconnect(ctx)) + }) + err = client.Ping(ctx, nil) if tc.pingErr != "" { @@ -392,6 +396,10 @@ func TestAuthenticationEnableNewAuthPLAIN(t *testing.T) { client, err := mongo.Connect(ctx, opts) require.NoError(t, err, "cannot connect to MongoDB") + t.Cleanup(func() { + require.NoError(t, client.Disconnect(ctx)) + }) + c := client.Database(db.Name()).Collection(cName) _, err = c.InsertOne(ctx, bson.D{{"ping", "pong"}}) diff --git a/internal/handler/authenticate.go b/internal/handler/authenticate.go index 1333e1470238..dbe29ab25988 100644 --- a/internal/handler/authenticate.go +++ b/internal/handler/authenticate.go @@ -59,8 +59,8 @@ func (h *Handler) authenticate(ctx context.Context) error { // For `PLAIN` mechanism $db field is always `$external` upon saslStart. // For `SCRAM-SHA-1` and `SCRAM-SHA-256` mechanisms $db field contains // authSource option of the client. - // Since _id field compromises of `dbName.username`, and dbName cannot be - // passed by client for `PLAIN` mechanism, it does not use _id for the filter. + // Let authorization handle the database access right. + // TODO https://github.com/FerretDB/FerretDB/issues/174 filter := must.NotFail(types.NewDocument("user", username)) qr, err := usersCol.Query(ctx, nil) diff --git a/internal/handler/msg_saslstart.go b/internal/handler/msg_saslstart.go index 08e423a45e1a..ab16e500e90e 100644 --- a/internal/handler/msg_saslstart.go +++ b/internal/handler/msg_saslstart.go @@ -172,14 +172,12 @@ func (h *Handler) scramCredentialLookup(ctx context.Context, username, dbName, m return nil, lazyerrors.Error(err) } - var filter *types.Document - - filter, err = usersInfoFilter(false, false, "", []usersInfoPair{ - {username: username, db: dbName}, - }) - if err != nil { - return nil, lazyerrors.Error(err) - } + // For `PLAIN` mechanism $db field is always `$external` upon saslStart. + // For `SCRAM-SHA-1` and `SCRAM-SHA-256` mechanisms $db field contains + // authSource option of the client. + // Let authorization handle the database access right. + // TODO https://github.com/FerretDB/FerretDB/issues/174 + filter := must.NotFail(types.NewDocument("user", username)) // Filter isn't being passed to the query as we are filtering after retrieving all data // from the database due to limitations of the internal/backends filters. From d5d2499b182374010435a02d878f422cf168b376 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Mon, 26 Feb 2024 10:43:30 +0900 Subject: [PATCH 40/42] do not use pwd as abbrev --- internal/handler/authenticate.go | 4 ++-- internal/handler/msg_createuser.go | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/handler/authenticate.go b/internal/handler/authenticate.go index dbe29ab25988..4430b9f1c274 100644 --- a/internal/handler/authenticate.go +++ b/internal/handler/authenticate.go @@ -54,7 +54,7 @@ func (h *Handler) authenticate(ctx context.Context) error { return lazyerrors.Error(err) } - username, pwd := conninfo.Get(ctx).Auth() + username, userPassword := conninfo.Get(ctx).Auth() // For `PLAIN` mechanism $db field is always `$external` upon saslStart. // For `SCRAM-SHA-1` and `SCRAM-SHA-256` mechanisms $db field contains @@ -140,7 +140,7 @@ func (h *Handler) authenticate(ctx context.Context) error { return lazyerrors.Errorf("field 'PLAIN' has type %T, expected Document", v) } - err = password.PlainVerify(pwd, doc) + err = password.PlainVerify(userPassword, doc) if err != nil { return handlererrors.NewCommandErrorMsgWithArgument( handlererrors.ErrAuthenticationFailed, diff --git a/internal/handler/msg_createuser.go b/internal/handler/msg_createuser.go index 9fd3b0a192f4..d3dfac1eebb6 100644 --- a/internal/handler/msg_createuser.go +++ b/internal/handler/msg_createuser.go @@ -183,10 +183,10 @@ func (h *Handler) MsgCreateUser(ctx context.Context, msg *wire.OpMsg) (*wire.OpM } // makeCredentials creates a document with credentials for the chosen mechanisms. -func makeCredentials(mechanisms *types.Array, username, pwd string) (*types.Document, error) { +func makeCredentials(mechanisms *types.Array, username, userPassword string) (*types.Document, error) { credentials := types.MakeDocument(0) - if pwd == "" { + if userPassword == "" { return nil, handlererrors.NewCommandErrorMsg( handlererrors.ErrSetEmptyPassword, "Password cannot be empty", @@ -217,16 +217,16 @@ func makeCredentials(mechanisms *types.Array, username, pwd string) (*types.Docu switch v { case "PLAIN": - credentials.Set("PLAIN", must.NotFail(password.PlainHash(pwd))) + credentials.Set("PLAIN", must.NotFail(password.PlainHash(userPassword))) case "SCRAM-SHA-1": - hash, err := password.SCRAMSHA1Hash(username, pwd) + hash, err := password.SCRAMSHA1Hash(username, userPassword) if err != nil { return nil, err } credentials.Set("SCRAM-SHA-1", hash) case "SCRAM-SHA-256": - hash, err := password.SCRAMSHA256Hash(pwd) + hash, err := password.SCRAMSHA256Hash(userPassword) if err != nil { if strings.Contains(err.Error(), "prohibited character") { return nil, handlererrors.NewCommandErrorMsg( From 7bcc892827f0e76a04008a6e0a04fbb99fe0e6a2 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Mon, 26 Feb 2024 10:52:03 +0900 Subject: [PATCH 41/42] update comment --- internal/clientconn/conninfo/conn_info.go | 3 +-- internal/handler/authenticate.go | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/internal/clientconn/conninfo/conn_info.go b/internal/clientconn/conninfo/conn_info.go index 7828685957e1..88fe95b49eff 100644 --- a/internal/clientconn/conninfo/conn_info.go +++ b/internal/clientconn/conninfo/conn_info.go @@ -122,8 +122,7 @@ func (connInfo *ConnInfo) SetBypassBackendAuth() { // UnsetBypassBackendAuth marks the connection as requiring backend authentication. // -// This is a temporary workaround to use backend authentication until local exception -// is implemented. +// This is a workaround to use backend authentication. // TODO https://github.com/FerretDB/FerretDB/issues/4100 func (connInfo *ConnInfo) UnsetBypassBackendAuth() { connInfo.rw.Lock() diff --git a/internal/handler/authenticate.go b/internal/handler/authenticate.go index 4430b9f1c274..f6b92c7e9128 100644 --- a/internal/handler/authenticate.go +++ b/internal/handler/authenticate.go @@ -33,7 +33,7 @@ import ( // authentication and let backend handle it. // // When admin.systems.user contains no user, the authentication is delegated to -// the backend. This may change once local exception is implemented. +// the backend. // TODO https://github.com/FerretDB/FerretDB/issues/4100 func (h *Handler) authenticate(ctx context.Context) error { if !h.EnableNewAuth { @@ -103,8 +103,7 @@ func (h *Handler) authenticate(ctx context.Context) error { if !hasUser { // There is no user in the database, let the backend check the authentication. // We do not want unauthenticated users accessing the database, while allowing - // users with valid backend credentials to access the database - // until local exception is implemented. + // users with valid backend credentials to access the database. // TODO https://github.com/FerretDB/FerretDB/issues/4100 conninfo.Get(ctx).UnsetBypassBackendAuth() h.L.Error("backend is used for authentication - no user in admin.system.users collection") From dbd27a8381e2f33d159ec978b0334c56c7f464f0 Mon Sep 17 00:00:00 2001 From: Chi Fujii Date: Tue, 27 Feb 2024 15:50:46 +0900 Subject: [PATCH 42/42] do not allow SCRAM if new authentication is not enabled --- internal/handler/msg_saslstart.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/internal/handler/msg_saslstart.go b/internal/handler/msg_saslstart.go index ab16e500e90e..084b2139646a 100644 --- a/internal/handler/msg_saslstart.go +++ b/internal/handler/msg_saslstart.go @@ -82,6 +82,13 @@ func (h *Handler) MsgSASLStart(ctx context.Context, msg *wire.OpMsg) (*wire.OpMs ))) case "SCRAM-SHA-1", "SCRAM-SHA-256": + if !h.EnableNewAuth { + return nil, handlererrors.NewCommandErrorMsg( + handlererrors.ErrAuthenticationFailed, + "SCRAM authentication is not enabled", + ) + } + response, err := h.saslStartSCRAM(ctx, mechanism, document) if err != nil { return nil, err