98 lines
4.2 KiB
Rust
98 lines
4.2 KiB
Rust
//! Freshly authenticated publication of a saved client's configuration.
|
|
|
|
use super::{Application, ReissueDelivery, ReissueError, ReissueFailureKind, ReissueStage};
|
|
use crate::delivery::files::{self, PreparedDestination};
|
|
use crate::delivery::qr;
|
|
use crate::delivery::{self, DeliverySettings};
|
|
use crate::domain::PublicKey;
|
|
use crate::vault::VaultBundle;
|
|
use crate::wireguard::ConfigDocument;
|
|
|
|
impl Application {
|
|
/// Reissue a saved client's configuration after freshly unlocking its
|
|
/// installed vault record with this action's password. This operation reads
|
|
/// only installed saved bytes, never uses a draft or retained source file,
|
|
/// and publishes the required file before attempting optional QR encoding.
|
|
pub fn reissue_client_file(
|
|
&self,
|
|
identity: PublicKey,
|
|
password: &str,
|
|
destination: PreparedDestination,
|
|
include_qr: bool,
|
|
) -> Result<ReissueDelivery, ReissueError> {
|
|
let installed = self.store.read_saved().map_err(|_| ReissueError {
|
|
stage: ReissueStage::SavedState,
|
|
kind: ReissueFailureKind::SavedStateUnavailable,
|
|
})?;
|
|
let document =
|
|
ConfigDocument::parse(&installed.server_config).map_err(|_| ReissueError {
|
|
stage: ReissueStage::SavedState,
|
|
kind: ReissueFailureKind::SavedStateUnavailable,
|
|
})?;
|
|
let encoded_vault = installed.vault.as_deref().ok_or(ReissueError {
|
|
stage: ReissueStage::Authorization,
|
|
kind: ReissueFailureKind::VaultUnavailable,
|
|
})?;
|
|
let vault_text = std::str::from_utf8(encoded_vault).map_err(|_| ReissueError {
|
|
stage: ReissueStage::Authorization,
|
|
kind: ReissueFailureKind::VaultUnavailable,
|
|
})?;
|
|
let vault = VaultBundle::from_toml(vault_text).map_err(|_| ReissueError {
|
|
stage: ReissueStage::Authorization,
|
|
kind: ReissueFailureKind::VaultUnavailable,
|
|
})?;
|
|
// Retrieval performs the password unlock and authenticated conflict
|
|
// check on every invocation. The short-lived owner is dropped when
|
|
// this method returns and is never kept in Application state.
|
|
let record = vault
|
|
.retrieve_client_record(identity, password)
|
|
.map_err(|_| ReissueError {
|
|
stage: ReissueStage::Authorization,
|
|
kind: ReissueFailureKind::PasswordOrRecordRejected,
|
|
})?;
|
|
let resolved = self
|
|
.store
|
|
.resolved_settings_for(&installed)
|
|
.map_err(|_| ReissueError {
|
|
stage: ReissueStage::SavedState,
|
|
kind: ReissueFailureKind::SavedStateUnavailable,
|
|
})?;
|
|
let delivery_settings = DeliverySettings {
|
|
server_endpoint: &resolved.server_endpoint,
|
|
dns_addresses: &resolved.dns_addresses,
|
|
};
|
|
let payload = if record.is_new_template() {
|
|
delivery::render_new_client(
|
|
identity,
|
|
record.private_key().ok_or(ReissueError {
|
|
stage: ReissueStage::Authorization,
|
|
kind: ReissueFailureKind::PasswordOrRecordRejected,
|
|
})?,
|
|
&document,
|
|
&delivery_settings,
|
|
)
|
|
} else {
|
|
delivery::render_imported_client(identity, &record, &document, &delivery_settings)
|
|
}
|
|
.map_err(|_| ReissueError {
|
|
stage: ReissueStage::Rendering,
|
|
kind: ReissueFailureKind::ConfigurationRejected,
|
|
})?;
|
|
|
|
files::publish(destination, &payload).map_err(|error| ReissueError {
|
|
stage: ReissueStage::FilePublication,
|
|
kind: ReissueFailureKind::FileOutput(error),
|
|
})?;
|
|
|
|
// A historical timestamp is deliberately insufficient: after reopen,
|
|
// the runtime observation is unknown. Warn unless this live session
|
|
// has verified the exact saved peer snapshot currently on disk.
|
|
let activation_warning = !self.saved_generation_is_verified_applied(&installed, &document);
|
|
let qr = include_qr.then(|| qr::encode_payload(&payload));
|
|
Ok(ReissueDelivery {
|
|
activation_warning,
|
|
qr,
|
|
})
|
|
}
|
|
}
|