diff options
| author | bt <bt@rctt.net> | 2026-05-24 18:52:58 +0200 |
|---|---|---|
| committer | bt <bt@rctt.net> | 2026-05-24 18:52:58 +0200 |
| commit | 51c603afe0373f45f8d389c99cac9d3aec959b75 (patch) | |
| tree | 77de75fbfec26c332bcaf79424c66cd0e9615be8 | |
| parent | cd925e3c7716216902f3a0ef4c333f32e5f1bdb4 (diff) | |
| download | solec-51c603afe0373f45f8d389c99cac9d3aec959b75.tar.gz solec-51c603afe0373f45f8d389c99cac9d3aec959b75.zip | |
[common] Users and permissions database
| -rw-r--r-- | cmd/daemon/main.go | 75 | ||||
| -rw-r--r-- | core/internal.go | 24 | ||||
| -rw-r--r-- | go.mod | 1 | ||||
| -rw-r--r-- | go.sum | 2 | ||||
| -rw-r--r-- | server/message.go | 2 | ||||
| -rw-r--r-- | server/storage.go | 11 | ||||
| -rw-r--r-- | server/user.go | 2 | ||||
| -rw-r--r-- | storage/storage.go | 62 |
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 +} @@ -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 ) @@ -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 +} |
