feat(auth): add auth hash (allows purging sessions & more)
Some checks failed
continuous-integration/drone/push Build encountered an error

This commit is contained in:
2024-01-27 21:02:08 -05:00
parent 9792a6ff19
commit 386b1c46f8
11 changed files with 217 additions and 61 deletions

View File

@@ -13,6 +13,7 @@ import (
log "github.com/sirupsen/logrus"
_ "modernc.org/sqlite"
"reichard.io/antholume/config"
_ "reichard.io/antholume/database/migrations"
)
type DBManager struct {
@@ -36,13 +37,15 @@ func NewMgr(c *config.Config) *DBManager {
cfg: c,
}
dbm.init()
if err := dbm.init(); err != nil {
log.Panic("Unable to init DB")
}
return dbm
}
// Init manager
func (dbm *DBManager) init() {
func (dbm *DBManager) init() error {
if dbm.cfg.DBType == "sqlite" || dbm.cfg.DBType == "memory" {
var dbLocation string = ":memory:"
if dbm.cfg.DBType == "sqlite" {
@@ -52,7 +55,8 @@ func (dbm *DBManager) init() {
var err error
dbm.DB, err = sql.Open("sqlite", dbLocation)
if err != nil {
log.Fatalf("Unable to open DB: %v", err)
log.Errorf("Unable to open DB: %v", err)
return err
}
// Single Open Connection
@@ -60,22 +64,26 @@ func (dbm *DBManager) init() {
// Execute DDL
if _, err := dbm.DB.Exec(ddl, nil); err != nil {
log.Fatalf("Error executing schema: %v", err)
log.Errorf("Error executing schema: %v", err)
return err
}
// Perform Migrations
err = dbm.performMigrations()
if err != nil && err != goose.ErrNoMigrationFiles {
log.Fatalf("Error running DB migrations: %v", err)
log.Errorf("Error running DB migrations: %v", err)
return err
}
// Cache Tables
dbm.CacheTempTables()
} else {
log.Fatal("Unsupported Database")
return fmt.Errorf("unsupported database")
}
dbm.Queries = New(dbm.DB)
return nil
}
// Reload manager (close DB & reinit)
@@ -87,7 +95,9 @@ func (dbm *DBManager) Reload() error {
}
// Reinit DB
dbm.init()
if err := dbm.init(); err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,63 @@
package migrations
import (
"context"
"database/sql"
"fmt"
"github.com/pressly/goose/v3"
"reichard.io/antholume/utils"
)
func init() {
goose.AddMigrationContext(upUserAuthHash, downUserAuthHash)
}
func upUserAuthHash(ctx context.Context, tx *sql.Tx) error {
// Create now column
_, err := tx.Exec("ALTER TABLE users ADD COLUMN auth_hash TEXT")
if err != nil {
return err
}
// Get current users
rows, err := tx.Query("SELECT id FROM users")
if err != nil {
return err
}
// Query existing users
var users []string
for rows.Next() {
var user string
if err := rows.Scan(&user); err != nil {
return err
}
users = append(users, user)
}
// Create auth hash per user
for _, user := range users {
rawAuthHash, err := utils.GenerateToken(64)
if err != nil {
return err
}
authHash := fmt.Sprintf("%x", rawAuthHash)
_, err = tx.Exec("UPDATE users SET auth_hash = ? WHERE id = ?", authHash, user)
if err != nil {
return err
}
}
return nil
}
func downUserAuthHash(ctx context.Context, tx *sql.Tx) error {
// Drop column
_, err := tx.Exec("ALTER users DROP COLUMN auth_hash")
if err != nil {
return err
}
return nil
}

View File

@@ -1,5 +1,9 @@
# DB Migrations
```bash
# SQL migration
goose create migration_name sql
# Go migration
goose create migration_name
```

View File

@@ -96,6 +96,7 @@ type Metadatum struct {
type User struct {
ID string `json:"id"`
Pass *string `json:"-"`
AuthHash string `json:"auth_hash"`
Admin bool `json:"-"`
TimeOffset *string `json:"time_offset"`
CreatedAt string `json:"created_at"`

View File

@@ -26,8 +26,8 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: CreateUser :execrows
INSERT INTO users (id, pass)
VALUES (?, ?)
INSERT INTO users (id, pass, auth_hash)
VALUES (?, ?, ?)
ON CONFLICT DO NOTHING;
-- name: DeleteDocument :execrows
@@ -368,6 +368,7 @@ RETURNING *;
UPDATE users
SET
pass = COALESCE($password, pass),
auth_hash = COALESCE($auth_hash, auth_hash),
time_offset = COALESCE($time_offset, time_offset)
WHERE id = $user_id
RETURNING *;

View File

@@ -113,18 +113,19 @@ func (q *Queries) AddMetadata(ctx context.Context, arg AddMetadataParams) (Metad
}
const createUser = `-- name: CreateUser :execrows
INSERT INTO users (id, pass)
VALUES (?, ?)
INSERT INTO users (id, pass, auth_hash)
VALUES (?, ?, ?)
ON CONFLICT DO NOTHING
`
type CreateUserParams struct {
ID string `json:"id"`
Pass *string `json:"-"`
ID string `json:"id"`
Pass *string `json:"-"`
AuthHash string `json:"auth_hash"`
}
func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (int64, error) {
result, err := q.db.ExecContext(ctx, createUser, arg.ID, arg.Pass)
result, err := q.db.ExecContext(ctx, createUser, arg.ID, arg.Pass, arg.AuthHash)
if err != nil {
return 0, err
}
@@ -954,7 +955,7 @@ func (q *Queries) GetProgress(ctx context.Context, arg GetProgressParams) ([]Get
}
const getUser = `-- name: GetUser :one
SELECT id, pass, admin, time_offset, created_at FROM users
SELECT id, pass, auth_hash, admin, time_offset, created_at FROM users
WHERE id = ?1 LIMIT 1
`
@@ -964,6 +965,7 @@ func (q *Queries) GetUser(ctx context.Context, userID string) (User, error) {
err := row.Scan(
&i.ID,
&i.Pass,
&i.AuthHash,
&i.Admin,
&i.TimeOffset,
&i.CreatedAt,
@@ -1092,7 +1094,7 @@ func (q *Queries) GetUserStreaks(ctx context.Context, userID string) ([]UserStre
}
const getUsers = `-- name: GetUsers :many
SELECT id, pass, admin, time_offset, created_at FROM users
SELECT id, pass, auth_hash, admin, time_offset, created_at FROM users
`
func (q *Queries) GetUsers(ctx context.Context) ([]User, error) {
@@ -1107,6 +1109,7 @@ func (q *Queries) GetUsers(ctx context.Context) ([]User, error) {
if err := rows.Scan(
&i.ID,
&i.Pass,
&i.AuthHash,
&i.Admin,
&i.TimeOffset,
&i.CreatedAt,
@@ -1214,23 +1217,31 @@ const updateUser = `-- name: UpdateUser :one
UPDATE users
SET
pass = COALESCE(?1, pass),
time_offset = COALESCE(?2, time_offset)
WHERE id = ?3
RETURNING id, pass, admin, time_offset, created_at
auth_hash = COALESCE(?2, auth_hash),
time_offset = COALESCE(?3, time_offset)
WHERE id = ?4
RETURNING id, pass, auth_hash, admin, time_offset, created_at
`
type UpdateUserParams struct {
Password *string `json:"-"`
AuthHash string `json:"auth_hash"`
TimeOffset *string `json:"time_offset"`
UserID string `json:"user_id"`
}
func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) (User, error) {
row := q.db.QueryRowContext(ctx, updateUser, arg.Password, arg.TimeOffset, arg.UserID)
row := q.db.QueryRowContext(ctx, updateUser,
arg.Password,
arg.AuthHash,
arg.TimeOffset,
arg.UserID,
)
var i User
err := row.Scan(
&i.ID,
&i.Pass,
&i.AuthHash,
&i.Admin,
&i.TimeOffset,
&i.CreatedAt,

View File

@@ -10,6 +10,7 @@ CREATE TABLE IF NOT EXISTS users (
id TEXT NOT NULL PRIMARY KEY,
pass TEXT NOT NULL,
auth_hash TEXT NOT NULL,
admin BOOLEAN NOT NULL DEFAULT 0 CHECK (admin IN (0, 1)),
time_offset TEXT NOT NULL DEFAULT '0 hours',