Added skeleton website from kbauth
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
118
auth.go
Normal file
118
auth.go
Normal file
@ -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, ""
|
||||||
|
}
|
||||||
2
main.go
2
main.go
@ -29,6 +29,7 @@ var (
|
|||||||
token string
|
token string
|
||||||
configFile string
|
configFile string
|
||||||
setupMsg string
|
setupMsg string
|
||||||
|
dg *discordgo.Session
|
||||||
lastPM = make(map[string]time.Time)
|
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."}
|
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() {
|
func main() {
|
||||||
|
runWeb()
|
||||||
defer log.PanicSafe()
|
defer log.PanicSafe()
|
||||||
if configFile == "" {
|
if configFile == "" {
|
||||||
configFile = "config.json"
|
configFile = "config.json"
|
||||||
|
|||||||
137
site-api.go
Normal file
137
site-api.go
Normal file
@ -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))
|
||||||
|
}
|
||||||
21
static/404.tpl
Normal file
21
static/404.tpl
Normal file
@ -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>
|
||||||
21
static/500.tpl
Normal file
21
static/500.tpl
Normal file
@ -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>
|
||||||
11
static/card.tpl
Normal file
11
static/card.tpl
Normal file
@ -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>
|
||||||
41
static/header.tpl
Normal file
41
static/header.tpl
Normal file
@ -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>
|
||||||
15
static/home.tpl
Normal file
15
static/home.tpl
Normal file
@ -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>
|
||||||
31
static/login.tpl
Normal file
31
static/login.tpl
Normal file
@ -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>
|
||||||
31
static/pass.tpl
Normal file
31
static/pass.tpl
Normal file
@ -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>
|
||||||
22
types.go
22
types.go
@ -1,7 +1,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
import "github.com/rudi9719/loggy"
|
"time"
|
||||||
|
|
||||||
|
"github.com/rudi9719/loggy"
|
||||||
|
)
|
||||||
|
|
||||||
// Config struct used for bot
|
// Config struct used for bot
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@ -18,7 +21,7 @@ type Config struct {
|
|||||||
Unverified map[string]time.Time
|
Unverified map[string]time.Time
|
||||||
Verifications map[string]Verification
|
Verifications map[string]Verification
|
||||||
Probations map[string]time.Time
|
Probations map[string]time.Time
|
||||||
LogOpts loggy.LogOpts
|
LogOpts loggy.LogOpts
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verification struct used for storing and logging
|
// Verification struct used for storing and logging
|
||||||
@ -31,3 +34,16 @@ type Verification struct {
|
|||||||
Admin string
|
Admin string
|
||||||
Closed time.Time
|
Closed time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type linkedAccount struct {
|
||||||
|
domainUser string
|
||||||
|
discordUser string
|
||||||
|
sigHash string
|
||||||
|
}
|
||||||
|
|
||||||
|
type tokens struct {
|
||||||
|
username string
|
||||||
|
ip string
|
||||||
|
password string
|
||||||
|
timestamp time.Time
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user