2023-05-10 11:07:56 +00:00
|
|
|
package controller
|
2023-04-29 17:38:21 +00:00
|
|
|
|
|
|
|
import (
|
2023-04-30 08:48:56 +00:00
|
|
|
"crypto/ed25519"
|
|
|
|
"encoding/hex"
|
2023-04-29 17:38:21 +00:00
|
|
|
"errors"
|
|
|
|
"git.lumeweb.com/LumeWeb/portal/db"
|
2023-05-22 23:05:38 +00:00
|
|
|
"git.lumeweb.com/LumeWeb/portal/logger"
|
2023-04-29 17:38:21 +00:00
|
|
|
"git.lumeweb.com/LumeWeb/portal/model"
|
2023-04-30 08:09:24 +00:00
|
|
|
_validator "git.lumeweb.com/LumeWeb/portal/validator"
|
|
|
|
"github.com/go-playground/validator/v10"
|
2023-04-29 17:38:21 +00:00
|
|
|
"github.com/kataras/iris/v12"
|
2023-05-19 13:04:47 +00:00
|
|
|
"go.uber.org/zap"
|
2023-04-29 17:38:21 +00:00
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
|
|
"gorm.io/gorm"
|
2023-04-30 08:09:24 +00:00
|
|
|
"reflect"
|
2023-04-29 17:38:21 +00:00
|
|
|
)
|
|
|
|
|
2023-05-10 11:07:56 +00:00
|
|
|
type AccountController struct {
|
2023-04-30 07:29:24 +00:00
|
|
|
Ctx iris.Context
|
2023-04-29 17:38:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type RegisterRequest struct {
|
2023-04-30 08:09:24 +00:00
|
|
|
Email string `json:"email" validate:"required"`
|
2023-04-29 17:38:21 +00:00
|
|
|
Password string `json:"password"`
|
2023-04-30 08:48:56 +00:00
|
|
|
Pubkey string `json:"pubkey"`
|
2023-04-29 17:38:21 +00:00
|
|
|
}
|
|
|
|
|
2023-04-30 08:09:24 +00:00
|
|
|
func init() {
|
|
|
|
jsonValidator := _validator.Get()
|
|
|
|
|
|
|
|
jsonValidator.RegisterStructValidation(ValidateRegisterRequest, RegisterRequest{})
|
|
|
|
}
|
|
|
|
|
|
|
|
func ValidateRegisterRequest(structLevel validator.StructLevel) {
|
|
|
|
|
|
|
|
request := structLevel.Current().Interface().(RegisterRequest)
|
|
|
|
|
2023-04-30 08:48:56 +00:00
|
|
|
pubkey := len(request.Pubkey) == 0
|
|
|
|
pass := len(request.Password) == 0
|
|
|
|
|
|
|
|
if pubkey == pass {
|
|
|
|
structLevel.ReportError(reflect.ValueOf(request.Email), "email", "Email", "emailorpubkey", "")
|
|
|
|
structLevel.ReportError(reflect.ValueOf(request.Pubkey), "pubkey", "Pubkey", "emailorpubkey", "")
|
|
|
|
}
|
|
|
|
|
|
|
|
if !pubkey {
|
|
|
|
pubkeyBytes, err := hex.DecodeString(request.Pubkey)
|
|
|
|
if err != nil || len(pubkeyBytes) != ed25519.PublicKeySize {
|
|
|
|
structLevel.ReportError(reflect.ValueOf(request.Pubkey), "pubkey", "Pubkey", "pubkey", "")
|
|
|
|
return
|
|
|
|
}
|
2023-04-30 08:09:24 +00:00
|
|
|
}
|
2023-04-30 08:48:56 +00:00
|
|
|
|
2023-04-30 08:09:24 +00:00
|
|
|
}
|
|
|
|
|
2023-04-29 17:38:21 +00:00
|
|
|
func hashPassword(password string) (string, error) {
|
|
|
|
|
|
|
|
// Generate a new bcrypt hash from the provided password.
|
|
|
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
|
|
|
if err != nil {
|
2023-05-22 23:05:38 +00:00
|
|
|
logger.Get().Error("failed to hash password", zap.Error(err))
|
2023-04-29 17:38:21 +00:00
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert the hashed password to a string and return it.
|
|
|
|
return string(hashedPassword), nil
|
|
|
|
}
|
|
|
|
|
2023-05-10 11:07:56 +00:00
|
|
|
func (a *AccountController) PostRegister() {
|
2023-04-29 17:38:21 +00:00
|
|
|
var r RegisterRequest
|
|
|
|
|
2023-04-30 07:29:24 +00:00
|
|
|
if err := a.Ctx.ReadJSON(&r); err != nil {
|
2023-05-22 23:05:38 +00:00
|
|
|
logger.Get().Debug("failed to parse request", zap.Error(err))
|
2023-04-30 07:29:24 +00:00
|
|
|
a.Ctx.StopWithError(iris.StatusBadRequest, err)
|
2023-04-29 17:38:21 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if an account with the same email address already exists.
|
|
|
|
existingAccount := model.Account{}
|
2023-04-30 08:48:56 +00:00
|
|
|
err := db.Get().Where("email = ?", r.Email).First(&existingAccount).Error
|
2023-04-29 17:38:21 +00:00
|
|
|
if err == nil {
|
2023-05-22 23:05:38 +00:00
|
|
|
logger.Get().Debug("account with email already exists", zap.Error(err), zap.String("email", r.Email))
|
2023-04-29 17:38:21 +00:00
|
|
|
// An account with the same email address already exists.
|
|
|
|
// Return an error response to the client.
|
2023-04-30 07:29:24 +00:00
|
|
|
a.Ctx.StopWithError(iris.StatusConflict, errors.New("an account with this email address already exists"))
|
2023-04-29 17:38:21 +00:00
|
|
|
return
|
|
|
|
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
|
2023-05-22 23:05:38 +00:00
|
|
|
logger.Get().Error("error querying accounts", zap.Error(err), zap.String("email", r.Email))
|
2023-04-29 17:38:21 +00:00
|
|
|
// An unexpected error occurred while querying the database.
|
|
|
|
// Return an error response to the client.
|
2023-04-30 07:29:24 +00:00
|
|
|
a.Ctx.StopWithError(iris.StatusInternalServerError, err)
|
2023-04-29 17:38:21 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a new Account model with the provided email and hashed password.
|
|
|
|
account := model.Account{
|
2023-04-30 08:48:56 +00:00
|
|
|
Email: r.Email,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Hash the password before saving it to the database.
|
|
|
|
if len(r.Password) > 0 {
|
|
|
|
hashedPassword, err := hashPassword(r.Password)
|
|
|
|
if err != nil {
|
|
|
|
a.Ctx.StopWithError(iris.StatusInternalServerError, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
account.Password = &hashedPassword
|
2023-04-29 17:38:21 +00:00
|
|
|
}
|
|
|
|
|
2023-04-30 08:48:56 +00:00
|
|
|
err = db.Get().Transaction(func(tx *gorm.DB) error {
|
|
|
|
// do some database operations in the transaction (use 'tx' from this point, not 'db')
|
|
|
|
if err := tx.Create(&account).Error; err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(r.Pubkey) > 0 {
|
|
|
|
if err := tx.Create(&model.Key{Account: account, Pubkey: r.Pubkey}).Error; err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// return nil will commit the whole transaction
|
|
|
|
return nil
|
|
|
|
})
|
2023-04-29 17:38:21 +00:00
|
|
|
if err != nil {
|
2023-05-22 23:05:38 +00:00
|
|
|
logger.Get().Error("failed to create account", zap.Error(err))
|
2023-04-30 07:29:24 +00:00
|
|
|
a.Ctx.StopWithError(iris.StatusInternalServerError, err)
|
2023-04-29 17:38:21 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return a success response to the client.
|
2023-04-30 07:29:24 +00:00
|
|
|
a.Ctx.StatusCode(iris.StatusCreated)
|
2023-04-29 17:38:21 +00:00
|
|
|
}
|