MapyWeb/main.go

230 lines
5.9 KiB
Go
Raw Normal View History

2020-01-11 17:56:14 +00:00
package main
import (
"fmt"
"html/template"
2021-09-20 10:36:20 +00:00
"net"
2020-01-11 17:56:14 +00:00
"net/http"
"regexp"
2021-09-20 10:36:20 +00:00
"strconv"
2020-01-11 17:56:14 +00:00
"strings"
"time"
2021-09-02 03:45:16 +00:00
2021-09-20 10:36:20 +00:00
"github.com/valyala/fasthttp"
"github.com/rs/zerolog/log"
2021-09-02 03:45:16 +00:00
"github.com/badoux/checkmail"
"github.com/gorilla/csrf"
"github.com/gorilla/mux"
_ "github.com/lib/pq"
"github.com/satori/go.uuid"
2021-09-20 10:36:20 +00:00
"mapyweb/db"
"mapyweb/extra"
2020-01-11 17:56:14 +00:00
)
2021-09-02 03:45:16 +00:00
func init() {
2021-09-20 10:36:20 +00:00
extra.Banner()
db.StartDatabases()
}
2021-09-02 03:45:16 +00:00
func main() {
HTTPServ()
}
2021-09-20 10:36:20 +00:00
type authError string
const (
usernameExists authError = "username is taken"
emailExists authError = "email is taken"
rateLimited authError = "you are being ratelimited, slow down"
badEmail authError = "not a valid email address"
internalErr authError = "internal server error"
invalidRequest authError = "invalid request"
badCreds authError = "invalid credentials"
)
func HTTPServ() {
r := mux.NewRouter()
2021-09-20 10:36:20 +00:00
r.HandleFunc("/", showIndex)
r.HandleFunc("/register", registrationForm)
r.HandleFunc("/login", loginForm)
r.HandleFunc("/EmailTest", EmailTest)
fmt.Println("Web server starting on port 42069")
2021-09-20 10:36:20 +00:00
log.Fatal().Err(http.ListenAndServe(":42069", csrf.Protect([]byte("7e3e2a60a55a223589f0bf218f23251619182602ae19fd829803d18645379f66"), csrf.Secure(false))(r))).
Msg("http failure")
}
2021-09-20 10:36:20 +00:00
func logAndReturnError(w http.ResponseWriter, e authError) {
log.Error().Str("error", string(e)).Msg("auth_fail")
if _, err := fmt.Fprint(w, e); err != nil {
log.Debug().Err(err).Msg("failed to write to ResponseWriter")
}
2020-01-11 17:56:14 +00:00
}
2021-09-20 10:36:20 +00:00
func fprinter(w http.ResponseWriter, s string, a ...interface{}) {
if _, err := fmt.Fprint(w, s, a); err != nil {
log.Debug().Err(err).Msg("failed to write to ResponseWriter")
}
}
func showIndex(w http.ResponseWriter, r *http.Request) {
2020-01-12 00:34:02 +00:00
c, err := r.Cookie("session_token")
if err != nil {
if err == http.ErrNoCookie {
// If the cookie is not set, return an unauthorized status
w.WriteHeader(http.StatusUnauthorized)
return
}
// For any other type of error, return a bad request status
w.WriteHeader(http.StatusBadRequest)
return
}
sessionToken := c.Value
// We then get the name of the user from our cache, where we set the session token
response, err := cache.Do("GET", sessionToken)
if err != nil {
// If there is an error fetching from cache, return an internal server error status
w.WriteHeader(http.StatusInternalServerError)
return
}
if response == nil {
// If the session token is not present in cache, redirect to login screen
http.Redirect(w, r, "/login", http.StatusSeeOther)
return
}
2021-09-20 10:36:20 +00:00
2020-01-12 00:34:02 +00:00
// Finally, return the welcome message to the user
2021-09-20 10:36:20 +00:00
fprinter(w, "Welcome %s!", response)
2020-01-11 17:56:14 +00:00
}
2021-09-20 10:36:20 +00:00
func loginForm(w http.ResponseWriter, r *http.Request) {
ip := strings.Split(r.RemoteAddr, ":")[0]
fmt.Println(ip)
2021-09-20 10:36:20 +00:00
t, err := template.ParseFiles("login.tmpl")
if err != nil {
panic(err)
}
if err := t.ExecuteTemplate(w, "login.tmpl", map[string]interface{}{
csrf.TemplateTag: csrf.TemplateField(r),
2021-09-20 10:36:20 +00:00
}); err != nil {
panic(err)
}
}
2021-09-20 10:36:20 +00:00
func registrationForm(w http.ResponseWriter, r *http.Request) {
ip := strings.Split(r.RemoteAddr, ":")[0]
fmt.Println(ip)
2021-09-20 10:36:20 +00:00
t, err := template.ParseFiles("register.tmpl")
if err != nil {
panic(err)
}
if err := t.ExecuteTemplate(w, "register.tmpl", map[string]interface{}{
csrf.TemplateTag: csrf.TemplateField(r),
2021-09-20 10:36:20 +00:00
}); err != nil {
panic(err)
}
}
2020-01-11 17:56:14 +00:00
2021-09-20 10:36:20 +00:00
func register(w http.ResponseWriter, r *http.Request) {
var ip string
var err error
ip, _, err = net.SplitHostPort(r.RemoteAddr)
if err != nil {
panic(err)
2020-01-11 17:56:14 +00:00
}
2021-09-20 10:36:20 +00:00
log.Debug().Str("ip", ip).Msg("register")
2020-01-11 17:56:14 +00:00
2021-09-20 10:36:20 +00:00
usernameInput := r.PostFormValue("username")
passwordInput := r.PostFormValue("password")
emailInput := r.PostFormValue("email")
genderInput := r.PostFormValue("gender")
2021-09-20 10:36:20 +00:00
// debug outputs
// fmt.Println(usernameInput)
// fmt.Println(passwordInput)
// fmt.Println(emailInput)
// fmt.Println(genderInput)
2021-09-20 10:36:20 +00:00
var gender int
// Verify Gender value is either 0(female) or 1(male)
// if not just silently send them a 400 because wtf
2021-09-20 10:36:20 +00:00
if gender, err = strconv.Atoi(genderInput); err != nil || gender > 1 {
logAndReturnError(w, invalidRequest)
2020-01-11 17:56:14 +00:00
}
2021-09-20 10:36:20 +00:00
// Usernames must only be letters, numbers, dashes, and underscores
var rxUsername = regexp.MustCompile("([\\w\\-]+)")
2021-09-20 10:36:20 +00:00
// Usernames must be under 16 characters
if len(usernameInput) > 16 || !rxUsername.MatchString(usernameInput) {
fmt.Println("ERROR: Username must only be alphanumeric and under 16 characters. \"-\" and \"_\" are also accepted.")
fmt.Println(w, "ERROR: Username must only be alphanumeric and under 16 characters. \"-\" and \"_\" are also accepted.")
return
}
2021-09-20 10:36:20 +00:00
// Check if username is taken
if db.UserExists(usernameInput) {
logAndReturnError(w, usernameExists)
return
}
2021-09-20 10:36:20 +00:00
// Check if email is taken
if db.EmailTaken(emailInput) {
logAndReturnError(w, emailExists)
return
}
2021-09-20 10:36:20 +00:00
// Validates email addresses
err = checkmail.ValidateFormat(emailInput)
2020-01-12 00:34:02 +00:00
if err != nil {
2021-09-20 10:36:20 +00:00
logAndReturnError(w, badEmail)
return
2020-01-12 00:34:02 +00:00
}
2021-09-20 10:36:20 +00:00
err = db.RegisterNewUser(usernameInput, passwordInput, r.RemoteAddr, genderInput, false)
if err != nil {
logAndReturnError(w, internalErr)
2020-01-11 17:56:14 +00:00
return
}
2021-09-20 10:36:20 +00:00
fmt.Println("Success!")
http.Redirect(w, r, "/?regsuccess=true", 301)
2020-01-11 17:56:14 +00:00
}
2021-09-20 10:36:20 +00:00
func newLogin(w http.ResponseWriter, r *http.Request) {
var u *db.User
var err error
2021-09-20 10:36:20 +00:00
usernameInput := r.PostFormValue("username")
passwordInput := r.PostFormValue("password")
2021-09-20 10:36:20 +00:00
if u, err = db.AttemptWebLogin(usernameInput, passwordInput, r.RemoteAddr, r.UserAgent()); err != nil {
logAndReturnError(w, badCreds)
}
2020-01-12 00:34:02 +00:00
fmt.Println("Authentication successful, setting session...")
2021-09-20 10:36:20 +00:00
// sets a 2 hour long session and cookie
2020-01-12 00:34:02 +00:00
sessionToken := uuid.NewV4().String()
2021-09-20 10:36:20 +00:00
_, err = cache.Do("SETEX", sessionToken, "7200", usernameInput)
2020-01-12 00:34:02 +00:00
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Println("Session management failed!")
http.SetCookie(w, &http.Cookie{
Name: "session_token",
Value: sessionToken,
Expires: time.Now().Add(7200 * time.Second),
})
return
}
2021-09-20 10:36:20 +00:00
fmt.Println("newLogin successful!")
fprinter(w, "newLogin successful!")
}