Skip to content

Commit

Permalink
Merge branch 'main' into ubicloud-blog
Browse files Browse the repository at this point in the history
  • Loading branch information
mergify[bot] authored Jan 31, 2024
2 parents fbac881 + de8e38d commit 689861e
Show file tree
Hide file tree
Showing 67 changed files with 745 additions and 38 deletions.
50 changes: 50 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,56 @@

<!-- markdownlint-disable MD024 MD034 -->

## [v1.19.0](https://github.com/FerretDB/FerretDB/releases/tag/v1.19.0) (2024-01-29)

### New Features 🎉

- Support creating an index on nested fields for SQLite by @fadyat in https://github.com/FerretDB/FerretDB/pull/3972

### Fixed Bugs 🐛

- Fix `maxTimeMS` for `getMore` command by @noisersup in https://github.com/FerretDB/FerretDB/pull/3919
- Fix `upsert` with `$setOnInsert` operator by @wazir-ahmed in https://github.com/FerretDB/FerretDB/pull/3931
- Fix validation process for creating duplicate `_id` index by @kropidlowsky in https://github.com/FerretDB/FerretDB/pull/3990

### Documentation 📄

- Bump deps by @AlekSi in https://github.com/FerretDB/FerretDB/pull/3955
- Add documentation for oplog by @Fashander in https://github.com/FerretDB/FerretDB/pull/3960
- Fix search queries by @Fashander in https://github.com/FerretDB/FerretDB/pull/3976

### Other Changes 🤖

- Fix Taskfile.yml indentation by @AlekSi in https://github.com/FerretDB/FerretDB/pull/3964
- Speed-up Docker builds by @AlekSi in https://github.com/FerretDB/FerretDB/pull/3965
- Run more `maxTimeMS` tests by @noisersup in https://github.com/FerretDB/FerretDB/pull/3940
- Store passwords for PLAIN authentication mechanism by @henvic in https://github.com/FerretDB/FerretDB/pull/3928
- Use PBKDF2 for storing `PLAIN` passwords by @AlekSi in https://github.com/FerretDB/FerretDB/pull/3970
- Shard extra CI configurations by @AlekSi in https://github.com/FerretDB/FerretDB/pull/3946
- Small fixes and tweaks by @AlekSi in https://github.com/FerretDB/FerretDB/pull/3971
- Implement `updateUser` command by @henvic in https://github.com/FerretDB/FerretDB/pull/3973
- Small assorted tweaks by @AlekSi in https://github.com/FerretDB/FerretDB/pull/3979
- Add MySQL backend Registry by @adetunjii in https://github.com/FerretDB/FerretDB/pull/3967
- Add new BSON decoding package by @AlekSi in https://github.com/FerretDB/FerretDB/pull/3905
- Refactor `bson2` encoding/decoding by @AlekSi in https://github.com/FerretDB/FerretDB/pull/3987
- Use `usersInfo` for `createUser` and `dropUser` integration tests by @henvic in https://github.com/FerretDB/FerretDB/pull/3980
- Improve `bson2` fuzzing by @AlekSi in https://github.com/FerretDB/FerretDB/pull/3988
- Update contributing documentation by @AlekSi in https://github.com/FerretDB/FerretDB/pull/3994
- Use `ListCollection` with a filter by @sachinpuranik in https://github.com/FerretDB/FerretDB/pull/3995
- Add tests for MySQL registry by @adetunjii in https://github.com/FerretDB/FerretDB/pull/3993
- Prepare CI to having multiple main branches by @AlekSi in https://github.com/FerretDB/FerretDB/pull/4002
- Ignore `$readPreference` field by @b1ron in https://github.com/FerretDB/FerretDB/pull/3996
- Hide `*types.Document` from `wire` struct fields by @AlekSi in https://github.com/FerretDB/FerretDB/pull/4000
- Add deep `bson2` decoding by @AlekSi in https://github.com/FerretDB/FerretDB/pull/3997
- Expose raw documents in the `wire` package by @AlekSi in https://github.com/FerretDB/FerretDB/pull/4011

