main.go (view raw)
1package main
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "html/template"
8 "log"
9 "net/http"
10 "time"
11
12 "github.com/BiRabittoh/gauth/gauth"
13 "github.com/glebarez/sqlite"
14 "gorm.io/gorm"
15)
16
17type key int
18
19type User struct {
20 gorm.Model
21 Username string
22 Email string
23 PasswordHash string
24 RememberToken string
25 ResetToken *string
26 ResetExpires *time.Time
27}
28
29var (
30 db *gorm.DB
31 durationDay = 24 * time.Hour
32 durationWeek = 7 * durationDay
33 g = gauth.NewGauth("superSecretPepper", durationDay, durationWeek)
34 templates = template.Must(template.ParseGlob("templates/*.html"))
35)
36
37const userContextKey key = 0
38
39func main() {
40 // Connessione al database SQLite
41 var err error
42 db, err = gorm.Open(sqlite.Open("database.db"), &gorm.Config{})
43 if err != nil {
44 log.Fatal(err)
45 }
46
47 // Creazione della tabella utenti
48 db.AutoMigrate(&User{})
49
50 // Gestione delle route
51 http.HandleFunc("GET /", loginRequired(examplePage))
52 http.HandleFunc("GET /register", getRegisterHandler)
53 http.HandleFunc("GET /login", getLoginHandler)
54 http.HandleFunc("GET /reset-password", getResetPasswordHandler)
55 http.HandleFunc("GET /reset-password-confirm", getResetPasswordConfirmHandler)
56 http.HandleFunc("GET /logout", logoutHandler)
57
58 http.HandleFunc("POST /login", postLoginHandler)
59 http.HandleFunc("POST /register", postRegisterHandler)
60 http.HandleFunc("POST /reset-password", postResetPasswordHandler)
61 http.HandleFunc("POST /reset-password-confirm", postResetPasswordConfirmHandler)
62
63 port := ":8080"
64 log.Println("Server in ascolto su " + port)
65 log.Fatal(http.ListenAndServe(port, nil))
66}
67
68// Middleware per controllare se l'utente è loggato
69// Funzione middleware per controllare se l'utente è autenticato
70func loginRequired(next http.HandlerFunc) http.HandlerFunc {
71 return func(w http.ResponseWriter, r *http.Request) {
72 cookie, err := r.Cookie("session_token")
73 if err != nil {
74 http.Redirect(w, r, "/login", http.StatusFound)
75 return
76 }
77
78 var user User
79 db.Where("remember_token = ?", cookie.Value).First(&user)
80
81 if user.ID == 0 {
82 http.Redirect(w, r, "/login", http.StatusFound)
83 return
84 }
85
86 ctx := context.WithValue(r.Context(), userContextKey, user)
87 next(w, r.WithContext(ctx))
88 }
89}
90
91func getLoggedUser(r *http.Request) (User, bool) {
92 user, ok := r.Context().Value(userContextKey).(User)
93 return user, ok
94}
95
96func examplePage(w http.ResponseWriter, r *http.Request) {
97 user, ok := getLoggedUser(r)
98 if !ok {
99 http.Error(w, "Utente non trovato nel contesto", http.StatusInternalServerError)
100 return
101 }
102
103 templates.ExecuteTemplate(w, "example.html", map[string]interface{}{"User": user})
104}
105
106func getRegisterHandler(w http.ResponseWriter, r *http.Request) {
107 templates.ExecuteTemplate(w, "register.html", nil)
108}
109
110func getLoginHandler(w http.ResponseWriter, r *http.Request) {
111 templates.ExecuteTemplate(w, "login.html", nil)
112}
113
114func getResetPasswordHandler(w http.ResponseWriter, r *http.Request) {
115 templates.ExecuteTemplate(w, "reset_password.html", nil)
116}
117
118// Gestione della registrazione
119func postRegisterHandler(w http.ResponseWriter, r *http.Request) {
120 username := r.FormValue("username")
121 email := r.FormValue("email")
122 password := r.FormValue("password")
123
124 hashedPassword, err := g.HashPassword(password)
125 if err != nil {
126 http.Error(w, "Errore durante la registrazione", http.StatusInternalServerError)
127 return
128 }
129
130 user := User{Username: username, Email: email, PasswordHash: hashedPassword}
131 db.Create(&user)
132 http.Redirect(w, r, "/login", http.StatusFound)
133 return
134}
135
136// Gestione del login
137func postLoginHandler(w http.ResponseWriter, r *http.Request) {
138 username := r.FormValue("username")
139 password := r.FormValue("password")
140 remember := r.FormValue("remember")
141
142 var user User
143 db.Where("username = ?", username).First(&user)
144
145 if user.ID == 0 || !g.CheckPassword(password, user.PasswordHash) {
146 http.Error(w, "Credenziali non valide", http.StatusUnauthorized)
147 return
148 }
149
150 cookie, err := g.GenerateCookie(remember == "on")
151 if err != nil {
152 http.Error(w, "Errore nella generazione del token", http.StatusInternalServerError)
153 }
154
155 user.RememberToken = cookie.Value
156 db.Save(&user)
157
158 http.SetCookie(w, cookie)
159 http.Redirect(w, r, "/", http.StatusFound)
160 return
161}
162
163// Logout
164func logoutHandler(w http.ResponseWriter, r *http.Request) {
165 http.SetCookie(w, g.GenerateEmptyCookie())
166 http.Redirect(w, r, "/login", http.StatusFound)
167}
168
169// Funzione per gestire la richiesta di reset password
170func postResetPasswordHandler(w http.ResponseWriter, r *http.Request) {
171 email := r.FormValue("email")
172
173 var user User
174 db.Where("email = ?", email).First(&user)
175
176 if user.ID == 0 {
177 // Non riveliamo se l'email non esiste per motivi di sicurezza
178 http.Redirect(w, r, "/login", http.StatusFound)
179 return
180 }
181
182 // Genera un token di reset
183 resetToken, err := g.GenerateRandomToken()
184 if err != nil {
185 http.Error(w, "Errore nella generazione del token di reset", http.StatusInternalServerError)
186 return
187 }
188
189 // Imposta una scadenza di 1 ora per il reset token
190 expiration := time.Now().Add(1 * time.Hour)
191 user.ResetToken = &resetToken
192 user.ResetExpires = &expiration
193 db.Save(&user)
194
195 // Simula l'invio di un'email con il link di reset (in questo caso viene stampato sul log)
196 resetURL := fmt.Sprintf("http://localhost:8080/reset-password-confirm?token=%s", resetToken)
197 log.Printf("Invio dell'email di reset per %s: %s", user.Email, resetURL)
198
199 http.Redirect(w, r, "/login", http.StatusFound)
200 return
201
202}
203
204func validateToken(r *http.Request) (user User, err error) {
205 token := r.URL.Query().Get("token")
206
207 db.Where("reset_token = ?", token).First(&user)
208
209 // Verifica se il token è valido e non scaduto
210 if user.ResetExpires == nil {
211 err = errors.New("nil value")
212 return
213 }
214
215 if user.ID == 0 || time.Now().After(*user.ResetExpires) {
216 err = errors.New("not found")
217 }
218 return
219}
220
221// Funzione per confermare il reset della password (usando il token)
222func getResetPasswordConfirmHandler(w http.ResponseWriter, r *http.Request) {
223 _, err := validateToken(r)
224 if err != nil {
225 http.Error(w, "Token non valido o scaduto", http.StatusUnauthorized)
226 return
227 }
228
229 templates.ExecuteTemplate(w, "new_password.html", nil)
230}
231
232func postResetPasswordConfirmHandler(w http.ResponseWriter, r *http.Request) {
233 user, err := validateToken(r)
234 if err != nil {
235 http.Error(w, "Token non valido o scaduto", http.StatusUnauthorized)
236 return
237 }
238
239 password := r.FormValue("password")
240
241 // Hash della nuova password
242 hashedPassword, err := g.HashPassword(password)
243 if err != nil {
244 http.Error(w, "Errore nella modifica della password", http.StatusInternalServerError)
245 return
246 }
247
248 // Aggiorna l'utente con la nuova password e rimuove il token di reset
249 user.PasswordHash = hashedPassword
250 user.ResetToken = nil
251 user.ResetExpires = nil
252 db.Save(&user)
253
254 // Reindirizza alla pagina di login
255 http.Redirect(w, r, "/login", http.StatusFound)
256 return
257}