quartzgun/indentalUserDB/indentalUserDB.go

188 lines
4.4 KiB
Go

package indentalUserDB
import (
"time"
"nilfm.cc/git/quartzgun/cookie"
"nilfm.cc/git/quartzgun/auth"
"golang.org/x/crypto/bcrypt"
"os"
"strings"
"fmt"
"errors"
)
type IndentalUserDB struct {
Users map[string]*auth.User
Basis string
}
func CreateIndentalUserDB(filePath string) *IndentalUserDB {
u, err := readDB(filePath)
if err == nil {
uMap := map[string]*auth.User{}
for _, usr := range u {
uMap[usr.Name] = usr
}
return &IndentalUserDB{
Users: uMap,
Basis: filePath,
}
} else {
return &IndentalUserDB{
Users: map[string]*auth.User{},
Basis: filePath,
}
}
}
func (self *IndentalUserDB) InitiateSession(user string, password string) (string, error) {
if _, exists := self.Users[user]; !exists {
return "", errors.New("User not in DB")
}
if bcrypt.CompareHashAndPassword([]byte(self.Users[user].Pass), []byte(password)) != nil {
return "", errors.New("Incorrect password")
}
sessionId := cookie.GenToken(64)
self.Users[user].Session = sessionId
writeDB(self.Basis, self.Users)
return sessionId, nil
}
func (self *IndentalUserDB) ValidateUser(user string, sessionId string) (bool, error) {
if _, exists := self.Users[user]; !exists {
return false, errors.New("User not in DB")
}
validated := self.Users[user].Session == sessionId
if validated {
self.Users[user].LastSeen = time.Now()
writeDB(self.Basis, self.Users)
}
return validated, nil
}
func (self *IndentalUserDB) EndSession(user string) error {
if _, exists := self.Users[user]; !exists {
return errors.New("User not in DB")
}
self.Users[user].Session = ""
self.Users[user].LastSeen = time.Now()
writeDB(self.Basis, self.Users)
return nil
}
func (self *IndentalUserDB) DeleteUser(user string) error {
if _, exists := self.Users[user]; !exists {
return errors.New("User not in DB")
}
delete(self.Users, user)
writeDB(self.Basis, self.Users)
return nil
}
func (self *IndentalUserDB) ChangePassword(user string, password string, oldPassword string) error {
if _, exists := self.Users[user]; !exists {
return errors.New("User not in DB")
}
if bcrypt.CompareHashAndPassword([]byte(self.Users[user].Pass), []byte(oldPassword)) != nil {
return errors.New("Incorrect password")
}
hash, _ := bcrypt.GenerateFromPassword([]byte(password), 10)
self.Users[user].Pass = string(hash[:])
writeDB(self.Basis, self.Users)
return nil
}
func (self *IndentalUserDB) AddUser(user string, password string) error{
if _, exists := self.Users[user]; exists {
return errors.New("User already in DB")
}
hash, _ := bcrypt.GenerateFromPassword([]byte(password), 10)
self.Users[user] = &auth.User{
Name: user,
Pass: string(hash[:]),
LastSeen: time.UnixMicro(0),
LoginTime: time.UnixMicro(0),
Session: "",
}
writeDB(self.Basis, self.Users)
return nil;
}
const timeFmt = "2006-01-02T15:04Z"
func readDB(filePath string) (map[string]*auth.User, error) {
f, err := os.ReadFile(filePath)
if err != nil {
return nil, err
}
data := string(f[:])
users := map[string]*auth.User{}
lines := strings.Split(data, "\n")
var name string
var pass string
var session string
var loginTime time.Time
var lastSeen time.Time
procFields := 0
for _, l := range lines {
if !strings.HasPrefix(l, " ") {
name = l
procFields++
} else {
kvp := strings.Split(l, ":")
k := strings.TrimSpace(kvp[0])
v := strings.TrimSpace(kvp[1])
switch k {
case "pass":
pass = v
case "session":
session = v
case "loginTime":
loginTime, _ = time.Parse(timeFmt, v)
case "lastSeen":
lastSeen, _ = time.Parse(timeFmt, v)
}
procFields++
if procFields == 5 {
users[name] = &auth.User{
Name: name,
Pass: pass,
Session: session,
LoginTime: loginTime,
LastSeen: lastSeen,
}
procFields = 0
}
}
}
return users, nil
}
func writeDB(filePath string, users map[string]*auth.User) error {
f, err := os.Create(filePath)
if err != nil {
return err
}
defer f.Close()
for _, user := range users {
f.WriteString(fmt.Sprintf("%s:\n pass: %s\n session: %s\n loginTime: %s\n lastSeen: %s\n",
user.Name,
user.Pass,
user.Session,
user.LoginTime,
user.LastSeen));
}
f.Sync()
return nil
}