authentification groundworks

This commit is contained in:
Mitsu
2025-07-11 08:30:53 +02:00
parent 5e9433f4ae
commit e411536477
7 changed files with 122 additions and 4 deletions
+30 -1
View File
@@ -2,6 +2,7 @@ package db
import ( import (
"database/sql" "database/sql"
"flag"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
"github.com/siestaw/laterna/server/cmd/internal/logger" "github.com/siestaw/laterna/server/cmd/internal/logger"
@@ -18,10 +19,18 @@ func ConnectDB() {
} }
logger.DBLogger.Printf("Successfully connected to the database!") logger.DBLogger.Printf("Successfully connected to the database!")
InitDB() InitDB()
resetAdmin := flag.Bool("resetAdminToken", false, "recreate the admin token")
flag.Parse()
if *resetAdmin {
ResetAdmin()
}
} }
func InitDB() { func InitDB() {
_, err := DB.Exec(` // Legacy purposes, DELETE
_, err := DB.Exec(`
CREATE TABLE IF NOT EXISTS lamp_state ( CREATE TABLE IF NOT EXISTS lamp_state (
id TEXT PRIMARY KEY, id TEXT PRIMARY KEY,
color TEXT NOT NULL, color TEXT NOT NULL,
@@ -32,4 +41,24 @@ func InitDB() {
if err != nil { if err != nil {
logger.DBLogger.Fatalf("An error occured while initializing the database: %v", err) logger.DBLogger.Fatalf("An error occured while initializing the database: %v", err)
} }
_, err = DB.Exec(`
CREATE TABLE IF NOT EXISTS controllers (
id INTEGER PRIMARY KEY,
token_hash TEXT NOT NULL,
color TEXT,
updated_at DATETIME
);`)
if err != nil {
logger.DBLogger.Fatalf("An error occured while initializing the database: %v", err)
}
_, err = DB.Exec(`
CREATE TABLE IF NOT EXISTS permissions (
controller_id INTEGER,
target_id INTEGER
);`)
if err != nil {
logger.DBLogger.Fatalf("An error occured while initializing the database: %v", err)
}
} }
+53
View File
@@ -8,6 +8,59 @@ import (
"github.com/siestaw/laterna/server/cmd/utils" "github.com/siestaw/laterna/server/cmd/utils"
) )
func CreateController(target int) (string, int, error) {
token, _ := utils.GenerateToken()
hash, _ := utils.HashToken(token)
var maxID int
stmt := DB.QueryRow("SELECT COALESCE(MAX(id), 0) FROM controllers")
stmt.Scan(&maxID)
id := maxID + 1
_, err := DB.Exec("INSERT INTO controllers VALUES (?, ?, '#FFFFFF', CURRENT_TIMESTAMP)", id, hash)
if err != nil {
logger.DBLogger.Printf("Failed to create a new controller: %v", err)
return "", id, err
}
_, err = DB.Exec("INSERT INTO permissions VALUES (?, ?) ", id, target)
if err != nil {
logger.DBLogger.Printf("Failed to set permissions for %d: %v", id, err)
return token, id, err
}
return token, id, nil
}
func CreateAdmin() string {
token, _ := utils.GenerateToken()
hash, _ := utils.HashToken(token)
_, err := DB.Exec("INSERT INTO controllers (id, token_hash) VALUES (0, ?)", hash)
if err != nil {
logger.DBLogger.Fatalf("Error creating admin user: %s", err)
}
return token
}
func ResetAdmin() {
_, err := DB.Exec("DELETE FROM controllers WHERE id = 0")
if err != nil {
logger.DBLogger.Printf("An error occured while resetting the admin account: %s", err)
}
}
func ControllerExists(id int) bool {
row := DB.QueryRow("SELECT COUNT(1) FROM controllers WHERE id = ?", id)
var count int
err := row.Scan(&count)
if err != nil {
logger.DBLogger.Printf("ControllerExists: %v", err)
}
return count > 0
}
func ViewColor(id int) (*models.LampState, error) { func ViewColor(id int) (*models.LampState, error) {
row := DB.QueryRow("SELECT * FROM lamp_state WHERE id = ?", id) row := DB.QueryRow("SELECT * FROM lamp_state WHERE id = ?", id)
+10 -1
View File
@@ -15,10 +15,19 @@ import (
func StartHTTPServer() { func StartHTTPServer() {
router := http.NewServeMux() router := http.NewServeMux()
router.HandleFunc("GET /api/v1/id/{ID}", getCurrent) router.HandleFunc("GET /api/v1/id/{ID}", getCurrent)
router.HandleFunc("PUT /api/v1/id/{ID}", setCurrent) router.HandleFunc("PUT /api/v1/id/{ID}", setCurrent)
if !db.ControllerExists(0) {
adminToken := db.CreateAdmin()
fmt.Println("IMPORTANT")
fmt.Println("- - - - - - - - - - - - - - - - - - ")
fmt.Println("ADMIN TOKEN:")
fmt.Printf("%s\n", adminToken)
fmt.Println("REGENERATE THE TOKEN BY RUNNING WITH -resetAdminToken")
fmt.Println("- - - - - - - - - - - - - - - - - - ")
}
port := config.AppConfig.HTTP.Port port := config.AppConfig.HTTP.Port
logger.HTTPLogger.Printf("HTTP Server running on :%v", port) logger.HTTPLogger.Printf("HTTP Server running on :%v", port)
http.ListenAndServe(fmt.Sprintf(":%d", port), router) http.ListenAndServe(fmt.Sprintf(":%d", port), router)
+1 -1
View File
@@ -11,6 +11,6 @@ func main() {
config.AppConfig = config.LoadConfig("config.json") config.AppConfig = config.LoadConfig("config.json")
logger.InitLoggers() logger.InitLoggers()
go db.ConnectDB() db.ConnectDB()
http.StartHTTPServer() http.StartHTTPServer()
} }
+22
View File
@@ -1,6 +1,8 @@
package utils package utils
import ( import (
"crypto/rand"
"encoding/hex"
"encoding/json" "encoding/json"
"errors" "errors"
"net/http" "net/http"
@@ -9,6 +11,7 @@ import (
"time" "time"
"github.com/siestaw/laterna/server/cmd/internal/models" "github.com/siestaw/laterna/server/cmd/internal/models"
"golang.org/x/crypto/bcrypt"
) )
func IDtoInt(id string) (int, error) { func IDtoInt(id string) (int, error) {
@@ -21,12 +24,31 @@ func IDtoInt(id string) (int, error) {
} }
return idInt, nil return idInt, nil
} }
func IsValidHexColor(color string) bool { func IsValidHexColor(color string) bool {
pattern := `^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$` pattern := `^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$`
match, _ := regexp.MatchString(pattern, color) match, _ := regexp.MatchString(pattern, color)
return match return match
} }
func GenerateToken() (string, error) {
bytes := make([]byte, 32)
if _, err := rand.Read(bytes); err != nil {
return "", err
}
return hex.EncodeToString(bytes), nil
}
func HashToken(token string) (string, error) {
hash, err := bcrypt.GenerateFromPassword([]byte(token), bcrypt.DefaultCost)
return string(hash), err
}
func ValidateToken(providedToken string, storedHash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(storedHash), []byte(providedToken))
return err == nil
}
func HTTPErrorHandling(w http.ResponseWriter, r *http.Request, status int, message string) { func HTTPErrorHandling(w http.ResponseWriter, r *http.Request, status int, message string) {
timestamp := time.Now().Format("2006-01-02_15-04-05") timestamp := time.Now().Format("2006-01-02_15-04-05")
errResp := models.HTTPError{ errResp := models.HTTPError{
+4 -1
View File
@@ -2,4 +2,7 @@ module github.com/siestaw/laterna/server
go 1.24.4 go 1.24.4
require github.com/mattn/go-sqlite3 v1.14.28 require (
github.com/mattn/go-sqlite3 v1.14.28
golang.org/x/crypto v0.40.0
)
+2
View File
@@ -1,2 +1,4 @@
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=