feat: registrationBegin method without email;
fix: missing sqlc query parameter name; feat: util for generating security codes; feat: enums package
This commit is contained in:
@@ -12,11 +12,11 @@ type BannedUser struct {
|
||||
ID int64
|
||||
UserID int64
|
||||
Date pgtype.Timestamp
|
||||
Reason pgtype.Text
|
||||
Reason *string
|
||||
ExpiresAt pgtype.Timestamp
|
||||
BannedBy pgtype.Text
|
||||
Pardoned pgtype.Bool
|
||||
PardonedBy pgtype.Text
|
||||
BannedBy *string
|
||||
Pardoned *bool
|
||||
PardonedBy *string
|
||||
}
|
||||
|
||||
type ConfirmationCode struct {
|
||||
@@ -25,17 +25,17 @@ type ConfirmationCode struct {
|
||||
CodeType int32
|
||||
CodeHash string
|
||||
ExpiresAt pgtype.Timestamp
|
||||
Used pgtype.Bool
|
||||
Deleted pgtype.Bool
|
||||
Used *bool
|
||||
Deleted *bool
|
||||
}
|
||||
|
||||
type LoginInformation struct {
|
||||
ID int64
|
||||
UserID int64
|
||||
Email pgtype.Text
|
||||
Email *string
|
||||
PasswordHash string
|
||||
TotpEncrypted pgtype.Text
|
||||
Email2faEnabled pgtype.Bool
|
||||
TotpEncrypted *string
|
||||
Email2faEnabled *bool
|
||||
PasswordChangeDate pgtype.Timestamp
|
||||
}
|
||||
|
||||
@@ -43,41 +43,41 @@ type Profile struct {
|
||||
ID int64
|
||||
UserID int64
|
||||
Name string
|
||||
Bio pgtype.Text
|
||||
AvatarUrl pgtype.Text
|
||||
Bio *string
|
||||
AvatarUrl *string
|
||||
Birthday pgtype.Timestamp
|
||||
Color pgtype.Text
|
||||
ColorGrad pgtype.Text
|
||||
Color *string
|
||||
ColorGrad *string
|
||||
}
|
||||
|
||||
type ProfileSetting struct {
|
||||
ID int64
|
||||
ProfileID int64
|
||||
HideFulfilled pgtype.Bool
|
||||
HideProfileDetails pgtype.Bool
|
||||
HideForUnauthenticated pgtype.Bool
|
||||
HideBirthday pgtype.Bool
|
||||
HideDates pgtype.Bool
|
||||
Captcha pgtype.Bool
|
||||
FollowersOnlyInteraction pgtype.Bool
|
||||
HideFulfilled *bool
|
||||
HideProfileDetails *bool
|
||||
HideForUnauthenticated *bool
|
||||
HideBirthday *bool
|
||||
HideDates *bool
|
||||
Captcha *bool
|
||||
FollowersOnlyInteraction *bool
|
||||
}
|
||||
|
||||
type Session struct {
|
||||
ID int64
|
||||
UserID int64
|
||||
Guid pgtype.UUID
|
||||
Name pgtype.Text
|
||||
Platform pgtype.Text
|
||||
LatestIp pgtype.Text
|
||||
Name *string
|
||||
Platform *string
|
||||
LatestIp *string
|
||||
LoginTime pgtype.Timestamp
|
||||
LastSeenDate pgtype.Timestamp
|
||||
Terminated pgtype.Bool
|
||||
Terminated *bool
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID int64
|
||||
Username string
|
||||
Verified pgtype.Bool
|
||||
Verified *bool
|
||||
RegistrationDate pgtype.Timestamp
|
||||
Deleted pgtype.Bool
|
||||
Deleted *bool
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ VALUES ( $1, $2, $3, $4) RETURNING id, user_id, date, reason, expires_at, banned
|
||||
type CreateBannedUserParams struct {
|
||||
UserID int64
|
||||
ExpiresAt pgtype.Timestamp
|
||||
Reason pgtype.Text
|
||||
BannedBy pgtype.Text
|
||||
Reason *string
|
||||
BannedBy *string
|
||||
}
|
||||
|
||||
func (q *Queries) CreateBannedUser(ctx context.Context, arg CreateBannedUserParams) (BannedUser, error) {
|
||||
@@ -45,24 +45,18 @@ func (q *Queries) CreateBannedUser(ctx context.Context, arg CreateBannedUserPara
|
||||
}
|
||||
|
||||
const createConfirmationCode = `-- name: CreateConfirmationCode :one
|
||||
INSERT INTO confirmation_codes(user_id, code_type, code_hash, expires_at)
|
||||
VALUES ($1, $2, crypt($3, gen_salt('bf')), $4) RETURNING id, user_id, code_type, code_hash, expires_at, used, deleted
|
||||
INSERT INTO confirmation_codes(user_id, code_type, code_hash)
|
||||
VALUES ($1, $2, crypt($3::text, gen_salt('bf'))) RETURNING id, user_id, code_type, code_hash, expires_at, used, deleted
|
||||
`
|
||||
|
||||
type CreateConfirmationCodeParams struct {
|
||||
UserID int64
|
||||
CodeType int32
|
||||
Crypt string
|
||||
ExpiresAt pgtype.Timestamp
|
||||
UserID int64
|
||||
CodeType int32
|
||||
Code string
|
||||
}
|
||||
|
||||
func (q *Queries) CreateConfirmationCode(ctx context.Context, arg CreateConfirmationCodeParams) (ConfirmationCode, error) {
|
||||
row := q.db.QueryRow(ctx, createConfirmationCode,
|
||||
arg.UserID,
|
||||
arg.CodeType,
|
||||
arg.Crypt,
|
||||
arg.ExpiresAt,
|
||||
)
|
||||
row := q.db.QueryRow(ctx, createConfirmationCode, arg.UserID, arg.CodeType, arg.Code)
|
||||
var i ConfirmationCode
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
@@ -83,7 +77,7 @@ VALUES ( $1, $2, crypt($3::text, gen_salt('bf')) ) RETURNING id, user_id, email,
|
||||
|
||||
type CreateLoginInformationParams struct {
|
||||
UserID int64
|
||||
Email pgtype.Text
|
||||
Email *string
|
||||
Password string
|
||||
}
|
||||
|
||||
@@ -110,11 +104,11 @@ VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id, user_id, name, bio, avatar_url
|
||||
type CreateProfileParams struct {
|
||||
UserID int64
|
||||
Name string
|
||||
Bio pgtype.Text
|
||||
Bio *string
|
||||
Birthday pgtype.Timestamp
|
||||
AvatarUrl pgtype.Text
|
||||
Color pgtype.Text
|
||||
ColorGrad pgtype.Text
|
||||
AvatarUrl *string
|
||||
Color *string
|
||||
ColorGrad *string
|
||||
}
|
||||
|
||||
func (q *Queries) CreateProfile(ctx context.Context, arg CreateProfileParams) (Profile, error) {
|
||||
@@ -170,9 +164,9 @@ VALUES ($1, $2, $3, $4) RETURNING id, user_id, guid, name, platform, latest_ip,
|
||||
|
||||
type CreateSessionParams struct {
|
||||
UserID int64
|
||||
Name pgtype.Text
|
||||
Platform pgtype.Text
|
||||
LatestIp pgtype.Text
|
||||
Name *string
|
||||
Platform *string
|
||||
LatestIp *string
|
||||
}
|
||||
|
||||
func (q *Queries) CreateSession(ctx context.Context, arg CreateSessionParams) (Session, error) {
|
||||
@@ -331,18 +325,18 @@ WHERE users.username = $1 AND ($2 IS FALSE OR profile_settings.hide_for_unauthen
|
||||
|
||||
type GetProfileByUsernameRestrictedParams struct {
|
||||
Username string
|
||||
Column2 pgtype.Bool
|
||||
Column2 *bool
|
||||
}
|
||||
|
||||
type GetProfileByUsernameRestrictedRow struct {
|
||||
Username string
|
||||
Name string
|
||||
Birthday pgtype.Timestamp
|
||||
Bio pgtype.Text
|
||||
AvatarUrl pgtype.Text
|
||||
Color pgtype.Text
|
||||
ColorGrad pgtype.Text
|
||||
HideProfileDetails pgtype.Bool
|
||||
Bio *string
|
||||
AvatarUrl *string
|
||||
Color *string
|
||||
ColorGrad *string
|
||||
HideProfileDetails *bool
|
||||
}
|
||||
|
||||
func (q *Queries) GetProfileByUsernameRestricted(ctx context.Context, arg GetProfileByUsernameRestrictedParams) (GetProfileByUsernameRestrictedRow, error) {
|
||||
@@ -405,17 +399,17 @@ LIMIT 20 OFFSET 20 * $1
|
||||
`
|
||||
|
||||
type GetProfilesRestrictedParams struct {
|
||||
Column1 pgtype.Int4
|
||||
Column2 pgtype.Bool
|
||||
Column1 *int32
|
||||
Column2 *bool
|
||||
}
|
||||
|
||||
type GetProfilesRestrictedRow struct {
|
||||
Username string
|
||||
Name string
|
||||
AvatarUrl pgtype.Text
|
||||
Color pgtype.Text
|
||||
ColorGrad pgtype.Text
|
||||
HideProfileDetails pgtype.Bool
|
||||
AvatarUrl *string
|
||||
Color *string
|
||||
ColorGrad *string
|
||||
HideProfileDetails *bool
|
||||
}
|
||||
|
||||
func (q *Queries) GetProfilesRestricted(ctx context.Context, arg GetProfilesRestrictedParams) ([]GetProfilesRestrictedRow, error) {
|
||||
@@ -558,7 +552,7 @@ type GetUserByLoginCredentialsRow struct {
|
||||
ID int64
|
||||
Username string
|
||||
PasswordHash string
|
||||
TotpEncrypted pgtype.Text
|
||||
TotpEncrypted *string
|
||||
}
|
||||
|
||||
func (q *Queries) GetUserByLoginCredentials(ctx context.Context, arg GetUserByLoginCredentialsParams) (GetUserByLoginCredentialsRow, error) {
|
||||
@@ -654,11 +648,11 @@ WHERE id = $1
|
||||
|
||||
type UpdateBannedUserParams struct {
|
||||
ID int64
|
||||
Reason pgtype.Text
|
||||
Reason *string
|
||||
ExpiresAt pgtype.Timestamp
|
||||
BannedBy pgtype.Text
|
||||
Pardoned pgtype.Bool
|
||||
PardonedBy pgtype.Text
|
||||
BannedBy *string
|
||||
Pardoned *bool
|
||||
PardonedBy *string
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateBannedUser(ctx context.Context, arg UpdateBannedUserParams) error {
|
||||
@@ -681,8 +675,8 @@ WHERE id = $1
|
||||
|
||||
type UpdateConfirmationCodeParams struct {
|
||||
ID int64
|
||||
Used pgtype.Bool
|
||||
Deleted pgtype.Bool
|
||||
Used *bool
|
||||
Deleted *bool
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateConfirmationCode(ctx context.Context, arg UpdateConfirmationCodeParams) error {
|
||||
@@ -699,10 +693,10 @@ WHERE users.username = $1 AND login_informations.user_id = users.id
|
||||
|
||||
type UpdateLoginInformationByUsernameParams struct {
|
||||
Username string
|
||||
Email pgtype.Text
|
||||
Email *string
|
||||
Password string
|
||||
TotpEncrypted pgtype.Text
|
||||
Email2faEnabled pgtype.Bool
|
||||
TotpEncrypted *string
|
||||
Email2faEnabled *bool
|
||||
PasswordChangeDate pgtype.Timestamp
|
||||
}
|
||||
|
||||
@@ -728,11 +722,11 @@ WHERE username = $1
|
||||
type UpdateProfileByUsernameParams struct {
|
||||
Username string
|
||||
Name string
|
||||
Bio pgtype.Text
|
||||
Bio *string
|
||||
Birthday pgtype.Timestamp
|
||||
AvatarUrl pgtype.Text
|
||||
Color pgtype.Text
|
||||
ColorGrad pgtype.Text
|
||||
AvatarUrl *string
|
||||
Color *string
|
||||
ColorGrad *string
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateProfileByUsername(ctx context.Context, arg UpdateProfileByUsernameParams) error {
|
||||
@@ -763,13 +757,13 @@ WHERE id = $1
|
||||
|
||||
type UpdateProfileSettingsParams struct {
|
||||
ID int64
|
||||
HideFulfilled pgtype.Bool
|
||||
HideProfileDetails pgtype.Bool
|
||||
HideForUnauthenticated pgtype.Bool
|
||||
HideBirthday pgtype.Bool
|
||||
HideDates pgtype.Bool
|
||||
Captcha pgtype.Bool
|
||||
FollowersOnlyInteraction pgtype.Bool
|
||||
HideFulfilled *bool
|
||||
HideProfileDetails *bool
|
||||
HideForUnauthenticated *bool
|
||||
HideBirthday *bool
|
||||
HideDates *bool
|
||||
Captcha *bool
|
||||
FollowersOnlyInteraction *bool
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateProfileSettings(ctx context.Context, arg UpdateProfileSettingsParams) error {
|
||||
@@ -794,12 +788,12 @@ WHERE id = $1
|
||||
|
||||
type UpdateSessionParams struct {
|
||||
ID int64
|
||||
Name pgtype.Text
|
||||
Platform pgtype.Text
|
||||
LatestIp pgtype.Text
|
||||
Name *string
|
||||
Platform *string
|
||||
LatestIp *string
|
||||
LoginTime pgtype.Timestamp
|
||||
LastSeenDate pgtype.Timestamp
|
||||
Terminated pgtype.Bool
|
||||
Terminated *bool
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateSession(ctx context.Context, arg UpdateSessionParams) error {
|
||||
@@ -823,8 +817,8 @@ WHERE id = $1
|
||||
|
||||
type UpdateUserParams struct {
|
||||
ID int64
|
||||
Verified pgtype.Bool
|
||||
Deleted pgtype.Bool
|
||||
Verified *bool
|
||||
Deleted *bool
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) error {
|
||||
@@ -840,8 +834,8 @@ WHERE username = $1
|
||||
|
||||
type UpdateUserByUsernameParams struct {
|
||||
Username string
|
||||
Verified pgtype.Bool
|
||||
Deleted pgtype.Bool
|
||||
Verified *bool
|
||||
Deleted *bool
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateUserByUsername(ctx context.Context, arg UpdateUserByUsernameParams) error {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
errs "easywish/internal/errors"
|
||||
"easywish/internal/models"
|
||||
"easywish/internal/utils"
|
||||
"easywish/internal/utils/enums"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
@@ -27,18 +28,50 @@ func NewAuthService(_log *zap.Logger, _dbctx database.DbContext) AuthService {
|
||||
|
||||
func (a *authServiceImpl) RegistrationBegin(request models.RegistrationBeginRequest) (bool, error) {
|
||||
|
||||
var user database.User
|
||||
var generatedCode string
|
||||
|
||||
helper, db, _ := database.NewDbHelperTransaction(a.dbctx)
|
||||
defer helper.Rollback()
|
||||
|
||||
user, err := db.TXQueries.CreateUser(db.CTX, request.Username) // TODO: validation
|
||||
var err error
|
||||
|
||||
if err != nil {
|
||||
if user, err = db.TXQueries.CreateUser(db.CTX, request.Username); err != nil { // TODO: validation
|
||||
a.log.Error("Failed to add user to database", zap.Error(err))
|
||||
return false, errs.ErrServerError
|
||||
}
|
||||
|
||||
a.log.Info("Registraion of a new user", zap.String("username", user.Username), zap.Int64("id", user.ID))
|
||||
|
||||
if _, err = db.TXQueries.CreateLoginInformation(db.CTX, database.CreateLoginInformationParams{
|
||||
UserID: user.ID,
|
||||
Email: request.Email,
|
||||
Password: request.Password, // Hashed in database
|
||||
}); err != nil {
|
||||
a.log.Error("Failed to add login information for user to database", zap.Error(err))
|
||||
return false, errs.ErrServerError
|
||||
}
|
||||
|
||||
if generatedCode, err = utils.GenerateSecure6DigitNumber(); err != nil {
|
||||
a.log.Error("Failed to generate a registration code", zap.Error(err))
|
||||
return false, errs.ErrServerError
|
||||
}
|
||||
|
||||
if _, err = db.TXQueries.CreateConfirmationCode(db.CTX, database.CreateConfirmationCodeParams{
|
||||
UserID: user.ID,
|
||||
CodeType: int32(enums.RegistrationCodeType),
|
||||
Code: generatedCode, // Hashed in database
|
||||
}); err != nil {
|
||||
a.log.Error("Failed to add registration code to database", zap.Error(err))
|
||||
return false, errs.ErrServerError
|
||||
}
|
||||
|
||||
a.log.Info("Registered a new user", zap.String("username", user.Username))
|
||||
|
||||
helper.Commit()
|
||||
|
||||
a.log.Debug("Declated registration code for a new user", zap.String("username", user.Username), zap.String("code", generatedCode))
|
||||
|
||||
// TODO: Send verification email
|
||||
|
||||
return true, nil
|
||||
|
||||
8
backend/internal/utils/enums/enums.go
Normal file
8
backend/internal/utils/enums/enums.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package enums
|
||||
|
||||
type ConfirmationCodeType int32
|
||||
|
||||
const (
|
||||
RegistrationCodeType ConfirmationCodeType = iota
|
||||
PasswordResetCodeType
|
||||
)
|
||||
26
backend/internal/utils/securityCode.go
Normal file
26
backend/internal/utils/securityCode.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
func GenerateSecure6DigitNumber() (string, error) {
|
||||
// Generate a random number between 0 and 999999 (inclusive)
|
||||
// This ensures we get a 6-digit number, including those starting with 0
|
||||
max := 1000000 // Upper bound (exclusive)
|
||||
b := make([]byte, 4) // A 4-byte slice is sufficient for a 32-bit integer
|
||||
|
||||
if _, err := io.ReadFull(rand.Reader, b); err != nil {
|
||||
return "", fmt.Errorf("failed to read random bytes: %w", err)
|
||||
}
|
||||
|
||||
// Convert bytes to an integer
|
||||
// We use a simple modulo operation to get a number within our desired range.
|
||||
// While this introduces a slight bias for very large ranges, for 1,000,000
|
||||
// it's negligible and simpler than more complex methods like rejection sampling.
|
||||
num := int(uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3])) % max
|
||||
|
||||
return fmt.Sprintf("%06d", num), nil
|
||||
}
|
||||
Reference in New Issue
Block a user