From 952f80dbc25de7d35aa59625ae75fc24e681b74e Mon Sep 17 00:00:00 2001 From: Derek Stevens Date: Sun, 9 Jul 2023 23:41:30 -0600 Subject: [PATCH] delete assets when destroying table, clear dice message on roll, clear map on socket disconnect; start registration controller --- admin/admin.go | 8 +-- config/config.go | 13 +++-- gametable/server.go | 4 +- main.go | 2 +- register/register.go | 91 ++++++++++++++++++++++++++++++++ static/admin.js | 11 ++++ static/dice.js | 1 + static/index.html | 2 +- static/map.js | 6 +-- static/socket.js | 12 +++-- static/style.css | 2 +- static/util.js | 34 +++++++----- {static => templates}/error.html | 0 templates/register.html | 0 templates/registered.html | 0 15 files changed, 151 insertions(+), 35 deletions(-) create mode 100644 register/register.go rename {static => templates}/error.html (100%) create mode 100644 templates/register.html create mode 100644 templates/registered.html diff --git a/admin/admin.go b/admin/admin.go index 1b48191..db9207f 100644 --- a/admin/admin.go +++ b/admin/admin.go @@ -110,7 +110,7 @@ func apiCreateTable(next http.Handler, udb auth.UserStore, dbAdapter mongodb.DbA return http.HandlerFunc(handlerFunc) } -func apiDestroyTable(next http.Handler, udb auth.UserStore, dbAdapter mongodb.DbAdapter) http.Handler { +func apiDestroyTable(next http.Handler, udb auth.UserStore, dbAdapter mongodb.DbAdapter, uploads string) http.Handler { handlerFunc := func(w http.ResponseWriter, req *http.Request) { // check table actually belongs to this user user := util.GetUserFromToken(req) @@ -139,6 +139,7 @@ func apiDestroyTable(next http.Handler, udb auth.UserStore, dbAdapter mongodb.Db } if destroy { + os.RemoveAll(filepath.Join(uploads, table.Name)) newTables := append(tables[:i], tables[i+1:]...) util.SetTablesForUser(user, newTables, udb) w.WriteHeader(204) @@ -338,7 +339,7 @@ func apiDeleteImage(next http.Handler, uploads string, uploadType string, udb au func CreateAdminInterface(udb auth.UserStore, dbAdapter mongodb.DbAdapter, uploads string, uploadMaxMB int) http.Handler { // create quartzgun router - rtr := &router.Router{Fallback: *template.Must(template.ParseFiles("static/error.html"))} + rtr := &router.Router{Fallback: *template.Must(template.ParseFiles("templates/error.html"))} scopes := map[string]string{} @@ -348,7 +349,7 @@ func CreateAdminInterface(udb auth.UserStore, dbAdapter mongodb.DbAdapter, uploa rtr.Get("/api/table/", Validate(apiGetTableList(renderer.JSON("tableList"), udb), udb, scopes)) rtr.Get(`/api/table/(?P\S+)`, Validate(apiGetTableData(renderer.JSON("tableData"), udb, dbAdapter), udb, scopes)) rtr.Post("/api/table/", Validate(apiCreateTable(renderer.JSON("result"), udb, dbAdapter), udb, scopes)) - rtr.Delete(`/api/table/(?P\S+)`, Validate(apiDestroyTable(renderer.JSON("result"), udb, dbAdapter), udb, scopes)) + rtr.Delete(`/api/table/(?P\S+)`, Validate(apiDestroyTable(renderer.JSON("result"), udb, dbAdapter, uploads), udb, scopes)) // asset management rtr.Post(`/api/upload/(?P\S+)/map/`, Validate(apiUploadImg(renderer.JSON("location"), dbAdapter, uploads, "map", uploadMaxMB), udb, scopes)) @@ -357,7 +358,6 @@ func CreateAdminInterface(udb auth.UserStore, dbAdapter mongodb.DbAdapter, uploa rtr.Delete(`/api/upload/(?P\S+)/token/(?P\S+)`, Validate(apiDeleteImage(renderer.JSON("deleted"), uploads, "token", udb, dbAdapter), udb, scopes)) rtr.Post(`/api/upload/(?P\S+)/token/`, Validate(apiUploadImg(renderer.JSON("location"), dbAdapter, uploads, "token", uploadMaxMB), udb, scopes)) rtr.Get(`/api/upload/(?P\S+)/token/`, Validate(apiListImages(renderer.JSON("files"), uploads, "token", udb, dbAdapter), udb, scopes)) - // DELETE /api/upload/
/token/ return http.HandlerFunc(rtr.ServeHTTP) } diff --git a/config/config.go b/config/config.go index a72ec2c..8e1e221 100644 --- a/config/config.go +++ b/config/config.go @@ -10,10 +10,11 @@ import ( ) type Config struct { - Port int - Uploads string - UploadMaxMB int - MongoURI string + Port int + Uploads string + UploadMaxMB int + MongoURI string + RegistrationSecret string } func GetConfigLocation() string { @@ -81,6 +82,10 @@ func (self *Config) RunWizard() { fmt.Printf("Max file upload size (MB)? ") self.UploadMaxMB = ensureNumberOption(&inputBuf) + fmt.Printf("Encryption secret for admin invite codes? ") + ensureNonEmptyOption(&inputBuf) + self.RegistrationSecret = inputBuf + fmt.Printf("Configuration complete!\n") self.Write() } diff --git a/gametable/server.go b/gametable/server.go index 75c118f..125739a 100644 --- a/gametable/server.go +++ b/gametable/server.go @@ -10,6 +10,7 @@ import ( "hacklab.nilfm.cc/felt/admin" "hacklab.nilfm.cc/felt/models" "hacklab.nilfm.cc/felt/mongodb" + "hacklab.nilfm.cc/felt/register" "hacklab.nilfm.cc/quartzgun/auth" "hacklab.nilfm.cc/quartzgun/renderer" "io/ioutil" @@ -37,7 +38,7 @@ type GameTableServer struct { udb auth.UserStore } -func New(adapter mongodb.DbAdapter, udb auth.UserStore, uploads string, uploadMaxMB int) *GameTableServer { +func New(adapter mongodb.DbAdapter, udb auth.UserStore, uploads string, uploadMaxMB int, registrationSecret string) *GameTableServer { srvr := &GameTableServer{ subscribeMessageBuffer: 16, logf: log.Printf, @@ -49,6 +50,7 @@ func New(adapter mongodb.DbAdapter, udb auth.UserStore, uploads string, uploadMa srvr.serveMux.Handle("/table/", http.StripPrefix("/table/", renderer.Subtree("./static"))) srvr.serveMux.Handle("/uploads/", http.StripPrefix("/uploads/", renderer.Subtree(uploads))) srvr.serveMux.Handle("/admin/", http.StripPrefix("/admin", admin.CreateAdminInterface(udb, adapter, uploads, uploadMaxMB))) + srvr.serveMux.Handle("/register/", http.StripPrefix("/register", register.CreateRegistrationInterface(udb, registrationSecret))) srvr.serveMux.HandleFunc("/subscribe", srvr.subscribeHandler) srvr.serveMux.HandleFunc("/publish", srvr.publishHandler) diff --git a/main.go b/main.go index 1f2cb9e..6d45104 100644 --- a/main.go +++ b/main.go @@ -48,7 +48,7 @@ func run() error { return err } - gt := gametable.New(dbEngine, udb, cfg.Uploads, cfg.UploadMaxMB) + gt := gametable.New(dbEngine, udb, cfg.Uploads, cfg.UploadMaxMB, cfg.RegistrationSecret) s := &http.Server{ Handler: gt, ReadTimeout: time.Second * 10, diff --git a/register/register.go b/register/register.go new file mode 100644 index 0000000..4a828a0 --- /dev/null +++ b/register/register.go @@ -0,0 +1,91 @@ +package register + +import ( + "context" + "crypto/aes" + "crypto/cipher" + "encoding/base64" + "html/template" + "net/http" + + "hacklab.nilfm.cc/quartzgun/auth" + "hacklab.nilfm.cc/quartzgun/renderer" + "hacklab.nilfm.cc/quartzgun/router" +) + +var bytes = []byte{99, 207, 33, 57, 28, 01, 50, 76, 01} + +type SymmetricCrypto interface { + Encode(b []byte) string + Decode(s string) []byte + Encrypt(text string) (string, error) + Decrypt(text string) (string, error) +} + +type SymmetricCrypt struct { + Secret string +} + +func (self *SymmetricCrypt) Encode(b []byte) string { + return base64.StdEncoding.EncodeToString(b) +} + +func (self *SymmetricCrypt) Decode(s string) []byte { + data, err := base64.StdEncoding.DecodeString(s) + if err != nil { + panic(err) + } + return data +} + +func (self *SymmetricCrypt) Encrypt(text string) (string, error) { + block, err := aes.NewCipher([]byte(self.Secret)) + if err != nil { + return "", err + } + plainText := []byte(text) + cfb := cipher.NewCFBEncrypter(block, bytes) + cipherText := make([]byte, len(plainText)) + cfb.XORKeyStream(cipherText, plainText) + return self.Encode(cipherText), nil +} + +func (self *SymmetricCrypt) Decrypt(text string) (string, error) { + block, err := aes.NewCipher([]byte(self.Secret)) + if err != nil { + return "", err + } + cipherText := self.Decode(text) + cfb := cipher.NewCFBDecrypter(block, bytes) + plainText := make([]byte, len(cipherText)) + cfb.XORKeyStream(plainText, cipherText) + return string(plainText), nil +} + +func WithCrypto(next http.Handler, crypto SymmetricCrypto) http.Handler { + handlerFunc := func(w http.ResponseWriter, req *http.Request) { + *req = *req.WithContext(context.WithValue(req.Context(), "crypto", crypto)) + next.ServeHTTP(w, req) + } + + return http.HandlerFunc(handlerFunc) +} + +func WithUserStore(next http.Handler, udb auth.UserStore) http.Handler { + handlerFunc := func(w http.ResponseWriter, req *http.Request) { + *req = *req.WithContext(context.WithValue(req.Context(), "udb", udb)) + next.ServeHTTP(w, req) + } + + return http.HandlerFunc(handlerFunc) +} + +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.*)`, WithCrypto(renderer.Template("templates/register.html"), crypto)) + rtr.Post(`/(?P.*)`, WithUserStore(WithCrypto(renderer.Template("templates/registered.html"), crypto), udb)) + + return http.HandlerFunc(rtr.ServeHTTP) +} diff --git a/static/admin.js b/static/admin.js index 017adaf..abbd468 100644 --- a/static/admin.js +++ b/static/admin.js @@ -180,9 +180,11 @@ function drawTokenOrigin() { function reinitializeSpritePreview() { const img = document.createElement("img"); img.src = tokenSpriteDropdown[tokenSpriteDropdown.selectedIndex].value; + const tokenNameParts = tokenSpriteDropdown[tokenSpriteDropdown.selectedIndex].text.split("."); tokenNameParts.pop(); tokenName.value = tokenNameParts.join("."); + img.onload = () => { const w = img.naturalWidth; const h = img.naturalHeight; @@ -191,6 +193,7 @@ function reinitializeSpritePreview() { tokenHeight.value = h; scaleSpritePreview(); } + previewZone.innerHTML = ""; previewZone.appendChild(img); } @@ -425,12 +428,20 @@ async function doLogin() { adminWrapper.style.display="inline"; adminZone.style.display = "block"; closeErr(); + replaceAdminModal(); } else { setErr("Incorrect credentials"); } } } +function replaceAdminModal() { + const adminModal = document.getElementById("admin_modal"); + if (adminModal) { + adminModal.innerHTML = "Logout"; + } +} + async function getAdminToken(user, pass) { const headers = new Headers(); headers.set('Authorization', 'Basic ' + btoa(user + ":" + pass)); diff --git a/static/dice.js b/static/dice.js index d21eec3..56be65e 100644 --- a/static/dice.js +++ b/static/dice.js @@ -30,5 +30,6 @@ function rollDice() { note: note.value, timestamp: new Date(), }}); + note.value = ""; } } \ No newline at end of file diff --git a/static/index.html b/static/index.html index 5d1fc95..52dcf3b 100644 --- a/static/index.html +++ b/static/index.html @@ -14,7 +14,7 @@