flounder

A simple gemini site builder
Log | Files | Refs | README | LICENSE

commit 48c4084ba652171948827c35a6803c8c54aa98da
parent d6be68418a350fc835a86dd82bba9935d7a609ba
Author: alex wennerberg <alex@alexwennerberg.com>
Date:   Sat, 24 Oct 2020 13:09:57 -0700

add registration handler

Diffstat:
Aadmin.go | 5+++++
Mgo.mod | 1+
Mgo.sum | 2++
Mhttp.go | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mmain.go | 14++++++++------
Aschema.sql | 9+++++++++
Atemplates/message.html | 5+++++
Mtemplates/register.html | 8++++----
8 files changed, 92 insertions(+), 10 deletions(-)

diff --git a/admin.go b/admin.go @@ -0,0 +1,5 @@ +package main + +// Commands for administering your instance +// reset user password -> generate link +// delete user diff --git a/go.mod b/go.mod @@ -6,6 +6,7 @@ require ( git.sr.ht/~adnano/gmi v0.1.0-alpha.2 github.com/BurntSushi/toml v0.3.1 github.com/gorilla/handlers v1.5.1 + github.com/mattn/go-sqlite3 v1.14.4 golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e ) diff --git a/go.sum b/go.sum @@ -6,6 +6,8 @@ github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8S github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= +github.com/mattn/go-sqlite3 v1.14.4 h1:4rQjbDxdu9fSgI/r3KN72G3c2goxknAqHHgPWWs8UlI= +github.com/mattn/go-sqlite3 v1.14.4/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d h1:2+ZP7EfsZV7Vvmx3TIqSlSzATMkTAKqM14YGFPoSKjI= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= diff --git a/http.go b/http.go @@ -1,8 +1,11 @@ package main import ( + "database/sql" "git.sr.ht/~adnano/gmi" "github.com/gorilla/handlers" + _ "github.com/mattn/go-sqlite3" + "golang.org/x/crypto/bcrypt" "html/template" "io" "io/ioutil" @@ -16,6 +19,7 @@ import ( ) var t *template.Template +var DB *sql.DB const InternalServerErrorMsg = "500: Internal Server Error" @@ -211,6 +215,22 @@ func loginHandler(w http.ResponseWriter, r *http.Request) { } } +const ok = "-0123456789abcdefghijklmnopqrstuvwxyz" + +func isOkUsername(s string) bool { + if len(s) < 1 { + return false + } + if len(s) > 31 { + return false + } + for _, char := range s { + if !strings.Contains(ok, strings.ToLower(string(char))) { + return false + } + } + return true +} func registerHandler(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { data := struct { @@ -225,6 +245,44 @@ func registerHandler(w http.ResponseWriter, r *http.Request) { return } } else if r.Method == "POST" { + r.ParseForm() + email := r.Form.Get("email") + password := r.Form.Get("password") + errors := []string{} + if !strings.Contains(email, "@") { + errors = append(errors, "Invalid Email") + } + if r.Form.Get("password") != r.Form.Get("password2") { + errors = append(errors, "Passwords don't match") + } + if len(password) < 6 { + errors = append(errors, "Password is too short") + } + username := strings.ToLower(r.Form.Get("username")) + if !isOkUsername(username) { + errors = append(errors, "Username is invalid: can only contain letters, numbers and hypens. Maximum 32 characters.") + } + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), 8) // TODO handle error + _, err = DB.Exec("insert into user (username, email, password_hash) values ($1, $2, $3)", username, email, string(hashedPassword)) + if err != nil { + log.Println(err) + errors = append(errors, "Username or email is already used") + } + if len(errors) > 0 { + data := struct { + Domain string + Errors []string + PageTitle string + }{c.RootDomain, errors, "Register"} + t.ExecuteTemplate(w, "register.html", data) + } else { + data := struct { + Domain string + Message string + PageTitle string + }{c.RootDomain, "Registration complete! The server admin will approve your request before you can log in.", "Registration Complete"} + t.ExecuteTemplate(w, "message.html", data) + } } } diff --git a/main.go b/main.go @@ -1,6 +1,7 @@ package main import ( + "database/sql" "flag" "fmt" "io/ioutil" @@ -16,10 +17,6 @@ import ( var c Config // global var to hold static configuration -const ( // todo make configurable - userFilesPath = "./files" -) - type File struct { Creator string Name string @@ -50,7 +47,7 @@ func checkIfValidFile(filename string, fileBytes []byte) error { func getIndexFiles() ([]*File, error) { // cache this function result := []*File{} - err := filepath.Walk(userFilesPath, func(path string, info os.FileInfo, err error) error { + err := filepath.Walk(c.FilesDirectory, func(path string, info os.FileInfo, err error) error { if err != nil { log.Printf("Failure accessing a path %q: %v\n", path, err) return err // think about @@ -79,7 +76,7 @@ func getIndexFiles() ([]*File, error) { // cache this function func getUserFiles(user string) ([]*File, error) { result := []*File{} - files, err := ioutil.ReadDir(path.Join(userFilesPath, user)) + files, err := ioutil.ReadDir(path.Join(c.FilesDirectory, user)) if err != nil { return nil, err } @@ -101,6 +98,11 @@ func main() { log.Fatal(err) } + DB, err = sql.Open("sqlite3", c.DBFile) + if err != nil { + log.Fatal(err) + } + wg := new(sync.WaitGroup) wg.Add(2) go func() { diff --git a/schema.sql b/schema.sql @@ -0,0 +1,9 @@ +CREATE TABLE user ( + id INTEGER PRIMARY KEY NOT NULL, + username TEXT NOT NULL UNIQUE, + email TEXT NOT NULL UNIQUE, + password_hash TEXT NOT NULL, + approved boolean NOT NULL DEFAULT false, + created_at INTEGER DEFAULT (strftime('%s', 'now')) +); + diff --git a/templates/message.html b/templates/message.html @@ -0,0 +1,5 @@ +{{template "header" .}} +<h1>{{.PageTitle}}</h1> +{{ .Message }} +<a href="https://{{.Domain}}">Go home</a> +{{template "footer" .}} diff --git a/templates/register.html b/templates/register.html @@ -2,7 +2,7 @@ <h1>Register</h1> <form action="/register" method="post"> <div> - <label for="username">Username</label> + <label for="username">Username</label><br> <input id="username" name="username" @@ -17,13 +17,13 @@ </div> <div> <label for="password">Password</label> - <input id="password" name="password" size="32" type="password" value="" /> + <input id="password" name="password" size="55" type="password" value="" /> </div> <div> <label for="password2">Repeat Password</label> - <input id="password2" name="password2" size="32" type="password" value="" /> + <input id="password2" name="password2" size="55" type="password" value="" /> </div> - <div class="error">{{ range .Errors}}<p>{{.}}</p>{{end}} </div> + <div class="error">{{ range .Errors}}{{.}}<br>{{end}} </div> <div> <input class="button"