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 getUserByName(username string, excluding uint) (user User, err error) {
28 err = db.Model(&User{}).Where("upper(username) == upper(?) AND id != ?", username, excluding).First(&user).Error
29 return
30}
31
32func sanitizeUsername(username string) (string, error) {
33 if !validUsername.MatchString(username) || len(username) < minUsernameLength || len(username) > maxUsernameLength {
34 return "", errors.New("invalid username")
35 }
36
37 return username, nil
38}
39
40func sanitizeEmail(email string) (string, error) {
41 email = strings.ToLower(email)
42
43 if !validEmail.MatchString(email) {
44 return "", fmt.Errorf("invalid email")
45 }
46
47 return email, nil
48}
49
50func login(w http.ResponseWriter, userID uint, remember bool) {
51 var duration time.Duration
52 if remember {
53 duration = durationWeek
54 } else {
55 duration = durationDay
56 }
57
58 cookie, err := g.GenerateCookie(duration)
59 if err != nil {
60 http.Error(w, "Could not generate session cookie.", http.StatusInternalServerError)
61 }
62
63 ks.Set("session:"+cookie.Value, userID, duration)
64 http.SetCookie(w, cookie)
65}
66
67func loadEmailConfig() *email.Client {
68 address := os.Getenv("APP_SMTP_EMAIL")
69 password := os.Getenv("APP_SMTP_PASSWORD")
70 host := os.Getenv("APP_SMTP_HOST")
71 port := os.Getenv("APP_SMTP_PORT")
72
73 if address == "" || password == "" || host == "" {
74 log.Println("Missing email configuration.")
75 return nil
76 }
77
78 if port == "" {
79 port = "587"
80 }
81
82 return email.NewClient(address, password, host, port)
83}
84
85func sendEmail(mail email.Email) error {
86 if m == nil {
87 return errors.New("email client is not initialized")
88 }
89 return m.Send(mail)
90}
91
92func sendResetEmail(address, token string) {
93 resetURL := fmt.Sprintf("%s/reset-password-confirm?token=%s", baseUrl, token)
94 err := sendEmail(email.Email{
95 To: []string{address},
96 Subject: "Reset password",
97 Body: fmt.Sprintf("Use the following link to reset your password:\n%s", resetURL),
98 })
99 if err != nil {
100 log.Printf("Could not send reset email for %s. Link: %s", address, resetURL)
101 }
102}
103
104func readSessionCookie(r *http.Request) (userID *uint, err error) {
105 cookie, err := r.Cookie("session_token")
106 if err != nil {
107 return
108 }
109 return ks.Get("session:" + cookie.Value)
110}
111
112// Middleware to check if the user is logged in
113func loginRequired(next http.HandlerFunc) http.HandlerFunc {
114 return func(w http.ResponseWriter, r *http.Request) {
115 userID, err := readSessionCookie(r)
116 if err != nil {
117 http.Redirect(w, r, "/login", http.StatusFound)
118 return
119 }
120
121 ctx := context.WithValue(r.Context(), userContextKey, *userID)
122 next(w, r.WithContext(ctx))
123 }
124}
125
126func getLoggedUser(r *http.Request) (user User, ok bool) {
127 userID, ok := r.Context().Value(userContextKey).(uint)
128 db.Find(&user, userID)
129 return user, ok
130}