131 lines
3.7 KiB
Go
131 lines
3.7 KiB
Go
package models
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"time"
|
|
|
|
"code.nonshy.com/nonshy/website/pkg/encryption"
|
|
"code.nonshy.com/nonshy/website/pkg/log"
|
|
"gorm.io/gorm/clause"
|
|
)
|
|
|
|
// DeletedUserMemory table stores security-related information, such as block lists, when a user
|
|
// deletes their account - so that when they sign up again later under the same username or email
|
|
// address, this information can be restored and other users on the site can have their block lists
|
|
// respected.
|
|
type DeletedUserMemory struct {
|
|
Username string `gorm:"uniqueIndex:idx_deleted_user_memory"`
|
|
HashedEmail string `gorm:"uniqueIndex:idx_deleted_user_memory"`
|
|
Data string
|
|
CreatedAt time.Time
|
|
UpdatedAt time.Time
|
|
}
|
|
|
|
// DeletedUserMemoryData is a JSON serializable struct for data stored in the DeletedUserMemory table.
|
|
type DeletedUserMemoryData struct {
|
|
PreviousUsername string
|
|
BlockingUserIDs []uint64
|
|
BlockedByUserIDs []uint64
|
|
}
|
|
|
|
// CreateDeletedUserMemory creates the row in the database just before a user account is deleted.
|
|
func CreateDeletedUserMemory(user *User) error {
|
|
// Get the user's blocked lists.
|
|
forward, reverse := GetAllBlockedUserIDs(user)
|
|
|
|
// Store the memory.
|
|
data := DeletedUserMemoryData{
|
|
PreviousUsername: user.Username,
|
|
BlockingUserIDs: forward,
|
|
BlockedByUserIDs: reverse,
|
|
}
|
|
bin, err := json.Marshal(data)
|
|
if err != nil {
|
|
return fmt.Errorf("JSON marshal: %s", err)
|
|
}
|
|
|
|
// Upsert the mute.
|
|
m := &DeletedUserMemory{
|
|
Username: user.Username,
|
|
HashedEmail: string(encryption.Hash([]byte(user.Email))),
|
|
Data: string(bin),
|
|
}
|
|
res := DB.Model(&DeletedUserMemory{}).Clauses(
|
|
clause.OnConflict{
|
|
Columns: []clause.Column{
|
|
{Name: "username"},
|
|
{Name: "hashed_email"},
|
|
},
|
|
UpdateAll: true,
|
|
},
|
|
).Create(m)
|
|
return res.Error
|
|
}
|
|
|
|
// RestoreDeletedUserMemory checks for remembered data and will restore and clear the memory if found.
|
|
func RestoreDeletedUserMemory(user *User) error {
|
|
// Anything stored?
|
|
var (
|
|
m *DeletedUserMemory
|
|
hashedEmail = string(encryption.Hash([]byte(user.Email)))
|
|
err = DB.Model(&DeletedUserMemory{}).Where(
|
|
"username = ? OR hashed_email = ?",
|
|
user.Username,
|
|
hashedEmail,
|
|
).First(&m).Error
|
|
)
|
|
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
// Parse the remembered payload.
|
|
var data DeletedUserMemoryData
|
|
err = json.Unmarshal([]byte(m.Data), &data)
|
|
if err != nil {
|
|
return fmt.Errorf("RestoreDeletedUserMemory: JSON unmarshal: %s", err)
|
|
}
|
|
|
|
// Bulk restore the user's block list.
|
|
blocks, err := BulkRestoreBlockedUserIDs(user, data.BlockingUserIDs, data.BlockedByUserIDs)
|
|
if err != nil {
|
|
log.Error("BulkRestoreBlockedUserIDs(%s): %s", user.Username, err)
|
|
}
|
|
|
|
// If any blocks were added, notify the admin that the user has returned - for visibility
|
|
// and to detect any mistakes.
|
|
if blocks > 0 {
|
|
fb := &Feedback{
|
|
Intent: "report",
|
|
Subject: "A deleted user has returned",
|
|
UserID: user.ID,
|
|
TableName: "users",
|
|
TableID: user.ID,
|
|
Message: fmt.Sprintf(
|
|
"The username **@%s**, who was previously deleted, has signed up a new account "+
|
|
"with the same username or e-mail address.\n\n"+
|
|
"Their previous username was **@%s** when they last deleted their account.\n\n"+
|
|
"Their block lists from when they deleted their old account have been restored:\n\n"+
|
|
"* Forward list: %d\n* Reverse list: %d",
|
|
user.Username,
|
|
data.PreviousUsername,
|
|
len(data.BlockingUserIDs),
|
|
len(data.BlockedByUserIDs),
|
|
),
|
|
}
|
|
|
|
// Save the feedback.
|
|
if err := CreateFeedback(fb); err != nil {
|
|
log.Error("Couldn't save feedback from user recovering their deleted account: %s", err)
|
|
}
|
|
}
|
|
|
|
// Delete the stored user memory.
|
|
return DB.Where(
|
|
"username = ? OR hashed_email = ?",
|
|
user.Username,
|
|
hashedEmail,
|
|
).Delete(&DeletedUserMemory{}).Error
|
|
}
|