refactor: simplify account standing and verification levels (#3130)

This commit is contained in:
Hampus
2026-10-02 18:17:41 +02:00
committed by GitHub
parent 1abde06824
commit 1ec8f31253
488 changed files with 10390 additions and 47117 deletions
+17 -349
View File
@@ -1683,7 +1683,7 @@
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
}
},
"description": "Enqueue one background administrative job. The `task` discriminator selects both the body variant and the ACL evaluated for the request: `update_user_flags` needs bulk:update:user_flags, `update_suspicious_activity_flags` needs bulk:update:suspicious_activity, `update_guild_features` needs bulk:update:guild_features, `add_guild_members` needs bulk:add:guild_members, `schedule_user_deletion` needs bulk:delete:users, and `delete_user_messages` needs bulk:delete:user_messages. Returns a job_id immediately; observe progress at /admin/jobs/:job_id.",
"description": "Enqueue one background administrative job. The `task` discriminator selects both the body variant and the ACL evaluated for the request: `update_user_flags` needs bulk:update:user_flags, `update_guild_features` needs bulk:update:guild_features, `add_guild_members` needs bulk:add:guild_members, `schedule_user_deletion` needs bulk:delete:users, and `delete_user_messages` needs bulk:delete:user_messages. Returns a job_id immediately; observe progress at /admin/jobs/:job_id.",
"security": [{"adminApiKey": []}],
"requestBody": {
"required": true,
@@ -7190,74 +7190,6 @@
]
}
},
"/admin/users/{user_id}/phone-verification": {
"put": {
"operationId": "update_admin_user_phone_verification",
"summary": "Update user phone verification flag",
"tags": ["Admin"],
"responses": {
"200": {
"description": "Success",
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserMutationResponse"}}}
},
"400": {
"description": "Bad Request - The request was malformed or contained invalid data",
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
},
"401": {
"description": "Unauthorized - Authentication is required or the token is invalid",
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
},
"403": {
"description": "Forbidden - You do not have permission to perform this action",
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
},
"429": {
"description": "Too Many Requests - You are being rate limited",
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ThrottledError"}}},
"headers": {
"Retry-After": {
"description": "Number of seconds to wait before retrying (only on 429)",
"schema": {"type": "integer"}
},
"X-RateLimit-Limit": {
"description": "The number of requests that can be made in the current window",
"schema": {"type": "integer"}
},
"X-RateLimit-Remaining": {
"description": "The number of remaining requests that can be made",
"schema": {"type": "integer"}
},
"X-RateLimit-Reset": {
"description": "Unix timestamp when the rate limit resets",
"schema": {"type": "integer"}
}
}
},
"500": {
"description": "Internal Server Error - An unexpected error occurred",
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
}
},
"description": "Set whether a user is treated as having completed phone verification. This is the only supported path for clearing the irreversible user-facing phone verification flag. Requires USER_UPDATE_PHONE permission.",
"security": [{"adminApiKey": []}],
"parameters": [
{
"name": "user_id",
"in": "path",
"required": true,
"schema": {"description": "The ID of the user", "allOf": [{"$ref": "#/components/schemas/SnowflakeType"}]},
"description": "The ID of the user"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {"schema": {"$ref": "#/components/schemas/AdminUserPhoneVerificationRequest"}}
}
}
}
},
"/admin/users/{user_id}/premium-flags": {
"patch": {
"operationId": "update_admin_user_premium_flags",
@@ -7851,142 +7783,6 @@
]
}
},
"/admin/users/{user_id}/suspicious-activity-disablement": {
"put": {
"operationId": "disable_admin_user_suspicious",
"summary": "Disable user for suspicious activity",
"tags": ["Admin"],
"responses": {
"200": {
"description": "Success",
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserMutationResponse"}}}
},
"400": {
"description": "Bad Request - The request was malformed or contained invalid data",
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
},
"401": {
"description": "Unauthorized - Authentication is required or the token is invalid",
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
},
"403": {
"description": "Forbidden - You do not have permission to perform this action",
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
},
"429": {
"description": "Too Many Requests - You are being rate limited",
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ThrottledError"}}},
"headers": {
"Retry-After": {
"description": "Number of seconds to wait before retrying (only on 429)",
"schema": {"type": "integer"}
},
"X-RateLimit-Limit": {
"description": "The number of requests that can be made in the current window",
"schema": {"type": "integer"}
},
"X-RateLimit-Remaining": {
"description": "The number of remaining requests that can be made",
"schema": {"type": "integer"}
},
"X-RateLimit-Reset": {
"description": "Unix timestamp when the rate limit resets",
"schema": {"type": "integer"}
}
}
},
"500": {
"description": "Internal Server Error - An unexpected error occurred",
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
}
},
"description": "Disable user account due to suspicious activity or abuse. Account is locked pending review. User cannot access services. Emails the user unless notify_user is false. Creates audit log entry. Requires USER_DISABLE_SUSPICIOUS permission.",
"security": [{"adminApiKey": []}],
"parameters": [
{
"name": "user_id",
"in": "path",
"required": true,
"schema": {"description": "The ID of the user", "allOf": [{"$ref": "#/components/schemas/SnowflakeType"}]},
"description": "The ID of the user"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {"schema": {"$ref": "#/components/schemas/AdminUserSuspiciousDisableRequest"}}
}
}
}
},
"/admin/users/{user_id}/suspicious-activity-flags": {
"put": {
"operationId": "update_admin_user_suspicious_activity_flags",
"summary": "Update suspicious activity flags",
"tags": ["Admin"],
"responses": {
"200": {
"description": "Success",
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/UserMutationResponse"}}}
},
"400": {
"description": "Bad Request - The request was malformed or contained invalid data",
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
},
"401": {
"description": "Unauthorized - Authentication is required or the token is invalid",
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
},
"403": {
"description": "Forbidden - You do not have permission to perform this action",
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
},
"429": {
"description": "Too Many Requests - You are being rate limited",
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ThrottledError"}}},
"headers": {
"Retry-After": {
"description": "Number of seconds to wait before retrying (only on 429)",
"schema": {"type": "integer"}
},
"X-RateLimit-Limit": {
"description": "The number of requests that can be made in the current window",
"schema": {"type": "integer"}
},
"X-RateLimit-Remaining": {
"description": "The number of remaining requests that can be made",
"schema": {"type": "integer"}
},
"X-RateLimit-Reset": {
"description": "Unix timestamp when the rate limit resets",
"schema": {"type": "integer"}
}
}
},
"500": {
"description": "Internal Server Error - An unexpected error occurred",
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
}
},
"description": "Flag user as suspicious for account abuse, fraud, or policy violations. Enables enforcement actions and rate limiting. Creates audit log entry. Requires USER_UPDATE_SUSPICIOUS_ACTIVITY permission.",
"security": [{"adminApiKey": []}],
"parameters": [
{
"name": "user_id",
"in": "path",
"required": true,
"schema": {"description": "The ID of the user", "allOf": [{"$ref": "#/components/schemas/SnowflakeType"}]},
"description": "The ID of the user"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {"schema": {"$ref": "#/components/schemas/AdminUserSuspiciousActivityFlagsRequest"}}
}
}
}
},
"/admin/users/{user_id}/system-status": {
"put": {
"operationId": "set_admin_user_system_status",
@@ -9506,31 +9302,6 @@
},
"required": ["system"]
},
"AdminUserSuspiciousActivityFlagsRequest": {
"type": "object",
"properties": {
"flags": {
"description": "Bitmask of suspicious activity flags",
"allOf": [{"$ref": "#/components/schemas/SuspiciousActivityFlags"}]
}
},
"required": ["flags"]
},
"AdminUserSuspiciousDisableRequest": {
"type": "object",
"properties": {
"flags": {
"description": "Bitmask of suspicious activity flags that triggered the disable",
"allOf": [{"$ref": "#/components/schemas/SuspiciousActivityFlags"}]
},
"notify_user": {
"default": true,
"description": "Whether to email the user that the account was disabled",
"type": "boolean"
}
},
"required": ["flags"]
},
"AdminStorePurchaseListResponse": {
"type": "object",
"properties": {
@@ -9685,16 +9456,6 @@
}
}
},
"AdminUserPhoneVerificationRequest": {
"type": "object",
"properties": {
"has_verified_phone": {
"type": "boolean",
"description": "Whether the user should be treated as having completed phone verification"
}
},
"required": ["has_verified_phone"]
},
"DeleteAllUserMessagesResponse": {
"type": "object",
"properties": {
@@ -10110,7 +9871,7 @@
"type": "object",
"properties": {
"acls": {
"maxItems": 108,
"maxItems": 104,
"type": "array",
"items": {"$ref": "#/components/schemas/AdminAclType"},
"description": "List of access control permissions to assign"
@@ -12436,37 +12197,6 @@
},
"required": ["user_ids", "task"]
},
{
"type": "object",
"properties": {
"user_ids": {
"maxItems": 1000,
"type": "array",
"items": {"$ref": "#/components/schemas/SnowflakeType"},
"description": "List of user IDs to update"
},
"add_flags": {
"default": [],
"description": "Suspicious activity flag names to add to all specified users",
"maxItems": 32,
"type": "array",
"items": {"type": "string"}
},
"remove_flags": {
"default": [],
"description": "Suspicious activity flag names to remove from all specified users",
"maxItems": 32,
"type": "array",
"items": {"type": "string"}
},
"task": {
"type": "string",
"enum": ["update_suspicious_activity_flags"],
"description": "Adds and removes verification requirements on every targeted user"
}
},
"required": ["user_ids", "task"]
},
{
"type": "object",
"properties": {
@@ -12999,7 +12729,7 @@
},
"acls": {
"description": "Replacement list of access control permissions for the key",
"maxItems": 108,
"maxItems": 104,
"type": "array",
"items": {"$ref": "#/components/schemas/AdminAclType"}
}
@@ -13017,7 +12747,7 @@
"type": "string"
},
"acls": {
"maxItems": 108,
"maxItems": 104,
"type": "array",
"items": {"type": "string"},
"description": "List of access control permissions for the key"
@@ -13047,7 +12777,7 @@
"maximum": 365
},
"acls": {
"maxItems": 108,
"maxItems": 104,
"type": "array",
"items": {"$ref": "#/components/schemas/AdminAclType"},
"description": "List of access control permissions for the key"
@@ -13068,7 +12798,7 @@
"type": "string"
},
"acls": {
"maxItems": 108,
"maxItems": 104,
"type": "array",
"items": {"type": "string"},
"description": "List of access control permissions for the key"
@@ -13081,7 +12811,7 @@
"type": "object",
"properties": {
"acls": {
"maxItems": 108,
"maxItems": 104,
"type": "array",
"items": {"type": "string", "minLength": 1, "maxLength": 64},
"description": "Every admin access control permission the admin API recognises"
@@ -13135,7 +12865,6 @@
"bulk:delete:users",
"bulk:delete:user_messages",
"bulk:update:guild_features",
"bulk:update:suspicious_activity",
"bulk:update:user_flags",
"csam:submit_ncmec",
"discovery:remove",
@@ -13171,7 +12900,6 @@
"system_dm:send",
"user:cancel:bulk_message_deletion",
"user:delete",
"user:disable:suspicious",
"user:list:dm_channels",
"user:list:guilds",
"user:list:relationships",
@@ -13188,9 +12916,7 @@
"user:update:email",
"user:update:flags",
"user:update:mfa",
"user:update:phone",
"user:update:profile",
"user:update:suspicious_activity",
"user:update:traits",
"user:update:username",
"voice:region:create",
@@ -13637,11 +13363,6 @@
},
{"name": "SPAMMER", "value": "64", "description": "User is flagged as a spammer"},
{"name": "DELETED", "value": "17179869184", "description": "User account has been deleted"},
{
"name": "DISABLED_SUSPICIOUS_ACTIVITY",
"value": "34359738368",
"description": "User account disabled due to suspicious activity"
},
{"name": "SELF_DELETED", "value": "68719476736", "description": "User account was self-deleted"},
{"name": "DISABLED", "value": "274877906944", "description": "User account is disabled"},
{"name": "HAS_SESSION_STARTED", "value": "549755813888", "description": "User has started a session"},
@@ -13652,6 +13373,7 @@
"value": "562949953421312",
"description": "User is verified as not underage"
},
{"name": "ACCOUNT_LIMITED", "value": "1125899906842624", "description": "User account is limited"},
{
"name": "HAS_DISMISSED_PREMIUM_ONBOARDING",
"value": "2251799813685248",
@@ -13669,14 +13391,9 @@
"description": "User has verified their age as an adult via credit card verification"
},
{
"name": "FORCE_INBOUND_PHONE_VERIFICATION",
"value": "2305843009213693952",
"description": "User is forced through inbound phone verification, for debugging"
},
{
"name": "NOT_SUSPICIOUS",
"name": "LIMIT_EXEMPT",
"value": "4611686018427387904",
"description": "User is permanently exempt from automatic suspicious-activity flagging"
"description": "User is permanently exempt from account limitation"
}
]
},
@@ -15019,15 +14736,14 @@
"GuildVerificationLevel": {
"description": "Required verification level for members",
"type": "integer",
"enum": [0, 1, 2, 3, 4],
"enum": [0, 1, 2, 3],
"format": "int32",
"x-enumNames": ["NONE", "LOW", "MEDIUM", "HIGH", "VERY_HIGH"],
"x-enumNames": ["NONE", "LOW", "MEDIUM", "HIGH"],
"x-enumDescriptions": [
"Unrestricted",
"Must have verified email",
"Registered for more than 5 minutes",
"Member of the server for more than 10 minutes",
"Must have a verified phone number"
"Member of the server for more than 10 minutes"
]
},
"GuildOperations": {
@@ -15109,15 +14825,14 @@
"minimum": 0,
"maximum": 2147483647,
"format": "int32",
"x-enumNames": ["NONE", "LOW", "MEDIUM", "HIGH", "VERY_HIGH"],
"x-enumNames": ["NONE", "LOW", "MEDIUM", "HIGH"],
"x-enumDescriptions": [
"Unrestricted",
"Must have verified email",
"Registered for more than 5 minutes",
"Member of the server for more than 10 minutes",
"Must have a verified phone number"
"Member of the server for more than 10 minutes"
],
"enum": [0, 1, 2, 3, 4]
"enum": [0, 1, 2, 3]
},
"PurgeGuildAssetErrorSchema": {
"type": "object",
@@ -15887,7 +15602,6 @@
"email": {"nullable": true, "type": "string"},
"email_verified": {"type": "boolean"},
"email_bounced": {"type": "boolean"},
"has_verified_phone": {"type": "boolean"},
"date_of_birth": {"nullable": true, "type": "string"},
"locale": {"nullable": true, "type": "string"},
"premium_type": {"nullable": true, "allOf": [{"$ref": "#/components/schemas/Int32Type"}]},
@@ -15895,11 +15609,6 @@
"premium_until": {"nullable": true, "type": "string"},
"premium_grace_ends_at": {"nullable": true, "type": "string"},
"premium_lifetime_sequence": {"nullable": true, "allOf": [{"$ref": "#/components/schemas/Int32Type"}]},
"suspicious_activity_flags": {"allOf": [{"$ref": "#/components/schemas/SuspiciousActivityFlags"}]},
"phone_verification_deferred": {
"type": "boolean",
"description": "Whether a stored phone requirement is deferred and not enforced"
},
"temp_banned_until": {"nullable": true, "type": "string"},
"pending_deletion_at": {"nullable": true, "type": "string"},
"pending_bulk_message_deletion_at": {"nullable": true, "type": "string"},
@@ -15920,7 +15629,7 @@
"description": "ISO 8601 timestamp when the pending deletion was scheduled",
"type": "string"
},
"acls": {"maxItems": 108, "type": "array", "items": {"type": "string"}},
"acls": {"maxItems": 104, "type": "array", "items": {"type": "string"}},
"traits": {"maxItems": 100, "type": "array", "items": {"type": "string"}},
"has_totp": {"type": "boolean"},
"authenticator_types": {"maxItems": 10, "type": "array", "items": {"$ref": "#/components/schemas/Int32Type"}},
@@ -15946,7 +15655,6 @@
"email",
"email_verified",
"email_bounced",
"has_verified_phone",
"date_of_birth",
"locale",
"premium_type",
@@ -15954,8 +15662,6 @@
"premium_until",
"premium_grace_ends_at",
"premium_lifetime_sequence",
"suspicious_activity_flags",
"phone_verification_deferred",
"temp_banned_until",
"pending_deletion_at",
"pending_bulk_message_deletion_at",
@@ -15975,44 +15681,6 @@
],
"additionalProperties": false
},
"SuspiciousActivityFlags": {
"type": "integer",
"minimum": 0,
"maximum": 2147483647,
"description": "Suspicious activity indicators",
"format": "int32",
"x-bitflagValues": [
{"name": "REQUIRE_VERIFIED_EMAIL", "value": "1", "description": "Requires verified email address"},
{"name": "REQUIRE_REVERIFIED_EMAIL", "value": "2", "description": "Requires re-verified email address"},
{"name": "REQUIRE_VERIFIED_PHONE", "value": "4", "description": "Requires verified phone number"},
{"name": "REQUIRE_REVERIFIED_PHONE", "value": "8", "description": "Requires re-verified phone number"},
{
"name": "REQUIRE_VERIFIED_EMAIL_OR_VERIFIED_PHONE",
"value": "16",
"description": "Requires verified email or verified phone"
},
{
"name": "REQUIRE_REVERIFIED_EMAIL_OR_VERIFIED_PHONE",
"value": "32",
"description": "Requires re-verified email or re-verified phone"
},
{
"name": "REQUIRE_VERIFIED_EMAIL_OR_REVERIFIED_PHONE",
"value": "64",
"description": "Requires verified email or re-verified phone"
},
{
"name": "REQUIRE_REVERIFIED_EMAIL_OR_REVERIFIED_PHONE",
"value": "128",
"description": "Requires re-verified email or re-verified phone"
},
{
"name": "REQUIRE_INBOUND_PHONE_VERIFICATION",
"value": "256",
"description": "Requires inbound SMS verification (user must text code to platform number)"
}
]
},
"PremiumFlags": {
"type": "integer",
"minimum": 0,
-8
View File
@@ -42,7 +42,6 @@ pub const BULK_ADD_GUILD_MEMBERS: &str = "bulk:add:guild_members";
pub const BULK_DELETE_USERS: &str = "bulk:delete:users";
pub const BULK_DELETE_USER_MESSAGES: &str = "bulk:delete:user_messages";
pub const BULK_UPDATE_GUILD_FEATURES: &str = "bulk:update:guild_features";
pub const BULK_UPDATE_SUSPICIOUS_ACTIVITY: &str = "bulk:update:suspicious_activity";
pub const BULK_UPDATE_USER_FLAGS: &str = "bulk:update:user_flags";
pub const CSAM_SUBMIT_NCMEC: &str = "csam:submit_ncmec";
pub const DISCOVERY_REMOVE: &str = "discovery:remove";
@@ -78,7 +77,6 @@ pub const REPORT_VIEW_REPORTER_PII: &str = "report:view:reporter_pii";
pub const SYSTEM_DM_SEND: &str = "system_dm:send";
pub const USER_CANCEL_BULK_MESSAGE_DELETION: &str = "user:cancel:bulk_message_deletion";
pub const USER_DELETE: &str = "user:delete";
pub const USER_DISABLE_SUSPICIOUS: &str = "user:disable:suspicious";
pub const USER_LIST_DM_CHANNELS: &str = "user:list:dm_channels";
pub const USER_LIST_GUILDS: &str = "user:list:guilds";
pub const USER_LIST_RELATIONSHIPS: &str = "user:list:relationships";
@@ -95,9 +93,7 @@ pub const USER_UPDATE_DOB: &str = "user:update:dob";
pub const USER_UPDATE_EMAIL: &str = "user:update:email";
pub const USER_UPDATE_FLAGS: &str = "user:update:flags";
pub const USER_UPDATE_MFA: &str = "user:update:mfa";
pub const USER_UPDATE_PHONE: &str = "user:update:phone";
pub const USER_UPDATE_PROFILE: &str = "user:update:profile";
pub const USER_UPDATE_SUSPICIOUS_ACTIVITY: &str = "user:update:suspicious_activity";
pub const USER_UPDATE_TRAITS: &str = "user:update:traits";
pub const USER_UPDATE_USERNAME: &str = "user:update:username";
pub const VOICE_REGION_CREATE: &str = "voice:region:create";
@@ -151,7 +147,6 @@ pub const ALL_ACLS: &[&str] = &[
BULK_DELETE_USERS,
BULK_DELETE_USER_MESSAGES,
BULK_UPDATE_GUILD_FEATURES,
BULK_UPDATE_SUSPICIOUS_ACTIVITY,
BULK_UPDATE_USER_FLAGS,
CSAM_SUBMIT_NCMEC,
DISCOVERY_REMOVE,
@@ -187,7 +182,6 @@ pub const ALL_ACLS: &[&str] = &[
SYSTEM_DM_SEND,
USER_CANCEL_BULK_MESSAGE_DELETION,
USER_DELETE,
USER_DISABLE_SUSPICIOUS,
USER_LIST_DM_CHANNELS,
USER_LIST_GUILDS,
USER_LIST_RELATIONSHIPS,
@@ -204,9 +198,7 @@ pub const ALL_ACLS: &[&str] = &[
USER_UPDATE_EMAIL,
USER_UPDATE_FLAGS,
USER_UPDATE_MFA,
USER_UPDATE_PHONE,
USER_UPDATE_PROFILE,
USER_UPDATE_SUSPICIOUS_ACTIVITY,
USER_UPDATE_TRAITS,
USER_UPDATE_USERNAME,
VOICE_REGION_CREATE,
+8 -52
View File
@@ -19,19 +19,18 @@ pub mod user_flag_bits {
pub const SPAMMER: u64 = 1 << 6;
pub const HIGH_GLOBAL_RATE_LIMIT: u64 = 1 << 33;
pub const DELETED: u64 = 1 << 34;
pub const DISABLED_SUSPICIOUS_ACTIVITY: u64 = 1 << 35;
pub const SELF_DELETED: u64 = 1 << 36;
pub const DISABLED: u64 = 1 << 38;
pub const HAS_SESSION_STARTED: u64 = 1 << 39;
pub const RATE_LIMIT_BYPASS: u64 = 1 << 47;
pub const REPORT_BANNED: u64 = 1 << 48;
pub const VERIFIED_NOT_UNDERAGE: u64 = 1 << 49;
pub const ACCOUNT_LIMITED: u64 = 1 << 50;
pub const HAS_DISMISSED_PREMIUM_ONBOARDING: u64 = 1 << 51;
pub const APP_STORE_REVIEWER: u64 = 1 << 53;
pub const STAFF_HIDDEN: u64 = 1 << 57;
pub const AGE_VERIFIED_ADULT: u64 = 1 << 60;
pub const FORCE_INBOUND_PHONE_VERIFICATION: u64 = 1 << 61;
pub const NOT_SUSPICIOUS: u64 = 1 << 62;
pub const LIMIT_EXEMPT: u64 = 1 << 62;
}
pub const USER_FLAGS: &[U64Flag] = &[
@@ -67,10 +66,6 @@ pub const USER_FLAGS: &[U64Flag] = &[
name: "DELETED",
value: user_flag_bits::DELETED,
},
U64Flag {
name: "DISABLED_SUSPICIOUS_ACTIVITY",
value: user_flag_bits::DISABLED_SUSPICIOUS_ACTIVITY,
},
U64Flag {
name: "SELF_DELETED",
value: user_flag_bits::SELF_DELETED,
@@ -95,6 +90,10 @@ pub const USER_FLAGS: &[U64Flag] = &[
name: "VERIFIED_NOT_UNDERAGE",
value: user_flag_bits::VERIFIED_NOT_UNDERAGE,
},
U64Flag {
name: "ACCOUNT_LIMITED",
value: user_flag_bits::ACCOUNT_LIMITED,
},
U64Flag {
name: "HAS_DISMISSED_PREMIUM_ONBOARDING",
value: user_flag_bits::HAS_DISMISSED_PREMIUM_ONBOARDING,
@@ -112,12 +111,8 @@ pub const USER_FLAGS: &[U64Flag] = &[
value: user_flag_bits::AGE_VERIFIED_ADULT,
},
U64Flag {
name: "FORCE_INBOUND_PHONE_VERIFICATION",
value: user_flag_bits::FORCE_INBOUND_PHONE_VERIFICATION,
},
U64Flag {
name: "NOT_SUSPICIOUS",
value: user_flag_bits::NOT_SUSPICIOUS,
name: "LIMIT_EXEMPT",
value: user_flag_bits::LIMIT_EXEMPT,
},
];
@@ -159,42 +154,3 @@ pub const PREMIUM_FLAGS: &[I32Flag] = &[
value: 1 << 8,
},
];
pub const SUSPICIOUS_ACTIVITY_FLAGS: &[I32Flag] = &[
I32Flag {
name: "REQUIRE_VERIFIED_EMAIL",
value: 1 << 0,
},
I32Flag {
name: "REQUIRE_REVERIFIED_EMAIL",
value: 1 << 1,
},
I32Flag {
name: "REQUIRE_VERIFIED_PHONE",
value: 1 << 2,
},
I32Flag {
name: "REQUIRE_REVERIFIED_PHONE",
value: 1 << 3,
},
I32Flag {
name: "REQUIRE_VERIFIED_EMAIL_OR_VERIFIED_PHONE",
value: 1 << 4,
},
I32Flag {
name: "REQUIRE_REVERIFIED_EMAIL_OR_VERIFIED_PHONE",
value: 1 << 5,
},
I32Flag {
name: "REQUIRE_VERIFIED_EMAIL_OR_REVERIFIED_PHONE",
value: 1 << 6,
},
I32Flag {
name: "REQUIRE_REVERIFIED_EMAIL_OR_REVERIFIED_PHONE",
value: 1 << 7,
},
I32Flag {
name: "REQUIRE_INBOUND_PHONE_VERIFICATION",
value: 1 << 8,
},
];
+3 -6
View File
@@ -12,7 +12,7 @@ impl AdminApiClient {
acls: &[String],
) -> ApiResult<CreateAdminApiKeyResponse> {
let body = generated_types::CreateAdminApiKeyRequest {
acls: parse_acls(acls)?,
acls: parse_acls(acls),
expires_in_days: None,
name: generated_types::CreateAdminApiKeyRequestName::try_from(name)
.map_err(|e| ApiError::Parse(e.to_string()))?,
@@ -44,11 +44,8 @@ impl AdminApiClient {
}
}
pub(super) fn parse_acls(acls: &[String]) -> ApiResult<Vec<generated_types::AdminAclType>> {
pub(super) fn parse_acls(acls: &[String]) -> Vec<generated_types::AdminAclType> {
acls.iter()
.map(|acl| {
generated_types::AdminAclType::try_from(acl.as_str())
.map_err(|e| ApiError::Parse(e.to_string()))
})
.filter_map(|acl| generated_types::AdminAclType::try_from(acl.as_str()).ok())
.collect()
}
-16
View File
@@ -22,22 +22,6 @@ impl AdminApiClient {
.await
}
pub async fn bulk_update_suspicious_activity_flags(
&self,
user_ids: &[String],
add_flags: &[String],
remove_flags: &[String],
audit_log_reason: Option<&str>,
) -> ApiResult<BulkJobResponse> {
let body = generated_types::AdminBulkJobCreateRequest::UpdateSuspiciousActivityFlags {
add_flags: add_flags.to_vec(),
remove_flags: remove_flags.to_vec(),
user_ids: snowflakes(user_ids),
};
self.post_typed_with_reason("/admin/bulk-jobs", &body, audit_log_reason)
.await
}
pub async fn bulk_update_guild_features(
&self,
guild_ids: &[String],
-3
View File
@@ -85,7 +85,6 @@ mod tests {
"email": "[email protected]",
"email_verified": true,
"email_bounced": false,
"has_verified_phone": false,
"date_of_birth": "2000-01-15",
"locale": "en-US",
"premium_type": 2,
@@ -93,8 +92,6 @@ mod tests {
"premium_until": null,
"premium_grace_ends_at": null,
"premium_lifetime_sequence": null,
"suspicious_activity_flags": 0,
"phone_verification_deferred": false,
"temp_banned_until": null,
"pending_deletion_at": null,
"pending_bulk_message_deletion_at": null,
-6
View File
@@ -108,15 +108,9 @@ pub struct AdminUser {
pub premium_grace_ends_at: Option<String>,
pub premium_lifetime_sequence: Option<i32>,
#[serde(default)]
pub suspicious_activity_flags: i32,
#[serde(default)]
pub phone_verification_deferred: bool,
#[serde(default)]
pub has_totp: bool,
#[serde(default)]
pub authenticator_types: Vec<i32>,
#[serde(default)]
pub has_verified_phone: bool,
pub temp_banned_until: Option<String>,
pub pending_deletion_at: Option<String>,
pub pending_bulk_message_deletion_at: Option<String>,
+1 -29
View File
@@ -231,22 +231,9 @@ impl AdminApiClient {
Ok(resp.user)
}
pub async fn update_suspicious_flags(&self, user_id: &str, flags: i32) -> ApiResult<AdminUser> {
let body = generated_types::AdminUserSuspiciousActivityFlagsRequest {
flags: generated_types::SuspiciousActivityFlags::from(flags),
};
let response = self
.generated()
.update_admin_user_suspicious_activity_flags(&snowflake(user_id), &body)
.await
.map_err(|e| self.generated_error(e))?;
let resp: UserMutationResponse = self.generated_value(response.into_inner())?;
Ok(resp.user)
}
pub async fn set_user_acls(&self, user_id: &str, acls: &[String]) -> ApiResult<AdminUser> {
let body = generated_types::AdminUserAclsRequest {
acls: super::admin_api_keys::parse_acls(acls)?,
acls: super::admin_api_keys::parse_acls(acls),
};
let response = self
.generated()
@@ -296,21 +283,6 @@ impl AdminApiClient {
Ok(resp.user)
}
pub async fn update_has_verified_phone(
&self,
user_id: &str,
has_verified_phone: bool,
) -> ApiResult<AdminUser> {
let body = generated_types::AdminUserPhoneVerificationRequest { has_verified_phone };
let response = self
.generated()
.update_admin_user_phone_verification(&snowflake(user_id), &body)
.await
.map_err(|e| self.generated_error(e))?;
let resp: UserMutationResponse = self.generated_value(response.into_inner())?;
Ok(resp.user)
}
pub async fn clear_user_fields(
&self,
user_id: &str,
@@ -219,19 +219,6 @@ pub(crate) async fn bulk_actions_post(
.bulk_update_user_flags(&user_ids, &add, &remove, audit_log_reason.as_deref())
.await
}
"bulk-update-suspicious-activity-flags" => {
let user_ids = form.list_values_any(&["user_ids[]", "user_ids"]);
let add = form.list_values_any(&["add_flags[]", "add_flags"]);
let remove = form.list_values_any(&["remove_flags[]", "remove_flags"]);
client
.bulk_update_suspicious_activity_flags(
&user_ids,
&add,
&remove,
audit_log_reason.as_deref(),
)
.await
}
"bulk-update-guild-features" => {
let guild_ids = form.list_values_any(&["guild_ids[]", "guild_ids"]);
let mut add = form.list_values_any(&["add_features[]", "add_features"]);
-21
View File
@@ -134,19 +134,6 @@ pub async fn dispatch(
"Failed to update premium flags",
)
}
"update_suspicious_flags" => {
let Ok(submitted) =
form.parse_list_values::<i32>(&["suspicious_flags[]", "suspicious_flags"])
else {
return DispatchOutcome::error("Invalid suspicious activity flag value");
};
let flags = submitted.into_iter().fold(0, |acc, flag| acc | flag);
DispatchOutcome::from_result(
client.update_suspicious_flags(user_id, flags).await,
"Suspicious activity flags updated successfully",
"Failed to update suspicious activity flags",
)
}
"update_acls" => {
let acls = form.list_values_any(&["acls[]", "acls"]);
DispatchOutcome::from_result(
@@ -178,14 +165,6 @@ pub async fn dispatch(
"Email verified successfully",
"Failed to verify email",
),
"update_has_verified_phone" => {
let val = form.bool_value("has_verified_phone");
DispatchOutcome::from_result(
client.update_has_verified_phone(user_id, val).await,
"Phone verification status updated successfully",
"Failed to update phone verification status",
)
}
"terminate_sessions" => DispatchOutcome::from_result(
client.terminate_user_sessions(user_id).await,
"User sessions terminated successfully",
@@ -54,7 +54,6 @@ pub const NAV_SECTIONS: &[NavSection] = &[
"bulk-actions",
[
acl::BULK_UPDATE_USER_FLAGS,
acl::BULK_UPDATE_SUSPICIOUS_ACTIVITY,
acl::BULK_UPDATE_GUILD_FEATURES,
acl::BULK_ADD_GUILD_MEMBERS,
acl::BULK_DELETE_USERS,
@@ -268,7 +267,6 @@ mod tests {
.expect("bulk actions nav item");
for required in [
acl::BULK_UPDATE_USER_FLAGS,
acl::BULK_UPDATE_SUSPICIOUS_ACTIVITY,
acl::BULK_UPDATE_GUILD_FEATURES,
acl::BULK_ADD_GUILD_MEMBERS,
acl::BULK_DELETE_USERS,
@@ -19,11 +19,7 @@ pub fn format_action(action: &str) -> String {
pub fn action_badge_variant(action: &str) -> BadgeVariant {
match action {
"temp_ban"
| "disable_suspicious_activity"
| "schedule_deletion"
| "ban_ip"
| "ban_email" => BadgeVariant::Danger,
"temp_ban" | "schedule_deletion" | "ban_ip" | "ban_email" => BadgeVariant::Danger,
"unban" | "cancel_deletion" | "unban_ip" | "unban_email" => BadgeVariant::Success,
"update_flags" | "update_features" | "set_acls" | "update_settings" | "annotate_ban" => {
BadgeVariant::Info
@@ -361,4 +357,12 @@ mod tests {
assert!(!markup.contains("<a "));
assert!(markup.contains("Email domain"));
}
#[test]
fn retired_action_names_still_render() {
let mut retired = entry("user", "1500000000000000002");
retired.action = "update_retired_toggle".to_string();
let markup = audit_log_table_body("/admin", &[retired]).into_string();
assert!(markup.contains("Update retired toggle"));
}
}
@@ -80,10 +80,6 @@ const PATCHABLE_USER_FLAGS: &[UserFlag] = &[
name: "DELETED",
value: 1 << 34,
},
UserFlag {
name: "DISABLED_SUSPICIOUS_ACTIVITY",
value: 1 << 35,
},
UserFlag {
name: "SELF_DELETED",
value: 1 << 36,
@@ -108,6 +104,10 @@ const PATCHABLE_USER_FLAGS: &[UserFlag] = &[
name: "VERIFIED_NOT_UNDERAGE",
value: 1 << 49,
},
UserFlag {
name: "ACCOUNT_LIMITED",
value: 1 << 50,
},
UserFlag {
name: "HAS_DISMISSED_PREMIUM_ONBOARDING",
value: 1 << 51,
@@ -125,26 +125,11 @@ const PATCHABLE_USER_FLAGS: &[UserFlag] = &[
value: 1 << 60,
},
UserFlag {
name: "FORCE_INBOUND_PHONE_VERIFICATION",
value: 1 << 61,
},
UserFlag {
name: "NOT_SUSPICIOUS",
name: "LIMIT_EXEMPT",
value: 1 << 62,
},
];
const SUSPICIOUS_ACTIVITY_FLAGS: &[&str] = &[
"REQUIRE_VERIFIED_EMAIL",
"REQUIRE_REVERIFIED_EMAIL",
"REQUIRE_VERIFIED_PHONE",
"REQUIRE_REVERIFIED_PHONE",
"REQUIRE_VERIFIED_EMAIL_OR_VERIFIED_PHONE",
"REQUIRE_REVERIFIED_EMAIL_OR_VERIFIED_PHONE",
"REQUIRE_VERIFIED_EMAIL_OR_REVERIFIED_PHONE",
"REQUIRE_REVERIFIED_EMAIL_OR_REVERIFIED_PHONE",
"REQUIRE_INBOUND_PHONE_VERIFICATION",
];
const GUILD_FEATURES: &[&str] = &[
"ANIMATED_ICON",
"ANIMATED_BANNER",
@@ -206,9 +191,6 @@ pub fn bulk_actions_page(config: &AdminConfig, auth: &AuthContext, csrf_token: &
@if acl::has_permission(admin_acls, acl::BULK_UPDATE_USER_FLAGS) {
(bulk_update_user_flags_section(base, csrf_token))
}
@if acl::has_permission(admin_acls, acl::BULK_UPDATE_SUSPICIOUS_ACTIVITY) {
(bulk_update_suspicious_activity_section(base, csrf_token))
}
@if acl::has_permission(admin_acls, acl::BULK_UPDATE_GUILD_FEATURES) {
(bulk_update_guild_features_section(base, csrf_token))
}
@@ -226,16 +208,6 @@ pub fn bulk_actions_page(config: &AdminConfig, auth: &AuthContext, csrf_token: &
admin_layout(config, auth, "Bulk Actions", "bulk-actions", None, content)
}
fn flag_checkbox_grid(prefix: &str, flags: &[&str]) -> Markup {
html! {
div class="grid grid-cols-1 gap-3 sm:grid-cols-2" {
@for flag in flags {
(checkbox(prefix, flag, flag, false, true))
}
}
}
}
fn guild_feature_checkbox_grid(prefix: &str, include_deprecated: bool) -> Markup {
html! {
div class="grid grid-cols-1 gap-3 sm:grid-cols-2" {
@@ -288,36 +260,6 @@ fn bulk_update_user_flags_section(base: &str, csrf_token: &str) -> Markup {
)
}
fn bulk_update_suspicious_activity_section(base: &str, csrf_token: &str) -> Markup {
section_card_simple(
"Bulk Update Suspicious Activity Flags",
html! {
form method="post" action={(base) "/bulk-actions?action=bulk-update-suspicious-activity-flags"} {
(csrf_input(csrf_token))
div class="space-y-4" {
(textarea_input("user_ids", "User IDs (one per line)", "123456789\n987654321", "", 5, true))
div {
p class="font-semibold text-neutral-500 text-xs uppercase tracking-wide mb-2" {
"Flags to Add"
}
(flag_checkbox_grid("add_flags[]", SUSPICIOUS_ACTIVITY_FLAGS))
}
div {
p class="font-semibold text-neutral-500 text-xs uppercase tracking-wide mb-2" {
"Flags to Remove"
}
(flag_checkbox_grid("remove_flags[]", SUSPICIOUS_ACTIVITY_FLAGS))
}
(text_input("audit_log_reason", "Audit Log Reason (optional)", "", "Reason for this bulk operation"))
(form_actions(html! {
(submit_button("Update Suspicious Activity Flags"))
}))
}
}
},
)
}
fn bulk_update_guild_features_section(base: &str, csrf_token: &str) -> Markup {
section_card_simple(
"Bulk Update Guild Features",
@@ -52,13 +52,12 @@ pub fn settings_tab(
"guild-verification-level",
"verification_level",
"Verification Level",
guild.verification_level.unwrap_or(0),
guild.verification_level.unwrap_or(0).min(3),
&[
(0, "None"),
(1, "Low (verified email)"),
(2, "Medium (5+ minutes)"),
(3, "High (10+ minutes)"),
(4, "Very High (verified phone)"),
],
))
(select_field(
@@ -225,12 +224,11 @@ fn select_field(
}
fn settings_tab_readonly(guild: &GuildDetailInfo) -> Markup {
let verification_label = match guild.verification_level.unwrap_or(0) {
let verification_label = match guild.verification_level.unwrap_or(0).min(3) {
0 => "None",
1 => "Low (verified email)",
2 => "Medium (5+ minutes)",
3 => "High (10+ minutes)",
4 => "Very High (verified phone)",
_ => "Unknown",
};
let mfa_label = match guild.mfa_level.unwrap_or(0) {
@@ -1403,7 +1403,7 @@ fn captcha_section(base: &str, csrf_token: &str, captcha: &CaptchaConfigResponse
))
p class="text-sm text-neutral-700" {
(format!(
"Average solve: about {estimate:.1} s on a low-end Android phone, \
"Average solve: about {estimate:.1} s on a low-end Android device, \
well under a second in desktop browsers."
))
}
@@ -154,10 +154,6 @@ fn session_entry(base: &str, s: &UserSession, is_tombstone: bool) -> Markup {
}
fn quick_actions_card(base: &str, user: &AdminUser, csrf_token: &str) -> Markup {
let phone_action = format!(
"{base}/users/{}?action=update_has_verified_phone&tab=account",
user.id
);
html! {
(card_with_header("Quick Actions", html! {
div class="flex flex-wrap gap-3" {
@@ -165,20 +161,6 @@ fn quick_actions_card(base: &str, user: &AdminUser, csrf_token: &str) -> Markup
(action_form(base, &user.id, "verify_email", "account", None,
"Verify Email", csrf_token))
}
form method="post"
action=(&phone_action)
hx-post=(&phone_action)
hx-target="#flash-container"
hx-swap="none"
hx-push-url="false" {
(csrf_input(csrf_token))
input type="hidden" name="has_verified_phone"
value=@if user.has_verified_phone { "false" } @else { "true" };
button type="submit" class=(BTN_CLS) {
@if user.has_verified_phone { "Clear Phone Verified" }
@else { "Mark Phone Verified" }
}
}
(action_form(base, &user.id, "send_password_reset", "account", None,
"Send Password Reset", csrf_token))
}
@@ -139,13 +139,6 @@ fn render_overview_tab(
}
}))
}
(detail_row("Phone", html! {
@if user.has_verified_phone {
span class="text-green-700" { "Verified" }
} @else {
span class="text-neutral-400" { "Not verified" }
}
}))
@if acl::has_permission(admin_acls, acl::USER_VIEW_DOB) {
(detail_row("Date of Birth", html! {
(user.date_of_birth.as_deref().unwrap_or("Not set"))
@@ -270,8 +263,6 @@ fn flags_card(
csrf_token: &str,
) -> Markup {
let can_update_flags = acl::has_permission(admin_acls, acl::USER_UPDATE_FLAGS);
let can_update_suspicious =
acl::has_permission(admin_acls, acl::USER_UPDATE_SUSPICIOUS_ACTIVITY);
html! {
div class="space-y-6" {
(u64_flag_form(
@@ -298,23 +289,6 @@ fn flags_card(
can_update_flags,
Some(acl::USER_UPDATE_FLAGS),
))
(i32_flag_form(
config,
&user.id,
"Suspicious Activity Flags",
"update_suspicious_flags",
"suspicious_flags[]",
user.suspicious_activity_flags,
admin_flags::SUSPICIOUS_ACTIVITY_FLAGS,
csrf_token,
can_update_suspicious,
Some(acl::USER_UPDATE_SUSPICIOUS_ACTIVITY),
))
@if user.phone_verification_deferred {
p class="text-sm text-amber-700 dark:text-amber-400" {
"Phone verification is deferred: the requirement above is stored but not enforced."
}
}
}
}
}
+6 -12
View File
@@ -23,7 +23,6 @@ fn deserialize_admin_users_me_response() {
"email": "[email protected]",
"email_verified": true,
"email_bounced": false,
"has_verified_phone": true,
"date_of_birth": "2003-02-25",
"locale": "en-US",
"premium_type": 2,
@@ -31,7 +30,6 @@ fn deserialize_admin_users_me_response() {
"premium_until": null,
"premium_grace_ends_at": null,
"premium_lifetime_sequence": 1,
"suspicious_activity_flags": 0,
"temp_banned_until": null,
"pending_deletion_at": null,
"pending_bulk_message_deletion_at": null,
@@ -64,9 +62,7 @@ fn deserialize_admin_users_me_response() {
assert_eq!(user.acls, vec!["super_admin"]);
assert_eq!(user.traits, vec!["beta_tester"]);
assert_eq!(user.premium_type, Some(2));
assert_eq!(user.suspicious_activity_flags, 0);
assert!(user.has_totp);
assert!(user.has_verified_phone);
assert_eq!(user.last_active_ip.as_deref(), Some("1.2.3.4"));
}
@@ -79,10 +75,10 @@ fn deserialize_flags_as_string_and_number() {
"premium_flags": 0, "avatar": null, "banner": null, "bio": null,
"pronouns": null, "accent_color": null, "email": null,
"email_verified": false, "email_bounced": false,
"has_verified_phone": false, "date_of_birth": null, "locale": null,
"date_of_birth": null, "locale": null,
"premium_type": null, "premium_since": null, "premium_until": null,
"premium_grace_ends_at": null, "premium_lifetime_sequence": null,
"suspicious_activity_flags": 0, "temp_banned_until": null,
"temp_banned_until": null,
"pending_deletion_at": null, "pending_bulk_message_deletion_at": null,
"deletion_reason_code": null, "deletion_public_reason": null, "deletion_audit_log_reason": null,
"deletion_scheduled_by": null, "deletion_scheduled_at": null,
@@ -111,10 +107,10 @@ fn deserialize_discriminator_int_and_string() {
"bot": true, "system": false, "flags": "0", "premium_flags": 0,
"avatar": null, "banner": null, "bio": null, "pronouns": null,
"accent_color": null, "email": null, "email_verified": false,
"email_bounced": false, "has_verified_phone": false, "date_of_birth": null,
"email_bounced": false, "date_of_birth": null,
"locale": null, "premium_type": null, "premium_since": null,
"premium_until": null, "premium_grace_ends_at": null,
"premium_lifetime_sequence": null, "suspicious_activity_flags": 0,
"premium_lifetime_sequence": null,
"temp_banned_until": null, "pending_deletion_at": null,
"pending_bulk_message_deletion_at": null, "deletion_reason_code": null,
"deletion_public_reason": null, "deletion_audit_log_reason": null,
@@ -161,7 +157,6 @@ fn deserialize_search_users_response() {
"email": null,
"email_verified": false,
"email_bounced": false,
"has_verified_phone": false,
"date_of_birth": null,
"locale": null,
"premium_type": null,
@@ -169,7 +164,6 @@ fn deserialize_search_users_response() {
"premium_until": null,
"premium_grace_ends_at": null,
"premium_lifetime_sequence": null,
"suspicious_activity_flags": 0,
"temp_banned_until": null,
"pending_deletion_at": null,
"pending_bulk_message_deletion_at": null,
@@ -881,10 +875,10 @@ fn deserialize_user_mutation_response() {
"flags": "1", "premium_flags": 0, "avatar": null, "banner": null,
"bio": null, "pronouns": null, "accent_color": null, "email": null,
"email_verified": false, "email_bounced": false,
"has_verified_phone": false, "date_of_birth": null, "locale": null,
"date_of_birth": null, "locale": null,
"premium_type": null, "premium_since": null, "premium_until": null,
"premium_grace_ends_at": null, "premium_lifetime_sequence": null,
"suspicious_activity_flags": 0, "temp_banned_until": null,
"temp_banned_until": null,
"pending_deletion_at": null, "pending_bulk_message_deletion_at": null,
"deletion_reason_code": null, "deletion_public_reason": null, "deletion_audit_log_reason": null,
"deletion_scheduled_by": null, "deletion_scheduled_at": null,
-3
View File
@@ -263,11 +263,8 @@ fn admin_user() -> Value {
"premium_until": null,
"premium_grace_ends_at": null,
"premium_lifetime_sequence": null,
"suspicious_activity_flags": 0,
"phone_verification_deferred": false,
"has_totp": false,
"authenticator_types": [],
"has_verified_phone": false,
"temp_banned_until": null,
"pending_deletion_at": null,
"pending_bulk_message_deletion_at": null,
+8 -9
View File
@@ -363,7 +363,9 @@ async fn user_account_actions_use_no_swap_htmx_toasts() {
assert!(body.contains("__fluxerAdminActionForms"), "{body}");
assert!(!body.contains(&native_confirm), "{body}");
assert!(
body.contains(r#"hx-post="/users/1500000000000000001?action=update_has_verified_phone&amp;tab=account""#),
body.contains(
r#"hx-post="/users/1500000000000000001?action=send_password_reset&amp;tab=account""#
),
"{body}"
);
assert!(body.contains(r##"hx-target="#flash-container""##), "{body}");
@@ -374,7 +376,7 @@ async fn user_account_actions_use_no_swap_htmx_toasts() {
.unwrap_or_else(|| panic!("account page did not set csrf_token cookie\n{body}"));
let (status, response_headers, response_body) = post_form_with_headers(
&app,
"/users/1500000000000000001?action=update_has_verified_phone&tab=account",
"/users/1500000000000000001?action=send_password_reset&tab=account",
&[
("HX-Request", "true"),
("HX-Target", "flash-container"),
@@ -383,7 +385,7 @@ async fn user_account_actions_use_no_swap_htmx_toasts() {
&format!("{}; csrf_token={}", app.session_cookie, csrf_token),
),
],
&format!("_csrf={csrf_token}&has_verified_phone=true"),
&format!("_csrf={csrf_token}"),
)
.await;
assert_eq!(status, StatusCode::NO_CONTENT, "{response_body}");
@@ -399,7 +401,7 @@ async fn user_account_actions_use_no_swap_htmx_toasts() {
.unwrap_or_else(|| panic!("missing toast header\n{response_body}"));
assert!(toast.contains("success"), "{toast}");
assert!(
toast.contains("Phone verification status updated successfully"),
toast.contains("Password reset sent successfully"),
"{toast}"
);
}
@@ -839,8 +841,8 @@ async fn mock_api(method: Method, uri: Uri) -> Response {
(Method::GET, "/admin/users/1500000000000000001") => {
json_response(json!({ "users": [searched_user()] }))
}
(Method::PUT, "/admin/users/1500000000000000001/phone-verification") => {
json_response(json!({ "user": searched_user() }))
(Method::POST, "/admin/users/1500000000000000001/password-reset") => {
StatusCode::NO_CONTENT.into_response()
}
(Method::GET, "/admin/guilds") => {
json_response(json!({ "guilds": [searched_guild()], "total": 1 }))
@@ -947,11 +949,8 @@ fn user(id: &str, username: &str) -> Value {
"premium_until": null,
"premium_grace_ends_at": null,
"premium_lifetime_sequence": null,
"suspicious_activity_flags": 0,
"phone_verification_deferred": false,
"has_totp": false,
"authenticator_types": [],
"has_verified_phone": false,
"temp_banned_until": null,
"pending_deletion_at": null,
"pending_bulk_message_deletion_at": null,
@@ -322,11 +322,8 @@ fn admin_user() -> Value {
"premium_until": null,
"premium_grace_ends_at": null,
"premium_lifetime_sequence": null,
"suspicious_activity_flags": 0,
"phone_verification_deferred": false,
"has_totp": false,
"authenticator_types": [],
"has_verified_phone": false,
"temp_banned_until": null,
"pending_deletion_at": null,
"pending_bulk_message_deletion_at": null,
@@ -16,7 +16,6 @@
"email": "[email protected]",
"email_verified": true,
"email_bounced": false,
"has_verified_phone": true,
"date_of_birth": "2000-01-01",
"locale": "en-US",
"premium_type": null,
@@ -24,8 +23,6 @@
"premium_until": null,
"premium_grace_ends_at": null,
"premium_lifetime_sequence": null,
"suspicious_activity_flags": 0,
"phone_verification_deferred": false,
"temp_banned_until": null,
"pending_deletion_at": null,
"pending_bulk_message_deletion_at": null,
@@ -17,7 +17,6 @@
"email": "[email protected]",
"email_verified": true,
"email_bounced": false,
"has_verified_phone": false,
"date_of_birth": null,
"locale": "en-US",
"premium_type": null,
@@ -25,8 +24,6 @@
"premium_until": null,
"premium_grace_ends_at": null,
"premium_lifetime_sequence": null,
"suspicious_activity_flags": 0,
"phone_verification_deferred": false,
"temp_banned_until": null,
"pending_deletion_at": null,
"pending_bulk_message_deletion_at": null,
@@ -17,7 +17,6 @@
"email": "[email protected]",
"email_verified": true,
"email_bounced": false,
"has_verified_phone": false,
"date_of_birth": null,
"locale": "en-US",
"premium_type": null,
@@ -25,8 +24,6 @@
"premium_until": null,
"premium_grace_ends_at": null,
"premium_lifetime_sequence": null,
"suspicious_activity_flags": 0,
"phone_verification_deferred": false,
"temp_banned_until": null,
"pending_deletion_at": null,
"pending_bulk_message_deletion_at": null,
@@ -294,11 +294,8 @@ fn admin_user() -> Value {
"premium_until": null,
"premium_grace_ends_at": null,
"premium_lifetime_sequence": null,
"suspicious_activity_flags": 0,
"phone_verification_deferred": false,
"has_totp": false,
"authenticator_types": [],
"has_verified_phone": false,
"temp_banned_until": null,
"pending_deletion_at": null,
"pending_bulk_message_deletion_at": null,