mirror of
https://github.com/micromdm/micromdm/
synced 2026-08-10 19:46:07 +08:00
Add account management web pages (#694)
Added internal/frontend/account to implement account management code. Added user registration templates and handlers. Set up a sqlite and postgres database in package main.
This commit is contained in:
@@ -60,10 +60,6 @@ func (u *User) ValidatePassword(plaintext string) error {
|
||||
}
|
||||
|
||||
func (u *User) setPassword(plaintext string) error {
|
||||
if plaintext == "" {
|
||||
return errors.New("password cannot be empty")
|
||||
}
|
||||
|
||||
salt, err := random(32, base64.StdEncoding)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -93,6 +89,24 @@ func random(keySize int, enc *base64.Encoding) ([]byte, error) {
|
||||
// DB
|
||||
|
||||
func create(username, email, password string) (*User, error) {
|
||||
val := Error{invalid: make(map[string]string)}
|
||||
|
||||
if username == "" {
|
||||
val.invalid["username"] = constraints["chk_username_not_empty"]["username"]
|
||||
}
|
||||
|
||||
if email == "" {
|
||||
val.invalid["email"] = constraints["chk_email_not_empty"]["email"]
|
||||
}
|
||||
|
||||
if password == "" {
|
||||
val.invalid["password"] = constraints["chk_password_not_empty"]["password"]
|
||||
}
|
||||
|
||||
if len(val.invalid) > 0 {
|
||||
return nil, val
|
||||
}
|
||||
|
||||
u := &User{
|
||||
ID: id.New(),
|
||||
Username: username,
|
||||
@@ -112,3 +126,38 @@ func create(username, email, password string) (*User, error) {
|
||||
|
||||
return u, nil
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
invalid map[string]string
|
||||
}
|
||||
|
||||
func (err Error) Invalid() map[string]string { return err.invalid }
|
||||
|
||||
func (err Error) Error() string {
|
||||
switch len(err.invalid) {
|
||||
case 0:
|
||||
return "user validation failed"
|
||||
case 1:
|
||||
var key, value string
|
||||
for k, v := range err.invalid {
|
||||
key = k
|
||||
value = v
|
||||
break
|
||||
}
|
||||
return fmt.Sprintf("user validation failed: %s - %s", key, value)
|
||||
default:
|
||||
var key, value string
|
||||
for k, v := range err.invalid {
|
||||
key = k
|
||||
value = v
|
||||
break
|
||||
}
|
||||
return fmt.Sprintf("user validation failed: %s - %s and %d other errors", key, value, len(err.invalid)-1)
|
||||
}
|
||||
}
|
||||
|
||||
var constraints = map[string]map[string]string{
|
||||
"chk_email_not_empty": map[string]string{"email": "You must provide an email address."},
|
||||
"chk_username_not_empty": map[string]string{"username": "You must provide a username."},
|
||||
"chk_password_not_empty": map[string]string{"password": "You must provide a password."},
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgconn"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
)
|
||||
|
||||
@@ -38,7 +39,7 @@ func (d *Postgres) CreateUser(ctx context.Context, username, email, password str
|
||||
u.Salt,
|
||||
u.ConfirmationHash,
|
||||
).Scan(&u.CreatedAt, &u.UpdatedAt); err != nil {
|
||||
return nil, fmt.Errorf("store created user in postgres: %w", err)
|
||||
return nil, fmt.Errorf("store created user in postgres: %w", checkPostgres(err))
|
||||
}
|
||||
|
||||
return u, nil
|
||||
@@ -58,3 +59,19 @@ func (d *Postgres) ConfirmUser(ctx context.Context, confirmation string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkPostgres(err error) error {
|
||||
var dbErr *pgconn.PgError
|
||||
if !errors.As(err, &dbErr) {
|
||||
return err
|
||||
}
|
||||
|
||||
switch dbErr.Code {
|
||||
case "23514":
|
||||
if kv, ok := constraints[dbErr.ConstraintName]; ok {
|
||||
return Error{invalid: kv}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"crawshaw.io/sqlite"
|
||||
"crawshaw.io/sqlite/sqlitex"
|
||||
)
|
||||
|
||||
@@ -42,7 +44,7 @@ func (d *SQLite) CreateUser(ctx context.Context, username, email, password strin
|
||||
stmt.SetBytes("$salt", u.Salt)
|
||||
stmt.SetText("$confirmationHash", *u.ConfirmationHash)
|
||||
if _, err := stmt.Step(); err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("store created user in sqlite: %w", checkSqlite(err))
|
||||
}
|
||||
|
||||
stmt = conn.Prep(`SELECT created_at, updated_at FROM users WHERE id = $id`)
|
||||
@@ -87,3 +89,20 @@ func (d *SQLite) ConfirmUser(ctx context.Context, confirmation string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkSqlite(err error) error {
|
||||
var sErr sqlite.Error
|
||||
if !errors.As(err, &sErr) {
|
||||
return err
|
||||
}
|
||||
|
||||
switch sErr.Code {
|
||||
case sqlite.SQLITE_CONSTRAINT_CHECK:
|
||||
c := strings.Split(sErr.Msg, ": ")
|
||||
if kv, ok := constraints[c[len(c)-1]]; ok {
|
||||
return Error{invalid: kv}
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("user: %w", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user