package shared import ( "context" "encoding/json" "net/http" "time" "git.markbailey.dev/cerbervs/ptpp/app/controller" "git.markbailey.dev/cerbervs/ptpp/app/session" "git.markbailey.dev/cerbervs/ptpp/lib/database" "git.markbailey.dev/cerbervs/ptpp/lib/database/dto" "git.markbailey.dev/cerbervs/ptpp/lib/logger" "git.markbailey.dev/cerbervs/ptpp/util" "git.markbailey.dev/cerbervs/ptpp/view/user" ) type PopulateHandler struct { controller.Controller } func (c PopulateHandler) Init(s session.IManager, d database.IDB, l logger.ILogger, ctx controller.IControllerCtx) controller.IController { c.Logger = l c.Db = d c.Session = s c.Ctx = ctx return c } func (c PopulateHandler) Get(w http.ResponseWriter, r *http.Request) error { existingOrg, err := c.Db.Repo().FindOrganizationByName("CerbervsSoft") if existingOrg != nil && err == nil { return util.Redirect(w, r, c.Ctx.GetRouteByName("app.user.sign_up"), http.StatusSeeOther, false) } authToken, err := util.CreateTokenForUser("CerbervsSoft") if err != nil { return err } organization := dto.Organization{ Name: "CerbervsSoft", OwnerName: "Mark Bailey", OwnerEmail: "email@provider.com", OwnerPhone: "+11111111111", Authorized: 1, AuthToken: authToken, CreatedAt: time.Now(), } _, err = c.Db.Repo().CreateOrganization(organization) if err != nil { c.Db.Error(err) return err } return util.Redirect(w, r, c.Ctx.GetRouteByName("app.user.sign_up"), http.StatusSeeOther, false) } type SignInHandler struct { controller.Controller } func (c SignInHandler) Init(s session.IManager, d database.IDB, l logger.ILogger, ctx controller.IControllerCtx) controller.IController { c.Logger = l c.Db = d c.Session = s return c } type UserSignInForm struct { Username string `json:"username"` Password string `json:"password"` } func (c SignInHandler) Get(w http.ResponseWriter, r *http.Request) error { sess := c.Session.SessionStart(w, r) username, ok := r.Context().Value("username").(string) if ok { if username == "" { return failWithFormError(w, r, "Invalid Username or Password.", sess) } else { foundUser, err := c.Db.Repo().FindUserByUsername(username) if foundUser == nil || err != nil { return failWithFormError(w, r, "Invalid Username or Password.", sess) } if foundUser.Admin == 1 { return util.Redirect(w, r, "/admin/", http.StatusSeeOther, false) } else { return util.Redirect(w, r, "/", http.StatusSeeOther, false) } } } formError, ok := sess.Get("formError").(string) if !ok { formError = "" } if err := user.SignIn(formError).Render(context.Background(), w); err != nil { c.Logger.Error(c.Logger.Wrap(err, "Error rendering sign in form")) return err } return nil } func (c SignInHandler) Post(w http.ResponseWriter, r *http.Request) error { sess := c.Session.SessionStart(w, r) fd := &UserSignInForm{} jsonDecoder := json.NewDecoder(r.Body) if err := jsonDecoder.Decode(&fd); err != nil { c.Logger.Error(c.Logger.Wrap(err, "Error decoding JSON")) return err } foundUser, err := c.Db.Repo().FindUserByUsername(fd.Username) if foundUser == nil || err != nil { return util.Redirect(w, r, "/sign-in", http.StatusPermanentRedirect, false) } authenticated, err := util.CheckPassword(fd.Password, foundUser.Password) if err != nil || !authenticated { return failWithFormError(w, r, "Invalid Username or Password.", sess) } err = sess.Set("username", foundUser.Username) if err != nil { return err } token, err := util.CreateTokenForUser(foundUser.Username) if err != nil { c.Logger.Error(c.Logger.Wrap(err, "Error creating token")) return err } err = sess.Set("token", token) if err != nil { return err } authToken, err := util.CreateTokenForUser(foundUser.Username) if err != nil { c.Logger.Error(err) return err } _, err = c.Db.Repo().CreateHeartbeat(&dto.Heartbeat{ User: foundUser.Identifier, CreatedAt: time.Now(), IpAddr: r.RemoteAddr, AuthToken: authToken, }) if err != nil { c.Db.Error(err) return err } jwtToken, err := util.CreateTokenForUser(fd.Username) if err != nil { return err } http.SetCookie(w, &http.Cookie{ Name: "token", Value: jwtToken, Expires: time.Now().Add(time.Hour * 1), Path: "/", }) err = sess.Delete("formError") if err != nil { return err } c.Logger.Info(foundUser.Username + " logged in") if foundUser.Admin == 1 { return util.Redirect(w, r, "/admin/", http.StatusSeeOther, false) } else { return util.Redirect(w, r, "/", http.StatusSeeOther, false) } } type SignUpHandler struct { controller.Controller } func (c SignUpHandler) Init(s session.IManager, d database.IDB, l logger.ILogger, ctx controller.IControllerCtx) controller.IController { c.Logger = l c.Db = d c.Session = s c.Ctx = ctx return c } type UserSignUpForm struct { Name string `json:"name"` Email string `json:"email"` Username string `json:"username"` Password string `json:"password"` PasswordConfirmation string `json:"password_confirmation"` } func (c SignUpHandler) Get(w http.ResponseWriter, r *http.Request) error { sess := c.Session.SessionStart(w, r) uname, ok := sess.Get("username").(string) if !ok { uname = "" } if uname != "" { return util.Redirect(w, r, "/", http.StatusSeeOther, false) } if err := user.SignUpForm().Render(context.Background(), w); err != nil { c.Logger.Error(c.Logger.Wrap(err, "Error rendering sign up form")) return err } return nil } func (c SignUpHandler) Post(w http.ResponseWriter, r *http.Request) error { fd := &UserSignUpForm{} jsonDecoder := json.NewDecoder(r.Body) if err := jsonDecoder.Decode(&fd); err == nil { if fd.Password != fd.PasswordConfirmation { if _, err := w.Write([]byte("Passwords don't match")); err != nil { c.Logger.Error(c.Logger.Wrap(err, "Error writing response")) return err } return nil } foundUser, err := c.Db.Repo().FindUserByUsername(fd.Username) if err != nil { c.Db.Error(err) return err } if foundUser != nil { if _, err := w.Write([]byte("Invalid username. Please try another")); err != nil { c.Logger.Error(c.Logger.Wrap(err, "Error writing response")) } return nil } token, err := util.CreateTokenForUser(fd.Username) if err != nil { c.Logger.Error(c.Logger.Wrap(err, "Error creating token")) return err } password, err := util.HashPassword(fd.PasswordConfirmation) if err != nil { c.Logger.Error(c.Logger.Wrap(err, "Error hashing password")) return err } org, err := c.Db.Repo().FindOrganizationByName("CerbervsSoft") if org == nil || err != nil { c.Db.Error(err) return err } newUser, err := c.Db.Repo().CreateUser(dto.User{ Username: fd.Username, Password: password, Name: fd.Name, Email: fd.Email, AuthToken: token, Authorized: 1, Admin: 1, Organization: org.Identifier, }) if err != nil { c.Db.Error(err) return err } _, err = c.Db.Repo().CreateHeartbeat(&dto.Heartbeat{ User: newUser.Identifier, CreatedAt: time.Now(), IpAddr: r.RemoteAddr, AuthToken: token, }) if err != nil { c.Db.Error(err) return err } jwtToken, err := util.CreateTokenForUser(fd.Username) if err != nil { return err } http.SetCookie(w, &http.Cookie{ Name: "token", Value: jwtToken, Expires: time.Now().Add(time.Hour * 1), Path: "/", }) return util.Redirect(w, r, "/sign-in", http.StatusSeeOther, false) } return nil } type SignOutHandler struct { controller.Controller } func (c SignOutHandler) Init(s session.IManager, d database.IDB, l logger.ILogger, ctx controller.IControllerCtx) controller.IController { c.Logger = l c.Db = d c.Session = s c.Ctx = ctx return c } func (c SignOutHandler) Get(w http.ResponseWriter, r *http.Request) error { sess := c.Session.SessionStart(w, r) err := sess.Delete("username") if err != nil { return err } err = sess.Delete("token") if err != nil { return err } http.SetCookie(w, &http.Cookie{ Name: "token", Value: "", Expires: time.Now().Add(-time.Hour), Path: "/", }) return util.Redirect(w, r, "/", http.StatusSeeOther, true) } func failWithFormError(w http.ResponseWriter, r *http.Request, formError string, sess session.ISession) error { err := sess.Set("formError", formError) if err != nil { return err } return util.Redirect(w, r, r.URL.Path, http.StatusSeeOther, false) }