123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- package auth
- import (
- "encoding/json"
- "errors"
- "net/http"
- "os"
- "path/filepath"
- "sync"
- "github.com/boltdb/bolt"
- )
- // LogFunc is a function type for logging.
- type LogFunc func(format string, v ...interface{})
- // Options holds configuration for AuthManager.
- type Options struct {
- DBPath string
- Log LogFunc
- }
- // AuthManager handles authentication.
- type AuthManager struct {
- db *bolt.DB
- log LogFunc
- mu sync.RWMutex
- }
- const (
- authBucket = "auth"
- passKey = "password"
- )
- // NewAuthManager creates a new AuthManager.
- func NewAuthManager(opt Options) (*AuthManager, error) {
- dir := filepath.Dir(opt.DBPath)
- if _, err := os.Stat(dir); os.IsNotExist(err) {
- if err := os.MkdirAll(dir, 0755); err != nil {
- return nil, err
- }
- }
- db, err := bolt.Open(opt.DBPath, 0755, nil)
- if err != nil {
- return nil, err
- }
- // Ensure bucket exists
- err = db.Update(func(tx *bolt.Tx) error {
- _, err := tx.CreateBucketIfNotExists([]byte(authBucket))
- return err
- })
- if err != nil {
- db.Close()
- return nil, err
- }
- return &AuthManager{db: db, log: opt.Log}, nil
- }
- // SetPassword sets the password (overwrites any existing).
- func (a *AuthManager) SetPassword(password string) error {
- a.mu.Lock()
- defer a.mu.Unlock()
- return a.db.Update(func(tx *bolt.Tx) error {
- b := tx.Bucket([]byte(authBucket))
- return b.Put([]byte(passKey), []byte(password))
- })
- }
- // ChangePassword changes password if oldpassword matches.
- func (a *AuthManager) ChangePassword(oldPassword, newPassword string) error {
- a.mu.Lock()
- defer a.mu.Unlock()
- return a.db.Update(func(tx *bolt.Tx) error {
- b := tx.Bucket([]byte(authBucket))
- stored := b.Get([]byte(passKey))
- if stored == nil || string(stored) != oldPassword {
- return errors.New("old password incorrect")
- }
- return b.Put([]byte(passKey), []byte(newPassword))
- })
- }
- // ResetPassword removes the password.
- func (a *AuthManager) ResetPassword() error {
- a.mu.Lock()
- defer a.mu.Unlock()
- return a.db.Update(func(tx *bolt.Tx) error {
- b := tx.Bucket([]byte(authBucket))
- return b.Delete([]byte(passKey))
- })
- }
- // ValidatePassword checks password from request
- func (a *AuthManager) ValidatePassword(password string) (bool, error) {
- a.mu.RLock()
- defer a.mu.RUnlock()
- var ok bool
- err := a.db.View(func(tx *bolt.Tx) error {
- b := tx.Bucket([]byte(authBucket))
- stored := b.Get([]byte(passKey))
- ok = stored != nil && string(stored) == password
- return nil
- })
- return ok, err
- }
- // UserIsLoggedIn checks if the user is logged in via cookie.
- func (a *AuthManager) UserIsLoggedIn(r *http.Request) bool {
- cookie, err := r.Cookie("dezukvm_auth")
- return err == nil && cookie.Value == "1"
- }
- // HandleFunc wraps an http.HandlerFunc with auth check.
- func (a *AuthManager) HandleFunc(pattern string, handler http.HandlerFunc, mux *http.ServeMux) {
- mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
- if a.UserIsLoggedIn(r) {
- handler(w, r)
- return
- }
- w.WriteHeader(http.StatusUnauthorized)
- w.Header().Set("Content-Type", "application/json")
- json.NewEncoder(w).Encode(map[string]string{"error": "unauthorized"})
- })
- }
- // LoginUser sets a session/cookie if password is correct
- func (a *AuthManager) LoginUser(w http.ResponseWriter, r *http.Request) error {
- var req struct {
- Password string `json:"password"`
- }
- if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
- return err
- }
- a.mu.RLock()
- defer a.mu.RUnlock()
- var ok bool
- err := a.db.View(func(tx *bolt.Tx) error {
- b := tx.Bucket([]byte(authBucket))
- stored := b.Get([]byte(passKey))
- ok = stored != nil && string(stored) == req.Password
- return nil
- })
- if err != nil {
- return err
- }
- if !ok {
- return errors.New("unauthorized")
- }
- // Set a simple session cookie
- http.SetCookie(w, &http.Cookie{
- Name: "dezukvm_auth",
- Value: "1",
- Path: "/",
- HttpOnly: true,
- MaxAge: 86400,
- })
- return nil
- }
- // LogoutUser removes the session/cookie for the user.
- func (a *AuthManager) LogoutUser(w http.ResponseWriter, r *http.Request) error {
- http.SetCookie(w, &http.Cookie{
- Name: "dezukvm_auth",
- Value: "",
- Path: "/",
- HttpOnly: true,
- MaxAge: -1,
- })
- return nil
- }
- // Close closes the underlying DB.
- func (a *AuthManager) Close() error {
- return a.db.Close()
- }
|