From 06e0eea782311761288a373341931420a54d74cc Mon Sep 17 00:00:00 2001 From: Braeden Sowinski Date: Mon, 15 Aug 2022 17:22:47 -0700 Subject: [PATCH] include password for creating account --- controllers/authController.go | 187 +++++++++++++++++++++++----------- models/adminModel.go | 40 ++++++-- models/studentModel.go | 12 +-- models/teacherModel.go | 12 +-- 4 files changed, 162 insertions(+), 89 deletions(-) diff --git a/controllers/authController.go b/controllers/authController.go index 9852c21..8b8cfd0 100644 --- a/controllers/authController.go +++ b/controllers/authController.go @@ -5,6 +5,7 @@ import ( "context" "fmt" "log" + "net/mail" "os" "strings" "time" @@ -37,6 +38,14 @@ var AdminCollection *mongo.Collection = database.OpenCollection(database.Client, var ImageCollection *mongo.Collection = database.OpenCollection(database.Client, "images") var LockerCollection *mongo.Collection = database.OpenCollection(database.Client, "lockers") +func validMailAddress(address string) (string, bool) { + addr, err := mail.ParseAddress(address) + if err != nil { + return "", false + } + return addr.Address, true +} + func confirm(s string) bool { r := bufio.NewReader(os.Stdin) @@ -74,7 +83,16 @@ func CreateDefaultAdmin() models.Admin { admin.LastName = lastname admin.Email = email - admin.SchoolEmail = admin.GenerateSchoolEmail() + var schoolEmail string = "" + offset := 0 + for { + schoolEmail = admin.GenerateSchoolEmail(offset, schoolEmail) + if !admin.EmailExists(schoolEmail) { + break + } + offset++ + } + admin.SchoolEmail = schoolEmail pass := strings.TrimSuffix(string(password), "\n") admin.Password = admin.HashPassword(pass) @@ -176,7 +194,10 @@ func Enroll(c *fiber.Ctx) error { } // Check minimum enroll field requirements are met - if data["firstname"] == nil || data["lastname"] == nil || data["age"] == nil || data["gradelevel"] == nil || data["dob"] == nil || data["email"] == nil || data["province"] == nil || data["city"] == nil || data["address"] == nil || data["postal"] == nil { + if data["firstname"] == nil || data["lastname"] == nil || data["age"] == nil || + data["gradelevel"] == nil || data["dob"] == nil || data["email"] == nil || + data["province"] == nil || data["city"] == nil || data["address"] == nil || + data["postal"] == nil || data["password1"] == nil || data["password2"] == nil { cancel() return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, @@ -184,7 +205,32 @@ func Enroll(c *fiber.Ctx) error { }) } + if _, validEmail := validMailAddress(data["email"].(string)); !validEmail { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "invalid email address", + }) + } + + if data["password1"] != data["password2"] { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "the passwords chosen must match", + }) + } + var student models.Student + + if !student.CheckPasswordStrength(data["password1"].(string)) { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "your password isnt strong enough", + }) + } + student.PersonalData.FirstName = data["firstname"].(string) student.PersonalData.MiddleName = data["middlename"].(string) student.PersonalData.LastName = data["lastname"].(string) @@ -227,23 +273,8 @@ func Enroll(c *fiber.Ctx) error { student.AccountData.Alerted = false student.AccountData.Attempts = 0 - // Generate temporary password - var tempPass string = student.GeneratePassword(12, 1, 1, 1) - student.AccountData.Password = student.HashPassword(tempPass) - student.AccountData.TempPassword = true - - // Send student personal email temp password - subject := "Password Changed" - receiver := student.PersonalData.Email - r := NewRequest([]string{receiver}, subject) - - if sent := r.Send("./templates/passwordChanged.html", map[string]string{"username": student.PersonalData.FirstName, "password": tempPass}); !sent { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Could not send password to students email", - }) - } + student.AccountData.Password = data["password1"].(string) + student.AccountData.TempPassword = false var sid string for { @@ -255,8 +286,9 @@ func Enroll(c *fiber.Ctx) error { student.SchoolData.SID = sid // Send student personal email student ID - subject = "Account Registered" - r = NewRequest([]string{receiver}, subject) + subject := "Account Registered" + receiver := student.PersonalData.Email + r := NewRequest([]string{receiver}, subject) if sent := r.Send("./templates/accountRegisreded.html", map[string]string{"username": student.PersonalData.FirstName, "id": sid, "userType": "student"}); !sent { cancel() @@ -329,7 +361,8 @@ func RegisterTeacher(c *fiber.Ctx) error { } // Check minimum register teacher field requirements are met - if data["firstname"] == nil || data["lastname"] == nil || data["dob"] == nil || data["email"] == nil { + if data["firstname"] == nil || data["lastname"] == nil || data["dob"] == nil || data["email"] == nil || + data["password1"] == nil || data["password2"] == nil { cancel() return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, @@ -337,7 +370,32 @@ func RegisterTeacher(c *fiber.Ctx) error { }) } + if _, validEmail := validMailAddress(data["email"].(string)); !validEmail { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "invalid email address", + }) + } + + if data["password1"] != data["password2"] { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "the passwords chosen must match", + }) + } + var teacher models.Teacher + + if !teacher.CheckPasswordStrength(data["password1"].(string)) { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "your password isnt strong enough", + }) + } + teacher.PersonalData.FirstName = data["firstname"].(string) teacher.PersonalData.MiddleName = data["middlename"].(string) teacher.PersonalData.LastName = data["lastname"].(string) @@ -374,22 +432,8 @@ func RegisterTeacher(c *fiber.Ctx) error { teacher.AccountData.AccountDisabled = false teacher.AccountData.Attempts = 0 - var tempPass string = teacher.GeneratePassword(12, 1, 1, 1) - teacher.AccountData.Password = teacher.HashPassword(tempPass) - teacher.AccountData.TempPassword = true - - // Send teacher personal email temp password - subject := "Password Changed" - receiver := teacher.PersonalData.Email - r := NewRequest([]string{receiver}, subject) - - if sent := r.Send("./templates/passwordChanged.html", map[string]string{"username": teacher.PersonalData.FirstName, "password": tempPass}); !sent { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Could not send password to teachers email", - }) - } + teacher.AccountData.Password = data["password"].(string) + teacher.AccountData.TempPassword = false var tid string // For the unlikely event that an ID is already in use this will simply try again till it gets a id not in use @@ -402,8 +446,9 @@ func RegisterTeacher(c *fiber.Ctx) error { teacher.SchoolData.TID = tid // Send teacher personal email student ID - subject = "Account Registered" - r = NewRequest([]string{receiver}, subject) + subject := "Account Registered" + receiver := teacher.PersonalData.Email + r := NewRequest([]string{receiver}, subject) if sent := r.Send("./templates/accountRegisreded.html", map[string]string{"username": teacher.PersonalData.FirstName, "id": tid, "userType": "teacher"}); !sent { cancel() @@ -457,7 +502,8 @@ func CreateAdmin(c *fiber.Ctx) error { } // Check minimum register teacher field requirements are met - if data["firstname"] == nil || data["lastname"] == nil || data["dob"] == nil || data["email"] == nil { + if data["firstname"] == nil || data["lastname"] == nil || data["dob"] == nil || data["email"] == nil || + data["password1"] == nil || data["password2"] == nil { cancel() return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, @@ -465,29 +511,49 @@ func CreateAdmin(c *fiber.Ctx) error { }) } + if _, validEmail := validMailAddress(data["email"].(string)); !validEmail { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "invalid email address", + }) + } + + if data["password1"] != data["password2"] { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "the passwords chosen must match", + }) + } + var admin models.Admin + + if !admin.CheckPasswordStrength(data["password1"].(string)) { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "your password isnt strong enough", + }) + } + admin.FirstName = data["firstname"].(string) admin.LastName = data["lastname"].(string) admin.Email = data["email"].(string) - admin.SchoolEmail = admin.GenerateSchoolEmail() - - tempPass := admin.GeneratePassword(12, 1, 1, 1) - admin.Password = admin.HashPassword(tempPass) - admin.TempPassword = true - - // Send admin personal email temp password - subject := "Password Changed" - receiver := admin.Email - r := NewRequest([]string{receiver}, subject) - - if sent := r.Send("./templates/passwordChanged.html", map[string]string{"username": admin.FirstName, "password": tempPass}); !sent { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Could not send password to admins email", - }) + var schoolEmail string = "" + offset := 0 + for { + schoolEmail = admin.GenerateSchoolEmail(offset, schoolEmail) + if !admin.EmailExists(schoolEmail) { + break + } + offset++ } + admin.SchoolEmail = schoolEmail + + admin.Password = data["password1"].(string) + admin.TempPassword = false var aid string for { @@ -499,8 +565,9 @@ func CreateAdmin(c *fiber.Ctx) error { admin.AID = aid // Send student personal email student ID - subject = "Account Registered" - r = NewRequest([]string{receiver}, subject) + subject := "Account Registered" + receiver := admin.Email + r := NewRequest([]string{receiver}, subject) if sent := r.Send("./templates/accountRegisreded.html", map[string]string{"username": admin.FirstName, "id": aid, "userType": "admin"}); !sent { cancel() diff --git a/models/adminModel.go b/models/adminModel.go index 208884d..4ab7425 100644 --- a/models/adminModel.go +++ b/models/adminModel.go @@ -1,17 +1,24 @@ package models import ( + "context" "math/rand" "os" + "strconv" "strings" "time" "unicode" + "github.com/SowinskiBraeden/school-management-api/database" "golang.org/x/crypto/bcrypt" + "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" ) +var AdminCollection *mongo.Collection = database.OpenCollection(database.Client, "admins") + type Admin struct { ID primitive.ObjectID `bson:"_id"` FirstName string `json:"firstname" validate:"required"` @@ -25,10 +32,24 @@ type Admin struct { Updated_at time.Time `json:"updated_at"` } -func (a *Admin) GenerateSchoolEmail() string { +func (a *Admin) EmailExists(email string) bool { + var admin Admin + findErr := AdminCollection.FindOne(context.TODO(), bson.M{"accountdata.schoolemail": email}).Decode(&admin) + return findErr == nil +} + +func (a *Admin) GenerateSchoolEmail(offset int, lastEmail string) string { addr := os.Getenv("SYSTEM_EMAIL_ADDRESS") var email string = strings.ToLower(a.LastName) + "_" + strings.ToLower(string(a.FirstName[0])) + addr - // Add check to see if email already exists + if offset > 0 && offset < len([]rune(a.FirstName))-1 { + email = lastEmail[:strings.Index(lastEmail, "_")+offset+1] + strings.ToLower(string(a.FirstName[offset])) + addr + } + if offset == len([]rune(a.FirstName))-1 { + email = strings.ToLower(a.LastName) + "_" + strings.ToLower(a.FirstName) + addr + } + if offset > len([]rune(a.FirstName))-1 { + email = strings.ToLower(a.LastName) + "_" + strings.ToLower(a.FirstName) + strconv.Itoa(offset-len([]rune(a.FirstName))) + addr + } return email } @@ -37,15 +58,12 @@ func (s *Admin) HashPassword(password string) string { return string(hash) } -func (s *Admin) ComparePasswords(password string) bool { - err := bcrypt.CompareHashAndPassword([]byte(s.Password), []byte(password)) - if err != nil { - return false - } - return true +func (a *Admin) ComparePasswords(password string) bool { + err := bcrypt.CompareHashAndPassword([]byte(a.Password), []byte(password)) + return err == nil } -func (s *Admin) CheckPasswordStrength(password string) bool { +func (a *Admin) CheckPasswordStrength(password string) bool { var hasUpper bool = false for _, r := range password { @@ -61,14 +79,14 @@ func (s *Admin) CheckPasswordStrength(password string) bool { } } - if strings.ContainsAny(password, specialCharSet) && hasLower && hasUpper { + if strings.ContainsAny(password, specialCharSet) && hasLower && hasUpper && len(password) >= 8 { return true } else { return false } } -func (s *Admin) GeneratePassword(passwordLength, minSpecialChar, minNum, minUpperCase int) string { +func (a *Admin) GeneratePassword(passwordLength, minSpecialChar, minNum, minUpperCase int) string { var password strings.Builder //Set special character diff --git a/models/studentModel.go b/models/studentModel.go index 0a06967..90b07a4 100644 --- a/models/studentModel.go +++ b/models/studentModel.go @@ -76,10 +76,7 @@ func (s *Student) HashPassword(password string) string { func (s *Student) EmailExists(email string) bool { var student Student findErr := StudentCollection.FindOne(context.TODO(), bson.M{"accountdata.schoolemail": email}).Decode(&student) - if findErr != nil { - return false - } - return true + return findErr == nil } func (s *Student) GenerateSchoolEmail(offset int, lastEmail string) string { @@ -99,10 +96,7 @@ func (s *Student) GenerateSchoolEmail(offset int, lastEmail string) string { func (s *Student) ComparePasswords(password string) bool { //True: passwords match, False: no match valid := bcrypt.CompareHashAndPassword([]byte(s.AccountData.Password), []byte(password)) - if valid != nil { - return false - } - return true + return valid == nil } func (s *Student) CheckPasswordStrength(password string) bool { @@ -121,7 +115,7 @@ func (s *Student) CheckPasswordStrength(password string) bool { } } - if strings.ContainsAny(password, specialCharSet) && hasLower && hasUpper { + if strings.ContainsAny(password, specialCharSet) && hasLower && hasUpper && len(password) >= 8 { return true } else { return false diff --git a/models/teacherModel.go b/models/teacherModel.go index acb256f..97a00a4 100644 --- a/models/teacherModel.go +++ b/models/teacherModel.go @@ -74,10 +74,7 @@ func (t *Teacher) HashPassword(password string) string { func (t *Teacher) EmailExists(email string) bool { var teacher Teacher findErr := TeacherCollection.FindOne(context.TODO(), bson.M{"accountdata.schoolemail": email}).Decode(&teacher) - if findErr != nil { - return false - } - return true + return findErr == nil } func (t *Teacher) GenerateSchoolEmail(offset int, lastEmail string) string { @@ -97,10 +94,7 @@ func (t *Teacher) GenerateSchoolEmail(offset int, lastEmail string) string { func (t *Teacher) ComparePasswords(password string) bool { err := bcrypt.CompareHashAndPassword([]byte(t.AccountData.Password), []byte(password)) - if err != nil { - return false - } - return true + return err == nil } func (t *Teacher) CheckPasswordStrength(password string) bool { @@ -119,7 +113,7 @@ func (t *Teacher) CheckPasswordStrength(password string) bool { } } - if strings.ContainsAny(password, specialCharSet) && hasLower && hasUpper { + if strings.ContainsAny(password, specialCharSet) && hasLower && hasUpper && len(password) >= 8 { return true } else { return false