[Updt] Mtp 0.3.0

This commit is contained in:
Alex 2026-08-20 17:05:43 +02:00
commit ad8555bc6e
Signed by: alex
SSH key fingerprint: SHA256:D1+Ub8o0v4K5y1JNivW8IxEOelqLSvPmUzBbDIoZkRQ
45 changed files with 2019 additions and 1441 deletions

View file

@ -1,10 +1,10 @@
use crate::log_buffer::LogBuffer;
use crate::{DaemonRuntime, DaemonServices};
use iota_ipc::{
CommunitySummary, ComponentStatusResponse, ConfigResponse, ExitIntent, IpcErrorCode,
LocalRequest, LogEntriesResponse, OmikronStatusResponse, ResponseEnvelope, ResponsePayload,
ResponseResult, StatusResponse, TaskSummary, UpdateStatusResponse, UserDetailResponse,
UserSummary,
CommunitySummary, ComponentStatusResponse, ConfigResponse, DaemonMessage, ExitIntent,
IpcErrorCode, LocalRequest, LogEntriesResponse, LogEntry, MAX_MESSAGE_SIZE,
OmikronStatusResponse, ResponseEnvelope, ResponsePayload, ResponseResult, StatusResponse,
TaskSummary, UpdateStatusResponse, UserDetailResponse, UserSummary,
};
use iota_logger::{log, log_command};
use iota_storage::users::user_manager;
@ -15,6 +15,37 @@ use std::time::Duration;
use crate::daemon_state::{ShutdownReason, StartupPhase};
pub use iota_ipc::IpcRole;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PeerContext {
pub pid: i32,
pub uid: u32,
pub role: IpcRole,
}
const MAX_LOG_ENTRIES_PER_RESPONSE: usize = 512;
fn bounded_log_entries(mut entries: Vec<LogEntry>) -> Vec<LogEntry> {
entries.truncate(MAX_LOG_ENTRIES_PER_RESPONSE);
while !entries.is_empty() {
let response = DaemonMessage::Response(ResponseEnvelope {
request_id: u64::MAX,
result: ResponseResult::Ok(ResponsePayload::LogEntries(LogEntriesResponse {
entries: entries.clone(),
})),
});
let fits = serde_json::to_vec(&response)
.map(|encoded| encoded.len() <= MAX_MESSAGE_SIZE)
.unwrap_or(false);
if fits {
return entries;
}
entries.remove(0);
}
entries
}
#[derive(Clone)]
pub struct CommandRouter {
runtime: Arc<DaemonRuntime>,
@ -35,8 +66,33 @@ impl CommandRouter {
}
}
pub async fn route(&self, request_id: u64, request: LocalRequest) -> ResponseEnvelope {
log_command!("{:?}", request);
pub async fn route(
&self,
peer: &PeerContext,
request_id: u64,
request: LocalRequest,
) -> ResponseEnvelope {
if !peer.role.allows(request.required_role()) {
log!(
"IPC authorization denied: pid={}, uid={}, role={:?}, request={:?}",
peer.pid,
peer.uid,
peer.role,
request
);
return ResponseEnvelope {
request_id,
result: ResponseResult::Error(IpcErrorCode::Unauthorized),
};
}
log_command!(
"pid={} uid={} role={:?} request={:?}",
peer.pid,
peer.uid,
peer.role,
request
);
let result = self.execute(request).await;
ResponseEnvelope { request_id, result }
}
@ -284,7 +340,7 @@ impl CommandRouter {
{
return ResponseResult::Error(IpcErrorCode::Conflict);
}
self.runtime.shutdown(match intent {
self.runtime.request_shutdown(match intent {
ExitIntent::Stop => ShutdownReason::Stop,
ExitIntent::Restart => ShutdownReason::Restart,
});
@ -298,13 +354,13 @@ impl CommandRouter {
},
)),
LocalRequest::RestartDaemon => {
self.runtime.shutdown(ShutdownReason::Restart);
self.runtime.request_shutdown(ShutdownReason::Restart);
ResponseResult::Ok(ResponsePayload::Acknowledged {
message: "Daemon restart requested".into(),
})
}
LocalRequest::StopDaemon => {
self.runtime.shutdown(ShutdownReason::Stop);
self.runtime.request_shutdown(ShutdownReason::Stop);
ResponseResult::Ok(ResponsePayload::Acknowledged {
message: "Daemon shutdown requested".into(),
})
@ -378,7 +434,7 @@ impl CommandRouter {
LocalRequest::ImportUser { .. } => ResponseResult::Error(IpcErrorCode::InvalidRequest),
LocalRequest::GetLogs { limit } => {
let entries = if let Ok(buf) = self.log_buffer.lock() {
buf.recent(limit)
bounded_log_entries(buf.recent(limit.min(MAX_LOG_ENTRIES_PER_RESPONSE)))
} else {
Vec::new()
};
@ -393,11 +449,10 @@ impl CommandRouter {
Err(_e) => ResponseResult::Error(IpcErrorCode::InternalFailure),
},
LocalRequest::ListCommunities => {
let iota_id = config_util::CONFIG
.load()
.iota_id
.map(|id| id as i64)
.unwrap_or(0);
let iota_id = config_util::CONFIG.load().iota_id;
let Ok(iota_id) = iota_id.map(i64::try_from).unwrap_or(Ok(0)) else {
return ResponseResult::Ok(ResponsePayload::Communities(Vec::new()));
};
let stored =
iota_storage::util::communities_util::CommunitiesUtil::get_communities(iota_id);
let summaries: Vec<CommunitySummary> = stored
@ -412,3 +467,77 @@ impl CommandRouter {
}
}
}
#[cfg(test)]
mod tests {
use super::{IpcRole, LocalRequest, bounded_log_entries};
use iota_ipc::{ExitIntent, LogEntry, SecretString};
#[test]
fn every_request_has_an_explicit_role_policy() {
let requests = [
LocalRequest::GetStatus,
LocalRequest::ListTasks,
LocalRequest::ListUsers,
LocalRequest::CreateUser {
username: "alice".into(),
},
LocalRequest::AttachUserFromTu {
credential: SecretString("credential".into()),
},
LocalRequest::PurgeUserData { user_id: 1 },
LocalRequest::ReleaseUser { user_id: 1 },
LocalRequest::CompleteDeleteUser {
user_id: 1,
credential: None,
},
LocalRequest::RemoveUser { user_id: 1 },
LocalRequest::ReconnectOmikron,
LocalRequest::RotateIotaIdentity,
LocalRequest::RequestProcessExit {
intent: ExitIntent::Stop,
},
LocalRequest::GetDaemonStatus,
LocalRequest::RestartDaemon,
LocalRequest::StopDaemon,
LocalRequest::GetConfig,
LocalRequest::SetConfig {
key: "port".into(),
value: "1984".into(),
},
LocalRequest::ReloadConfig,
LocalRequest::GetOmikronStatus,
LocalRequest::ListComponents,
LocalRequest::GetUser { user_id: 1 },
LocalRequest::ImportUser {
username: "alice".into(),
},
LocalRequest::GetLogs { limit: 10 },
LocalRequest::CheckUpdate,
LocalRequest::ListCommunities,
];
assert_eq!(requests.len(), 25);
for request in requests {
let required = request.required_role();
assert!(IpcRole::Admin.allows(required));
assert_eq!(
IpcRole::Operate.allows(required),
required != IpcRole::Admin
);
assert_eq!(IpcRole::Read.allows(required), required == IpcRole::Read);
}
}
#[test]
fn log_responses_drop_entries_that_cannot_fit_one_ipc_frame() {
let entries = vec![LogEntry {
timestamp_ms: 0,
sender: "test".into(),
message: "x".repeat(2 * 1024 * 1024),
is_error: false,
}];
assert!(bounded_log_entries(entries).is_empty());
}
}