summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbt <bt@rctt.net>2026-05-24 18:52:58 +0200
committerbt <bt@rctt.net>2026-05-24 18:52:58 +0200
commit51c603afe0373f45f8d389c99cac9d3aec959b75 (patch)
tree77de75fbfec26c332bcaf79424c66cd0e9615be8
parentcd925e3c7716216902f3a0ef4c333f32e5f1bdb4 (diff)
downloadsolec-51c603afe0373f45f8d389c99cac9d3aec959b75.tar.gz
solec-51c603afe0373f45f8d389c99cac9d3aec959b75.zip
[common] Users and permissions database
-rw-r--r--cmd/daemon/main.go75
-rw-r--r--core/internal.go24
-rw-r--r--go.mod1
-rw-r--r--go.sum2
-rw-r--r--server/message.go2
-rw-r--r--server/storage.go11
-rw-r--r--server/user.go2
-rw-r--r--storage/storage.go62
8 files changed, 168 insertions, 11 deletions
diff --git a/cmd/daemon/main.go b/cmd/daemon/main.go
index aebc085..a118df3 100644
--- a/cmd/daemon/main.go
+++ b/cmd/daemon/main.go
@@ -8,9 +8,11 @@ import (
"maps"
"os"
"slices"
+ "strconv"
"strings"
"time"
+ "go.rctt.net/solec/core"
"go.rctt.net/solec/server"
"go.rctt.net/solec/storage"
@@ -21,6 +23,9 @@ var (
cmds = map[string]func(args []string){
"broadcast": sendBroadcast,
"read": readHistory,
+ "user_set": setUser,
+ "user_del": delUser,
+ "perm_set": setPerm,
"exit": exit,
}
serv *server.Server
@@ -107,17 +112,83 @@ func sendBroadcast(args []string) {
func readHistory(args []string) {
if len(args) != 1 {
fmt.Println("usage: read <channel>")
+ return
}
- hist, err := serv.Storage.Read(args[0], time.Time{}, 999999, 0)
+ hist, err := serv.Storage.GetHistory(args[0], time.Time{}, 999999, 0)
if err != nil {
- fmt.Println(err)
+ printErr(err)
return
}
fmt.Println(hist)
}
+func setUser(args []string) {
+ if len(args) != 2 {
+ fmt.Println("usage: user_set <name> <password>")
+ return
+ }
+
+ pass, err := core.HashPass(args[1])
+ if err != nil {
+ printErr(err)
+ return
+ }
+
+ user := core.UserData{
+ Name: args[0],
+ Pass: pass,
+ }
+
+ if err := serv.Storage.SetUser(user); err != nil {
+ printErr(err)
+ }
+}
+
+func delUser(args []string) {
+ if len(args) != 1 {
+ fmt.Println("usage: user_del <name>")
+ return
+ }
+
+ if err := serv.Storage.DelUser(args[0]); err != nil {
+ printErr(err)
+ }
+}
+
+func setPerm(args []string) {
+ if len(args) != 4 {
+ fmt.Println("usage: perm_set <user> <channel> <read:bool> <write:bool>")
+ return
+ }
+
+ perm := core.PermissionData{
+ User: args[0],
+ Channel: args[1],
+ }
+
+ var err error
+
+ if perm.Read, err = strconv.ParseBool(args[2]); err != nil {
+ printErr(err)
+ return
+ }
+
+ if perm.Write, err = strconv.ParseBool(args[3]); err != nil {
+ printErr(err)
+ return
+ }
+
+ if err := serv.Storage.SetPermission(perm); err != nil {
+ printErr(err)
+ }
+}
+
func exit(args []string) {
os.Exit(0)
}
+
+func printErr(err error) {
+ fmt.Println("error:", err)
+}
diff --git a/core/internal.go b/core/internal.go
new file mode 100644
index 0000000..e00c0f2
--- /dev/null
+++ b/core/internal.go
@@ -0,0 +1,24 @@
+package core
+
+import (
+ "encoding/base64"
+
+ "golang.org/x/crypto/bcrypt"
+)
+
+type UserData struct {
+ Name string
+ Pass string
+}
+
+type PermissionData struct {
+ User string
+ Channel string
+ Read bool
+ Write bool
+}
+
+func HashPass(pass string) (string, error) {
+ hash, err := bcrypt.GenerateFromPassword([]byte(pass), 12)
+ return base64.StdEncoding.EncodeToString(hash), err
+}
diff --git a/go.mod b/go.mod
index 6e83f85..ca1b0f6 100644
--- a/go.mod
+++ b/go.mod
@@ -5,4 +5,5 @@ go 1.26.1
require (
github.com/google/go-cmp v0.7.0
github.com/mattn/go-sqlite3 v1.14.44
+ golang.org/x/crypto v0.52.0
)
diff --git a/go.sum b/go.sum
index 2c38b0c..112c08e 100644
--- a/go.sum
+++ b/go.sum
@@ -2,3 +2,5 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/mattn/go-sqlite3 v1.14.44 h1:3VSe+xafpbzsLbdr2AWlAZk9yRHiBhTBakioXaCKTF8=
github.com/mattn/go-sqlite3 v1.14.44/go.mod h1:pjEuOr8IwzLJP2MfGeTb0A35jauH+C2kbHKBr7yXKVQ=
+golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
+golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
diff --git a/server/message.go b/server/message.go
index e814d9d..19bfadf 100644
--- a/server/message.go
+++ b/server/message.go
@@ -42,7 +42,7 @@ func (s *Server) handleMessage(sender net.Conn, connType core.ConnType, msg core
return err
}
- if err := s.Storage.Write(msg); err != nil {
+ if err := s.Storage.AddMessage(msg); err != nil {
log.Println("cannot write to database", err)
}
diff --git a/server/storage.go b/server/storage.go
index 3473898..037c40f 100644
--- a/server/storage.go
+++ b/server/storage.go
@@ -7,6 +7,13 @@ import (
)
type Storage interface {
- Write(msg core.Message) (err error)
- Read(channel string, since time.Time, num, offset int) (history []core.Message, err error)
+ AddMessage(msg core.Message) (err error)
+ GetHistory(channel string, since time.Time, num, offset int) (history []core.Message, err error)
+
+ SetUser(user core.UserData) error
+ DelUser(name string) error
+ GetUserPass(name string) (string, error)
+
+ SetPermission(data core.PermissionData) error
+ GetPermission(user, channel string) (core.PermissionData, error)
}
diff --git a/server/user.go b/server/user.go
index af3e1a6..5b8049f 100644
--- a/server/user.go
+++ b/server/user.go
@@ -175,7 +175,7 @@ func (s *Server) handleHistory(user *User, conn net.Conn, hist core.History) err
return user.Send(conn, core.Error{core.ErrorNotFound})
}
- messages, err := s.Storage.Read(hist.Channel, hist.Since, int(hist.Count), int(hist.Offset))
+ messages, err := s.Storage.GetHistory(hist.Channel, hist.Since, int(hist.Count), int(hist.Offset))
if err != nil {
fmt.Println("cannot get message history:", err)
return user.Send(conn, core.Error{core.ErrorNotFound})
diff --git a/storage/storage.go b/storage/storage.go
index 1dcea40..28f2564 100644
--- a/storage/storage.go
+++ b/storage/storage.go
@@ -2,7 +2,6 @@ package storage
import (
"database/sql"
- "fmt"
"log"
"time"
@@ -16,6 +15,12 @@ type Database struct {
const initSql = `
CREATE TABLE IF NOT EXISTS messages
(id INTEGER NOT NULL PRIMARY KEY, source STRING, target STRING, timestamp INT, content STRING);
+
+ CREATE TABLE IF NOT EXISTS users
+ (name STRING NOT NULL PRIMARY KEY, pass STRING);
+
+ CREATE TABLE IF NOT EXISTS permissions
+ (user STRING, channel STRING, read INT, write INT, PRIMARY KEY (user, channel));
`
func InitDb(path string) (*Database, error) {
@@ -28,9 +33,7 @@ func InitDb(path string) (*Database, error) {
return &Database{db}, nil
}
-func (db *Database) Write(msg core.Message) (err error) {
- fmt.Println(msg.Timestamp)
-
+func (db *Database) AddMessage(msg core.Message) (err error) {
_, err = db.Exec(
"INSERT INTO messages (source, target, timestamp, content) VALUES (?, ?, ?, ?);",
msg.Source, msg.Target, msg.Timestamp.Unix(), msg.Content,
@@ -39,7 +42,7 @@ func (db *Database) Write(msg core.Message) (err error) {
return err
}
-func (db *Database) Read(channel string, since time.Time, num int, offset int) (history []core.Message, err error) {
+func (db *Database) GetHistory(channel string, since time.Time, num int, offset int) (history []core.Message, err error) {
rows, err := db.Query(
`SELECT source, target, timestamp, content FROM messages WHERE target = ? AND timestamp > ? LIMIT ? OFFSET ?`,
channel, since.Unix(), num, offset,
@@ -70,3 +73,52 @@ func (db *Database) Read(channel string, since time.Time, num int, offset int) (
return history, nil
}
+
+func (db *Database) SetUser(user core.UserData) error {
+ _, err := db.Exec(
+ "INSERT OR REPLACE INTO users (name, pass) VALUES (?, ?);",
+ user.Name, user.Pass,
+ )
+
+ return err
+}
+
+func (db *Database) DelUser(name string) error {
+ _, err := db.Exec("DELETE FROM users WHERE name = ?", name)
+ return err
+}
+
+func (db *Database) GetUserPass(name string) (string, error) {
+ var pass string
+
+ err := db.QueryRow("SELECT pass FROM users WHERE name = ?", name).Scan(&pass)
+ if err != nil {
+ return "", err
+ }
+
+ return pass, nil
+}
+
+func (db *Database) SetPermission(perm core.PermissionData) error {
+ _, err := db.Exec(
+ "INSERT OR REPLACE INTO permissions (user, channel, read, write) VALUES (?, ?, ?, ?);",
+ perm.User, perm.Channel, perm.Read, perm.Write, perm.User, perm.Channel,
+ )
+
+ return err
+}
+
+func (db *Database) GetPermission(user, channel string) (core.PermissionData, error) {
+ var perm core.PermissionData
+
+ err := db.QueryRow(
+ "SELECT (read, write) FROM permissions WHERE user = ? AND channel = ?", user, channel).
+ Scan(&perm.Read, &perm.Write)
+ if err != nil {
+ return core.PermissionData{}, err
+ }
+
+ perm.User = user
+ perm.Channel = channel
+ return perm, nil
+}