From f8e7051e79e8541a44b58869b315067d518949c8 Mon Sep 17 00:00:00 2001 From: Gregory Rudolph Date: Wed, 2 Dec 2020 08:45:32 -0500 Subject: [PATCH] Added skeleton website from kbauth --- auth.go | 118 +++++++++++++++++++++++++++++++++++++++ main.go | 2 + site-api.go | 137 ++++++++++++++++++++++++++++++++++++++++++++++ static/404.tpl | 21 +++++++ static/500.tpl | 21 +++++++ static/card.tpl | 11 ++++ static/header.tpl | 41 ++++++++++++++ static/home.tpl | 15 +++++ static/login.tpl | 31 +++++++++++ static/pass.tpl | 31 +++++++++++ types.go | 22 +++++++- 11 files changed, 447 insertions(+), 3 deletions(-) create mode 100644 auth.go create mode 100644 site-api.go create mode 100644 static/404.tpl create mode 100644 static/500.tpl create mode 100644 static/card.tpl create mode 100644 static/header.tpl create mode 100644 static/home.tpl create mode 100644 static/login.tpl create mode 100644 static/pass.tpl diff --git a/auth.go b/auth.go new file mode 100644 index 0000000..75022c6 --- /dev/null +++ b/auth.go @@ -0,0 +1,118 @@ +package main + +import ( + "fmt" + "math/rand" + "net/http" + "strings" + "time" + + "github.com/gorilla/mux" +) + +func reqPass(w http.ResponseWriter, r *http.Request) { + username := r.FormValue("UserName") + ipaddr := r.Header.Get("X-Real-IP") + log.LogInfo(fmt.Sprintf("reqPass called:```username: %s\nip : %s```", username, ipaddr)) + sendPassword(username, ipaddr) + http.Redirect(w, r, "/login", 302) +} + +func tryLogin(w http.ResponseWriter, r *http.Request) { + session, err := store.Get(r, "2fa") + if err != nil { + log.LogWarn("Error opening session for 2fa store") + } + vars := mux.Vars(r) + username := vars["username"] + password := vars["password"] + ip := r.Header.Get("X-Real-IP") + if len(username) == 0 { + username = r.FormValue("UserName") + password = r.FormValue("TempPass") + } + access, _ := detectUser(r, "tryLogin") + if !access { + log.LogDebug(fmt.Sprintf("%s is attempting login", getSessionIdentifier(r))) + access = usePassword(username, password, ip) + if access { + log.LogInfo(fmt.Sprintf("%s has successfully logged in from %s", username, ip)) + log.LogDebug(fmt.Sprintf("```%+v```", session.Values)) + session.Values["username"] = username + session.Values["ip"] = ip + session.Values["timestamp"] = fmt.Sprintf("%+v", time.Now()) + err = session.Save(r, w) + if err != nil { + log.LogWarn(fmt.Sprintf("Error saving cookie. ```%+v```", err)) + } + } + } + + fmt.Fprintf(w, "

Login Success: %+v


Session: %+v", access, session.Values) +} + +func usePassword(user string, pass string, ip string) bool { + tok := toks[user] + delete(toks, user) + if time.Since(tok.timestamp) > (time.Minute * 5) { + log.LogWarn(fmt.Sprintf("%s attempted to use expired token.", user)) + return false + } + if tok.ip != ip { + log.LogWarn(fmt.Sprintf("%s attempted to use an improper IP.", user)) + return false + } + if tok.password != pass { + log.LogWarn(fmt.Sprintf("%s attempted to use an improper password. %s vs %s", user, tok.password, pass)) + return false + } + + return true +} + +func genPassword(length int) string { + rand.Seed(time.Now().UnixNano()) + chars := []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + "abcdefghijklmnopqrstuvwxyz" + + "0123456789") + var b strings.Builder + for i := 0; i < length; i++ { + b.WriteRune(chars[rand.Intn(len(chars))]) + } + return b.String() // E.g. "ExcbsVQs" +} +func sendPassword(user string, ipaddr string) { + str := genPassword(8) + m, _ := dg.GuildMember(config.GuildID, user) + toks[m.User.Username] = tokens{ + username: user, + ip: ipaddr, + password: str, + timestamp: time.Now(), + } + pmChann, _ := dg.UserChannelCreate(user) + dg.ChannelMessageSend(pmChann.ID, fmt.Sprintf("A temporary password was requested from %s:", ipaddr)) + dg.ChannelMessageSend(pmChann.ID, fmt.Sprintf("```%s```", str)) + +} + +func getSessionIdentifier(r *http.Request) string { + ipaddr := r.Header.Get("X-Real-IP") + if ipaddr == "" { + ipaddr = r.RemoteAddr + } + uri := r.URL.Path + return fmt.Sprintf("%s:%s", ipaddr, uri) +} + +func detectUser(r *http.Request, callFunc string) (bool, string) { + log.LogInfo(fmt.Sprintf("%s called detectUser", getSessionIdentifier(r))) + session, err := store.Get(r, "2fa") + if err != nil { + log.LogDebug(fmt.Sprintf("Unable to open 2fa session in %s", callFunc)) + } + if session.Values["username"] != nil { + return true, fmt.Sprintf("%s", session.Values["username"]) + } + return false, "" +} diff --git a/main.go b/main.go index 65f19af..0b47108 100644 --- a/main.go +++ b/main.go @@ -29,6 +29,7 @@ var ( token string configFile string setupMsg string + dg *discordgo.Session lastPM = make(map[string]time.Time) quotes = []string{"The hardest choices require the strongest wills.", "You're strong, but I could snap my fingers and you'd all cease to exist.", "Fun isn't something one considers when balancing the universe. But this... does put a smile on my face.", "Perfectly balanced, as all things should be.", "I am inevitable."} ) @@ -40,6 +41,7 @@ func init() { } func main() { + runWeb() defer log.PanicSafe() if configFile == "" { configFile = "config.json" diff --git a/site-api.go b/site-api.go new file mode 100644 index 0000000..d128f67 --- /dev/null +++ b/site-api.go @@ -0,0 +1,137 @@ +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "os" + "strings" + + "github.com/gorilla/mux" + "github.com/gorilla/sessions" +) + +var ( + store = sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY"))) + toks = make(map[string]tokens) + acctLinks = make(map[string]linkedAccount) +) + +func topWrapper(r *http.Request) string { + log.LogInfo(fmt.Sprintf("%s called topWrapper", getSessionIdentifier(r))) + headerTemplate, err := ioutil.ReadFile("./static/header.tpl") + if err != nil { + log.LogError(fmt.Sprintf("Unable to open header template: ```%+v```", err)) + return "" + } + header := string(headerTemplate) + login := "Login" + loggedIn, user := detectUser(r, "topWrapper") + if loggedIn { + login = fmt.Sprintf("Logout %s", user) + } + header = strings.Replace(header, "$LOGIN", login, -1) + return header +} + +func bodyWrapper(r *http.Request, template string) string { + log.LogInfo(fmt.Sprintf("%s called bodyWrapper", getSessionIdentifier(r))) + bodyTemplate, err := ioutil.ReadFile(fmt.Sprintf("./static/%+v.tpl", template)) + if err != nil { + log.LogError(fmt.Sprintf("Attempt to load %s.tpl failed. ```%+v```", template, err)) + return bodyWrapper(r, "404") + } + return string(bodyTemplate) + +} +func pageBuilder(r *http.Request, pageName string) string { + pageCode := topWrapper(r) + pageCode += bodyWrapper(r, pageName) + return pageCode +} + +func greetUser(w http.ResponseWriter, r *http.Request) { + log.LogInfo(fmt.Sprintf("%s called greetUser", getSessionIdentifier(r))) + loggedIn, username := detectUser(r, "greetUser") + fmt.Fprintf(w, pageBuilder(r, "home")) + if loggedIn { + fmt.Fprintf(w, strings.Replace(bodyWrapper(r, "loggedIn"), "$USER", username, -1)) + } +} + +func passPage(w http.ResponseWriter, r *http.Request) { + log.LogInfo(fmt.Sprintf("%s called passPage", getSessionIdentifier(r))) + fmt.Fprintf(w, pageBuilder(r, "pass")) +} +func loginPage(w http.ResponseWriter, r *http.Request) { + log.LogInfo(fmt.Sprintf("%s called loginPage", getSessionIdentifier(r))) + session, err := store.Get(r, "2fa") + if err != nil { + log.LogWarn("Unable to open 2fa session in loginpage") + } + loggedIn, _ := detectUser(r, "loginPage") + if loggedIn { + session.Values["username"] = nil + err = session.Save(r, w) + if err != nil { + log.LogWarn("Error logging out from loginPage()") + } + fmt.Fprintf(w, pageBuilder(r, "home")) + return + } + fmt.Fprintf(w, pageBuilder(r, "login")) +} + +func notFoundPage(w http.ResponseWriter, r *http.Request) { + go log.LogWarn(fmt.Sprintf("%s triggered notFoundPage", getSessionIdentifier(r))) + fmt.Fprintf(w, topWrapper(r)) + + fmt.Fprintf(w, card("Oops! That Page Was Not found.", + "Sorry, a 404 error has occured. The requested page not found!

"+ + "", + "
Take Me Home Contact Support
")) + +} +func card(title string, content string, footer string) string { + cardTemplate, err := ioutil.ReadFile("./static/card.tpl") + if err != nil { + log.LogError("Unable to open card template") + return "" + } + cardString := string(cardTemplate) + cardString = strings.Replace(cardString, "$TITLE", title, -1) + cardString = strings.Replace(cardString, "$CONTENT", content, -1) + cardString = strings.Replace(cardString, "$FOOTER", footer, -1) + return cardString + +} + +func getPending(w http.ResponseWriter, r *http.Request) { + loggedIn, _ := detectUser(r, "getPending") + if loggedIn { + pending, err := json.Marshal(config.Verifications) + if err != nil { + log.LogErrorType(err) + notFoundPage(w, r) + } + fmt.Fprintf(w, string(pending)) + } else { + notFoundPage(w, r) + } +} + +func runWeb() { + router := mux.NewRouter().StrictSlash(true) + log.LogInfo("Adding HandleFuncs to router") + router.NotFoundHandler = http.HandlerFunc(notFoundPage) + router.HandleFunc("/pass", passPage) + router.HandleFunc("/login", loginPage) + router.HandleFunc("/api/login", tryLogin) + router.HandleFunc("/api/pending", getPending) + router.HandleFunc("/api/passreq", reqPass) + router.HandleFunc("/", greetUser) + router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("./static")))) + log.LogInfo("Starting server") + log.LogErrorType(http.ListenAndServe(":8080", router)) +} diff --git a/static/404.tpl b/static/404.tpl new file mode 100644 index 0000000..cf61c77 --- /dev/null +++ b/static/404.tpl @@ -0,0 +1,21 @@ +
+
+
+
+

+ Oops!


+

+ 404 Not Found

+
+ Sorry, an error has occured, Requested page not found! +
+
+ +
+
+
+
+
diff --git a/static/500.tpl b/static/500.tpl new file mode 100644 index 0000000..b9bfaf1 --- /dev/null +++ b/static/500.tpl @@ -0,0 +1,21 @@ +
+
+
+
+

+ Oops!


+

Something has gone HORRIBLY wrong! Congrats!

+
+ Not really sorry, but an error has occured. 500 means something went wrong on the server side, but.. Let's be honest. It was really a user malfunction. +
+
+ +
+
+
+
+ +
diff --git a/static/card.tpl b/static/card.tpl new file mode 100644 index 0000000..d215105 --- /dev/null +++ b/static/card.tpl @@ -0,0 +1,11 @@ +

+
+ +
+
+

$TITLE

+

$CONTENT

+

$FOOTER

+
+
+
\ No newline at end of file diff --git a/static/header.tpl b/static/header.tpl new file mode 100644 index 0000000..a4c4ddb --- /dev/null +++ b/static/header.tpl @@ -0,0 +1,41 @@ + + + +Thanos + + + + + + + + + + + + + diff --git a/static/home.tpl b/static/home.tpl new file mode 100644 index 0000000..93c7ebe --- /dev/null +++ b/static/home.tpl @@ -0,0 +1,15 @@ +

+
+ + +
+ +
+
+

Cookie Policy

+

What website doesn't use cookies nowadays?

+

This website does not use any 3rd party cookies. All cookies are encrypted and only used by this website.

+

If you are actually reading this, chances are your data isn't valuable enough for me to care about tracking you.

+
+
+
diff --git a/static/login.tpl b/static/login.tpl new file mode 100644 index 0000000..253cd82 --- /dev/null +++ b/static/login.tpl @@ -0,0 +1,31 @@ +
+
+
+
+ +
+
+
\ No newline at end of file diff --git a/static/pass.tpl b/static/pass.tpl new file mode 100644 index 0000000..488ed24 --- /dev/null +++ b/static/pass.tpl @@ -0,0 +1,31 @@ + +

+
+
+
+
+ + + + + + + + + +
+Discord User: + + +
+ + +
+
+ +

Click the "Submit" button and a temporary password will be sent to the Discord User.

+
+
+
+ + diff --git a/types.go b/types.go index 0f9f275..1fdcad6 100644 --- a/types.go +++ b/types.go @@ -1,7 +1,10 @@ package main -import "time" -import "github.com/rudi9719/loggy" +import ( + "time" + + "github.com/rudi9719/loggy" +) // Config struct used for bot type Config struct { @@ -18,7 +21,7 @@ type Config struct { Unverified map[string]time.Time Verifications map[string]Verification Probations map[string]time.Time - LogOpts loggy.LogOpts + LogOpts loggy.LogOpts } // Verification struct used for storing and logging @@ -31,3 +34,16 @@ type Verification struct { Admin string Closed time.Time } + +type linkedAccount struct { + domainUser string + discordUser string + sigHash string +} + +type tokens struct { + username string + ip string + password string + timestamp time.Time +}