summaryrefslogtreecommitdiff
path: root/handlers/user.go
blob: 799975513113556248a80b3d6d484c312985c69d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package handlers

import (
	"auth-proxy/config"
	"auth-proxy/logging"
	"auth-proxy/models"
	"auth-proxy/types"
	"auth-proxy/utils"
	"encoding/json"
	"net/http"

	"github.com/gorilla/csrf"
)

// AccountPageHandler serves the account page, passing user data to the template.
func AccountPageHandler(w http.ResponseWriter, r *http.Request) {
	user, ok := r.Context().Value(types.SessionDataContextKey).(*types.User)
	if !ok || user == nil {
		// This should ideally not happen if AuthMiddleware is working correctly.
		http.Error(w, "User not authenticated", http.StatusUnauthorized)
		return
	}

	data := map[string]interface{}{
		"Paths":     config.Paths,
		"User":      user,
		"CSRFToken": csrf.Token(r),
	}
	if err := templates.ExecuteTemplate(w, "account.html", data); err != nil {
		logging.AppLog.Error("Failed to execute account template", "error", err, "user_id", user.ID)
	}
}

// ChangePasswordHandler handles both displaying the page (GET) and processing the update (POST).
func ChangePasswordHandler(w http.ResponseWriter, r *http.Request) {
	// For GET request, just serve the page.
	if r.Method == http.MethodGet {
		data := map[string]interface{}{
			"Paths":     config.Paths,
			"CSRFToken": csrf.Token(r),
		}
		if err := templates.ExecuteTemplate(w, "account_password.html", data); err != nil {
			logging.AppLog.Error("Failed to execute account_password template", "error", err)
		}
		return
	}

	// POST request logic (API call)
	if r.Method == http.MethodPost {
		// This is a protected route, so AuthMiddleware should have put the user in the context.
		user, ok := r.Context().Value(types.SessionDataContextKey).(*types.User)
		if !ok || user == nil {
			http.Error(w, "User not authenticated", http.StatusUnauthorized)
			return
		}

		w.Header().Set("Content-Type", "application/json")

		if err := r.ParseForm(); err != nil {
			logging.AppLog.Error("Failed to parse change password form", "error", err)
			w.WriteHeader(http.StatusBadRequest)
			if err := json.NewEncoder(w).Encode(map[string]string{"error": "Could not read request."}); err != nil {
				logging.AppLog.Error("Failed to encode json error response", "error", err)
			}
			return
		}
		currentPassword := r.FormValue("current_password")
		newPassword := r.FormValue("new_password")
		confirmNewPassword := r.FormValue("confirm_new_password")

		if ok, message := utils.ValidatePassword(newPassword); !ok {
			w.WriteHeader(http.StatusBadRequest)
			if err := json.NewEncoder(w).Encode(map[string]string{"error": message}); err != nil {
				logging.AppLog.Error("Failed to encode json error response", "error", err)
			}
			return
		}

		if newPassword != confirmNewPassword {
			w.WriteHeader(http.StatusBadRequest)
			if err := json.NewEncoder(w).Encode(map[string]string{"error": "New passwords do not match."}); err != nil {
				logging.AppLog.Error("Failed to encode json error response", "error", err)
			}
			return
		}

		dbUser, err := models.GetUserByID(user.ID)
		if err != nil || dbUser == nil {
			w.WriteHeader(http.StatusInternalServerError)
			if err := json.NewEncoder(w).Encode(map[string]string{"error": "Could not retrieve user data."}); err != nil {
				logging.AppLog.Error("Failed to encode json error response", "error", err)
			}
			return
		}

		if !models.CheckPasswordHash(currentPassword, dbUser.PasswordHash) {
			w.WriteHeader(http.StatusUnauthorized)
			if err := json.NewEncoder(w).Encode(map[string]string{"error": "Current password is incorrect."}); err != nil {
				logging.AppLog.Error("Failed to encode json error response", "error", err)
			}
			return
		}

		newPasswordHash, err := models.HashPassword(newPassword)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			if err := json.NewEncoder(w).Encode(map[string]string{"error": "Failed to process new password."}); err != nil {
				logging.AppLog.Error("Failed to encode json error response", "error", err)
			}
			return
		}

		if err := models.UpdateUserPassword(dbUser.ID, newPasswordHash); err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			if err := json.NewEncoder(w).Encode(map[string]string{"error": "Failed to update password."}); err != nil {
				logging.AppLog.Error("Failed to encode json error response", "error", err)
			}
			return
		}

		w.WriteHeader(http.StatusOK)
		if err := json.NewEncoder(w).Encode(map[string]string{"message": "Password updated successfully."}); err != nil {
			logging.AppLog.Error("Failed to encode json error response", "error", err)
		}
		return
	}

	// Handle other methods
	http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}