### New Contributors

- @fadyat made their first contribution in https://github.com/FerretDB/FerretDB/pull/3972

[All closed issues and pull requests](https://github.com/FerretDB/FerretDB/milestone/61?closed=1).
[All commits](https://github.com/FerretDB/FerretDB/compare/v1.18.0...v1.19.0).

## [v1.18.0](https://github.com/FerretDB/FerretDB/releases/tag/v1.18.0) (2024-01-08)

### What's Changed
Expand Down
6 changes: 4 additions & 2 deletions ferretdb/ferretdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ func New(config *Config) (*FerretDB, error) {
//
// When this method returns, listener and all connections, as well as handler are closed.
//
// It is required to run this method in order to initialise the listeners with their respective
// It is required to run this method in order to initialize the listeners with their respective
// IP address and port. Calling methods which require the listener's address (eg: [*FerretDB.MongoDBURI]
// requires it for configuring its Host URL) before calling this method might result in a deadlock.
func (f *FerretDB) Run(ctx context.Context) error {
Expand Down Expand Up @@ -216,11 +216,13 @@ func (f *FerretDB) MongoDBURI() string {

// logger is a global logger used by FerretDB.
//
// If it is a problem for you, please create an issue.
// TODO https://github.com/FerretDB/FerretDB/issues/4014
var logger *zap.Logger

// Initialize the global logger there to avoid creating too many issues for zap users that initialize it in their
// `main()` functions. It is still not a full solution; eventually, we should remove the usage of the global logger.
//
// TODO https://github.com/FerretDB/FerretDB/issues/4014
func init() {
l := zap.ErrorLevel
if version.Get().DebugBuild {
Expand Down
184 changes: 184 additions & 0 deletions integration/users/connection_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
// 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 users

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/x/mongo/driver/topology"

"github.com/FerretDB/FerretDB/integration/setup"
"github.com/FerretDB/FerretDB/internal/util/testutil/testtb"
)

func TestAuthentication(t *testing.T) {
t.Parallel()

s := setup.SetupWithOpts(t, &setup.SetupOpts{})
ctx := s.Ctx
collection := s.Collection
db := collection.Database()

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

userNotFound bool
wrongPassword bool

failsForFerretDB bool
}{
"Success": {
username: "common",
password: "password",
failsForFerretDB: true,
},
"Updated": {
username: "updated",
password: "pass123",
updatePassword: "somethingelse",
failsForFerretDB: true,
},
"ScramSHA256": {
username: "scramsha256",
password: "password",
mechanism: "SCRAM-SHA-256",
failsForFerretDB: true,
},
"ScramSHA256Updated": {
username: "scramsha256updated",
password: "pass123",
updatePassword: "anotherpassword",
mechanism: "SCRAM-SHA-256",
failsForFerretDB: true,
},
"NotFoundUser": {
username: "notfound",
password: "something",
mechanism: "SCRAM-SHA-256",
userNotFound: true,
},
"BadPassword": {
username: "baduser",
password: "something",
mechanism: "SCRAM-SHA-256",
wrongPassword: true,
failsForFerretDB: true,
},
}

for name, tc := range testCases {
name, tc := name, tc
t.Run(name, func(tt *testing.T) {
tt.Parallel()

var t testtb.TB = tt

if tc.failsForFerretDB {
t = setup.FailsForFerretDB(tt, "https://github.com/FerretDB/FerretDB/issues/3784")
}

if !tc.userNotFound {
createPayload := bson.D{
{"createUser", tc.username},
{"roles", bson.A{}},
{"pwd", tc.password},
}

switch tc.mechanism {
case "": // Use default mechanism for MongoDB and SCRAM-SHA-256 for FerretDB as SHA-1 won't be supported as it's deprecated.
if !setup.IsMongoDB(t) {
createPayload = append(createPayload, bson.E{"mechanisms", bson.A{"SCRAM-SHA-256"}})
}
case "SCRAM-SHA-256", "PLAIN":
createPayload = append(createPayload, bson.E{"mechanisms", bson.A{tc.mechanism}})
default:
t.Fatalf("unimplemented mechanism %s", tc.mechanism)
}

err := db.RunCommand(ctx, createPayload).Err()
require.NoErrorf(t, err, "cannot create user")
}

if tc.updatePassword != "" {
updatePayload := bson.D{
{"updateUser", tc.username},
{"pwd", tc.updatePassword},
}

err := db.RunCommand(ctx, updatePayload).Err()
require.NoErrorf(t, err, "cannot update user")
}

serverAPI := options.ServerAPI(options.ServerAPIVersion1)

password := tc.password
if tc.updatePassword != "" {
password = tc.updatePassword
}
if tc.wrongPassword {
password = "wrongpassword"
}

credential := options.Credential{
AuthMechanism: tc.mechanism,
AuthSource: db.Name(),
Username: tc.username,
Password: password,
}

opts := options.Client().ApplyURI(s.MongoDBURI).SetServerAPIOptions(serverAPI).SetAuth(credential)

client, err := mongo.Connect(ctx, opts)
require.NoError(t, err, "cannot connect to MongoDB")

// Ping to force connection to be established and tested.
err = client.Ping(ctx, nil)

if tc.wrongPassword || tc.userNotFound {
var ce topology.ConnectionError
require.ErrorAs(t, err, &ce, "expected a connection error")
return
}

require.NoError(t, err, "cannot ping MongoDB")

connCollection := client.Database(db.Name()).Collection(collection.Name())
r, err := connCollection.InsertOne(ctx, bson.D{{"ping", "pong"}})
require.NoError(t, err, "cannot insert document")
id := r.InsertedID.(primitive.ObjectID)
require.NotEmpty(t, id)

var result bson.D
err = connCollection.FindOne(ctx, bson.D{{"_id", id}}).Decode(&result)
require.NoError(t, err, "cannot find document")
assert.Equal(t, bson.D{
{"_id", id},
{"ping", "pong"},
}, result)

require.NoError(t, client.Disconnect(context.Background()))
})
}
}
53 changes: 48 additions & 5 deletions integration/users/create_user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/FerretDB/FerretDB/internal/types"
"github.com/FerretDB/FerretDB/internal/util/must"
"github.com/FerretDB/FerretDB/internal/util/testutil"
"github.com/FerretDB/FerretDB/internal/util/testutil/testtb"
)

func TestCreateUser(t *testing.T) {
Expand All @@ -41,6 +42,8 @@ func TestCreateUser(t *testing.T) {
err *mongo.CommandError
altMessage string
expected bson.D

failsForFerretDB bool
}{
"Empty": {
payload: bson.D{
Expand Down Expand Up @@ -137,6 +140,18 @@ func TestCreateUser(t *testing.T) {
{"ok", float64(1)},
},
},
"SuccessWithSCRAMSHA256": {
payload: bson.D{
{"createUser", "success_user_with_scram_sha_256"},
{"roles", bson.A{}},
{"pwd", "password"},
{"mechanisms", bson.A{"SCRAM-SHA-256"}},
},
expected: bson.D{
{"ok", float64(1)},
},
failsForFerretDB: true,
},
"WithComment": {
payload: bson.D{
{"createUser", "with_comment_user"},
Expand Down Expand Up @@ -172,14 +187,20 @@ func TestCreateUser(t *testing.T) {

for name, tc := range testCases {
name, tc := name, tc
t.Run(name, func(t *testing.T) {
t.Run(name, func(tt *testing.T) {
tt.Parallel()

var t testtb.TB = tt

if tc.failsForFerretDB {
t = setup.FailsForFerretDB(tt, "https://github.com/FerretDB/FerretDB/issues/3784")
}

payload := integration.ConvertDocument(t, tc.payload)
if payload.Has("mechanisms") {
setup.SkipForMongoDB(t, "PLAIN credentials are only supported via LDAP (PLAIN) by MongoDB Enterprise")
}

t.Parallel()

var res bson.D
err := db.RunCommand(ctx, tc.payload).Decode(&res)
if tc.err != nil {
Expand Down Expand Up @@ -212,7 +233,15 @@ func TestCreateUser(t *testing.T) {
user.Remove("userId")

if payload.Has("mechanisms") {
assertPlainCredentials(t, "PLAIN", must.NotFail(user.Get("credentials")).(*types.Document))
payloadMechanisms := must.NotFail(payload.Get("mechanisms")).(*types.Array)

if payloadMechanisms.Contains("PLAIN") {
assertPlainCredentials(t, "PLAIN", must.NotFail(user.Get("credentials")).(*types.Document))
}

if payloadMechanisms.Contains("SCRAM-SHA-256") {
assertSCRAMSHA256Credentials(t, "SCRAM-SHA-256", must.NotFail(user.Get("credentials")).(*types.Document))
}
}

user.Remove("mechanisms")
Expand All @@ -231,7 +260,7 @@ func TestCreateUser(t *testing.T) {
}

// assertPlainCredentials checks if the credential is a valid PLAIN credential.
func assertPlainCredentials(t testing.TB, key string, cred *types.Document) {
func assertPlainCredentials(t testtb.TB, key string, cred *types.Document) {
t.Helper()

require.True(t, cred.Has(key), "missing credential %q", key)
Expand All @@ -243,3 +272,17 @@ func assertPlainCredentials(t testing.TB, key string, cred *types.Document) {
assert.NotEmpty(t, must.NotFail(c.Get("hash")))
assert.NotEmpty(t, must.NotFail(c.Get("salt")))
}

// assertSCRAMSHA256Credentials checks if the credential is a valid SCRAM-SHA-256 credential.
func assertSCRAMSHA256Credentials(t testtb.TB, key string, cred *types.Document) {
t.Helper()

require.True(t, cred.Has(key), "missing credential %q", key)

c := must.NotFail(cred.Get(key)).(*types.Document)

assert.Equal(t, must.NotFail(c.Get("iterationCount")), int32(15000))
assert.NotEmpty(t, must.NotFail(c.Get("salt")).(string))
assert.NotEmpty(t, must.NotFail(c.Get("serverKey")).(string))
assert.NotEmpty(t, must.NotFail(c.Get("storedKey")).(string))
}
9 changes: 4 additions & 5 deletions integration/users/drop_all_users_from_database_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ func TestDropAllUsersFromDatabase(t *testing.T) {
ctx, collection := setup.Setup(t)
db := collection.Database()
client := collection.Database().Client()
users := client.Database("admin").Collection("system.users")

quantity := 5 // Add some users to the database.
for i := 1; i <= quantity; i++ {
Expand All @@ -51,16 +50,16 @@ func TestDropAllUsersFromDatabase(t *testing.T) {

// Dropping all users from another database shouldn't influence on the number of users remaining on the current database.
// 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"), users, 0)
assertDropAllUsersFromDatabase(t, ctx, client.Database(t.Name()+"_another_database"), 0)

assertDropAllUsersFromDatabase(t, ctx, db, users, 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, users, 0)
assertDropAllUsersFromDatabase(t, ctx, db, 0)
}

func assertDropAllUsersFromDatabase(t *testing.T, ctx context.Context, db *mongo.Database, users *mongo.Collection, quantity int) {
func assertDropAllUsersFromDatabase(t *testing.T, ctx context.Context, db *mongo.Database, quantity int) {
t.Helper()

var res bson.D
Expand Down
Loading

0 comments on commit 689861e

Please sign in to comment.