Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforce new authentication #4075

Merged
merged 52 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
fbaf68a
get authentication mechanism from db
chilagrow Feb 14, 2024
f5c6049
add test for conneting without plain username password
chilagrow Feb 14, 2024
88b6316
add authentication to sasl start
chilagrow Feb 15, 2024
bcfd350
add authentication to all handlers except command query
chilagrow Feb 15, 2024
3699034
move files
chilagrow Feb 15, 2024
8e65773
Merge branch 'main' into enforce-new-auth
chilagrow Feb 15, 2024
70f0183
until first user is created new authentication always succeeds
chilagrow Feb 15, 2024
470ca14
is master does not need authentication
chilagrow Feb 15, 2024
40be66d
handle database without any pool yet
chilagrow Feb 16, 2024
2e98e84
merge conflict
chilagrow Feb 16, 2024
fd048c6
do not authenticate on some handlers
chilagrow Feb 16, 2024
4a6b841
authentication for sha256 is done by conversation step, so handler ch…
chilagrow Feb 16, 2024
90de3fa
Plain credential hashes password
chilagrow Feb 16, 2024
b4b4c57
add test for scram sha256 user for empty database
chilagrow Feb 16, 2024
5dc7ae5
fix create update and drop user tests
chilagrow Feb 16, 2024
c3583d1
update error and panic
chilagrow Feb 16, 2024
8d72103
create pool upon registry creation
chilagrow Feb 19, 2024
f694f4e
do not authenticate on handler if bypass backend auth is not set
chilagrow Feb 19, 2024
b08beea
user tests use credentials for test runner
chilagrow Feb 19, 2024
4286628
lint
chilagrow Feb 19, 2024
287b728
Revert "user tests use credentials for test runner"
chilagrow Feb 19, 2024
950e567
authentication checks user instead of db.user
chilagrow Feb 19, 2024
a9be0e2
backend fallback
chilagrow Feb 19, 2024
a4b5699
cleanup
chilagrow Feb 19, 2024
daba28a
missing import
chilagrow Feb 19, 2024
5a8014e
revert
chilagrow Feb 19, 2024
eda4ed1
tidy up
chilagrow Feb 19, 2024
add453e
add test for plain mechanism backend user
chilagrow Feb 19, 2024
0d786cb
simplify test user
chilagrow Feb 19, 2024
5bdcfc4
update comments
chilagrow Feb 19, 2024
f74532c
remove unused var
chilagrow Feb 19, 2024
5c4b117
Merge branch 'main' into enforce-new-auth
chilagrow Feb 19, 2024
d407f5f
sqlite does not have backend auth
chilagrow Feb 19, 2024
0ab1f56
use opt out way
chilagrow Feb 20, 2024
306c10b
add todo links
chilagrow Feb 20, 2024
55a52b9
merge
chilagrow Feb 20, 2024
0128186
rename reorder
chilagrow Feb 20, 2024
a0df39f
add todo
chilagrow Feb 20, 2024
24c393c
address feedback
chilagrow Feb 21, 2024
e3edaae
Merge branch 'main' into enforce-new-auth
chilagrow Feb 21, 2024
73ec82a
create user during the setup
chilagrow Feb 21, 2024
ae2d02b
Merge branch 'main' into enforce-new-auth
chilagrow Feb 22, 2024
07c749a
fix test
chilagrow Feb 22, 2024
3f95100
update comment add explaination add more mechanisms
chilagrow Feb 22, 2024
c4c1224
PLAIN and SHA handles authenticated users the same way
chilagrow Feb 22, 2024
53d76ae
Merge branch 'main' into enforce-new-auth
chilagrow Feb 26, 2024
d5d2499
do not use pwd as abbrev
chilagrow Feb 26, 2024
7bcc892
update comment
chilagrow Feb 26, 2024
dbd27a8
do not allow SCRAM if new authentication is not enabled
chilagrow Feb 27, 2024
e45cc60
Merge branch 'main' into enforce-new-auth
AlekSi Feb 27, 2024
339b52d
Merge branch 'main' into enforce-new-auth
AlekSi Feb 28, 2024
fb4dd9e
Merge branch 'main' into enforce-new-auth
AlekSi Feb 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
move files
  • Loading branch information
