[Fix] User deletion & migration
This commit is contained in:
parent
326ebf3b37
commit
7dc98ef29b
20 changed files with 742 additions and 129 deletions
|
|
@ -9,8 +9,9 @@ use iota_ipc::{
|
|||
use iota_logger::{log, log_command};
|
||||
use iota_storage::users::user_manager;
|
||||
use iota_storage::util::config_util::{self};
|
||||
use mtp::codec::{CommunicationType, CommunicationValue};
|
||||
use mtp::codec::{CommunicationType, CommunicationValue, DataType, DataValue};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::daemon_state::{ShutdownReason, StartupPhase};
|
||||
|
||||
|
|
@ -51,7 +52,10 @@ impl CommandRouter {
|
|||
}
|
||||
let needs_omikron = matches!(
|
||||
request,
|
||||
LocalRequest::CreateUser { .. } | LocalRequest::RemoveUser { .. }
|
||||
LocalRequest::CreateUser { .. }
|
||||
| LocalRequest::AttachUserFromTu { .. }
|
||||
| LocalRequest::ReleaseUser { .. }
|
||||
| LocalRequest::CompleteDeleteUser { .. }
|
||||
);
|
||||
if needs_omikron && !self.services.omikron.is_connected().await {
|
||||
return ResponseResult::Error(
|
||||
|
|
@ -92,11 +96,15 @@ impl CommandRouter {
|
|||
ResponseResult::Ok(ResponsePayload::Tasks(tasks))
|
||||
}
|
||||
LocalRequest::ListUsers => {
|
||||
let users: Vec<UserSummary> = user_manager::get_users()
|
||||
let users: Vec<UserSummary> = user_manager::get_residency()
|
||||
.into_iter()
|
||||
.map(|user| UserSummary {
|
||||
credential_present: user.state == user_manager::LocalUserState::Managed
|
||||
&& user_manager::get_user(user.user_id).is_some_and(|profile| iota_util::file_util::read_user_credential_with_legacy(user.user_id, &profile.username).ok().flatten().is_some()),
|
||||
user_id: user.user_id,
|
||||
username: user.username,
|
||||
state: match user.state { user_manager::LocalUserState::Managed => iota_ipc::LocalUserState::Managed, user_manager::LocalUserState::Released => iota_ipc::LocalUserState::Released },
|
||||
data_present: user.data_present,
|
||||
})
|
||||
.collect();
|
||||
ResponseResult::Ok(ResponsePayload::Users(users))
|
||||
|
|
@ -137,18 +145,61 @@ impl CommandRouter {
|
|||
}
|
||||
}
|
||||
}
|
||||
LocalRequest::RemoveUser { user_id } => {
|
||||
let user = match user_manager::get_user(user_id) {
|
||||
Some(user) => user,
|
||||
None => return ResponseResult::Error(IpcErrorCode::NotFound),
|
||||
LocalRequest::PurgeUserData { user_id } => match user_manager::purge_user_data(user_id) {
|
||||
Ok(()) => ResponseResult::Ok(ResponsePayload::UserDataPurged { user_id }),
|
||||
Err(error) => {
|
||||
log!("User data purge failed for {user_id}: {error}");
|
||||
ResponseResult::Error(IpcErrorCode::StorageFailure)
|
||||
}
|
||||
},
|
||||
LocalRequest::AttachUserFromTu { credential } => {
|
||||
match omikron_connector::user_ops::attach_user_from_tu(self.services.omikron.as_ref(), &credential.0).await {
|
||||
Ok(user) => ResponseResult::Ok(ResponsePayload::Acknowledged { message: format!("Added {} ({}) to this Iota", user.username, user.user_id) }),
|
||||
Err(error) => {
|
||||
log!("Credential attach failed: {error:?}");
|
||||
ResponseResult::Error(IpcErrorCode::Unauthorized)
|
||||
}
|
||||
}
|
||||
}
|
||||
LocalRequest::CompleteDeleteUser { user_id, credential } => {
|
||||
let contents = match credential {
|
||||
Some(value) => Ok(value.0),
|
||||
None => user_manager::get_user(user_id)
|
||||
.ok_or(())
|
||||
.and_then(|user| iota_util::file_util::read_user_credential_with_legacy(user_id, &user.username).map_err(|_| ()))
|
||||
.and_then(|value| value.ok_or(())),
|
||||
};
|
||||
let message = CommunicationValue::new(CommunicationType::DeleteUser)
|
||||
.with_sender(user.user_id as u64);
|
||||
if let Err(_e) = self.services.omikron.send_message(&message).await {
|
||||
return ResponseResult::Error(IpcErrorCode::OmikronUnavailable);
|
||||
let Ok(contents) = contents else { return ResponseResult::Error(IpcErrorCode::Unauthorized); };
|
||||
match omikron_connector::user_ops::complete_delete_user_with_tu(self.services.omikron.as_ref(), &contents, user_id).await {
|
||||
Ok(()) => ResponseResult::Ok(ResponsePayload::Acknowledged { message: format!("Deleted Tensamin account {user_id}") }),
|
||||
Err(error) => {
|
||||
log!("Credential deletion failed for {user_id}: {error:?}");
|
||||
ResponseResult::Error(IpcErrorCode::Unauthorized)
|
||||
}
|
||||
}
|
||||
}
|
||||
LocalRequest::RemoveUser { .. } => ResponseResult::Error(IpcErrorCode::InvalidRequest),
|
||||
LocalRequest::ReleaseUser { user_id } => {
|
||||
if user_manager::get_user(user_id).is_none() {
|
||||
return ResponseResult::Error(IpcErrorCode::NotFound);
|
||||
}
|
||||
let request = CommunicationValue::new(CommunicationType::ReleaseUserFromIota)
|
||||
.add_typed_default(DataType::UserId, DataValue::SignedNumber(user_id.into()));
|
||||
match self.services.omikron.await_response(&request, Duration::from_secs(20)).await {
|
||||
Ok(response) if response.is_type(CommunicationType::Success) => match user_manager::release_user(user_id) {
|
||||
Ok(()) => ResponseResult::Ok(ResponsePayload::Acknowledged {
|
||||
message: format!("Released user {user_id}; hosted data was retained"),
|
||||
}),
|
||||
Err(error) => {
|
||||
log!("Remote release succeeded but local cleanup failed for {user_id}: {error}");
|
||||
ResponseResult::Error(IpcErrorCode::StorageFailure)
|
||||
}
|
||||
},
|
||||
Ok(response) if response.is_type(CommunicationType::ErrorNotAuthenticated) => ResponseResult::Error(IpcErrorCode::Unauthorized),
|
||||
Ok(_) => ResponseResult::Error(IpcErrorCode::Conflict),
|
||||
Err(omikron_connector::OmikronError::Timeout(_)) => ResponseResult::Error(IpcErrorCode::Timeout),
|
||||
Err(_) => ResponseResult::Error(IpcErrorCode::OmikronUnavailable),
|
||||
}
|
||||
user_manager::remove_user(user.user_id);
|
||||
ResponseResult::Ok(ResponsePayload::UserRemoved { user_id })
|
||||
}
|
||||
LocalRequest::ReconnectOmikron => match self.services.omikron.reconnect().await {
|
||||
Ok(()) => ResponseResult::Ok(ResponsePayload::Acknowledged {
|
||||
|
|
@ -242,23 +293,22 @@ impl CommandRouter {
|
|||
ResponseResult::Ok(ResponsePayload::Components(components))
|
||||
}
|
||||
LocalRequest::GetUser { user_id } => match user_manager::get_user(user_id) {
|
||||
Some(user) => ResponseResult::Ok(ResponsePayload::UserDetail(UserDetailResponse {
|
||||
Some(user) => {
|
||||
let credential_present = iota_util::file_util::read_user_credential_with_legacy(user_id, &user.username).ok().flatten().is_some();
|
||||
ResponseResult::Ok(ResponsePayload::UserDetail(UserDetailResponse {
|
||||
user_id: user.user_id,
|
||||
username: user.username,
|
||||
display_name: user.display_name,
|
||||
created_at: user.created_at,
|
||||
trusted_apps: user.trusted_apps.keys().cloned().collect(),
|
||||
})),
|
||||
state: iota_ipc::LocalUserState::Managed,
|
||||
data_present: user_manager::get_residency().iter().find(|entry| entry.user_id == user_id).is_none_or(|entry| entry.data_present),
|
||||
credential_present,
|
||||
}))
|
||||
},
|
||||
None => ResponseResult::Error(IpcErrorCode::NotFound),
|
||||
},
|
||||
LocalRequest::ImportUser { username } => {
|
||||
match user_manager::load_from_tu(&username).await {
|
||||
Ok(()) => ResponseResult::Ok(ResponsePayload::Acknowledged {
|
||||
message: format!("Imported user {username}"),
|
||||
}),
|
||||
Err(()) => ResponseResult::Error(IpcErrorCode::StorageFailure),
|
||||
}
|
||||
}
|
||||
LocalRequest::ImportUser { .. } => ResponseResult::Error(IpcErrorCode::InvalidRequest),
|
||||
LocalRequest::GetLogs { limit } => {
|
||||
let entries = if let Ok(buf) = self.log_buffer.lock() {
|
||||
buf.recent(limit)
|
||||
|
|
|
|||
Loading…
Reference in a new issue