add registration handler
alex wennerberg alex@alexwennerberg.com
Sat, 24 Oct 2020 13:09:57 -0700
8 files changed,
92 insertions(+),
10 deletions(-)
A
admin.go
@@ -0,0 +1,5 @@
+package main + +// Commands for administering your instance +// reset user password -> generate link +// delete user
M
go.sum
→
go.sum
@@ -6,6 +6,8 @@ github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
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=
M
http.go
→
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 @@ "time"
) var t *template.Template +var DB *sql.DB const InternalServerErrorMsg = "500: Internal Server Error"@@ -211,6 +215,22 @@ // check for errors
} } +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 @@ renderError(w, InternalServerErrorMsg, 500)
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) + } } }
M
main.go
→
main.go
@@ -1,6 +1,7 @@
package main import ( + "database/sql" "flag" "fmt" "io/ioutil"@@ -15,10 +16,6 @@ "time"
) var c Config // global var to hold static configuration - -const ( // todo make configurable - userFilesPath = "./files" -) type File struct { Creator string@@ -50,7 +47,7 @@ }
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 @@ } // todo clean up paths
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 }@@ -97,6 +94,11 @@ func main() {
configPath := flag.String("c", "flounder.toml", "path to config file") var err error c, err = getConfig(*configPath) + if err != nil { + log.Fatal(err) + } + + DB, err = sql.Open("sqlite3", c.DBFile) if err != nil { log.Fatal(err) }
A
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')) +); +
A
templates/message.html
@@ -0,0 +1,5 @@
+{{template "header" .}} +<h1>{{.PageTitle}}</h1> +{{ .Message }} +<a href="https://{{.Domain}}">Go home</a> +{{template "footer" .}}
M
templates/register.html
→
templates/register.html
@@ -2,7 +2,7 @@ {{template "header" .}}
<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 @@ <input id="email" name="email" size="64" type="text" value="" />
</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"