From bf7d9c79cae53f64fcd04527248987bd4e7ca3c4 Mon Sep 17 00:00:00 2001 From: Mitchell Riedstra Date: Sun, 19 Jun 2022 23:57:04 -0400 Subject: 0.0.17a / Alpha. Introduce users and page editing. Breaking changes: inc/base.html is now tpl/base.md by default. This can be overridden on the command line. 404.md is now tpl/404.md. This can be overridden with templatedirectory in the configuration file. Additional files: `auth.json` file that stores credentials and settings for authorization cookie. Further notes: This will likely receive some major updates and changes over the next few commits. The scaffolidng is now in place for user accounts, login handling, and page editing. It's all extremely basic at the moment, on the idea list: Listing of all markdown files File uploader and general content management Flags to turn on/off git integration for edits. Download / Upload of all markdown files as a backup/restore. It's of course, all subject to change. --- cmd/server/auth.go | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 cmd/server/auth.go (limited to 'cmd/server/auth.go') diff --git a/cmd/server/auth.go b/cmd/server/auth.go new file mode 100644 index 0000000..caade97 --- /dev/null +++ b/cmd/server/auth.go @@ -0,0 +1,108 @@ +package main + +import ( + "crypto/rand" + "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "os" + "strings" + + "riedstra.dev/mitch/go-website/users" +) + +type Auth struct { + Users []*users.SiteUser `json:"Users"` + // How long are JWTs valid? + LoginHours int `json:"LoginHours"` + // JWT secret + TokenKey string `json:"TokenKey"` + // Are cookies HTTP only? + HTTPOnly bool `json:"HTTPOnly"` + // Do they require HTTPs? + Secure bool `json:"SecureCookie"` + // See https://pkg.go.dev/net/http#SameSite + // You probably want this set to 3 + SameSiteStrict http.SameSite `json:"SameSiteStrict"` +} + +func GenTokenKey() string { + r := make([]byte, 16) // 128 bits + _, err := rand.Read(r) + + if err != nil { + // Not my favorite thing, but I consider this to be a + // critical issue + panic(fmt.Errorf("reading random bytes: %w", err)) + } + + return base64.RawURLEncoding.EncodeToString(r) +} + +func (a *App) ReadAuth(fn string) error { //nolint + auth := &Auth{ + Users: []*users.SiteUser{ + { + Username: "admin", + Password: "admin", + }, + }, + LoginHours: 1, + TokenKey: GenTokenKey(), + HTTPOnly: true, + Secure: true, + SameSiteStrict: http.SameSiteStrictMode, + } + + var dec *json.Decoder + + fh, err := os.Open(fn) + if err != nil { + if strings.Contains(err.Error(), "no such file") { + goto write + } + + return fmt.Errorf("opening %s: %w", fn, err) + } + + dec = json.NewDecoder(fh) + dec.DisallowUnknownFields() + + err = dec.Decode(auth) + if err != nil { + return fmt.Errorf("decoding file %s: %w", fn, err) + } + + err = fh.Close() + if err != nil { + return fmt.Errorf("closing file %s: %w", fn, err) + } + + for _, u := range auth.Users { + err = u.SetPasswordHashIfNecessary() + if err != nil { + return fmt.Errorf("setting password hash for: %s: %w", + u.Username, err) + } + } + +write: + fh, err = os.OpenFile(fn, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) + + if err != nil { + return fmt.Errorf("opening file %s: %w", fn, err) + } + + enc := json.NewEncoder(fh) + enc.SetIndent("", " ") + + err = enc.Encode(auth) + if err != nil { + return fmt.Errorf("encoding to file %s: %w", fn, err) + } + + a.auth = auth + + return nil +} -- cgit v1.2.3