chore: rename project quicnprotochat -> quicproquo (binaries: qpq)
Rename the entire workspace:
- Crate packages: quicnprotochat-{core,proto,server,client,gui,p2p,mobile} -> quicproquo-*
- Binary names: quicnprotochat -> qpq, quicnprotochat-server -> qpq-server,
quicnprotochat-gui -> qpq-gui
- Default files: *-state.bin -> qpq-state.bin, *-server.toml -> qpq-server.toml,
*.db -> qpq.db
- Environment variable prefix: QUICNPROTOCHAT_* -> QPQ_*
- App identifier: chat.quicnproto.gui -> chat.quicproquo.gui
- Proto package: quicnprotochat.bench -> quicproquo.bench
- All documentation, Docker, CI, and script references updated
HKDF domain-separation strings and P2P ALPN remain unchanged for
backward compatibility with existing encrypted state and wire protocol.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
131
crates/quicproquo-server/src/node_service/user_ops.rs
Normal file
131
crates/quicproquo-server/src/node_service/user_ops.rs
Normal file
@@ -0,0 +1,131 @@
|
||||
//! resolveUser / resolveIdentity RPCs: bidirectional username ↔ identity key lookup.
|
||||
|
||||
use capnp::capability::Promise;
|
||||
use quicproquo_proto::node_capnp::node_service;
|
||||
|
||||
use crate::auth::{coded_error, validate_auth_context};
|
||||
use crate::error_codes::*;
|
||||
use crate::storage::StorageError;
|
||||
|
||||
use super::NodeServiceImpl;
|
||||
|
||||
fn storage_err(err: StorageError) -> capnp::Error {
|
||||
coded_error(E009_STORAGE_ERROR, err)
|
||||
}
|
||||
|
||||
impl NodeServiceImpl {
|
||||
pub fn handle_resolve_user(
|
||||
&mut self,
|
||||
params: node_service::ResolveUserParams,
|
||||
mut results: node_service::ResolveUserResults,
|
||||
) -> Promise<(), capnp::Error> {
|
||||
let p = match params.get() {
|
||||
Ok(p) => p,
|
||||
Err(e) => return Promise::err(coded_error(E020_BAD_PARAMS, e)),
|
||||
};
|
||||
let username = match p.get_username() {
|
||||
Ok(u) => u,
|
||||
Err(e) => return Promise::err(coded_error(E020_BAD_PARAMS, e)),
|
||||
};
|
||||
let _auth_ctx = match validate_auth_context(&self.auth_cfg, &self.sessions, p.get_auth()) {
|
||||
Ok(ctx) => ctx,
|
||||
Err(e) => return Promise::err(e),
|
||||
};
|
||||
|
||||
let username_str = match username.to_str() {
|
||||
Ok(s) => s,
|
||||
Err(e) => return Promise::err(coded_error(E020_BAD_PARAMS, e)),
|
||||
};
|
||||
|
||||
if username_str.is_empty() {
|
||||
return Promise::err(coded_error(E020_BAD_PARAMS, "username must not be empty"));
|
||||
}
|
||||
|
||||
// Federation: parse user@domain format.
|
||||
let addr = crate::federation::address::FederatedAddress::parse(username_str);
|
||||
let is_remote = match (&addr.domain, &self.local_domain) {
|
||||
(Some(d), Some(ld)) => d != ld,
|
||||
(Some(_), None) => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if is_remote {
|
||||
// Proxy to remote server via federation.
|
||||
if let (Some(ref fed_client), Some(ref domain)) = (&self.federation_client, &addr.domain) {
|
||||
if fed_client.has_peer(domain) {
|
||||
let fed = fed_client.clone();
|
||||
let user = addr.username.clone();
|
||||
let dom = domain.clone();
|
||||
return Promise::from_future(async move {
|
||||
match fed.proxy_resolve_user(&dom, &user).await {
|
||||
Ok(Some(key)) => {
|
||||
results.get().set_identity_key(&key);
|
||||
}
|
||||
Ok(None) => {
|
||||
// Not found on remote — return empty.
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!(error = %e, "federation proxy_resolve_user failed");
|
||||
// Fall through — return empty (not found).
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
// No federation client or unknown peer — return empty (not found).
|
||||
return Promise::ok(());
|
||||
}
|
||||
|
||||
// Local resolution.
|
||||
match self.store.get_user_identity_key(&addr.username) {
|
||||
Ok(Some(key)) => {
|
||||
results.get().set_identity_key(&key);
|
||||
}
|
||||
Ok(None) => {
|
||||
// Return empty Data — caller checks length to detect "not found".
|
||||
}
|
||||
Err(e) => return Promise::err(storage_err(e)),
|
||||
}
|
||||
|
||||
Promise::ok(())
|
||||
}
|
||||
|
||||
pub fn handle_resolve_identity(
|
||||
&mut self,
|
||||
params: node_service::ResolveIdentityParams,
|
||||
mut results: node_service::ResolveIdentityResults,
|
||||
) -> Promise<(), capnp::Error> {
|
||||
let p = match params.get() {
|
||||
Ok(p) => p,
|
||||
Err(e) => return Promise::err(coded_error(E020_BAD_PARAMS, e)),
|
||||
};
|
||||
let identity_key = match p.get_identity_key() {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Promise::err(coded_error(E020_BAD_PARAMS, e)),
|
||||
};
|
||||
let _auth_ctx = match validate_auth_context(&self.auth_cfg, &self.sessions, p.get_auth()) {
|
||||
Ok(ctx) => ctx,
|
||||
Err(e) => return Promise::err(e),
|
||||
};
|
||||
|
||||
if identity_key.len() != 32 {
|
||||
return Promise::err(coded_error(
|
||||
E004_IDENTITY_KEY_LENGTH,
|
||||
format!("identityKey must be exactly 32 bytes, got {}", identity_key.len()),
|
||||
));
|
||||
}
|
||||
|
||||
match self.store.resolve_identity_key(identity_key) {
|
||||
Ok(Some(username)) => {
|
||||
results.get().set_username(&username);
|
||||
}
|
||||
Ok(None) => {
|
||||
// Return empty string — caller checks length to detect "not found".
|
||||
}
|
||||
Err(e) => return Promise::err(storage_err(e)),
|
||||
}
|
||||
|
||||
Promise::ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user