/*
* Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
*
* 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
}