all repos — quartzgun @ 8179aee263f4ec822716818c610fb550a278f720

lightweight web framework in go

add nested template support in renderer and IndentalUserDB UserStore implementation
Iris Lightshard nilix@nilfm.cc
PGP Signature
-----BEGIN PGP SIGNATURE-----

iQIzBAABCAAdFiEEkFh6dA+k/6CXFXU4O3+8IhROY5gFAmHZJqUACgkQO3+8IhRO
Y5jQSw//WQrE18jwWOd2Ro7Tz8TCxmt6rryh3N3xOdewqWg3fvqFz1AagkSBEym0
qUo3vtr6RHdhMkGcgIBmu5fPSnJ7SvPtcWFE6CLNrTFGncIFq54eVLvbXtv2LaNK
fgsc2Ab6KDWTNXxNSysjMCtW5U1s65bcDOxVL4Wo163o7nwmhQ/kcnKE/qXXuNbD
hfHaOUFeUu07LMz1VS3LlfjhtUYzj81Y2rIRKcs9L9drDvFC5f+YGjA31JXiTVbn
sEVb/Xr04hxtkSNBBavNyP5Ko7smx1vtg9FWcrRyJAgRh3CbOIK8iUuQBtRCWzct
zaAoM/54J2+pheJVTn4V8LFMWpzF5qciEMwRB4hPwdXJCsMqLHD81TansglTnqgw
qZgNtJc7hoJscy5VNJYjc1nHYgke2kaw9/SI/vV/ZmMiqiuKQlffS8zkPQZvjdHh
VaXOLZOLOzL34e7/DrFAonpeJ0WNyR99PKMiJkJBU6eLgtknU43WZworavjkID9r
9W+DJ31/RuyoWtVseoHAkW/T8MtW/GyVD8p4VhE7eqTfp6UWRIWMBghyo7EBs1/+
k72Vmlu7NhcJuhEi1zX9xG/di6pkvOBNVyzar0R+AvPTAFwTfuzTu1rbWgYS5iHf
zDqUBLSDOcxANWVgOup9McKHy/5EolxknwPx4JnjrNIcy62LvjA=
=3ZLM
-----END PGP SIGNATURE-----
commit

8179aee263f4ec822716818c610fb550a278f720

parent

b17fa798b7e3c2ef80cd774f00158b7e2c3c4fa6

5 files changed, 220 insertions(+), 12 deletions(-)

jump to
M auth/auth.goauth/auth.go

@@ -2,12 +2,26 @@ package auth

import ( //nilfm.cc/git/goldbug/cookie + "time" ) +type User struct { + Name string + Pass string + Session string + LoginTime time.Time + LastSeen time.Time + + Data map[string]interface{} +} + type UserStore interface { - InitiateSession(user string, sessionId string) error - ValidateUser(user string, password string, sessionId string) (bool, error) + InitiateSession(user string, password string) (string, error) + ValidateUser(user string, sessionId string) (bool, error) EndSession(user string) error + AddUser(user string, password string) error + DeleteUser(user string) error + ChangePassword(user string, oldPassword string, newPassword string) error } func Login(user string, password string, userStore UserStore) (string, error) {
A indentalUserDB/indentalUserDB.go

@@ -0,0 +1,189 @@

+package indentalUserDB + +import ( + "time" + "nilfm.cc/git/goldbug/cookie" + "nilfm.cc/git/goldbug/auth" + "golang.org/x/crypto/bcrypt" + //"io" + "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 +}
M renderer/renderer.gorenderer/renderer.go

@@ -7,8 +7,8 @@ "encoding/json"

"encoding/xml" ) -func Template(t string) http.Handler { - tmpl := template.Must(template.ParseFiles(t)) +func Template(t ...string) http.Handler { + tmpl := template.Must(template.ParseFiles(t...)) handlerFunc := func(w http.ResponseWriter, req *http.Request) { tmpl.Execute(w, req)
M router/router.gorouter/router.go

@@ -10,6 +10,7 @@ "strings"

"path" "os" "errors" + "context" ) type Router struct {

@@ -23,7 +24,6 @@ * value = file path

*/ StaticPaths map[string]string } - type Route struct { path *regexp.Regexp

@@ -125,7 +125,7 @@ continue

} for method, handler := range r.handlerMap { if method == req.Method { - /* Parse the form and add the params to it */ + /* Parse the form and add the params to the context */ req.ParseForm() ProcessParams(req, params) /* handle the request! */

@@ -142,9 +142,7 @@ * Utility Methods *

*******************/ func ProcessParams(req *http.Request, params map[string]string) { - for key, value := range params { - req.Form.Add(key, value) - } + *req = *req.WithContext(context.WithValue(req.Context(), "params", params)) } func (self *Route) Match(r *http.Request) map[string]string {

@@ -165,8 +163,10 @@ }

func (self *Router) ErrorPage(w http.ResponseWriter, req *http.Request, code int, errMsg string) { w.WriteHeader(code) - req.ParseForm() - req.Form.Add("ErrorCode", strconv.Itoa(code)) - req.Form.Add("ErrorMessage", errMsg) + params := map[string]string{ + "ErrorCode": strconv.Itoa(code), + "ErrorMessage": errMsg, + } + ProcessParams(req, params) self.Fallback.Execute(w, req) }
A userDB.ndtl

@@ -0,0 +1,5 @@

+nilix: + pass: $2a$10$.Y59TRn/.qBjT8KwleyrBePsC34EuPzrRlQr014bjEKuLoUCWDMtO + session: eMOrLtCvjo_DTV_NqDLicJOugUALtiCIjdvPuzY@O!TOAAzunOs!jnCvCv#sQFxR + loginTime: 1969-12-31 17:00:00 -0700 MST + lastSeen: 1969-12-31 17:00:00 -0700 MST