package main import ( "fmt" "math/rand" "net/http" "strings" "time" "github.com/bwmarrin/discordgo" "github.com/gorilla/mux" ) func reqPass(w http.ResponseWriter, r *http.Request) { defer log.PanicSafe() log.LogInfo("reqPass called.") username := r.URL.Query()["UserName"][0] log.LogInfo("reqPass username is %+v.", username) var userID string if dg == nil { log.LogError("Discord session was nill.") } g, err := dg.GuildMembers(config.GuildID, "", 1000) log.LogInfo("reqPass guild is %+v.", config.GuildID) if err == nil { for _, m := range g { if strings.EqualFold(m.Nick, username) { for _, r := range m.Roles { if r == config.AdminRole { userID = m.User.ID log.LogInfo("User ID found for %+v as %+v", username, userID) } } } } } else { log.LogError("Unable to find user ID for %+v", username) } ipaddr := r.Header.Get("X-Real-IP") log.LogInfo("reqPass IP is %+v.", ipaddr) log.LogInfo(fmt.Sprintf("reqPass called:```username: %s\nip : %s```", username, ipaddr)) go sendPassword(userID, ipaddr) http.Redirect(w, r, "/login", http.StatusTemporaryRedirect) } func tryLogin(w http.ResponseWriter, r *http.Request) { defer log.PanicSafe() 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)) } } } greetUser(w, r) } func usePassword(user string, pass string, ip string) bool { defer log.PanicSafe() log.LogInfo("%+v", toks) tok := toks[strings.ToUpper(user)] delete(toks, strings.ToUpper(user)) 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 } if time.Since(tok.Timestamp) > (time.Minute * 5) { log.LogWarn("%s attempted to use expired token. \n%+v\n%+v\n%+v", user, time.Since(tok.Timestamp), tok.Timestamp, time.Now()) return false } return true } func genPassword(length int) string { defer log.PanicSafe() 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) { defer log.PanicSafe() str := genPassword(15) log.LogInfo("sending password to %+v for %+v: %+v", ipaddr, user, str) m, err := dg.GuildMember(config.GuildID, user) if err != nil { log.LogErrorType(err) } now := time.Now() toks[strings.ToUpper(m.Nick)] = Tokens{ Username: user, IP: ipaddr, Password: str, Timestamp: now, } pmChann, err := dg.UserChannelCreate(user) if err != nil { log.LogErrorType(err) } 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 { defer log.PanicSafe() 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) { defer log.PanicSafe() 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, "" } func userFromID(i string) discordgo.User { u, err := dg.GuildMember(config.GuildID, i) if err != nil { log.LogErrorType(err) return discordgo.User{} } return *u.User } func idFromUsername(username string) string { userID := "" g, err := dg.GuildMembers(config.GuildID, "", 1000) log.LogInfo("reqPass guild is %+v.", config.GuildID) if err == nil { for _, m := range g { if strings.EqualFold(m.User.Username, username) { userID = m.User.ID log.LogInfo("User ID found for %+v as %+v", username, userID) } } } else { log.LogError("Unable to find user ID for %+v", username) } return userID } func isAdmin(m *discordgo.Member) bool { log.LogDebug("Checking %+v for %+v", m.Roles, config.AdminRole) for _, role := range m.Roles { if role == config.AdminRole { return true } else { log.LogDebug("%+v != %+v", role, config.AdminRole) } } return false }