Add users system, required for uploading new pastes
This commit is contained in:
@@ -38,6 +38,9 @@ type FileUpload struct {
|
||||
// UUID publically identifies this FileUpload.
|
||||
PubID uuid.UUID `gorm:"uniqueIndex"`
|
||||
|
||||
// User ID that created this file
|
||||
CreatedBy uint `gorm:"index"`
|
||||
|
||||
// FileName contains the original filename of this FileUpload.
|
||||
FileName string
|
||||
|
||||
@@ -126,7 +129,7 @@ func (fs *FileStore) filePath(pubID uuid.UUID, fileName string) string {
|
||||
//
|
||||
// Internally, this function detects the type of the file stored in `r` using
|
||||
// `http.DetectContentType`.
|
||||
func NewFileUpload(fs *FileStore, r io.Reader, fileName string) (*FileUpload, error) {
|
||||
func NewFileUpload(fs *FileStore, r io.Reader, user *User, fileName string) (*FileUpload, error) {
|
||||
// Generate a file ID
|
||||
pubID, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
@@ -168,6 +171,7 @@ func NewFileUpload(fs *FileStore, r io.Reader, fileName string) (*FileUpload, er
|
||||
fu := &FileUpload{
|
||||
State: FileUploadStatePresent,
|
||||
PubID: pubID,
|
||||
CreatedBy: user.ID,
|
||||
FileName: baseName,
|
||||
ContentType: contentType,
|
||||
Checksum: hash.Sum32(),
|
||||
|
||||
@@ -42,5 +42,56 @@ func Gormigrate(db *gorm.DB) *gormigrate.Gormigrate {
|
||||
return tx.Migrator().DropTable(&FileUpload{}, &Paste{})
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "202304301337",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
type User struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
User string `gorm:"uniqueIndex"`
|
||||
Password string
|
||||
Admin bool
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt gorm.DeletedAt
|
||||
}
|
||||
type FileUpload struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
State FileUploadState `gorm:"index"`
|
||||
PubID uuid.UUID `gorm:"uniqueIndex"`
|
||||
CreatedBy uint `gorm:"index"`
|
||||
FileName string
|
||||
ContentType string
|
||||
Checksum uint32
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt gorm.DeletedAt
|
||||
}
|
||||
type Paste struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
Type PasteType `gorm:"index"`
|
||||
State PasteState `gorm:"index"`
|
||||
CreatedBy uint `gorm:"index"`
|
||||
Content []byte
|
||||
Key string `gorm:"uniqueIndex"`
|
||||
DeleteToken string
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt gorm.DeletedAt
|
||||
}
|
||||
return tx.AutoMigrate(&User{}, &FileUpload{}, &Paste{})
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
if err := tx.Migrator().DropTable(&User{}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Migrator().DropColumn(&FileUpload{}, "CreatedBy"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Migrator().DropColumn(&Paste{}, "CreatedBy"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ type Paste struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
Type PasteType `gorm:"index"`
|
||||
State PasteState `gorm:"index"`
|
||||
CreatedBy uint `gorm:"index"`
|
||||
Content []byte
|
||||
Key string `gorm:"uniqueIndex"`
|
||||
DeleteToken string
|
||||
|
||||
153
internal/db/user.go
Normal file
153
internal/db/user.go
Normal file
@@ -0,0 +1,153 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/crypto/argon2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
User string `gorm:"uniqueIndex"`
|
||||
Password string
|
||||
Admin bool
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt gorm.DeletedAt
|
||||
}
|
||||
|
||||
func NewUser(db *gorm.DB, username string, password string, admin bool) error {
|
||||
// Generate a new UUID for the user
|
||||
id, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Hash the password using argon2id
|
||||
hashedPassword, err := HashPassword(password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create a new user record
|
||||
user := &User{
|
||||
ID: uint(id.ID()),
|
||||
User: username,
|
||||
Password: hashedPassword,
|
||||
Admin: admin,
|
||||
}
|
||||
return db.Create(user).Error
|
||||
}
|
||||
|
||||
func Authenticate(db *gorm.DB, username string, password string) (*User, error) {
|
||||
// Get the user record by username
|
||||
var user User
|
||||
if err := db.Where("user = ?", username).First(&user).Error; err != nil {
|
||||
return nil, errors.New("user not found")
|
||||
}
|
||||
|
||||
// Compare the hashed password with the provided password
|
||||
if !comparePassword(user.Password, password) {
|
||||
return nil, errors.New("invalid password")
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func HashPassword(password string) (string, error) {
|
||||
// Generate a salt for the password hash
|
||||
salt := make([]byte, 16)
|
||||
if _, err := rand.Read(salt); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Hash the password using argon2id
|
||||
hash := argon2.IDKey([]byte(password), salt, 2, 64*1024, 1, 64)
|
||||
|
||||
// Encode the salt and hash as a string
|
||||
return string(salt) + string(hash), nil
|
||||
}
|
||||
|
||||
func comparePassword(hashedPassword string, password string) bool {
|
||||
// Decode the salt and hash from the hashed password string
|
||||
salt := []byte(hashedPassword)[:16]
|
||||
hash := []byte(hashedPassword)[16:]
|
||||
|
||||
// Hash the password using the same salt and parameters
|
||||
computedHash := argon2.IDKey([]byte(password), salt, 2, 64*1024, 1, 64)
|
||||
|
||||
// Compare the computed hash with the stored hash
|
||||
return bytes.Equal(hash, computedHash)
|
||||
}
|
||||
|
||||
// DeleteUser deletes a user with the specified username from the database.
|
||||
func DeleteUser(db *gorm.DB, username string) error {
|
||||
// Find the user by username
|
||||
var user User
|
||||
if err := db.Where("user = ?", username).First(&user).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete the user
|
||||
if err := db.Delete(&user).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ChangeUser(db *gorm.DB, username string, updatedUser *User) error {
|
||||
// Retrieve the existing user
|
||||
var existingUser User
|
||||
if err := db.Where("user = ?", username).First(&existingUser).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update the user fields
|
||||
existingUser.User = updatedUser.User
|
||||
existingUser.Password = updatedUser.Password
|
||||
existingUser.Admin = updatedUser.Admin
|
||||
|
||||
// Save the updated user to the database
|
||||
if err := db.Save(&existingUser).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateAdminUser(db *gorm.DB, adminUsername string) error {
|
||||
// Check if the admin user already exists
|
||||
var admins []User
|
||||
if err := db.Unscoped().Limit(1).Where("user = ?", adminUsername).Find(&admins).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if len(admins) > 0 {
|
||||
// already exists
|
||||
return nil
|
||||
}
|
||||
|
||||
// Generate a random 24-char password
|
||||
passwordBytes := make([]byte, 24)
|
||||
if _, err := rand.Read(passwordBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
password := base64.URLEncoding.EncodeToString(passwordBytes)
|
||||
|
||||
// Create the admin user
|
||||
if err := NewUser(db, adminUsername, password, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Print out the generated password
|
||||
fmt.Printf("Generated password for admin user %s: %s\n", adminUsername, password)
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user