package main import ( "errors" "fmt" ratelimit "github.com/JGLTechnologies/gin-rate-limit" "github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions/cookie" "github.com/gin-gonic/gin" "html/template" "net/http" "os" "strconv" "strings" "time" ) var sessionStore sessions.Store var htmlRL gin.HandlerFunc var jsonRL gin.HandlerFunc func createSessionStore() { secureBool, err := strconv.ParseBool(os.Getenv("SECURE_SESSION")) if err != nil { fmt.Printf("Unable to get Secure Session Env, defaulting to \"true\". Error: %v\n", err.Error()) secureBool = true } sessionStore = cookie.NewStore([]byte(os.Getenv("AUTHENTICATION_KEY")), []byte(os.Getenv("ENCRYPTION_KEY"))) sessionStore.Options(sessions.Options{ Path: "/", MaxAge: 60 * 60 * 24, // 1 day HttpOnly: true, Secure: secureBool, SameSite: http.SameSiteLaxMode, }) } func keyFunc(c *gin.Context) string { return c.ClientIP() } func createRateLimiters() { store := ratelimit.InMemoryStore(&ratelimit.InMemoryOptions{ Rate: time.Second, Limit: 5, }) htmlRL = ratelimit.RateLimiter(store, &ratelimit.Options{ ErrorHandler: func(c *gin.Context, info ratelimit.Info) { c.HTML(http.StatusTooManyRequests, "base.html.tmpl", gin.H{ "header": "Error", "body": fmt.Sprintf("Too many requests. Try again in %v", time.Until(info.ResetTime).String()), }) }, KeyFunc: keyFunc, }) jsonRL = ratelimit.RateLimiter(store, &ratelimit.Options{ ErrorHandler: func(c *gin.Context, info ratelimit.Info) { c.JSON(http.StatusTooManyRequests, gin.H{}) }, KeyFunc: keyFunc, }) } func webRoot(c *gin.Context) { c.File("./html/home.html") } func webRegister(c *gin.Context) { c.File("./html/register.html") } func webRegisterSuccess(c *gin.Context) { c.HTML(http.StatusOK, "base.html.tmpl", gin.H{ "header": "Registration Success", "body": "We will reach out soon to get your tag to you!", }) } func webQr(c *gin.Context) { c.File("./html/genqr.html") } func webPing(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "pong", }) } func webUserApi(c *gin.Context) { user, err := db.queryUser(strings.ToLower(c.Param("user"))) if err != nil { if errors.Is(err, NoEntriesFoundError) { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ "status": 401, "error": "Unauthorized", }) return } c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ "status": 500, "error": fmt.Sprintf("Internal Server Error: %s", err.Error()), }) return } if user.CurrentToken == nil { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ "status": 401, "error": "Unauthorized", }) return } if strings.Compare(c.Query("token"), *user.CurrentToken) == 0 { user.Status = 200 c.JSON(http.StatusOK, user) return } c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ "status": 401, "error": "Unauthorized", }) } func webVerifyUserApi(c *gin.Context) { user, err := db.queryUser(strings.ToLower(c.Param("user"))) if err != nil { if errors.Is(err, NoEntriesFoundError) { c.AbortWithStatusJSON(http.StatusNotFound, gin.H{ "status": 404, "user": "", "error": "User not found", }) return } fmt.Printf("Error: %s\n", err) c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{ "status": 500, "user": "", "error": err.Error(), }) return } codes := strings.Split(user.SecretCodes, ",") codeHeader := c.GetHeader("Authorization") reqCodeRaw := strings.Split(codeHeader, " ") reqCode := strings.ReplaceAll(reqCodeRaw[len(reqCodeRaw)-1], " ", "") for _, code := range codes { if strings.Compare(code, reqCode) == 0 { token, err := GenerateToken(16) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "status": 500, "user": "", "error": err.Error(), }) return } err = db.updateToken(user.UserName, token) // TODO make a more robust system for authorizing info page if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "status": 500, "user": "", "error": err.Error(), }) return } user.Status = 200 c.JSON(http.StatusOK, gin.H{ "user": user.UserName, "error": "", "token": token, }) return } } c.JSON(http.StatusNotFound, gin.H{ "status": 404, "user": "", "error": "User not found", }) } func webCheckNameApi(c *gin.Context) { users, err := db.getUsers() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "status": 500, "error": err.Error(), }) return } anyMatch := false for _, user := range users { if strings.Compare(user, strings.ToLower(c.Param("user"))) == 0 { anyMatch = true break } } if anyMatch { c.JSON(http.StatusConflict, gin.H{ "status": 409, "error": "userid already exists", }) return } c.JSON(http.StatusOK, gin.H{ "status": 200, "error": "", }) } func webRegisterApi(c *gin.Context) { //TODO refactor with JS form submission or under logged in user dashboard var newUser User err := c.ShouldBind(&newUser) if err != nil { fmt.Printf("Error: %v\n", err) c.HTML(http.StatusInternalServerError, "base.html.tmpl", gin.H{ "header": "Registration Error", "body": err.Error(), }) return } err = db.checkRegistrationCode(newUser.RegistrationCode) if errors.Is(err, NoEntriesFoundError) { c.HTML(http.StatusNotFound, "base.html.tmpl", gin.H{ "header": "Registration Error", "body": err.Error(), }) return } c.Redirect(http.StatusSeeOther, "/register/success") } func webUser(c *gin.Context) { c.File("./html/verify.html") } func webUserInfo(c *gin.Context) { user, err := db.queryUser(strings.ToLower(c.Param("user"))) if err != nil { if errors.Is(err, NoEntriesFoundError) { body := template.HTML("The user searched is not found, please try again.") c.HTML(http.StatusNotFound, "base.html.tmpl", gin.H{ "header": "User Not Found", "body": body, }) return } } if user.CurrentToken == nil { c.HTML(http.StatusUnauthorized, "base.html.tmpl", gin.H{ "header": "Unauthorized", "body": "You don't have the right token, please try again.", }) return } if strings.Compare(c.Query("token"), *user.CurrentToken) == 0 { if err != nil { if errors.Is(err, NoEntriesFoundError) { c.HTML(http.StatusUnauthorized, "base.html.tmpl", gin.H{ "header": "Unauthorized", "body": "You don't have the right token, please try again.", }) return } fmt.Printf("Error: %s\n", err) c.HTML(http.StatusInternalServerError, "base.html.tmpl", gin.H{ "header": "Error", "body": err.Error(), }) return } addressStr := fmt.Sprintf("%s", user.MailingAddress.Street1) if user.MailingAddress.Street2 == nil { addressStr = fmt.Sprintf("%s, %s", addressStr, user.MailingAddress.City) } else { addressStr = fmt.Sprintf("%s, %s, %s", addressStr, *user.MailingAddress.Street2, user.MailingAddress.City) } if user.MailingAddress.State == nil { addressStr = fmt.Sprintf("%s, %s, %s", addressStr, user.MailingAddress.PostalCode, user.MailingAddress.Country) } else { addressStr = fmt.Sprintf("%s, %s, %s, %s", addressStr, *user.MailingAddress.State, user.MailingAddress.PostalCode, user.MailingAddress.Country) } c.HTML(http.StatusOK, "info.html.tmpl", gin.H{ "contact_name": user.ContactName, "phone_number": user.ContactNumber, "email_address": user.EmailAddress, "address": addressStr, }) return } c.HTML(http.StatusUnauthorized, "base.html.tmpl", gin.H{ "header": "Unauthorized", "body": "You don't have the right token :/", }) } func webLoginAuth(c *gin.Context) { } func webLoginAuthPost(c *gin.Context) { } func webLogoutAuth(c *gin.Context) { }