finish implementing registration invites

This commit is contained in:
Iris Lightshard 2023-07-10 22:38:19 -06:00
parent b7888158b8
commit e7caa27a35
Signed by: nilix
GPG key ID: 3B7FBC22144E6398
5 changed files with 117 additions and 14 deletions

View file

@ -13,7 +13,7 @@ func ProcessCmd(args []string, userStore auth.UserStore, crypto register.Symmetr
return false return false
} }
switch args[1] { switch args[1] {
case "register": case "invite":
now := time.Now().UnixMicro() now := time.Now().UnixMicro()
strNow := strconv.FormatInt(now, 10) strNow := strconv.FormatInt(now, 10)
self, err := crypto.Encrypt(strNow) self, err := crypto.Encrypt(strNow)

View file

@ -1,31 +1,50 @@
package register package register
import ( import (
"context"
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"encoding/hex" "encoding/hex"
"html/template" "html/template"
"net/http" "net/http"
"time"
"strconv"
"fmt"
"hacklab.nilfm.cc/quartzgun/auth" "hacklab.nilfm.cc/quartzgun/auth"
"hacklab.nilfm.cc/quartzgun/renderer" "hacklab.nilfm.cc/quartzgun/renderer"
"hacklab.nilfm.cc/quartzgun/router" "hacklab.nilfm.cc/quartzgun/router"
"hacklab.nilfm.cc/quartzgun/util"
) )
var bytes = []byte{99, 207, 33, 57, 28, 01, 50, 76, 01, 92, 33, 10, 48, 07, 00, 250}
type SymmetricCrypto interface { type SymmetricCrypto interface {
Encode(b []byte) string Encode(b []byte) string
Decode(s string) []byte Decode(s string) []byte
Encrypt(text string) (string, error) Encrypt(text string) (string, error)
Decrypt(text string) (string, error) Decrypt(text string) (string, error)
IsValid(text string) bool
} }
type SymmetricCrypt struct { type SymmetricCrypt struct {
Secret string Secret string
} }
var iv []byte = []byte {107, 53, 46, 249, 52, 70, 36, 185,
168, 139, 144, 249, 242, 2, 125, 183 }
func (self *SymmetricCrypt) IsValid(cipher string) bool {
stringTimestamp, err := self.Decrypt(cipher)
if err != nil {
return false
}
int64Timestamp, err := strconv.ParseInt(stringTimestamp, 10, 64)
if err != nil {
return false
}
then := time.UnixMicro(int64Timestamp)
return time.Since(then).Minutes() <= 15
}
func (self *SymmetricCrypt) Encode(b []byte) string { func (self *SymmetricCrypt) Encode(b []byte) string {
return hex.EncodeToString(b) return hex.EncodeToString(b)
} }
@ -39,12 +58,13 @@ func (self *SymmetricCrypt) Decode(s string) []byte {
} }
func (self *SymmetricCrypt) Encrypt(text string) (string, error) { func (self *SymmetricCrypt) Encrypt(text string) (string, error) {
fmt.Println(text)
block, err := aes.NewCipher([]byte(self.Secret)) block, err := aes.NewCipher([]byte(self.Secret))
if err != nil { if err != nil {
return "", err return "", err
} }
plainText := []byte(text) plainText := []byte(text)
cfb := cipher.NewCFBEncrypter(block, bytes) cfb := cipher.NewCFBEncrypter(block, iv)
cipherText := make([]byte, len(plainText)) cipherText := make([]byte, len(plainText))
cfb.XORKeyStream(cipherText, plainText) cfb.XORKeyStream(cipherText, plainText)
return self.Encode(cipherText), nil return self.Encode(cipherText), nil
@ -56,7 +76,7 @@ func (self *SymmetricCrypt) Decrypt(text string) (string, error) {
return "", err return "", err
} }
cipherText := self.Decode(text) cipherText := self.Decode(text)
cfb := cipher.NewCFBDecrypter(block, bytes) cfb := cipher.NewCFBDecrypter(block, iv)
plainText := make([]byte, len(cipherText)) plainText := make([]byte, len(cipherText))
cfb.XORKeyStream(plainText, cipherText) cfb.XORKeyStream(plainText, cipherText)
return string(plainText), nil return string(plainText), nil
@ -64,18 +84,24 @@ func (self *SymmetricCrypt) Decrypt(text string) (string, error) {
func WithCrypto(next http.Handler, crypto SymmetricCrypto) http.Handler { func WithCrypto(next http.Handler, crypto SymmetricCrypto) http.Handler {
handlerFunc := func(w http.ResponseWriter, req *http.Request) { handlerFunc := func(w http.ResponseWriter, req *http.Request) {
//urlParams := req.Context().Value("params").(map[string]string) util.AddContextValue(req, "crypto", crypto);
//cipher := urlParams["cipher"]
next.ServeHTTP(w, req) next.ServeHTTP(w, req)
} }
return http.HandlerFunc(handlerFunc) return http.HandlerFunc(handlerFunc)
} }
func WithUserStore(next http.Handler, udb auth.UserStore) http.Handler { func WithUserStoreAndCrypto(next http.Handler, udb auth.UserStore, crypto SymmetricCrypto) http.Handler {
handlerFunc := func(w http.ResponseWriter, req *http.Request) { handlerFunc := func(w http.ResponseWriter, req *http.Request) {
*req = *req.WithContext(context.WithValue(req.Context(), "udb", udb)) urlParams := req.Context().Value("params").(map[string]string)
success := false
cipher := urlParams["cipher"]
username := req.FormValue("username")
password := req.FormValue("password")
if crypto.IsValid(cipher) && len(username) > 0 && len(password) > 0 {
success = udb.AddUser(username, password) == nil
}
util.AddContextValue(req, "success", success);
next.ServeHTTP(w, req) next.ServeHTTP(w, req)
} }
@ -86,8 +112,8 @@ func CreateRegistrationInterface(udb auth.UserStore, secret string) http.Handler
rtr := &router.Router{Fallback: *template.Must(template.ParseFiles("templates/error.html"))} rtr := &router.Router{Fallback: *template.Must(template.ParseFiles("templates/error.html"))}
crypto := &SymmetricCrypt{Secret: secret} crypto := &SymmetricCrypt{Secret: secret}
rtr.Get(`/(?P<cipher>.*)`, WithCrypto(renderer.Template("templates/register.html"), crypto)) rtr.Get(`/(?P<cipher>\S+)`, WithCrypto(renderer.Template("templates/register.html"), crypto))
rtr.Post(`/(?P<cipher>.*)`, WithUserStore(WithCrypto(renderer.Template("templates/registered.html"), crypto), udb)) rtr.Post(`/(?P<cipher>\S+)`, WithUserStoreAndCrypto(renderer.Template("templates/registered.html"), udb, crypto))
return http.HandlerFunc(rtr.ServeHTTP) return http.HandlerFunc(rtr.ServeHTTP)
} }

View file

@ -142,7 +142,7 @@ pre {
} }
.ui_win a { .ui_win a {
color: #1f9b92; color: var(--main_color);
} }
.ui_win a:hover, ui_win a:active { .ui_win a:hover, ui_win a:active {
@ -198,3 +198,34 @@ nav {
display: grid; display: grid;
grid-template-columns: 1fr auto auto; grid-template-columns: 1fr auto auto;
} }
#registration {
background: var(--bg_color);
color: var(--fg_color);
width: 500px;
max-width: 80vw;
margin: 2em auto;
text-align: center;
padding: 1em;
}
#registration h1, #registration form {
margin: 1em auto;
}
#registration label, #registration button {
display: block;
margin: 1em;
}
#registration button {
margin: 0 auto;
}
#registration a {
color: var(--main_color);
}
#registration a:hover {
color: var(--fg_color);
}

