package app import ( "crypto/rand" "encoding/hex" "encoding/json" "net/http" "github.com/andyleap/hnh-map/internal/app/response" "github.com/andyleap/hnh-map/internal/app/store" "go.etcd.io/bbolt" "golang.org/x/crypto/bcrypt" ) func (a *App) getSession(req *http.Request) *Session { c, err := req.Cookie("session") if err != nil { return nil } var s *Session a.db.View(func(tx *bbolt.Tx) error { sessions := tx.Bucket(store.BucketSessions) if sessions == nil { return nil } session := sessions.Get([]byte(c.Value)) if session == nil { return nil } err := json.Unmarshal(session, &s) if err != nil { return err } if s.TempAdmin { s.Auths = Auths{AUTH_ADMIN} return nil } users := tx.Bucket(store.BucketUsers) if users == nil { return nil } raw := users.Get([]byte(s.Username)) if raw == nil { s = nil return nil } u := User{} err = json.Unmarshal(raw, &u) if err != nil { s = nil return err } s.Auths = u.Auths return nil }) return s } func (a *App) deleteSession(s *Session) { a.db.Update(func(tx *bbolt.Tx) error { sessions, err := tx.CreateBucketIfNotExists(store.BucketSessions) if err != nil { return err } return sessions.Delete([]byte(s.ID)) }) } func (a *App) saveSession(s *Session) { a.db.Update(func(tx *bbolt.Tx) error { sessions, err := tx.CreateBucketIfNotExists(store.BucketSessions) if err != nil { return err } buf, err := json.Marshal(s) if err != nil { return err } return sessions.Put([]byte(s.ID), buf) }) } func (a *App) getPage(req *http.Request) Page { p := Page{} a.db.View(func(tx *bbolt.Tx) error { c := tx.Bucket(store.BucketConfig) if c == nil { return nil } p.Title = string(c.Get([]byte("title"))) return nil }) return p } func (a *App) getUser(user, pass string) (u *User) { a.db.View(func(tx *bbolt.Tx) error { users := tx.Bucket(store.BucketUsers) if users == nil { return nil } raw := users.Get([]byte(user)) if raw != nil { json.Unmarshal(raw, &u) if u.Pass == nil { u = nil return nil } if bcrypt.CompareHashAndPassword(u.Pass, []byte(pass)) != nil { u = nil return nil } } return nil }) return u } // createSession creates a session for username, returns session ID or empty string. func (a *App) createSession(username string, tempAdmin bool) string { session := make([]byte, 32) if _, err := rand.Read(session); err != nil { return "" } sid := hex.EncodeToString(session) s := &Session{ ID: sid, Username: username, TempAdmin: tempAdmin, } a.saveSession(s) return sid } // setupRequired returns true if no users exist (first run). func (a *App) setupRequired() bool { var required bool a.db.View(func(tx *bbolt.Tx) error { ub := tx.Bucket(store.BucketUsers) if ub == nil { required = true return nil } if ub.Stats().KeyN == 0 { required = true return nil } return nil }) return required } func (a *App) requireAdmin(rw http.ResponseWriter, req *http.Request) *Session { s := a.getSession(req) if s == nil || !s.Auths.Has(AUTH_ADMIN) { response.JSONError(rw, http.StatusUnauthorized, "Unauthorized", "UNAUTHORIZED") return nil } return s }