Fix pg user already exists (#1076)

* Return newly created error if user already exists (#1002)

Signed-off-by: Till Faelligen <tfaelligen@gmail.com>

* Rename variable

* Remove check for account and use returned error

* Return ErrUserExists

Signed-off-by: Till Faelligen <tfaelligen@gmail.com>

* State that CreateAccount will return err ErrUserExists if the user exists

Signed-off-by: Till Faelligen <tfaelligen@gmail.com>

* Also check sqlite for constraint error

* Revert "Also check sqlite for constraint error"

This reverts commit 7d310514

* Check for sqlite3 constraint error

* Add documentation to CreateAccount

* Move ErrUserExists to accounts package

* Revert "Move ErrUserExists to accounts package"
Import Cycle..

This reverts commit be3d4cda

Co-authored-by: Kegsay <kegan@matrix.org>
This commit is contained in:
S7evinK 2020-06-01 19:34:29 +02:00 committed by GitHub
parent cfc137652e
commit 895c8f03c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 26 additions and 20 deletions

View File

@ -16,6 +16,7 @@ package appservice
import ( import (
"context" "context"
"errors"
"net/http" "net/http"
"sync" "sync"
"time" "time"
@ -29,6 +30,7 @@ import (
"github.com/matrix-org/dendrite/appservice/workers" "github.com/matrix-org/dendrite/appservice/workers"
"github.com/matrix-org/dendrite/clientapi/auth/storage/accounts" "github.com/matrix-org/dendrite/clientapi/auth/storage/accounts"
"github.com/matrix-org/dendrite/clientapi/auth/storage/devices" "github.com/matrix-org/dendrite/clientapi/auth/storage/devices"
"github.com/matrix-org/dendrite/internal"
"github.com/matrix-org/dendrite/internal/basecomponent" "github.com/matrix-org/dendrite/internal/basecomponent"
"github.com/matrix-org/dendrite/internal/config" "github.com/matrix-org/dendrite/internal/config"
"github.com/matrix-org/dendrite/internal/transactions" "github.com/matrix-org/dendrite/internal/transactions"
@ -117,12 +119,12 @@ func generateAppServiceAccount(
ctx := context.Background() ctx := context.Background()
// Create an account for the application service // Create an account for the application service
acc, err := accountsDB.CreateAccount(ctx, as.SenderLocalpart, "", as.ID) _, err := accountsDB.CreateAccount(ctx, as.SenderLocalpart, "", as.ID)
if err != nil { if err != nil {
if errors.Is(err, internal.ErrUserExists) { // This account already exists
return nil
}
return err return err
} else if acc == nil {
// This account already exists
return nil
} }
// Create a dummy device with a dummy token for the application service // Create a dummy device with a dummy token for the application service

View File

@ -29,6 +29,9 @@ type Database interface {
GetProfileByLocalpart(ctx context.Context, localpart string) (*authtypes.Profile, error) GetProfileByLocalpart(ctx context.Context, localpart string) (*authtypes.Profile, error)
SetAvatarURL(ctx context.Context, localpart string, avatarURL string) error SetAvatarURL(ctx context.Context, localpart string, avatarURL string) error
SetDisplayName(ctx context.Context, localpart string, displayName string) error SetDisplayName(ctx context.Context, localpart string, displayName string) error
// CreateAccount makes a new account with the given login name and password, and creates an empty profile
// for this account. If no password is supplied, the account will be a passwordless account. If the
// account already exists, it will return nil, ErrUserExists.
CreateAccount(ctx context.Context, localpart, plaintextPassword, appserviceID string) (*authtypes.Account, error) CreateAccount(ctx context.Context, localpart, plaintextPassword, appserviceID string) (*authtypes.Account, error)
CreateGuestAccount(ctx context.Context) (*authtypes.Account, error) CreateGuestAccount(ctx context.Context) (*authtypes.Account, error)
UpdateMemberships(ctx context.Context, eventsToAdd []gomatrixserverlib.Event, idsToRemove []string) error UpdateMemberships(ctx context.Context, eventsToAdd []gomatrixserverlib.Event, idsToRemove []string) error

View File

@ -138,7 +138,7 @@ func (d *Database) CreateGuestAccount(ctx context.Context) (acc *authtypes.Accou
// CreateAccount makes a new account with the given login name and password, and creates an empty profile // CreateAccount makes a new account with the given login name and password, and creates an empty profile
// for this account. If no password is supplied, the account will be a passwordless account. If the // for this account. If no password is supplied, the account will be a passwordless account. If the
// account already exists, it will return nil, nil. // account already exists, it will return nil, ErrUserExists.
func (d *Database) CreateAccount( func (d *Database) CreateAccount(
ctx context.Context, localpart, plaintextPassword, appserviceID string, ctx context.Context, localpart, plaintextPassword, appserviceID string,
) (acc *authtypes.Account, err error) { ) (acc *authtypes.Account, err error) {
@ -164,7 +164,7 @@ func (d *Database) createAccount(
} }
if err := d.profiles.insertProfile(ctx, txn, localpart); err != nil { if err := d.profiles.insertProfile(ctx, txn, localpart); err != nil {
if internal.IsUniqueConstraintViolationErr(err) { if internal.IsUniqueConstraintViolationErr(err) {
return nil, nil return nil, internal.ErrUserExists
} }
return nil, err return nil, err
} }

View File

@ -27,8 +27,8 @@ import (
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
// Import the postgres database driver. // Import the sqlite3 database driver.
_ "github.com/mattn/go-sqlite3" "github.com/mattn/go-sqlite3"
) )
// Database represents an account database // Database represents an account database
@ -148,7 +148,7 @@ func (d *Database) CreateGuestAccount(ctx context.Context) (acc *authtypes.Accou
// CreateAccount makes a new account with the given login name and password, and creates an empty profile // CreateAccount makes a new account with the given login name and password, and creates an empty profile
// for this account. If no password is supplied, the account will be a passwordless account. If the // for this account. If no password is supplied, the account will be a passwordless account. If the
// account already exists, it will return nil, nil. // account already exists, it will return nil, ErrUserExists.
func (d *Database) CreateAccount( func (d *Database) CreateAccount(
ctx context.Context, localpart, plaintextPassword, appserviceID string, ctx context.Context, localpart, plaintextPassword, appserviceID string,
) (acc *authtypes.Account, err error) { ) (acc *authtypes.Account, err error) {
@ -172,8 +172,8 @@ func (d *Database) createAccount(
} }
} }
if err := d.profiles.insertProfile(ctx, txn, localpart); err != nil { if err := d.profiles.insertProfile(ctx, txn, localpart); err != nil {
if internal.IsUniqueConstraintViolationErr(err) { if errors.Is(err, sqlite3.ErrConstraint) {
return nil, nil return nil, internal.ErrUserExists
} }
return nil, err return nil, err
} }

View File

@ -830,15 +830,16 @@ func completeRegistration(
acc, err := accountDB.CreateAccount(ctx, username, password, appserviceID) acc, err := accountDB.CreateAccount(ctx, username, password, appserviceID)
if err != nil { if err != nil {
if errors.Is(err, internal.ErrUserExists) { // user already exists
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.UserInUse("Desired user ID is already taken."),
}
}
return util.JSONResponse{ return util.JSONResponse{
Code: http.StatusInternalServerError, Code: http.StatusInternalServerError,
JSON: jsonerror.Unknown("failed to create account: " + err.Error()), JSON: jsonerror.Unknown("failed to create account: " + err.Error()),
} }
} else if acc == nil {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.UserInUse("Desired user ID is already taken."),
}
} }
// Increment prometheus counter for created users // Increment prometheus counter for created users

View File

@ -69,13 +69,10 @@ func main() {
os.Exit(1) os.Exit(1)
} }
account, err := accountDB.CreateAccount(context.Background(), *username, *password, "") _, err = accountDB.CreateAccount(context.Background(), *username, *password, "")
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Println(err.Error())
os.Exit(1) os.Exit(1)
} else if account == nil {
fmt.Println("Username already exists")
os.Exit(1)
} }
deviceDB, err := devices.NewDatabase(*database, nil, serverName) deviceDB, err := devices.NewDatabase(*database, nil, serverName)

View File

@ -26,6 +26,9 @@ import (
"go.uber.org/atomic" "go.uber.org/atomic"
) )
// ErrUserExists is returned if a username already exists in the database.
var ErrUserExists = errors.New("Username already exists")
// A Transaction is something that can be committed or rolledback. // A Transaction is something that can be committed or rolledback.
type Transaction interface { type Transaction interface {
// Commit the transaction // Commit the transaction