2020-01-11 17:56:14 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2020-01-11 21:11:51 +00:00
|
|
|
"html/template"
|
2021-09-20 10:36:20 +00:00
|
|
|
"net"
|
2020-01-11 17:56:14 +00:00
|
|
|
"net/http"
|
2020-01-11 21:11:51 +00:00
|
|
|
"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()
|
2020-01-11 23:43:22 +00:00
|
|
|
}
|
|
|
|
|
2021-09-02 03:45:16 +00:00
|
|
|
func main() {
|
|
|
|
HTTPServ()
|
2020-01-11 23:43:22 +00:00
|
|
|
}
|
|
|
|
|
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"
|
|
|
|
)
|
|
|
|
|
2020-01-11 23:43:22 +00:00
|
|
|
func HTTPServ() {
|
2020-01-11 21:11:51 +00:00
|
|
|
r := mux.NewRouter()
|
2021-09-20 10:36:20 +00:00
|
|
|
r.HandleFunc("/", showIndex)
|
|
|
|
r.HandleFunc("/register", registrationForm)
|
|
|
|
r.HandleFunc("/login", loginForm)
|
2020-01-11 23:43:22 +00:00
|
|
|
r.HandleFunc("/EmailTest", EmailTest)
|
2020-01-11 21:11:51 +00:00
|
|
|
|
2020-01-11 23:43:22 +00:00
|
|
|
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")
|
|
|
|
|
|
|
|
}
|
2020-01-11 23:43:22 +00:00
|
|
|
|
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) {
|
2020-01-11 21:11:51 +00:00
|
|
|
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{}{
|
2020-01-11 21:11:51 +00:00
|
|
|
csrf.TemplateTag: csrf.TemplateField(r),
|
2021-09-20 10:36:20 +00:00
|
|
|
}); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2020-01-11 21:11:51 +00:00
|
|
|
}
|
|
|
|
|
2021-09-20 10:36:20 +00:00
|
|
|
func registrationForm(w http.ResponseWriter, r *http.Request) {
|
2020-01-11 21:11:51 +00:00
|
|
|
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{}{
|
2020-01-11 21:11:51 +00:00
|
|
|
csrf.TemplateTag: csrf.TemplateField(r),
|
2021-09-20 10:36:20 +00:00
|
|
|
}); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2020-01-11 21:11:51 +00:00
|
|
|
}
|
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")
|
2020-01-11 21:11:51 +00:00
|
|
|
|
2021-09-20 10:36:20 +00:00
|
|
|
// debug outputs
|
|
|
|
// fmt.Println(usernameInput)
|
|
|
|
// fmt.Println(passwordInput)
|
|
|
|
// fmt.Println(emailInput)
|
|
|
|
// fmt.Println(genderInput)
|
2020-01-11 21:11:51 +00:00
|
|
|
|
2021-09-20 10:36:20 +00:00
|
|
|
var gender int
|
2020-01-11 21:11:51 +00:00
|
|
|
// 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
|
|
|
}
|
2020-01-11 21:11:51 +00:00
|
|
|
|
2021-09-20 10:36:20 +00:00
|
|
|
// Usernames must only be letters, numbers, dashes, and underscores
|
2020-01-11 21:11:51 +00:00
|
|
|
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) {
|
2020-01-11 21:11:51 +00:00
|
|
|
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)
|
2020-01-11 21:11:51 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-09-20 10:36:20 +00:00
|
|
|
// Check if email is taken
|
|
|
|
if db.EmailTaken(emailInput) {
|
|
|
|
logAndReturnError(w, emailExists)
|
2020-01-11 21:11:51 +00:00
|
|
|
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
|
|
|
}
|
2020-01-11 21:11:51 +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
|
2020-01-11 23:43:22 +00:00
|
|
|
|
2021-09-20 10:36:20 +00:00
|
|
|
usernameInput := r.PostFormValue("username")
|
|
|
|
passwordInput := r.PostFormValue("password")
|
2020-01-11 23:43:22 +00:00
|
|
|
|
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-11 21:11:51 +00:00
|
|
|
}
|
|
|
|
|
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!")
|
2020-01-11 21:11:51 +00:00
|
|
|
}
|