feat: change email, export my data, delete account (with data anonymization)

This commit is contained in:
Florian Sylvain committed 2026-10-30 20:19:07 +01:00
1 parent bb191c389a
commit 10a66cd979
123 files changed
+10789 -76

No files matched your search

+10
View File
@@ -22,9 +22,16 @@ var ErrPasswordTooLong = errors.New("password too long")
var ErrRoleChanged = errors.New("user role changed concurrently")
var ErrInvalidTransfer = errors.New("invalid ownership transfer")
var ErrConfirmationMismatch = errors.New("confirmation email does not match")
var ErrWrongPassword = errors.New("wrong password")
var ErrSameEmail = errors.New("new email is the current email")
var ErrNoPendingEmailChange = errors.New("no pending email change")
var ErrInvalidEmailChangeCode = errors.New("invalid email change code")
var ErrEmailUnavailable = errors.New("email unavailable")
const MaxLookupIDs = 100
const EmailChangeLifetime = 2 * time.Hour
const MaxPasswordBytes = 72
func ValidatePassword(password string) error {
@@ -47,6 +54,9 @@ type User struct {
VerificationCode string `json:"verification_code"`
VerificationExpiration time.Time `json:"verification_expiration"`
PasswordResetExpiration time.Time `json:"password_reset_expiration"`
PendingEmail string `json:"pending_email"`
EmailChangeCode string `json:"email_change_code"`
EmailChangeExpiration time.Time `json:"email_change_expiration"`
TokenVersion uint32 `json:"token_version"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
+42
View File
@@ -0,0 +1,42 @@
package user
import "errors"
const DeletedUserName = "Deleted user"
const (
LastRoleOwner = "Owner"
LastRoleAdmin = "Admin"
)
var ErrLastOwner = errors.New("last owner")
var ErrLastAdmin = errors.New("last administrator")
var ErrAccountNotDeletable = errors.New("account not deletable")
type LastAdminError struct {
Role string
}
func (e LastAdminError) Error() string {
if e.Role == LastRoleOwner {
return ErrLastOwner.Error()
}
return ErrLastAdmin.Error()
}
func (e LastAdminError) Is(target error) bool {
if target == ErrLastAdmin {
return true
}
return target == ErrLastOwner && e.Role == LastRoleOwner
}
type Erasure struct {
UserID uint32
GuardRoleIDs []uint32
AvatarImageID *uint32
}
type Eraser interface {
Erase(erasure Erasure) error
}
@@ -17,6 +17,10 @@ type Repository interface {
ClearVerificationCode(userId uint32) (User, error)
ResetPassword(userId uint32, hashedPassword string) (User, error)
UpdateDisplayName(userId uint32, displayName string) (User, error)
IsEmailInUse(email string, exceptUserId uint32) (bool, error)
SetPendingEmailChange(userId uint32, pendingEmail string, code string, expiration time.Time) (User, error)
ClearPendingEmailChange(userId uint32) (User, error)
ConfirmEmailChange(userId uint32, pendingEmail string, code string, now time.Time) (User, error)
UpdateRole(id uint32, from uint32, to uint32, assignedBy uint32) (User, error)
TransferOwnership(from uint32, to uint32, ownerRoleID uint32, formerRoleID uint32) error
}