mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 12:37:00 +01:00
Add tools for remigration and domain level raising (#2481)
This commit is contained in:
parent
a1fbde9f2f
commit
9050188b29
|
@ -173,6 +173,14 @@ pub enum Oauth2ClaimMapJoin {
|
|||
Array,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct DomainInfo {
|
||||
pub name: String,
|
||||
pub displayname: String,
|
||||
pub uuid: Uuid,
|
||||
pub level: u32,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fstype_deser() {
|
||||
assert_eq!(FsType::try_from("zfs"), Ok(FsType::Zfs));
|
||||
|
|
|
@ -296,7 +296,11 @@ pub enum OperationError {
|
|||
VS0001IncomingReplSshPublicKey,
|
||||
// Value Errors
|
||||
VL0001ValueSshPublicKeyString,
|
||||
// SCIM
|
||||
SC0001IncomingSshPublicKey,
|
||||
// Migration
|
||||
MG0001InvalidReMigrationLevel,
|
||||
MG0002RaiseDomainLevelExceedsMaximum,
|
||||
}
|
||||
|
||||
impl PartialEq for OperationError {
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
ARG BASE_IMAGE=opensuse/tumbleweed:latest
|
||||
# ARG BASE_IMAGE=opensuse/leap:15.5
|
||||
|
||||
# FROM ${BASE_IMAGE} as repos
|
||||
FROM ${BASE_IMAGE}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Build the main Kanidmd server
|
||||
ARG BASE_IMAGE=opensuse/tumbleweed:latest
|
||||
# ARG BASE_IMAGE=opensuse/leap:15.5
|
||||
|
||||
FROM ${BASE_IMAGE} AS repos
|
||||
ADD scripts/zypper_fixing.sh /zypper_fixing.sh
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use std::{iter, sync::Arc};
|
||||
|
||||
use kanidm_proto::internal::DomainInfo as ProtoDomainInfo;
|
||||
use kanidm_proto::internal::ImageValue;
|
||||
use kanidm_proto::internal::Oauth2ClaimMapJoin as ProtoOauth2ClaimMapJoin;
|
||||
use kanidm_proto::v1::{
|
||||
|
@ -1820,4 +1821,57 @@ impl QueryServerWriteV1 {
|
|||
|
||||
idms_prox_write.commit().map(|()| pw)
|
||||
}
|
||||
|
||||
#[instrument(
|
||||
level = "info",
|
||||
skip_all,
|
||||
fields(uuid = ?eventid)
|
||||
)]
|
||||
pub(crate) async fn handle_domain_show(
|
||||
&self,
|
||||
eventid: Uuid,
|
||||
) -> Result<ProtoDomainInfo, OperationError> {
|
||||
trace!("Begin domain show event");
|
||||
let ct = duration_from_epoch_now();
|
||||
let mut idms_prox_write = self.idms.proxy_write(ct).await;
|
||||
|
||||
let domain_info = idms_prox_write.qs_write.domain_info()?;
|
||||
|
||||
idms_prox_write.commit().map(|()| domain_info)
|
||||
}
|
||||
|
||||
#[instrument(
|
||||
level = "info",
|
||||
skip_all,
|
||||
fields(uuid = ?eventid)
|
||||
)]
|
||||
pub(crate) async fn handle_domain_raise(&self, eventid: Uuid) -> Result<u32, OperationError> {
|
||||
trace!("Begin domain raise event");
|
||||
let ct = duration_from_epoch_now();
|
||||
let mut idms_prox_write = self.idms.proxy_write(ct).await;
|
||||
|
||||
idms_prox_write.qs_write.domain_raise(DOMAIN_MAX_LEVEL)?;
|
||||
|
||||
idms_prox_write.commit().map(|()| DOMAIN_MAX_LEVEL)
|
||||
}
|
||||
|
||||
#[instrument(
|
||||
level = "info",
|
||||
skip_all,
|
||||
fields(uuid = ?eventid)
|
||||
)]
|
||||
pub(crate) async fn handle_domain_remigrate(
|
||||
&self,
|
||||
level: Option<u32>,
|
||||
eventid: Uuid,
|
||||
) -> Result<(), OperationError> {
|
||||
let level = level.unwrap_or(DOMAIN_MIN_REMIGRATION_LEVEL);
|
||||
trace!(%level, "Begin domain remigrate event");
|
||||
let ct = duration_from_epoch_now();
|
||||
let mut idms_prox_write = self.idms.proxy_write(ct).await;
|
||||
|
||||
idms_prox_write.qs_write.domain_remigrate(level)?;
|
||||
|
||||
idms_prox_write.commit()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,18 +17,25 @@ use tokio_util::codec::{Decoder, Encoder, Framed};
|
|||
use tracing::{span, Instrument, Level};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub use kanidm_proto::internal::DomainInfo as ProtoDomainInfo;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub enum AdminTaskRequest {
|
||||
RecoverAccount { name: String },
|
||||
ShowReplicationCertificate,
|
||||
RenewReplicationCertificate,
|
||||
RefreshReplicationConsumer,
|
||||
DomainShow,
|
||||
DomainRaise,
|
||||
DomainRemigrate { level: Option<u32> },
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub enum AdminTaskResponse {
|
||||
RecoverAccount { password: String },
|
||||
ShowReplicationCertificate { cert: String },
|
||||
DomainRaise { level: u32 },
|
||||
DomainShow { domain_info: ProtoDomainInfo },
|
||||
Success,
|
||||
Error,
|
||||
}
|
||||
|
@ -315,6 +322,30 @@ async fn handle_client(
|
|||
AdminTaskResponse::Error
|
||||
}
|
||||
},
|
||||
|
||||
AdminTaskRequest::DomainShow => match server.handle_domain_show(eventid).await {
|
||||
Ok(domain_info) => AdminTaskResponse::DomainShow { domain_info },
|
||||
Err(e) => {
|
||||
error!(err = ?e, "error during domain show");
|
||||
AdminTaskResponse::Error
|
||||
}
|
||||
},
|
||||
AdminTaskRequest::DomainRaise => match server.handle_domain_raise(eventid).await {
|
||||
Ok(level) => AdminTaskResponse::DomainRaise { level },
|
||||
Err(e) => {
|
||||
error!(err = ?e, "error during domain raise");
|
||||
AdminTaskResponse::Error
|
||||
}
|
||||
},
|
||||
AdminTaskRequest::DomainRemigrate { level } => {
|
||||
match server.handle_domain_remigrate(level, eventid).await {
|
||||
Ok(()) => AdminTaskResponse::Success,
|
||||
Err(e) => {
|
||||
error!(err = ?e, "error during domain remigrate");
|
||||
AdminTaskResponse::Error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.instrument(nspan)
|
||||
|
|
|
@ -30,7 +30,7 @@ use clap::{Args, Parser, Subcommand};
|
|||
use futures::{SinkExt, StreamExt};
|
||||
#[cfg(not(target_family = "windows"))] // not needed for windows builds
|
||||
use kanidm_utils_users::{get_current_gid, get_current_uid, get_effective_gid, get_effective_uid};
|
||||
use kanidmd_core::admin::{AdminTaskRequest, AdminTaskResponse, ClientCodec};
|
||||
use kanidmd_core::admin::{AdminTaskRequest, AdminTaskResponse, ClientCodec, ProtoDomainInfo};
|
||||
use kanidmd_core::config::{Configuration, ServerConfig};
|
||||
use kanidmd_core::{
|
||||
backup_server_core, cert_generate_core, create_server_core, dbscan_get_id2entry_core,
|
||||
|
@ -89,8 +89,17 @@ impl KanidmdOpt {
|
|||
commands: DbScanOpt::GetId2Entry(dopt),
|
||||
} => &dopt.commonopts,
|
||||
KanidmdOpt::DomainSettings {
|
||||
commands: DomainSettingsCmds::DomainChange(sopt),
|
||||
} => sopt,
|
||||
commands: DomainSettingsCmds::Show { commonopts },
|
||||
}
|
||||
| KanidmdOpt::DomainSettings {
|
||||
commands: DomainSettingsCmds::Change { commonopts },
|
||||
}
|
||||
| KanidmdOpt::DomainSettings {
|
||||
commands: DomainSettingsCmds::Raise { commonopts },
|
||||
}
|
||||
| KanidmdOpt::DomainSettings {
|
||||
commands: DomainSettingsCmds::Remigrate { commonopts, .. },
|
||||
} => commonopts,
|
||||
KanidmdOpt::Database {
|
||||
commands: DbCommands::Verify(sopt),
|
||||
}
|
||||
|
@ -161,6 +170,36 @@ async fn submit_admin_req(path: &str, req: AdminTaskRequest, output_mode: Consol
|
|||
info!(certificate = ?cert)
|
||||
}
|
||||
},
|
||||
|
||||
Some(Ok(AdminTaskResponse::DomainRaise { level })) => match output_mode {
|
||||
ConsoleOutputMode::JSON => {
|
||||
eprintln!("{{\"success\":\"{}\"}}", level)
|
||||
}
|
||||
ConsoleOutputMode::Text => {
|
||||
info!("success - raised domain level to {}", level)
|
||||
}
|
||||
},
|
||||
Some(Ok(AdminTaskResponse::DomainShow { domain_info })) => match output_mode {
|
||||
ConsoleOutputMode::JSON => {
|
||||
let json_output = serde_json::json!({
|
||||
"domain_info": domain_info
|
||||
});
|
||||
println!("{}", json_output);
|
||||
}
|
||||
ConsoleOutputMode::Text => {
|
||||
let ProtoDomainInfo {
|
||||
name,
|
||||
displayname,
|
||||
uuid,
|
||||
level,
|
||||
} = domain_info;
|
||||
|
||||
info!("domain_name : {}", name);
|
||||
info!("domain_display: {}", displayname);
|
||||
info!("domain_uuid : {}", uuid);
|
||||
info!("domain_level : {}", level);
|
||||
}
|
||||
},
|
||||
Some(Ok(AdminTaskResponse::Success)) => match output_mode {
|
||||
ConsoleOutputMode::JSON => {
|
||||
eprintln!("\"success\"")
|
||||
|
@ -717,11 +756,49 @@ async fn kanidm_main() -> ExitCode {
|
|||
}
|
||||
|
||||
KanidmdOpt::DomainSettings {
|
||||
commands: DomainSettingsCmds::DomainChange(_dopt),
|
||||
commands: DomainSettingsCmds::Change { .. },
|
||||
} => {
|
||||
info!("Running in domain name change mode ... this may take a long time ...");
|
||||
domain_rename_core(&config).await;
|
||||
}
|
||||
|
||||
KanidmdOpt::DomainSettings {
|
||||
commands: DomainSettingsCmds::Show { commonopts },
|
||||
} => {
|
||||
info!("Running domain show ...");
|
||||
let output_mode: ConsoleOutputMode = commonopts.output_mode.to_owned().into();
|
||||
submit_admin_req(
|
||||
config.adminbindpath.as_str(),
|
||||
AdminTaskRequest::DomainShow,
|
||||
output_mode,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
KanidmdOpt::DomainSettings {
|
||||
commands: DomainSettingsCmds::Raise { commonopts },
|
||||
} => {
|
||||
info!("Running domain raise ...");
|
||||
let output_mode: ConsoleOutputMode = commonopts.output_mode.to_owned().into();
|
||||
submit_admin_req(
|
||||
config.adminbindpath.as_str(),
|
||||
AdminTaskRequest::DomainRaise,
|
||||
output_mode,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
KanidmdOpt::DomainSettings {
|
||||
commands: DomainSettingsCmds::Remigrate { commonopts, level },
|
||||
} => {
|
||||
info!("Running domain remigrate ...");
|
||||
let output_mode: ConsoleOutputMode = commonopts.output_mode.to_owned().into();
|
||||
submit_admin_req(
|
||||
config.adminbindpath.as_str(),
|
||||
AdminTaskRequest::DomainRemigrate { level: *level },
|
||||
output_mode,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
KanidmdOpt::Database {
|
||||
commands: DbCommands::Vacuum(_copt),
|
||||
} => {
|
||||
|
|
|
@ -28,9 +28,32 @@ struct RestoreOpt {
|
|||
|
||||
#[derive(Debug, Subcommand)]
|
||||
enum DomainSettingsCmds {
|
||||
#[clap(name = "show")]
|
||||
/// Show the current domain
|
||||
Show {
|
||||
#[clap(flatten)]
|
||||
commonopts: CommonOpt,
|
||||
},
|
||||
#[clap(name = "rename")]
|
||||
/// Change the IDM domain name
|
||||
DomainChange(CommonOpt),
|
||||
/// Change the IDM domain name based on the values in the configuration
|
||||
Change {
|
||||
#[clap(flatten)]
|
||||
commonopts: CommonOpt,
|
||||
},
|
||||
#[clap(name = "raise")]
|
||||
/// Raise the functional level of this domain to the maximum available.
|
||||
Raise {
|
||||
#[clap(flatten)]
|
||||
commonopts: CommonOpt,
|
||||
},
|
||||
#[clap(name = "remigrate")]
|
||||
/// Rerun migrations of this domains database, optionally nominating the level
|
||||
/// to start from.
|
||||
Remigrate {
|
||||
#[clap(flatten)]
|
||||
commonopts: CommonOpt,
|
||||
level: Option<u32>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
|
|
|
@ -49,6 +49,8 @@ pub const DOMAIN_LEVEL_2: DomainVersion = 2;
|
|||
pub const DOMAIN_LEVEL_3: DomainVersion = 3;
|
||||
pub const DOMAIN_LEVEL_4: DomainVersion = 4;
|
||||
pub const DOMAIN_LEVEL_5: DomainVersion = 5;
|
||||
// The minimum level that we can re-migrate from
|
||||
pub const DOMAIN_MIN_REMIGRATION_LEVEL: DomainVersion = DOMAIN_LEVEL_2;
|
||||
// The minimum supported domain functional level
|
||||
pub const DOMAIN_MIN_LEVEL: DomainVersion = DOMAIN_LEVEL_5;
|
||||
// The target supported domain functional level
|
||||
|
|
|
@ -781,10 +781,10 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
self.internal_modify(&filter, &modlist)?;
|
||||
|
||||
// Now move all oauth2 rs name.
|
||||
let filter = filter!(f_eq(
|
||||
Attribute::Class,
|
||||
EntryClass::OAuth2ResourceServer.into()
|
||||
));
|
||||
let filter = filter!(f_and!([
|
||||
f_eq(Attribute::Class, EntryClass::OAuth2ResourceServer.into()),
|
||||
f_pres(Attribute::OAuth2RsName),
|
||||
]));
|
||||
|
||||
let pre_candidates = self.internal_search(filter).map_err(|err| {
|
||||
admin_error!(?err, "migrate_domain_4_to_5 internal search failure");
|
||||
|
|
|
@ -11,6 +11,7 @@ use std::collections::BTreeSet;
|
|||
use tokio::sync::{Semaphore, SemaphorePermit};
|
||||
use tracing::trace;
|
||||
|
||||
use kanidm_proto::internal::DomainInfo as ProtoDomainInfo;
|
||||
use kanidm_proto::v1::{ConsistencyError, UiHint};
|
||||
|
||||
use crate::be::{Backend, BackendReadTransaction, BackendTransaction, BackendWriteTransaction};
|
||||
|
@ -1385,6 +1386,48 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
&mut self.dyngroup_cache
|
||||
}
|
||||
|
||||
pub fn domain_info(&mut self) -> Result<ProtoDomainInfo, OperationError> {
|
||||
let d_info = &self.d_info;
|
||||
|
||||
Ok(ProtoDomainInfo {
|
||||
name: d_info.d_name.clone(),
|
||||
displayname: d_info.d_display.clone(),
|
||||
uuid: d_info.d_uuid,
|
||||
level: d_info.d_vers,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn domain_raise(&mut self, level: u32) -> Result<(), OperationError> {
|
||||
if level > DOMAIN_MAX_LEVEL {
|
||||
return Err(OperationError::MG0002RaiseDomainLevelExceedsMaximum);
|
||||
}
|
||||
|
||||
let modl = ModifyList::new_purge_and_set(Attribute::Version, Value::Uint32(level));
|
||||
let udi = PVUUID_DOMAIN_INFO.clone();
|
||||
let filt = filter_all!(f_eq(Attribute::Uuid, udi));
|
||||
self.internal_modify(&filt, &modl)
|
||||
}
|
||||
|
||||
pub fn domain_remigrate(&mut self, level: u32) -> Result<(), OperationError> {
|
||||
let mut_d_info = self.d_info.get_mut();
|
||||
|
||||
if level > mut_d_info.d_vers {
|
||||
// Nothing to do.
|
||||
return Ok(());
|
||||
} else if level < DOMAIN_MIN_REMIGRATION_LEVEL {
|
||||
return Err(OperationError::MG0001InvalidReMigrationLevel);
|
||||
};
|
||||
|
||||
debug!(
|
||||
"Prepare to re-migrate from {} -> {}",
|
||||
level, mut_d_info.d_vers
|
||||
);
|
||||
mut_d_info.d_vers = level;
|
||||
self.changed_domain = true;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
pub(crate) fn reload_schema(&mut self) -> Result<(), OperationError> {
|
||||
// supply entries to the writable schema to reload from.
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# This builds the kanidm CLI tools
|
||||
ARG BASE_IMAGE=opensuse/tumbleweed:latest
|
||||
# ARG BASE_IMAGE=opensuse/leap:15.5
|
||||
|
||||
FROM ${BASE_IMAGE} AS repos
|
||||
ADD ../scripts/zypper_fixing.sh /zypper_fixing.sh
|
||||
RUN --mount=type=cache,id=zypp,target=/var/cache/zypp /zypper_fixing.sh
|
||||
|
|
Loading…
Reference in a new issue