draincloud-core/internal/app/register.go

106 lines
3.0 KiB
Go

package app
import (
"fmt"
"net/http"
"time"
"git.optclblast.xyz/draincloud/draincloud-core/internal/domain"
"git.optclblast.xyz/draincloud/draincloud-core/internal/logger"
"git.optclblast.xyz/draincloud/draincloud-core/internal/storage/models"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"golang.org/x/crypto/bcrypt"
)
func (d *DrainCloud) Register(ctx *gin.Context) {
logger.Debug(ctx, "[register] new request")
// TODO check if registration is enabled
req := new(domain.RegisterRequest)
err := ctx.BindJSON(req)
if err != nil {
logger.Error(ctx, "[register] failed to bind request", logger.Err(err))
ctx.JSON(http.StatusBadRequest, map[string]string{
"error": "bad request",
})
return
}
resp, err := d.register(ctx, req)
if err != nil {
logger.Error(ctx, "[register] failed to register user", logger.Err(err))
ctx.JSON(http.StatusInternalServerError, map[string]string{
"error": err.Error(),
})
return
}
ctx.JSON(http.StatusOK, resp)
}
func (d *DrainCloud) register(ctx *gin.Context, req *domain.RegisterRequest) (*domain.RegisterResponse, error) {
if err := validateLoginAndPassword(req.Login, req.Password); err != nil {
return nil, fmt.Errorf("invalid creds: %w", err)
}
passwordHash, err := bcrypt.GenerateFromPassword([]byte(req.Password), 10)
if err != nil {
logger.Error(ctx, "[register] failed to generate password hash", logger.Err(err))
return nil, fmt.Errorf("failed to generate password hash: %w", err)
}
userID, err := uuid.NewV7()
if err != nil {
return nil, fmt.Errorf("failed to generate user id: %w", err)
}
user := &models.User{
ID: userID,
Username: req.Login,
Login: req.Login,
PasswordHash: passwordHash,
}
err = d.database.AddUser(ctx, userID, user.Login, user.Username, user.PasswordHash)
if err != nil {
return nil, fmt.Errorf("failed to add new user: %w", err)
}
sessionCreatedAt := time.Now()
sessionExpiredAt := sessionCreatedAt.Add(time.Hour * 24 * 7)
sessionToken, err := generateSessionToken(100)
if err != nil {
return nil, fmt.Errorf("failed to generate a session token: %w", err)
}
ctx.SetCookie(sessionTokenCookie, sessionToken, int(sessionExpiredAt.Sub(sessionCreatedAt).Seconds()), "_path", "_domain", true, true)
csrfToken, err := generateSessionToken(100)
if err != nil {
return nil, fmt.Errorf("failed to generate a csrf token: %w", err)
}
ctx.SetCookie(csrfTokenCookie, csrfToken, int(sessionExpiredAt.Sub(sessionCreatedAt).Seconds()), "_path", "_domain", true, false)
sessionID, err := uuid.NewV7()
if err != nil {
return nil, fmt.Errorf("failed to generate session id: %w", err)
}
if _, err = d.database.AddSession(ctx, &models.Session{
ID: sessionID,
SessionToken: sessionToken,
CsrfToken: csrfToken,
UserID: user.ID,
CreatedAt: sessionCreatedAt,
ExpiredAt: sessionExpiredAt,
}); err != nil {
return nil, fmt.Errorf("failed to save session: %w", err)
}
return &domain.RegisterResponse{
Ok: true,
}, nil
}