|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"math/rand"
|
|
|
|
"net/http"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"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.Guild(config.GuildID)
|
|
|
|
log.LogInfo("reqPass guild is %+v.", config.GuildID)
|
|
|
|
if err == nil {
|
|
|
|
for _, m := range g.Members {
|
|
|
|
if strings.ToUpper(m.Nick) == strings.ToUpper(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)
|
|
|
|
}
|
|
|
|
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", 302)
|
|
|
|
}
|
|
|
|
|
|
|
|
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))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "<br><p>Login Success: %+v</p><br>Session: %+v", access, session.Values)
|
|
|
|
}
|
|
|
|
|
|
|
|
func usePassword(user string, pass string, ip string) bool {
|
|
|
|
defer log.PanicSafe()
|
|
|
|
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 {
|
|
|
|
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()
|
|
|
|
log.LogInfo("sending password to %+v for %+v", ipaddr, user)
|
|
|
|
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 {
|
|
|
|
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, ""
|
|
|
|
}
|