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 } 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 }