commit 80cb62136204c5d7003f7834b42de22a454e781c
parent 20f4659909b8d65f17cd8b40f19504df09679e95
Author: triesap <tyson@radroots.org>
Date: Sat, 25 Apr 2026 09:20:29 +0000
signer: bind myc actor to user identity
Diffstat:
4 files changed, 64 insertions(+), 29 deletions(-)
diff --git a/src/runtime/signer.rs b/src/runtime/signer.rs
@@ -21,7 +21,7 @@ const SIGNER_BINDING_MODEL: &str = "session_authorized_remote_signer";
struct MycBindingResolution {
view: SignerBindingStatusView,
resolved_account_id: Option<String>,
- resolved_signer_public_key_hex: Option<String>,
+ resolved_account_public_key_hex: Option<String>,
}
#[derive(Debug, Clone)]
@@ -74,15 +74,15 @@ pub fn resolve_actor_write_authority(
}
}
- let Some(resolved_signer_public_key_hex) = resolution.resolved_signer_public_key_hex else {
+ let Some(resolved_account_public_key_hex) = resolution.resolved_account_public_key_hex else {
return Err(ActorWriteBindingError::Unconfigured(
- "myc signer binding reported ready without a resolved signer identity".to_owned(),
+ "myc signer binding reported ready without a resolved user identity".to_owned(),
));
};
- if !resolved_signer_public_key_hex.eq_ignore_ascii_case(actor_pubkey) {
+ if !resolved_account_public_key_hex.eq_ignore_ascii_case(actor_pubkey) {
return Err(ActorWriteBindingError::Unconfigured(format!(
- "configured myc signer binding resolves signer pubkey `{resolved_signer_public_key_hex}` instead of {actor_role} pubkey `{actor_pubkey}`"
+ "configured myc signer binding resolves user pubkey `{resolved_account_public_key_hex}` instead of {actor_role} pubkey `{actor_pubkey}`"
)));
}
@@ -308,7 +308,7 @@ fn resolve_myc_binding(config: &RuntimeConfig, myc: &MycStatusView) -> MycBindin
),
},
resolved_account_id: None,
- resolved_signer_public_key_hex: None,
+ resolved_account_public_key_hex: None,
};
};
@@ -407,7 +407,7 @@ fn resolve_myc_binding(config: &RuntimeConfig, myc: &MycStatusView) -> MycBindin
}
if let Some(account_ref) = binding.managed_account_ref.as_deref() {
- if session.signer_identity.id != account_ref {
+ if session.user_identity.id != account_ref {
return binding_status(
binding,
"unauthorized",
@@ -415,8 +415,8 @@ fn resolve_myc_binding(config: &RuntimeConfig, myc: &MycStatusView) -> MycBindin
Some(1),
None,
format!(
- "configured signer session `{session_ref}` resolves signer `{}` instead of managed account `{account_ref}`",
- session.signer_identity.id
+ "configured signer session `{session_ref}` resolves user `{}` instead of managed account `{account_ref}`",
+ session.user_identity.id
),
);
}
@@ -435,7 +435,7 @@ fn resolve_myc_binding(config: &RuntimeConfig, myc: &MycStatusView) -> MycBindin
if let Some(account_ref) = binding.managed_account_ref.as_deref() {
let matching_sessions = signing_sessions
.into_iter()
- .filter(|session| session.signer_identity.id == account_ref)
+ .filter(|session| session.user_identity.id == account_ref)
.collect::<Vec<_>>();
return resolve_matching_sessions(binding, account_ref, matching_sessions);
}
@@ -548,9 +548,9 @@ fn binding_status(
Some(reason)
},
},
- resolved_account_id: resolved_session.map(|session| session.signer_identity.id.clone()),
- resolved_signer_public_key_hex: resolved_session
- .map(|session| session.signer_identity.public_key_hex.clone()),
+ resolved_account_id: resolved_session.map(|session| session.user_identity.id.clone()),
+ resolved_account_public_key_hex: resolved_session
+ .map(|session| session.user_identity.public_key_hex.clone()),
}
}
diff --git a/tests/listing.rs b/tests/listing.rs
@@ -10,6 +10,7 @@ use std::thread::{self, JoinHandle};
use std::time::Duration;
use assert_cmd::prelude::*;
+use radroots_identity::RadrootsIdentity;
use radroots_sql_core::{SqlExecutor, SqliteExecutor};
use serde_json::{Value, json};
use tempfile::tempdir;
@@ -105,21 +106,28 @@ fn sample_myc_status_payload(
public_identity: &Value,
connection_id: &str,
) -> Value {
+ let signer_identity =
+ serde_json::to_value(RadrootsIdentity::generate().to_public()).expect("signer identity");
+ let signer_account_id = signer_identity["id"]
+ .as_str()
+ .expect("signer id")
+ .to_owned();
+ assert_ne!(signer_account_id, account_id);
json!({
"status": "healthy",
"ready": true,
"reasons": [],
"signer_backend": {
"local_signer": {
- "account_id": account_id,
- "public_identity": public_identity,
+ "account_id": signer_account_id,
+ "public_identity": signer_identity.clone(),
"availability": "SecretBacked"
},
"remote_session_count": 1,
"remote_sessions": [
{
"connection_id": connection_id,
- "signer_identity": public_identity,
+ "signer_identity": signer_identity,
"user_identity": public_identity,
"relays": ["wss://relay.one"],
"permissions": "sign_event"
@@ -1014,7 +1022,7 @@ managed_account_ref = "{mismatch_account_id}"
assert_eq!(publish_json["state"], "unconfigured");
assert_eq!(publish_json["signer_mode"], "myc");
assert!(publish_json["reason"].as_str().is_some_and(|value| {
- value.contains("configured myc signer binding resolves signer pubkey")
+ value.contains("configured myc signer binding resolves user pubkey")
}));
assert!(requests.lock().expect("requests").is_empty());
}
diff --git a/tests/myc_status.rs b/tests/myc_status.rs
@@ -195,9 +195,14 @@ fn signer_status_reports_ready_for_configured_myc_managed_account_binding() {
dir.path(),
successful_status_script(payload.to_string()).as_str(),
);
- let managed_account_ref = payload["signer_backend"]["local_signer"]["account_id"]
+ let managed_account_ref =
+ payload["signer_backend"]["remote_sessions"][0]["user_identity"]["id"]
+ .as_str()
+ .expect("managed account ref");
+ let provider_account_ref = payload["signer_backend"]["local_signer"]["account_id"]
.as_str()
- .expect("managed account ref");
+ .expect("provider account ref");
+ assert_ne!(managed_account_ref, provider_account_ref);
let signer_session_ref = payload["signer_backend"]["remote_sessions"][0]["connection_id"]
.as_str()
.expect("signer session ref");
@@ -244,6 +249,18 @@ signer_session_ref = "{signer_session_ref}"
assert_eq!(json["binding"]["managed_account_ref"], managed_account_ref);
assert_eq!(json["binding"]["signer_session_ref"], signer_session_ref);
assert_eq!(json["myc"]["remote_session_count"], 1);
+ assert_eq!(
+ json["myc"]["remote_sessions"][0]["user_identity"]["id"],
+ managed_account_ref
+ );
+ assert_ne!(
+ json["myc"]["remote_sessions"][0]["signer_identity"]["id"],
+ json["myc"]["remote_sessions"][0]["user_identity"]["id"]
+ );
+ assert_ne!(
+ json["myc"]["local_signer"]["account_id"],
+ json["myc"]["remote_sessions"][0]["user_identity"]["id"]
+ );
}
#[test]
@@ -371,9 +388,10 @@ fn signer_status_reports_unauthorized_for_session_without_sign_event_permission(
dir.path(),
successful_status_script(payload.to_string()).as_str(),
);
- let managed_account_ref = payload["signer_backend"]["local_signer"]["account_id"]
- .as_str()
- .expect("managed account ref");
+ let managed_account_ref =
+ payload["signer_backend"]["remote_sessions"][0]["user_identity"]["id"]
+ .as_str()
+ .expect("managed account ref");
let signer_session_ref = payload["signer_backend"]["remote_sessions"][0]["connection_id"]
.as_str()
.expect("signer session ref");
@@ -426,9 +444,10 @@ fn signer_status_reports_unavailable_for_missing_bound_session() {
dir.path(),
successful_status_script(payload.to_string()).as_str(),
);
- let managed_account_ref = payload["signer_backend"]["local_signer"]["account_id"]
- .as_str()
- .expect("managed account ref");
+ let managed_account_ref =
+ payload["signer_backend"]["remote_sessions"][0]["user_identity"]["id"]
+ .as_str()
+ .expect("managed account ref");
write_user_config(
dir.path(),
format!(
diff --git a/tests/order.rs b/tests/order.rs
@@ -10,6 +10,7 @@ use std::thread::{self, JoinHandle};
use std::time::Duration;
use assert_cmd::prelude::*;
+use radroots_identity::RadrootsIdentity;
use radroots_sql_core::{SqlExecutor, SqliteExecutor};
use serde_json::{Value, json};
use tempfile::tempdir;
@@ -193,21 +194,28 @@ fn sample_myc_status_payload(
public_identity: &Value,
connection_id: &str,
) -> Value {
+ let signer_identity =
+ serde_json::to_value(RadrootsIdentity::generate().to_public()).expect("signer identity");
+ let signer_account_id = signer_identity["id"]
+ .as_str()
+ .expect("signer id")
+ .to_owned();
+ assert_ne!(signer_account_id, account_id);
json!({
"status": "healthy",
"ready": true,
"reasons": [],
"signer_backend": {
"local_signer": {
- "account_id": account_id,
- "public_identity": public_identity,
+ "account_id": signer_account_id,
+ "public_identity": signer_identity.clone(),
"availability": "SecretBacked"
},
"remote_session_count": 1,
"remote_sessions": [
{
"connection_id": connection_id,
- "signer_identity": public_identity,
+ "signer_identity": signer_identity,
"user_identity": public_identity,
"relays": ["wss://relay.one"],
"permissions": "sign_event"
@@ -1468,7 +1476,7 @@ managed_account_ref = "{mismatch_account_id}"
assert_eq!(submit_json["state"], "unconfigured");
assert_eq!(submit_json["signer_mode"], "myc");
assert!(submit_json["reason"].as_str().is_some_and(|value| {
- value.contains("configured myc signer binding resolves signer pubkey")
+ value.contains("configured myc signer binding resolves user pubkey")
}));
assert!(requests.lock().expect("requests lock").is_empty());
}