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-----
5 files changed,
117 insertions(+),
14 deletions(-)
M
cmd/cmd.go
→
cmd/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.go
→
register/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.css
→
static/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.html
→
templates/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 — 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.html
→
templates/registered.html
@@ -0,0 +1,21 @@
+ +{{ $success := ((.Context).Value "success") }} +<html lang="en-US"> + <head> + <meta charset="UTF-8" /> + <title>Felt — 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>