all repos — felt @ e7caa27a353012e2ac7ce891b54417a01b793ae9

virtual tabletop for dungeons and dragons (and similar) using Go, MongoDB, and websockets

finish implementing registration invites
Iris Lightshard nilix@nilfm.cc
PGP Signature
-----BEGIN PGP SIGNATURE-----

iQIyBAABCAAdFiEEkFh6dA+k/6CXFXU4O3+8IhROY5gFAmSs3LsACgkQO3+8IhRO
Y5jRnA/0CWgwySaijh5rN+hrGcnkYn9wAjnE4GbRRte4//OqQDlPoLMBNSOdQlN+
+ZUEzCxi5QFZLYFf92AdEhTzxTMH35eh52945vrUN7H1n8XbixnHzimjCV7a42vu
7HWsiC32YvWpVcrUwWjf2nS8am6IaEW/Fba1vQ9v9W1uxY8ZSXGGOIU/HjCsmiUw
qQs7R9G0BZdhzu29asJ+6OzP81j40mNMJYMn5pQaIXtR3hqH4HcpH+mmLQLu0Wly
XN9vxPMZQENx/SfjTnHOYQKV4a6vzy1y4CfXf6yxyXLdO3gEpbjDfmhMAw6zy+1N
VDJ3iZnUQIl3fSGMi5BGGoV933wfCO2U55/Awo8dLb+yU4nXL5p4Gkjda5z3785t
Q0PSXH1r+h2teYCLGinj0s6vSuZX7Bp6GZY6vhY/4LWirW9QUof6bemSCW3CVZjw
GqsMlI/0UPQLacY+6XMD66p1rxp/ebFxOu8y42RAXTlM1aI8Lppd3yNQF5xmnAvj
mknndOhJC78SMdISYAEBUO1nCXgYbYuq6eKSKnnqQNScL0KdQZ0fUPpXwLI/sBz4
VuwB+b8m4xGzdfVlsRavwQyqqf2Xv6HkMTl1iL+JZ5RoRGbmlUUPxnLxBGfWyVgw
m0+2B9zLhNyQ0llZQSToUbB1XYQP2J2356ZH4lvlSZdNWi0Lmg==
=qXXR
-----END PGP SIGNATURE-----
commit

e7caa27a353012e2ac7ce891b54417a01b793ae9

parent

b7888158b870cd74837ec8aa0a716b1834afd969

5 files changed, 117 insertions(+), 14 deletions(-)

jump to
M cmd/cmd.gocmd/cmd.go

@@ -13,7 +13,7 @@ if len(args) == 1 {

return false } switch args[1] { - case "register": + case "invite": now := time.Now().UnixMicro() strNow := strconv.FormatInt(now, 10) self, err := crypto.Encrypt(strNow)
M register/register.goregister/register.go

@@ -1,29 +1,48 @@

package register import ( - "context" "crypto/aes" "crypto/cipher" "encoding/hex" "html/template" "net/http" + "time" + "strconv" + "fmt" + "hacklab.nilfm.cc/quartzgun/auth" "hacklab.nilfm.cc/quartzgun/renderer" "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 { Encode(b []byte) string Decode(s string) []byte Encrypt(text string) (string, error) Decrypt(text string) (string, error) + IsValid(text string) bool } type SymmetricCrypt struct { 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 {

@@ -39,12 +58,13 @@ return data

} func (self *SymmetricCrypt) Encrypt(text string) (string, error) { + fmt.Println(text) block, err := aes.NewCipher([]byte(self.Secret)) if err != nil { return "", err } plainText := []byte(text) - cfb := cipher.NewCFBEncrypter(block, bytes) + cfb := cipher.NewCFBEncrypter(block, iv) cipherText := make([]byte, len(plainText)) cfb.XORKeyStream(cipherText, plainText) return self.Encode(cipherText), nil

@@ -56,7 +76,7 @@ if err != nil {

return "", err } cipherText := self.Decode(text) - cfb := cipher.NewCFBDecrypter(block, bytes) + cfb := cipher.NewCFBDecrypter(block, iv) plainText := make([]byte, len(cipherText)) cfb.XORKeyStream(plainText, cipherText) return string(plainText), nil

@@ -64,18 +84,24 @@ }

func WithCrypto(next http.Handler, crypto SymmetricCrypto) http.Handler { handlerFunc := func(w http.ResponseWriter, req *http.Request) { - //urlParams := req.Context().Value("params").(map[string]string) - //cipher := urlParams["cipher"] - + util.AddContextValue(req, "crypto", crypto); next.ServeHTTP(w, req) } 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) { - *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) }

@@ -86,8 +112,8 @@ func CreateRegistrationInterface(udb auth.UserStore, secret string) http.Handler {

rtr := &router.Router{Fallback: *template.Must(template.ParseFiles("templates/error.html"))} crypto := &SymmetricCrypt{Secret: secret} - rtr.Get(`/(?P<cipher>.*)`, WithCrypto(renderer.Template("templates/register.html"), crypto)) - rtr.Post(`/(?P<cipher>.*)`, WithUserStore(WithCrypto(renderer.Template("templates/registered.html"), crypto), udb)) + rtr.Get(`/(?P<cipher>\S+)`, WithCrypto(renderer.Template("templates/register.html"), crypto)) + rtr.Post(`/(?P<cipher>\S+)`, WithUserStoreAndCrypto(renderer.Template("templates/registered.html"), udb, crypto)) return http.HandlerFunc(rtr.ServeHTTP) }
M static/style.cssstatic/style.css

@@ -142,7 +142,7 @@ margin: 0.25em;

} .ui_win a { - color: #1f9b92; + color: var(--main_color); } .ui_win a:hover, ui_win a:active {

@@ -197,4 +197,35 @@

.two_btn_list li { display: grid; 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); }
M templates/register.htmltemplates/register.html

@@ -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>
M templates/registered.htmltemplates/registered.html

@@ -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>