Also, handle first user differently under the hood
This commit is contained in:
		@@ -1,6 +1,8 @@
 | 
				
			|||||||
package model
 | 
					package model
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"database/sql"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -198,3 +200,71 @@ func (db *DB) UpdateUserAttributes(id int64, user, first, last, oldPass, newPass
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return fmt.Errorf("error: %v unsuccessful retries for DB operation, aborting", TxMaxRetries)
 | 
						return fmt.Errorf("error: %v unsuccessful retries for DB operation, aborting", TxMaxRetries)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (db *DB) AddFirstUser(u *User, pass string) (int64, error) {
 | 
				
			||||||
 | 
						var numUsers int64
 | 
				
			||||||
 | 
						txOptions := &sql.TxOptions{Isolation: sql.LevelSerializable}
 | 
				
			||||||
 | 
						selectQuery := "SELECT COUNT(*) FROM users"
 | 
				
			||||||
 | 
						insertQuery := `
 | 
				
			||||||
 | 
					    INSERT INTO users (username, password, first_name, last_name, role)
 | 
				
			||||||
 | 
					    VALUES (?, ?, ?, ?, ?)
 | 
				
			||||||
 | 
					    `
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i := 0; i < TxMaxRetries; i++ {
 | 
				
			||||||
 | 
							id, err := func() (int64, error) {
 | 
				
			||||||
 | 
								tx, err := db.BeginTx(context.Background(), txOptions)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return 0, fmt.Errorf("error starting transaction: %v", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if err := tx.QueryRow(selectQuery).Scan(&numUsers); err != nil {
 | 
				
			||||||
 | 
									if rollbackErr := tx.Rollback(); rollbackErr != nil {
 | 
				
			||||||
 | 
										log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return 0, fmt.Errorf("error getting ID of %v: %v", u.UserName, err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if numUsers != 0 {
 | 
				
			||||||
 | 
									if err = tx.Commit(); err != nil {
 | 
				
			||||||
 | 
										return 0, fmt.Errorf("error committing transaction: %v", err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return 2, nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								hashedPass, err := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									if rollbackErr := tx.Rollback(); rollbackErr != nil {
 | 
				
			||||||
 | 
										log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return 0, fmt.Errorf("error creating password hash: %v", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								result, err := tx.Exec(insertQuery, u.UserName, string(hashedPass), u.FirstName, u.LastName, u.Role)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									if rollbackErr := tx.Rollback(); rollbackErr != nil {
 | 
				
			||||||
 | 
										log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return 0, fmt.Errorf("error inserting new user %v into DB: %v", u.UserName, err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								id, err := result.LastInsertId()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									if rollbackErr := tx.Rollback(); rollbackErr != nil {
 | 
				
			||||||
 | 
										log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return 0, fmt.Errorf("error inserting user into DB: %v", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if err = tx.Commit(); err != nil {
 | 
				
			||||||
 | 
									return 0, fmt.Errorf("error committing transaction: %v", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return id, nil
 | 
				
			||||||
 | 
							}()
 | 
				
			||||||
 | 
							if err == nil {
 | 
				
			||||||
 | 
								return id, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							log.Println(err)
 | 
				
			||||||
 | 
							wait(i)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0, fmt.Errorf("error: %v unsuccessful retries for DB operation, aborting", TxMaxRetries)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -236,12 +236,18 @@ func AddFirstUser(c *control.CliArgs, db *model.DB, s *control.CookieStore) http
 | 
				
			|||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		htmlData.ID, err = db.AddUser(htmlData.User, pass)
 | 
							htmlData.ID, err = db.AddFirstUser(htmlData.User, pass)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Println(err)
 | 
								log.Println(err)
 | 
				
			||||||
			http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
								http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if htmlData.ID > 1 {
 | 
				
			||||||
 | 
								errString := "error: there is already a first user"
 | 
				
			||||||
 | 
								log.Println(errString)
 | 
				
			||||||
 | 
								http.Error(w, errString, http.StatusInternalServerError)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if err := saveSession(w, r, s, htmlData.User); err != nil {
 | 
							if err := saveSession(w, r, s, htmlData.User); err != nil {
 | 
				
			||||||
			log.Println(err)
 | 
								log.Println(err)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
{{define "page-content"}}
 | 
					{{define "page-content"}}
 | 
				
			||||||
<h2>Neuer Benutzer</h2>
 | 
					<h2>Erster Benutzer (Administrator)</h2>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<form>
 | 
					<form>
 | 
				
			||||||
    <div class="grid grid-cols-3 gap-4">
 | 
					    <div class="grid grid-cols-3 gap-4">
 | 
				
			||||||
        <div>
 | 
					        <div>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user