fix(tinyplace): switch active handle across multiple purchased identities (#4216)

This commit is contained in:
Mega Mind
2026-06-28 21:26:40 -07:00
committed by GitHub
parent 090360e38a
commit 51aeefd49f
6 changed files with 204 additions and 14 deletions
+44
View File
@@ -2396,6 +2396,35 @@ pub(crate) fn handle_tinyplace_registry_export(params: Map<String, Value>) -> Co
})
}
/// Assign one of the wallet's purchased handles as its **primary** (active)
/// identity. The backend clears the primary flag on the wallet's other names
/// (one primary per wallet), so this is how a user who owns multiple identities
/// switches which handle is shown as active across feed / profile / directory
/// (#4198). ANTI-SPOOF: the owning wallet is proven by the request signature the
/// SDK attaches from the unlocked signer, never from params — so a caller can
/// only re-point their *own* wallet's primary.
pub(crate) fn handle_tinyplace_registry_assign_primary(
params: Map<String, Value>,
) -> ControllerFuture {
Box::pin(async move {
let name = req_str(&params, "name")?
.trim()
.trim_start_matches('@')
.to_string();
if name.is_empty() {
return Err("missing required param 'name'".to_string());
}
log::debug!("{LOG_PREFIX} registry_assign_primary name={name}");
let client = global_state().client().await?;
let identity = client
.registry
.assign_primary(&name)
.await
.map_err(map_err)?;
to_value(serde_json::json!({ "identity": identity }))
})
}
// ── Users email verification ────────────────────────────────────────────────
pub(crate) fn handle_tinyplace_users_start_email_verification(
@@ -5922,6 +5951,21 @@ mod tests {
);
}
/// registry_assign_primary rejects a blank/@-only name before any client
/// work (#4198) — the trim + strip-@ must not leave an empty handle that
/// would hit the backend.
#[test]
fn registry_assign_primary_rejects_blank_name() {
let mut params = Map::new();
params.insert("name".to_string(), Value::String(" @ ".to_string()));
let result = block_on(handle_tinyplace_registry_assign_primary(params));
assert!(result.is_err());
assert!(
result.unwrap_err().contains("name"),
"expected 'name' in error"
);
}
/// bounties_submit rejects blank url before any client work.
#[test]
fn bounties_submit_rejects_blank_url() {
+23
View File
@@ -138,6 +138,7 @@ use crate::openhuman::tinyplace::manifest::{
handle_tinyplace_profiles_broadcasts,
handle_tinyplace_profiles_get,
handle_tinyplace_profiles_groups,
handle_tinyplace_registry_assign_primary,
handle_tinyplace_registry_export,
handle_tinyplace_registry_get,
handle_tinyplace_registry_register,
@@ -1424,6 +1425,23 @@ fn schema_follows_feed() -> ControllerSchema {
// ── Registry export schema ─────────────────────────────────────────────────────
fn schema_registry_assign_primary() -> ControllerSchema {
ControllerSchema {
namespace: "tinyplace",
function: "registry_assign_primary",
description: "Assign one of the wallet's handles as its primary (active) identity; \
clears the primary flag on the wallet's other handles.",
inputs: vec![required_string(
"name",
"The handle to make primary/active (with or without a leading @).",
)],
outputs: vec![json_output(
"identity",
"The updated Identity with primary=true.",
)],
}
}
fn schema_registry_export() -> ControllerSchema {
ControllerSchema {
namespace: "tinyplace",
@@ -2584,6 +2602,7 @@ pub fn all_tinyplace_controller_schemas() -> Vec<ControllerSchema> {
schema_marketplace_recent(),
schema_registry_get(),
schema_registry_register(),
schema_registry_assign_primary(),
schema_marketplace_buy_product(),
schema_marketplace_buy_identity(),
schema_marketplace_bid(),
@@ -2823,6 +2842,10 @@ pub fn all_tinyplace_registered_controllers() -> Vec<RegisteredController> {
schema: schema_registry_register(),
handler: handle_tinyplace_registry_register,
},
RegisteredController {
schema: schema_registry_assign_primary(),
handler: handle_tinyplace_registry_assign_primary,
},
RegisteredController {
schema: schema_marketplace_buy_product(),
handler: handle_tinyplace_marketplace_buy_product,