Ctrl K
ComunidadGuías…

auth/auth.go

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package auth

import (
	"encoding/csv"
	"encoding/json"
	"errors"
	"io"
	"net/http"
	"os"

	"github.com/gorilla/sessions"
	"github.com/rs/zerolog/log"
	"golang.org/x/crypto/bcrypt"
)

type Users struct {
	Lookup         map[string]string
	store          sessions.Store
	sessionTimeout int
}

type UserPW struct {
	Name string
	Pass string
}

func read(r io.Reader) ([]UserPW, error) {
	reader := csv.NewReader(r)
	reader.Comma = ':'
	reader.Comment = '#'
	reader.TrimLeadingSpace = true

	records, err := reader.ReadAll()
	if err != nil {
		return nil, err
	}

	result := []UserPW{}
	for _, record := range records {
		if len(record) != 2 {
			return nil, errors.New("malformed users file")
		}
		result = append(result, UserPW{Name: record[0], Pass: record[1]})
	}
	return result, nil
}

func ReadPasswordsFile(path string, secret []byte, sessionTimeout int) (*Users, error) {
	users := &Users{
		Lookup:         map[string]string{},
		sessionTimeout: sessionTimeout,
		store:          sessions.NewCookieStore(secret),
	}
	if path == "" {
		log.Info().Msg("Users file not specified")
		return users, nil
	}

	file, err := os.Open(path)
	if err != nil {
		return users, err
	}
	defer file.Close()
	userPws, err := read(file)
	if err != nil {
		return users, err
	}

	for _, record := range userPws {
		users.Lookup[record.Name] = record.Pass
	}
	log.Info().Int("amount", len(users.Lookup)).Msg("Loaded Users")
	return users, nil
}

type Response struct {
	Message string `json:"message"`
}

func (u *Users) CurrentUser(r *http.Request) (string, bool) {
	s, _ := u.store.Get(r, "user")
	user, ok := s.Values["user"].(string)
	if !ok {
		return "guest", ok
	}
	return user, ok
}

func (u *Users) Logout(w http.ResponseWriter, r *http.Request) {
	session := sessions.NewSession(u.store, "user")
	session.IsNew = true
	if err := u.store.Save(r, w, session); err != nil {
		w.WriteHeader(500)
		_ = json.NewEncoder(w).Encode(&Response{
			Message: err.Error(),
		})
		return
	}
	w.WriteHeader(200)
}

func (u *Users) Authenticate(w http.ResponseWriter, r *http.Request) {
	user := r.FormValue("user")
	pass := r.FormValue("pass")

	if !u.Validate(user, pass) {
		w.WriteHeader(401)
		_ = json.NewEncoder(w).Encode(&Response{
			Message: "could not authenticate",
		})
		return
	}

	session := sessions.NewSession(u.store, "user")
	session.IsNew = true
	session.Options.MaxAge = u.sessionTimeout
	session.Values["user"] = user
	if err := u.store.Save(r, w, session); err != nil {
		w.WriteHeader(500)
		_ = json.NewEncoder(w).Encode(&Response{
			Message: err.Error(),
		})
		return
	}
	w.WriteHeader(200)
	_ = json.NewEncoder(w).Encode(&Response{
		Message: "authenticated",
	})
}

func (u Users) Validate(user, password string) bool {
	realPassword, exists := u.Lookup[user]
	return exists && bcrypt.CompareHashAndPassword([]byte(realPassword), []byte(password)) == nil
}