chilagrow committed Feb 15, 2024
commit 3699034b3bad98602ce615109edfe9755852164f
125 changes: 125 additions & 0 deletions internal/handler/authentication.go
Original file line number Diff line number Diff line change
@@ -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

Check warning on line 39 in internal/handler/authentication.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/authentication.go#L39

Added line #L39 was not covered by tests
}

adminDB, err := h.b.Database("admin")
if err != nil {
return lazyerrors.Error(err)

Check warning on line 44 in internal/handler/authentication.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/authentication.go#L44

Added line #L44 was not covered by tests
}

usersCol, err := adminDB.Collection("system.users")
if err != nil {
return lazyerrors.Error(err)

Check warning on line 49 in internal/handler/authentication.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/authentication.go#L49

Added line #L49 was not covered by tests
}

document, err := msg.Document()
if err != nil {
return lazyerrors.Error(err)

Check warning on line 54 in internal/handler/authentication.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/authentication.go#L54

Added line #L54 was not covered by tests
}

var dbName string

if dbName, err = common.GetRequiredParam[string](document, "$db"); err != nil {
return err

Check warning on line 60 in internal/handler/authentication.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/authentication.go#L60

Added line #L60 was not covered by tests
}

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)

Check warning on line 70 in internal/handler/authentication.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/authentication.go#L70

Added line #L70 was not covered by tests
}

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)

Check warning on line 88 in internal/handler/authentication.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/authentication.go#L87-L88

Added lines #L87 - L88 were not covered by tests
}

hasUser = true

Check warning on line 91 in internal/handler/authentication.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/authentication.go#L91

Added line #L91 was not covered by tests

var matches bool

Check warning on line 93 in internal/handler/authentication.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/authentication.go#L93

Added line #L93 was not covered by tests

if matches, err = common.FilterDocument(v, filter); err != nil {
return lazyerrors.Error(err)

Check warning on line 96 in internal/handler/authentication.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/authentication.go#L95-L96

Added lines #L95 - L96 were not covered by tests
}

if matches {
storedUser = v
break

Check warning on line 101 in internal/handler/authentication.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/authentication.go#L99-L101

Added lines #L99 - L101 were not covered by tests
}
}

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",
)

Check warning on line 116 in internal/handler/authentication.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/authentication.go#L110-L116

Added lines #L110 - L116 were not covered by tests
}

err = password.PlainVerify(pwd, credentials)
if err != nil {
return lazyerrors.Error(err)

Check warning on line 121 in internal/handler/authentication.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/authentication.go#L119-L121

Added lines #L119 - L121 were not covered by tests
}

return nil

Check warning on line 124 in internal/handler/authentication.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/authentication.go#L124

Added line #L124 was not covered by tests
}
99 changes: 2 additions & 97 deletions internal/handler/msg_saslstart.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,14 @@
"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"
)

Expand Down Expand Up @@ -65,7 +62,7 @@
}

if err = h.authenticate(ctx, msg); err != nil {
return nil, err

Check warning on line 65 in internal/handler/msg_saslstart.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/msg_saslstart.go#L65

Added line #L65 was not covered by tests
}
default:
msg := fmt.Sprintf("Unsupported authentication mechanism %q.\n", mechanism) +
Expand All @@ -74,9 +71,11 @@
}

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)

Check warning on line 78 in internal/handler/msg_saslstart.go

View check run for this annotation

Codecov / codecov/patch

internal/handler/msg_saslstart.go#L78

Added line #L78 was not covered by tests
}

var emptyPayload types.Binary
Expand Down Expand Up @@ -139,97 +138,3 @@

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
}
Loading