Gregory Rudolph
4 years ago
11 changed files with 447 additions and 3 deletions
@ -0,0 +1,118 @@
@@ -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, "<br><p>Login Success: %+v</p><br>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, "" |
||||
} |
@ -0,0 +1,137 @@
@@ -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! <br><br>"+ |
||||
"<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/t3otBjVZzT0\" frameborder=\"0\" allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>", |
||||
"<div class=\"error-actions\"><a href=\"/\" class=\"btn btn-primary btn-lg\"><span class=\"glyphicon glyphicon-home\"></span>Take Me Home </a> <a href=\"mailto://rudi@nmare.net\" class=\"btn btn-default btn-lg\"><span class=\"glyphicon glyphicon-envelope\"></span> Contact Support </a></div>")) |
||||
|
||||
} |
||||
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)) |
||||
} |
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
<div class="container"> |
||||
<div class="row"> |
||||
<div class="col-md-12"> |
||||
<div class="error-template"> |
||||
<h1> |
||||
Oops!</h1><br> |
||||
<h2> |
||||
404 Not Found</h2> |
||||
<div class="error-details"> |
||||
Sorry, an error has occured, Requested page not found! |
||||
</div> |
||||
<br> |
||||
<div class="error-actions"> |
||||
<a href="/" class="btn btn-primary btn-lg"><span class="glyphicon glyphicon-home"></span> |
||||
Take Me Home </a> <a href="mailto://rudi@nmare.net" class="btn btn-default btn-lg"><span class="glyphicon glyphicon-envelope"></span> Contact Support </a> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<br> |
||||
</div> |
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
<div class="container"> |
||||
<div class="row"> |
||||
<div class="col-md-12"> |
||||
<div class="error-template"> |
||||
<h1> |
||||
Oops!</h1><br> |
||||
<h2>Something has gone HORRIBLY wrong! Congrats!</h2> |
||||
<div class="error-details"> |
||||
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. |
||||
</div> |
||||
<br> |
||||
<div class="error-actions"> |
||||
<a href="/" class="btn btn-primary btn-lg"><span class="glyphicon glyphicon-home"></span> |
||||
Take Me Home </a> <a href="mailto://rudi@nmare.net" class="btn btn-default btn-lg"><span class="glyphicon glyphicon-envelope"></span> Contact Support </a> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<br> |
||||
<iframe width="560" height="315" src="https://www.youtube.com/embed/t3otBjVZzT0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> |
||||
</div> |
@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
|
||||
<br><br> |
||||
<div class="container h-100 d-flex justify-content-center align-items-center"> |
||||
|
||||
<div class="card" style="width: 50rem;"> |
||||
<div class="card-body col"> |
||||
<h2 class="card-title">$TITLE</h1> |
||||
<p class="card-text">$CONTENT</p> |
||||
<p class="card-text">$FOOTER</p> |
||||
</div> |
||||
</div> |
||||
</div> |
@ -0,0 +1,41 @@
@@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html> |
||||
<html> |
||||
<head> |
||||
<title>Thanos</title> |
||||
<meta charset="utf-8"> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1"> |
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"> |
||||
<link rel="icon" type="image/png" href="https://lh6.googleusercontent.com/BvczCCYhJUsKIs3dsowl1vuvnBtCGSDcMDekt5PehwQk3cQLfHkEn80cR3IuMxUFmd5Sh_UQ=w16383"> |
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> |
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script> |
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script> |
||||
|
||||
<!-- Navigation --> |
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark static-top"> |
||||
<div class="container"> |
||||
<a class="navbar-brand" href="/"> |
||||
<img src="" alt=""> |
||||
</a> |
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation"> |
||||
<span class="navbar-toggler-icon"></span> |
||||
</button> |
||||
<div class="collapse navbar-collapse" id="navbarResponsive"> |
||||
<ul class="navbar-nav ml-auto"> |
||||
<li class="nav-item active"> |
||||
<a class="nav-link" href="/">Home |
||||
<span class="sr-only">(current)</span> |
||||
</a> |
||||
</li> |
||||
<li class="nav-item"> |
||||
<a class="nav-link" href="/pass">Request Token</a> |
||||
</li> |
||||
<li class="nav-item"> |
||||
<a class="nav-link" href="/login">$LOGIN</a> |
||||
</li> |
||||
</ul> |
||||
</div> |
||||
</div> |
||||
</nav> |
||||
|
||||
|
||||
</head> |
@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
|
||||
<br><br> |
||||
<div class="container h-100 d-flex justify-content-center align-items-center"> |
||||
|
||||
|
||||
<div class="container h-100 d-flex justify-content-center align-items-center"> |
||||
|
||||
<div class="card" style="width: 50rem;"> |
||||
<div class="card-body col"> |
||||
<h2 class="card-title">Cookie Policy</h1> |
||||
<p class="card-text">What website doesn't use cookies nowadays?</p> |
||||
<p class="card-text">This website does not use any 3rd party cookies. All cookies are encrypted and only used by this website. </p> |
||||
<p class="card-text">If you are actually reading this, chances are your data isn't valuable enough for me to care about tracking you.</p> |
||||
</div> |
||||
</div> |
||||
</div> |
@ -0,0 +1,31 @@
@@ -0,0 +1,31 @@
|
||||
<div class="container-fluid"> |
||||
<div class="row no-gutter"> |
||||
<div class="d-none d-md-flex col-md-4 col-lg-6 bg-image"></div> |
||||
<div class="col-md-8 col-lg-6"> |
||||
<div class="login d-flex align-items-center py-5"> |
||||
<div class="container"> |
||||
<div class="row"> |
||||
<div class="col-md-9 col-lg-8 mx-auto"> |
||||
<h3 class="login-heading mb-4">Thanos OTP Login</h3> |
||||
<form action="/api/login"> |
||||
<div class="form-label-group"> |
||||
<input type="text" name="UserName" id="Username" class="form-control" placeholder="Username" required autofocus> |
||||
<label for="inputEmail">Username</label> |
||||
</div> |
||||
|
||||
<div class="form-label-group"> |
||||
<input type="password" name="TempPass" id="TempPassword" class="form-control" placeholder="Do Not Use Your Discord Password" required> |
||||
<label for="inputPassword">Temporary Password</label> |
||||
</div> |
||||
|
||||
<button class="btn btn-lg btn-primary btn-block btn-login text-uppercase font-weight-bold mb-2" type="submit">Sign in</button> |
||||
<div class="text-center"> |
||||
<a class="small" href="/pass">Need to request a password?</a></div> |
||||
</form> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
@ -0,0 +1,31 @@
@@ -0,0 +1,31 @@
|
||||
<body> |
||||
<br><br> |
||||
<div class="row h-100 justify-content-center align-items-center"> |
||||
<div class="card"> |
||||
<div class="container"> |
||||
<form action="/api/passreq"> |
||||
<table> |
||||
<tr> |
||||
<td> |
||||
Discord User: |
||||
</td> |
||||
<td> |
||||
<input type="text" name="UserName" value=""> |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
</td> |
||||
<td> |
||||
<input type="submit" value="Submit" class="btn btn-primary" style="float: right;"> |
||||
</td> |
||||
</tr> |
||||
</table> |
||||
</form> |
||||
|
||||
<p>Click the "Submit" button and a temporary password will be sent to the Discord User.</p> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</body> |
||||
</html> |
Loading…
Reference in new issue