package main import ( "fmt" "log" "net/http" "time" "golang.org/x/crypto/bcrypt" ) func getUserAndToken(tokenBody string) (User, Token, error) { var t Token var u User err := db.Where("body = ?", tokenBody).First(&t).Error if err != nil { return User{}, Token{}, fmt.Errorf("error when fetching token: %w", err) } err = db.Where("id = ?", t.UserID).First(&u).Error if err != nil { return User{}, Token{}, fmt.Errorf("error when fetching user: %w", err) } return u, t, nil } func logoutUser(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie("wasmcloud-token") if err != nil { http.Redirect(w, r, "/login", http.StatusSeeOther) return } u, t, err := getUserAndToken(cookie.Value) if err != nil { http.Error(w, "unknown authentication token", http.StatusBadRequest) return } err = db.Delete(&t).Error if err != nil { log.Printf("can't delete token %d for %s: %v", t.ID, u.Username, err) http.Error(w, "internal server error, please contact support", http.StatusInternalServerError) return } http.SetCookie(w, &http.Cookie{Name: "wasmcloud-token", MaxAge: -1, Expires: time.Now().Add(-500 * time.Hour)}) log.Printf("logout for %s", u.Username) http.Redirect(w, r, "/login", http.StatusSeeOther) } func loginUser(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { showLoginForm(w, r) return } r.ParseMultipartForm(1024) defer r.Body.Close() for _, val := range []string{"username", "password"} { if r.FormValue(val) == "" { http.Error(w, "missing form data "+val, http.StatusBadRequest) return } } uname := r.FormValue("username") pw := r.FormValue("password") var u User err := db.Where("username = ?", uname).First(&u).Error if err != nil { log.Printf("can't lookup user %s: %v", uname, err) http.Error(w, "unknown user/password", http.StatusBadRequest) return } err = bcrypt.CompareHashAndPassword(u.CryptedPassword, []byte(pw)) if err != nil { log.Printf("wrong password for %s: %v", uname, err) http.Error(w, "unknown user/password", http.StatusBadRequest) return } t, err := makeToken(u) if err != nil { log.Printf("can't make token: %v", err) http.Error(w, "internal server error, contact support", http.StatusInternalServerError) return } log.Printf("login for %s", u.Username) http.SetCookie(w, t.ToCookie()) http.Redirect(w, r, "/control/", http.StatusSeeOther) } func registerUser(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { showRegisterForm(w, r) return } r.ParseMultipartForm(1024) defer r.Body.Close() for _, val := range []string{"username", "password", "email"} { if r.FormValue(val) == "" { http.Error(w, "missing form data "+val, http.StatusBadRequest) return } } cryptPW, err := bcrypt.GenerateFromPassword([]byte(r.FormValue("password")), bcrypt.DefaultCost) if err != nil { panic(err) } u := User{ Username: r.FormValue("username"), CryptedPassword: cryptPW, Email: r.FormValue("email"), } err = db.Save(&u).Error if err != nil { log.Printf("can't save user: %v", err) http.Error(w, "can't save new user", http.StatusInternalServerError) return } tok, err := makeToken(u) if err != nil { log.Printf("can't make token: %v", err) http.Error(w, "internal server error, contact support", http.StatusInternalServerError) return } log.Printf("created user %s", u.Username) http.SetCookie(w, tok.ToCookie()) http.Redirect(w, r, "/control/", http.StatusSeeOther) }