all repos — auth-boilerplate @ 183cedb78911fa8dfa5c9831a6a010a0c8a805a1

A simple Go web-app boilerplate.

src/app/functions.go (view raw)

  1package app
  2
  3import (
  4	"context"
  5	"errors"
  6	"fmt"
  7	"log"
  8	"net/http"
  9	"os"
 10	"regexp"
 11	"strings"
 12	"time"
 13
 14	"github.com/birabittoh/auth-boilerplate/src/email"
 15)
 16
 17const (
 18	minUsernameLength = 3
 19	maxUsernameLength = 10
 20)
 21
 22var (
 23	validUsername = regexp.MustCompile(`(?i)^[a-z0-9._-]+$`)
 24	validEmail    = regexp.MustCompile(`^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$`)
 25)
 26
 27func showError(w http.ResponseWriter, text string, status int) {
 28	w.WriteHeader(status)
 29	xt.ExecuteTemplate(w, "error.tmpl", map[string]interface{}{"Status": status, "Text": text})
 30}
 31
 32func getUserByName(username string, excluding uint) (user User, err error) {
 33	err = db.Model(&User{}).Where("upper(username) == upper(?) AND id != ?", username, excluding).First(&user).Error
 34	return
 35}
 36
 37func sanitizeUsername(username string) (string, error) {
 38	if !validUsername.MatchString(username) || len(username) < minUsernameLength || len(username) > maxUsernameLength {
 39		return "", errors.New("invalid username")
 40	}
 41
 42	return username, nil
 43}
 44
 45func sanitizeEmail(email string) (string, error) {
 46	email = strings.ToLower(email)
 47
 48	if !validEmail.MatchString(email) {
 49		return "", fmt.Errorf("invalid email")
 50	}
 51
 52	return email, nil
 53}
 54
 55func login(w http.ResponseWriter, userID uint, remember bool) {
 56	var duration time.Duration
 57	if remember {
 58		duration = durationWeek
 59	} else {
 60		duration = durationDay
 61	}
 62
 63	cookie, err := g.GenerateCookie(duration)
 64	if err != nil {
 65		showError(w, "Could not generate session cookie.", http.StatusInternalServerError)
 66	}
 67
 68	ks.Set("session:"+cookie.Value, userID, duration)
 69	http.SetCookie(w, cookie)
 70}
 71
 72func loadEmailConfig() *email.Client {
 73	address := os.Getenv("APP_SMTP_EMAIL")
 74	password := os.Getenv("APP_SMTP_PASSWORD")
 75	host := os.Getenv("APP_SMTP_HOST")
 76	port := os.Getenv("APP_SMTP_PORT")
 77
 78	if address == "" || password == "" || host == "" {
 79		log.Println("Missing email configuration.")
 80		return nil
 81	}
 82
 83	if port == "" {
 84		port = "587"
 85	}
 86
 87	return email.NewClient(address, password, host, port)
 88}
 89
 90func sendEmail(mail email.Email) error {
 91	if m == nil {
 92		return errors.New("email client is not initialized")
 93	}
 94	return m.Send(mail)
 95}
 96
 97func sendResetEmail(address, token string) {
 98	resetURL := fmt.Sprintf("%s/reset-password-confirm?token=%s", baseUrl, token)
 99	err := sendEmail(email.Email{
100		To:      []string{address},
101		Subject: "Reset password",
102		Body:    fmt.Sprintf("Use the following link to reset your password:\n%s", resetURL),
103	})
104	if err != nil {
105		log.Printf("Could not send reset email for %s. Link: %s", address, resetURL)
106	}
107}
108
109func readSessionCookie(r *http.Request) (userID *uint, err error) {
110	cookie, err := r.Cookie("session_token")
111	if err != nil {
112		return
113	}
114	return ks.Get("session:" + cookie.Value)
115}
116
117// Middleware to check if the user is logged in
118func loginRequired(next http.HandlerFunc) http.HandlerFunc {
119	return func(w http.ResponseWriter, r *http.Request) {
120		userID, err := readSessionCookie(r)
121		if err != nil {
122			http.Redirect(w, r, "/login", http.StatusFound)
123			return
124		}
125
126		ctx := context.WithValue(r.Context(), userContextKey, *userID)
127		next(w, r.WithContext(ctx))
128	}
129}
130
131func getLoggedUser(r *http.Request) (user User, ok bool) {
132	userID, ok := r.Context().Value(userContextKey).(uint)
133	db.Find(&user, userID)
134	return user, ok
135}