218 lines
5.2 KiB
Go
218 lines
5.2 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
|
|
self.Users[user].LoginTime = time.Now()
|
|
self.Users[user].LastSeen = time.Now()
|
|
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
|
|
}
|
|
|
|
fileData := string(f[:])
|
|
users := map[string]*auth.User{}
|
|
|
|
lines := strings.Split(fileData, "\n")
|
|
|
|
indentLevel := ""
|
|
|
|
var name string
|
|
var pass string
|
|
var session string
|
|
var loginTime time.Time
|
|
var lastSeen time.Time
|
|
var data map[string]interface{}
|
|
|
|
for _, l := range lines {
|
|
if strings.HasPrefix(l, indentLevel) {
|
|
switch indentLevel {
|
|
case "":
|
|
name = l
|
|
indentLevel = "\t"
|
|
|
|
case "\t":
|
|
if strings.Contains(l, ":") {
|
|
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)
|
|
}
|
|
} else {
|
|
data = map[string]interface{}{}
|
|
indentLevel = "\t\t"
|
|
}
|
|
|
|
case "\t\t":
|
|
if strings.Contains(l, ":") {
|
|
kvp := strings.Split(l, ":")
|
|
k := strings.TrimSpace(kvp[0])
|
|
v := strings.TrimSpace(kvp[1])
|
|
data[k] = v
|
|
}
|
|
}
|
|
} else {
|
|
if indentLevel != "\t\t" {
|
|
panic("Malformed indental file")
|
|
} else {
|
|
users[name] = &auth.User{
|
|
Name: name,
|
|
Pass: pass,
|
|
Session: session,
|
|
LoginTime: loginTime,
|
|
LastSeen: lastSeen,
|
|
Data: data,
|
|
}
|
|
indentLevel = ""
|
|
}
|
|
}
|
|
}
|
|
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\tpass: %s\n\tsession: %s\n\tloginTime: %s\n\tlastSeen: %s\n\tdata\n",
|
|
user.Name,
|
|
user.Pass,
|
|
user.Session,
|
|
user.LoginTime,
|
|
user.LastSeen));
|
|
for k, v := range user.Data {
|
|
f.WriteString(fmt.Sprintf("\t\t%s: %s\n", k, v))
|
|
}
|
|
f.WriteString("\n")
|
|
}
|
|
f.Sync()
|
|
return nil
|
|
}
|