Keep deleted players' names reserved

This commit is contained in:
Claude committed 2026-09-30 17:46:54 +00:00
1 parent e344e7144e
commit 78da10364f
3 files changed
+8 -6

No files matched your search

+2 -1
View File
@@ -119,7 +119,8 @@ pub async fn purge_user(state: &AppState, user: &UserRow) -> AppResult<PurgeRepo
run(c, "UPDATE economy_transactions SET to_uuid = ?, to_name = ? WHERE to_uuid = ?", &[DELETED_UUID, DELETED_NAME, uuid]).await?;
// ---- identity ----
report.add("name_reservations", run(c, "DELETE FROM reserved_usernames WHERE uuid = ?", &[uuid]).await?);
// `reserved_usernames` is kept on purpose: a deleted player's name stays
// taken so nobody can impersonate them. It holds only the name and UUID.
// Explicit, in case a foreign key is ever relaxed.
let id = user.id.to_string();
for (area, sql) in [
+5 -4
View File
@@ -146,8 +146,8 @@ async fn deleting_an_account_removes_all_its_data() {
assert!(v["report"]["guilds_transferred"] == 1 && v["report"]["removed"]["stats"].as_u64().unwrap() > 0, "{v}");
// Nothing refers to them any more (their name appears only as plain text in free-form rows we don't keep).
assert_eq!(mentions(&t, &steve_uuid).await, Vec::<String>::new());
for table in ["users", "user_levels", "player_stats", "friendships", "direct_messages", "user_profiles", "user_posts", "server_market", "server_economy", "reserved_usernames"] {
assert_eq!(mentions(&t, &steve_uuid).await, vec!["reserved_usernames.uuid".to_string()]);
for table in ["users", "user_levels", "player_stats", "friendships", "direct_messages", "user_profiles", "user_posts", "server_market", "server_economy"] {
let n: i64 = sqlx::query_scalar(&format!("SELECT COUNT(*) FROM {table} WHERE CAST(uuid AS TEXT) = ?")).bind(&steve_uuid).fetch_one(&t.db).await.unwrap_or(0);
assert_eq!(n, 0, "{table}");
}
@@ -166,9 +166,10 @@ async fn deleting_an_account_removes_all_its_data() {
assert!(friends.as_array().unwrap().is_empty());
let (_, list) = t.call("GET", "/api/v1/members/search", Some(&alex), None).await;
assert!(list.as_array().unwrap().iter().all(|m| m["username"] != "Steve"));
// The old name can be taken again, and the deleted token no longer works.
// The deleted token no longer works, and the name stays reserved against impersonation.
let (s, _) = t.call("GET", "/api/v1/auth/me", Some(&steve), None).await;
assert_eq!(s, StatusCode::UNAUTHORIZED);
player(&t, &admin, "Steve").await;
let (s, _) = t.call("POST", "/api/admin/users", Some(&admin), Some(json!({"username": "Steve", "password": "password123"}))).await;
assert_eq!(s, StatusCode::CONFLICT);
assert_ne!(mia_uuid, steve_uuid);
}