package api import ( "crypto/rand" "fmt" "math/big" "net/http" "time" "dns.froth.zone/pomme/internal" "github.com/go-chi/render" "golang.org/x/crypto/bcrypt" "gorm.io/gorm" ) // NewUser takes a POST request and user form and creates a user in the database. func NewUser(w http.ResponseWriter, r *http.Request) { db, ok := r.Context().Value(keyPrincipalContextID).(*gorm.DB) if !ok { APIError(w, r, genericResponseFields{"message": "internal server error", "status": http.StatusInternalServerError, "error": "unable to connect to DB"}) } var result internal.User err := r.ParseForm() if err != nil { APIError(w, r, genericResponseFields{"message": "unable to parse request", "status": http.StatusInternalServerError, "error": err.Error()}) return } username := r.Form.Get("username") if username == "" { username = autoUname() } password := r.Form.Get("password") if password == "" { APIError(w, r, genericResponseFields{"message": "no password provided", "status": http.StatusInternalServerError}) return } db.Where("username = ?", username).First(&result) if result.Username != "" { APIError(w, r, genericResponseFields{"message": "internal server error", "status": http.StatusInternalServerError}) return } hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { APIError(w, r, genericResponseFields{"message": "login failed", "status": http.StatusUnauthorized, "Realm": "authentication"}) return } db.Create(&internal.User{Username: username, HashedPassword: string(hashedPassword)}) token, err := makeToken(username) if err != nil { APIError(w, r, genericResponseFields{"message": "internal server error", "status": http.StatusInternalServerError, "error": err.Error()}) return } http.SetCookie(w, &http.Cookie{ HttpOnly: true, Expires: time.Now().Add(1 * time.Hour), MaxAge: 3600, SameSite: http.SameSiteLaxMode, // Uncomment below for HTTPS: // Secure: true, Name: "jwt", // Must be named "jwt" or else the token cannot be searched for by jwtauth.Verifier. Value: token, }) w.Header().Set("Content-Type", "application/json; charset=utf-8") w.WriteHeader(http.StatusCreated) resp := internal.Response{ Message: "Successfully created account and logged in", } render.JSON(w, r, resp) http.Redirect(w, r, "/", http.StatusSeeOther) } func autoUname() string { n, err := rand.Int(rand.Reader, big.NewInt(1000)) if err != nil { return "" } return fmt.Sprintf("user%d", n.Int64()) }