package models import ( "encoding/json" "log" "strings" "time" "github.com/gobuffalo/nulls" "github.com/gobuffalo/pop/v6" "github.com/gobuffalo/validate/v3" "github.com/gobuffalo/validate/v3/validators" "github.com/gofrs/uuid" "github.com/pkg/errors" "golang.org/x/crypto/bcrypt" ) // User is a generated model from buffalo-auth, it serves as the base for username/password authentication. type User struct { ID uuid.UUID `json:"id" db:"id"` Name string `json:"name" db:"name" form:"name"` Username string `json:"username" db:"username" form:"username"` Email nulls.String `json:"email" db:"email" form:"email"` PasswordHash string `json:"password_hash" db:"password_hash"` Provider string `json:"provider" db:"provider"` ProviderID string `json:"provider_id" db:"provider_id"` Password string `json:"-" db:"-" form:"password"` PasswordConfirmation string `json:"-" db:"-" form:"password_confirmation"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` EmailVerification UserEmailVerification `has_one:"user_email_verification" fk_id:"owner_id"` Preferences UserPreference `has_one:"user_preference" fk_id:"owner_id" form:"preferences"` } // Create wraps up the pattern of encrypting the password and // running validations. Useful when writing tests. func (u *User) Create(tx *pop.Connection) (*validate.Errors, error) { emailStr := u.Email.Interface().(string) u.Email = nulls.NewString(strings.ToLower(emailStr)) ph, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost) if err != nil { return validate.NewErrors(), errors.WithStack(err) } u.PasswordHash = string(ph) return tx.ValidateAndCreate(u) } // String is not required by pop and may be deleted func (u User) String() string { ju, _ := json.Marshal(u) return string(ju) } // Users is not required by pop and may be deleted type Users []User // String is not required by pop and may be deleted func (u Users) String() string { ju, _ := json.Marshal(u) return string(ju) } // Validate gets run every time you call a "pop.Validate*" (pop.ValidateAndSave, pop.ValidateAndCreate, pop.ValidateAndUpdate) method. // This method is not required and may be deleted. func (u *User) Validate(tx *pop.Connection) (*validate.Errors, error) { var err error return validate.Validate( &validators.EmailIsPresent{Field: u.Email.Interface().(string), Name: "Email", Message: "Not a valid email address"}, &validators.FuncValidator{ Field: u.Email.Interface().(string), Name: "Email", Message: "%s is already taken", Fn: func() bool { var b bool q := tx.Where("email = ?", u.Email) if u.ID != uuid.Nil { q = q.Where("id != ?", u.ID) } b, err = q.Exists(u) if err != nil { return false } return !b }, }, ), err } // ValidateCreate gets run every time you call "pop.ValidateAndCreate" method. // This method is not required and may be deleted. func (u *User) ValidateCreate(tx *pop.Connection) (*validate.Errors, error) { var err error return validate.Validate( &validators.StringIsPresent{Field: u.Password, Name: "Password"}, &validators.StringsMatch{Name: "Password", Field: u.Password, Field2: u.PasswordConfirmation, Message: "Password does not match confirmation"}, ), err } // ValidateUpdate gets run every time you call "pop.ValidateAndUpdate" method. // This method is not required and may be deleted. func (u *User) ValidateUpdate(tx *pop.Connection) (*validate.Errors, error) { log.Println(u.Preferences) if u.Password != "" || u.PasswordConfirmation != "" { ph, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost) if err != nil { return validate.NewErrors(), errors.WithStack(err) } u.PasswordHash = string(ph) return validate.Validate( &validators.StringIsPresent{Field: u.Password, Name: "Password", Message: "Password must not be empty"}, &validators.StringIsPresent{Field: u.PasswordConfirmation, Name: "PasswordConfirmation", Message: "Confirmation must be present"}, &validators.StringsMatch{Name: "Password", Field: u.Password, Field2: u.PasswordConfirmation, Message: "Password does not match confirmation"}, ), nil } return validate.NewErrors(), nil }