diff --git a/src/migration/migrationTemplate.txt b/src/migration/migrationTemplate.txt index 53b4e35..7656e2f 100644 --- a/src/migration/migrationTemplate.txt +++ b/src/migration/migrationTemplate.txt @@ -5,6 +5,7 @@ import ( "time" "git.handmade.network/hmn/hmn/src/migration/types" + "git.handmade.network/hmn/hmn/src/oops" "github.com/jackc/pgx/v4" ) diff --git a/src/migration/migrations/2021-04-27T034327Z_UsernameUniqueIndex.go b/src/migration/migrations/2021-04-27T034327Z_UsernameUniqueIndex.go new file mode 100644 index 0000000..e54b8a6 --- /dev/null +++ b/src/migration/migrations/2021-04-27T034327Z_UsernameUniqueIndex.go @@ -0,0 +1,40 @@ +package migrations + +import ( + "context" + "time" + + "git.handmade.network/hmn/hmn/src/migration/types" + "git.handmade.network/hmn/hmn/src/oops" + "github.com/jackc/pgx/v4" +) + +func init() { + registerMigration(UsernameUniqueIndex{}) +} + +type UsernameUniqueIndex struct{} + +func (m UsernameUniqueIndex) Version() types.MigrationVersion { + return types.MigrationVersion(time.Date(2021, 4, 27, 3, 43, 27, 0, time.UTC)) +} + +func (m UsernameUniqueIndex) Name() string { + return "UsernameUniqueIndex" +} + +func (m UsernameUniqueIndex) Description() string { + return "Prevent the creation of similar usernames with different character cases" +} + +func (m UsernameUniqueIndex) Up(ctx context.Context, tx pgx.Tx) error { + _, err := tx.Exec(ctx, `CREATE UNIQUE INDEX auth_user_unique_username_case_insensitive ON auth_user (LOWER(username))`) + if err != nil { + return oops.New(err, "failed to add unique index") + } + return nil +} + +func (m UsernameUniqueIndex) Down(ctx context.Context, tx pgx.Tx) error { + panic("Implement me") +} diff --git a/src/website/login.go b/src/website/login.go index f0a7b18..bc7e7b2 100644 --- a/src/website/login.go +++ b/src/website/login.go @@ -30,7 +30,7 @@ func Login(c *RequestContext) ResponseData { redirect = "/" } - userRow, err := db.QueryOne(c.Context(), c.Conn, models.User{}, "SELECT $columns FROM auth_user WHERE username = $1", username) + userRow, err := db.QueryOne(c.Context(), c.Conn, models.User{}, "SELECT $columns FROM auth_user WHERE LOWER(username) = LOWER($1)", username) if err != nil { if errors.Is(err, db.ErrNoMatchingRows) { return ResponseData{ @@ -57,7 +57,7 @@ func Login(c *RequestContext) ResponseData { if hashed.IsOutdated() { newHashed, err := auth.HashPassword(password) if err == nil { - err := auth.UpdatePassword(c.Context(), c.Conn, username, newHashed) + err := auth.UpdatePassword(c.Context(), c.Conn, user.Username, newHashed) if err != nil { c.Logger.Error().Err(err).Msg("failed to update user's password") } @@ -67,7 +67,7 @@ func Login(c *RequestContext) ResponseData { // If errors happen here, we can still continue with logging them in } - session, err := auth.CreateSession(c.Context(), c.Conn, username) + session, err := auth.CreateSession(c.Context(), c.Conn, user.Username) if err != nil { return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to create session")) }