/* * Copyright (C) 2023 Jonni Liljamo * * This file is licensed under AGPL-3.0-or-later, see NOTICE and LICENSE for * more information. */ package db import ( "database/sql" "errors" "github.com/matthewhartstonge/argon2" "github.com/oklog/ulid/v2" ) func CreateUser(email string, pwd string) error { argon := argon2.DefaultConfig() encoded, err := argon.HashEncoded([]byte(pwd)) if err != nil { return err } ulid := ulid.Make().String() _, err = DBConn.Exec(`INSERT INTO users(id, email, pwd) VALUES ($1, $2, $3)`, ulid, email, string(encoded)) if err != nil { return err } return nil } func CreateAdmin(email string, pwd string) error { argon := argon2.DefaultConfig() encoded, err := argon.HashEncoded([]byte(pwd)) if err != nil { return err } ulid := ulid.Make().String() _, err = DBConn.Exec(`INSERT INTO users(id, email, pwd, is_admin) VALUES ($1, $2, $3, TRUE)`, ulid, email, string(encoded)) if err != nil { return err } return nil } type User struct { Id string Email string IsAdmin bool } func FetchUserWithCreds(email string, pwd string) (*User, error) { user := User{ Email: email } var encodedPwd string err := DBConn.QueryRow(`SELECT id, pwd FROM users WHERE email = $1`, email).Scan(&user.Id, &encodedPwd) if err == sql.ErrNoRows { return nil, errors.New("User not found") } if err != nil { return nil, errors.New("User query failed") } ok, err := argon2.VerifyEncoded([]byte(pwd), []byte(encodedPwd)) if err != nil { return nil, errors.New("User not found") } if !ok { return nil, errors.New("User not found") } return &user, nil } func FetchUserWithId(id string) (*User, error) { user := User{ Id: id } err := DBConn.QueryRow(`SELECT email, is_admin FROM users WHERE id = $1`, id).Scan(&user.Email, &user.IsAdmin) if err == sql.ErrNoRows { return nil, errors.New("User not found") } if err != nil { return nil, errors.New("User query failed") } return &user, nil } func FetchAllUsers() ([]User, error) { rows, err := DBConn.Query(`SELECT id, email, is_admin FROM users`) if err != nil { return nil, err } defer rows.Close() var users []User for rows.Next() { var user User err = rows.Scan(&user.Id, &user.Email, &user.IsAdmin) if err != nil { return nil, err } users = append(users, user) } err = rows.Err() if err != nil { return nil, err } return users, nil } func DeleteUser(id string) error { err := DeleteDomainsForUser(id) if err != nil { return err } _, err = DBConn.Exec(`DELETE FROM users WHERE id = $1`, id) if err != nil { return err } return nil } func UpdateUserEmail(id string, email string) error { _, err := DBConn.Exec(`UPDATE users SET email = $1 WHERE id = $2`, email, id) if err != nil { return err } return nil } func UpdateUserPassword(id string, pwd string) error { argon := argon2.DefaultConfig() encoded, err := argon.HashEncoded([]byte(pwd)) if err != nil { return err } _, err = DBConn.Exec(`UPDATE users SET pwd = $1 WHERE id = $2`, string(encoded), id) if err != nil { return err } return nil } func VerifyUserPassword(id string, pwd string) bool { // FIXME: Currently doesn't return any errors, and just return false in error cases // I mean... Shouldn't really error, but who knows var encodedPwd string err := DBConn.QueryRow(`SELECT pwd FROM users WHERE id = $1`, id).Scan(&encodedPwd) if err == sql.ErrNoRows { return false } if err != nil { return false } ok, err := argon2.VerifyEncoded([]byte(pwd), []byte(encodedPwd)) if err != nil { return false } if !ok { return false } return true }