feat: validate auth and user request structs
Ultraworked with Sisyphus (https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
|||||||
"crussell/auth"
|
"crussell/auth"
|
||||||
"crussell/db"
|
"crussell/db"
|
||||||
"crussell/internal/dav"
|
"crussell/internal/dav"
|
||||||
|
"crussell/internal/validators"
|
||||||
"crussell/mw"
|
"crussell/mw"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
@@ -56,19 +57,19 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RegisterRequest struct {
|
type RegisterRequest struct {
|
||||||
FirstName string `json:"firstName"`
|
FirstName string `json:"firstName" validate:"required,min=1,max=50"`
|
||||||
LastName string `json:"lastName"`
|
LastName string `json:"lastName" validate:"required,min=1,max=50"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email" validate:"required,email,max=254"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password" validate:"required,max=72"`
|
||||||
Phone string `json:"phone"`
|
Phone string `json:"phone" validate:"required"`
|
||||||
DateOfBirth string `json:"dateOfBirth"`
|
DateOfBirth string `json:"dateOfBirth" validate:"required"`
|
||||||
AgreedToPolicy bool `json:"agreedToPolicy"`
|
AgreedToPolicy bool `json:"agreedToPolicy"`
|
||||||
ReferralCode string `json:"referralCode,omitempty"`
|
ReferralCode string `json:"referralCode,omitempty" validate:"omitempty,max=12"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LoginRequest struct {
|
type LoginRequest struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email" validate:"required,email,max=254"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password" validate:"required,max=72"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST /api/register
|
// POST /api/register
|
||||||
@@ -79,6 +80,11 @@ func RegisterHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := validators.Validate.Struct(&req); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Must accept terms
|
// Must accept terms
|
||||||
if !req.AgreedToPolicy {
|
if !req.AgreedToPolicy {
|
||||||
http.Error(w, "must agree to terms", http.StatusBadRequest)
|
http.Error(w, "must agree to terms", http.StatusBadRequest)
|
||||||
@@ -282,6 +288,11 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := validators.Validate.Struct(&req); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Normalize email
|
// Normalize email
|
||||||
req.Email = strings.ToLower(strings.TrimSpace(req.Email))
|
req.Email = strings.ToLower(strings.TrimSpace(req.Email))
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,14 @@ import (
|
|||||||
|
|
||||||
"crussell/db"
|
"crussell/db"
|
||||||
"crussell/handlers/auth"
|
"crussell/handlers/auth"
|
||||||
|
"crussell/internal/validators"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CreateGuestUserRequest struct {
|
type CreateGuestUserRequest struct {
|
||||||
FirstName string `json:"firstName"`
|
FirstName string `json:"firstName" validate:"required,min=1,max=50"`
|
||||||
LastName string `json:"lastName"`
|
LastName string `json:"lastName" validate:"required,min=1,max=50"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email" validate:"required,email,max=254"`
|
||||||
Phone string `json:"phone"`
|
Phone string `json:"phone" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateGuestUserResponse struct {
|
type CreateGuestUserResponse struct {
|
||||||
@@ -32,6 +33,11 @@ func CreateGuestUserHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := validators.Validate.Struct(&req); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Normalize input
|
// Normalize input
|
||||||
req.FirstName = strings.TrimSpace(req.FirstName)
|
req.FirstName = strings.TrimSpace(req.FirstName)
|
||||||
req.LastName = strings.TrimSpace(req.LastName)
|
req.LastName = strings.TrimSpace(req.LastName)
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import (
|
|||||||
|
|
||||||
"crussell/db"
|
"crussell/db"
|
||||||
"crussell/handlers/auth"
|
"crussell/handlers/auth"
|
||||||
|
"crussell/internal/images"
|
||||||
"crussell/internal/s3"
|
"crussell/internal/s3"
|
||||||
"crussell/internal/validators"
|
"crussell/internal/validators"
|
||||||
"crussell/mw"
|
"crussell/mw"
|
||||||
@@ -52,9 +53,9 @@ type UserProfile struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UpdateProfileRequest struct {
|
type UpdateProfileRequest struct {
|
||||||
FirstName string `json:"firstName"`
|
FirstName string `json:"firstName" validate:"required,min=1,max=50"`
|
||||||
LastName string `json:"lastName"`
|
LastName string `json:"lastName" validate:"required,min=1,max=50"`
|
||||||
Phone string `json:"phone"`
|
Phone string `json:"phone" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AdminUserDetail struct {
|
type AdminUserDetail struct {
|
||||||
@@ -172,7 +173,11 @@ END:VCARD`, uid, firstName, lastName, lastName, firstName, email, phone, dob, ph
|
|||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "text/vcard; charset=utf-8")
|
req.Header.Set("Content-Type", "text/vcard; charset=utf-8")
|
||||||
req.SetBasicAuth("admin", "admin")
|
davPassword := os.Getenv("DAV_ADMIN_PASSWORD")
|
||||||
|
if davPassword == "" {
|
||||||
|
davPassword = "admin"
|
||||||
|
}
|
||||||
|
req.SetBasicAuth("admin", davPassword)
|
||||||
|
|
||||||
client := &http.Client{Timeout: 10 * time.Second}
|
client := &http.Client{Timeout: 10 * time.Second}
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
@@ -198,6 +203,11 @@ func UpdateProfileHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := validators.Validate.Struct(&req); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Normalize input
|
// Normalize input
|
||||||
req.FirstName = strings.TrimSpace(req.FirstName)
|
req.FirstName = strings.TrimSpace(req.FirstName)
|
||||||
req.LastName = strings.TrimSpace(req.LastName)
|
req.LastName = strings.TrimSpace(req.LastName)
|
||||||
@@ -511,8 +521,8 @@ func ListAdminUsersHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ChangePasswordRequest struct {
|
type ChangePasswordRequest struct {
|
||||||
CurrentPassword string `json:"current_password"`
|
CurrentPassword string `json:"current_password" validate:"required,max=72"`
|
||||||
NewPassword string `json:"new_password"`
|
NewPassword string `json:"new_password" validate:"required,min=8,max=72"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func ChangePasswordHandler(w http.ResponseWriter, r *http.Request) {
|
func ChangePasswordHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -528,6 +538,11 @@ func ChangePasswordHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := validators.Validate.Struct(&req); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if req.CurrentPassword == "" || req.NewPassword == "" {
|
if req.CurrentPassword == "" || req.NewPassword == "" {
|
||||||
http.Error(w, "current password and new password are required", http.StatusBadRequest)
|
http.Error(w, "current password and new password are required", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
@@ -789,7 +804,7 @@ func UploadProfilePictureHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
r.ParseMultipartForm(10 << 20)
|
r.ParseMultipartForm(10 << 20)
|
||||||
|
|
||||||
file, header, err := r.FormFile("file")
|
file, _, err := r.FormFile("file")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to get file: %v", err)
|
log.Printf("Failed to get file: %v", err)
|
||||||
http.Error(w, "No file provided", http.StatusBadRequest)
|
http.Error(w, "No file provided", http.StatusBadRequest)
|
||||||
@@ -797,13 +812,6 @@ func UploadProfilePictureHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
ext := ".jpg"
|
|
||||||
if idx := strings.LastIndex(header.Filename, "."); idx != -1 {
|
|
||||||
ext = strings.ToLower(header.Filename[idx:])
|
|
||||||
}
|
|
||||||
|
|
||||||
key := fmt.Sprintf("profiles/%s%s", userID, ext)
|
|
||||||
|
|
||||||
fileBytes, err := io.ReadAll(file)
|
fileBytes, err := io.ReadAll(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to read file: %v", err)
|
log.Printf("Failed to read file: %v", err)
|
||||||
@@ -811,6 +819,14 @@ func UploadProfilePictureHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := images.ValidateImageBytes(fileBytes); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ext := ".jpg"
|
||||||
|
key := fmt.Sprintf("profiles/%s%s", userID, ext)
|
||||||
|
|
||||||
fileBytes, err = processProfileImage(fileBytes)
|
fileBytes, err = processProfileImage(fileBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to process image: %v", err)
|
log.Printf("Failed to process image: %v", err)
|
||||||
@@ -870,14 +886,14 @@ type ContactInfo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type NotificationPreferencesResponse struct {
|
type NotificationPreferencesResponse struct {
|
||||||
EmailEnabled bool `json:"emailEnabled"`
|
EmailEnabled bool `json:"emailEnabled"`
|
||||||
SMSEnabled bool `json:"smsEnabled"`
|
SMSEnabled bool `json:"smsEnabled"`
|
||||||
BrowserPushEnabled bool `json:"browserPushEnabled"`
|
BrowserPushEnabled bool `json:"browserPushEnabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateNotificationPreferencesRequest struct {
|
type UpdateNotificationPreferencesRequest struct {
|
||||||
EmailEnabled *bool `json:"emailEnabled"`
|
EmailEnabled *bool `json:"emailEnabled"`
|
||||||
SMSEnabled *bool `json:"smsEnabled"`
|
SMSEnabled *bool `json:"smsEnabled"`
|
||||||
BrowserPushEnabled *bool `json:"browserPushEnabled"`
|
BrowserPushEnabled *bool `json:"browserPushEnabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user