View file

@ -0,0 +1,25 @@
{{ $cipher := ((.Context).Value "params").cipher }}
{{ $valid := ((.Context).Value "crypto").IsValid $cipher }}
<html lang="en-US">
<head>
<meta charset="UTF-8" />
<title>Felt &mdash; Admin Registration</title>
<meta name="viewport" content="width=device-width" />
<link href="/table/style.css" rel="stylesheet" />
</head>
<body>
<main id="registration">
<h1>Felt Admin Registration</h1>
{{ if $valid }}
<form action="/register/{{ $cipher }}" method="post">
<label>username <input id="username" name="username"/></label>
<label>password <input id="password" name="password" type="password"/></label>
<button type="submit">Register</button>
</form>
{{ else }}
<p class="error">The registration token you provided is invalid</p>
{{end}}
</main>
</body>
</html>

View file

@ -0,0 +1,21 @@
{{ $success := ((.Context).Value "success") }}
<html lang="en-US">
<head>
<meta charset="UTF-8" />
<title>Felt &mdash; Registration Complete</title>
<meta name="viewport" content="width=device-width" />
<link href="/table/style.css" rel="stylesheet" />
</head>
<body>
<main id="registration">
{{ if $success }}
<h1>Registration Complete</h1>
<p>Success! <a href="/table">Let's game!</a></p>
{{ else }}
<h1>Error</h1>
<p class="error">Something went wrong; please try a different username or obtain a new registration code.</p>
{{end}}
</main>
</body>
</html>