Skip to content

Commit 591f4e2

Browse files
feat(dev-load): Allow to save pki local pending request
Closes #11525
1 parent 0040a24 commit 591f4e2

File tree

8 files changed

+157
-10
lines changed

8 files changed

+157
-10
lines changed

libparsec/crates/platform_device_loader/src/lib.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,20 @@ pub fn get_default_key_file(config_dir: &Path, device_id: DeviceID) -> PathBuf {
135135
device_path
136136
}
137137

138+
pub fn get_default_local_pending_file(config_dir: &Path, enrollment_id: EnrollmentID) -> PathBuf {
139+
let mut local_pending_path = get_local_pending_dir(config_dir);
140+
141+
local_pending_path.push(format!("{}.{LOCAL_PENDING_EXT}", enrollment_id.hex()));
142+
143+
local_pending_path
144+
}
145+
146+
const LOCAL_PENDING_EXT: &str = ".pending";
147+
148+
fn get_local_pending_dir(config_dir: &Path) -> PathBuf {
149+
config_dir.join("pending_requests")
150+
}
151+
138152
#[derive(Debug, thiserror::Error)]
139153
pub enum ListAvailableDeviceError {
140154
#[error("Device storage is not available")]
@@ -727,3 +741,32 @@ fn server_url_from_device(device: &LocalDevice) -> String {
727741
.to_http_url(None)
728742
.to_string()
729743
}
744+
745+
#[derive(Debug, thiserror::Error)]
746+
pub enum SavePkiLocalPendingError {
747+
#[error("Device storage is not available")]
748+
StorageNotAvailable,
749+
#[error(transparent)]
750+
InvalidPath(anyhow::Error),
751+
#[error(transparent)]
752+
Internal(anyhow::Error),
753+
}
754+
755+
impl From<SaveDeviceError> for SavePkiLocalPendingError {
756+
fn from(value: SaveDeviceError) -> Self {
757+
match value {
758+
SaveDeviceError::StorageNotAvailable => Self::StorageNotAvailable,
759+
SaveDeviceError::InvalidPath(error) => Self::InvalidPath(error),
760+
SaveDeviceError::Internal(error) => Self::Internal(error),
761+
_ => unreachable!(),
762+
}
763+
}
764+
}
765+
766+
pub async fn save_pki_local_pending(
767+
local_pending: LocalPendingEnrollment,
768+
local_file: PathBuf,
769+
) -> Result<(), SavePkiLocalPendingError> {
770+
log::debug!("Saving local device at {}", local_file.display());
771+
platform::save_pki_local_pending(local_pending, local_file).await
772+
}

libparsec/crates/platform_device_loader/src/native/mod.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use crate::{
1010
encrypt_device, get_device_archive_path, AccountVaultOperationsFetchOpaqueKeyError,
1111
AccountVaultOperationsUploadOpaqueKeyError, ArchiveDeviceError, AvailableDevice,
1212
DeviceAccessStrategy, DeviceSaveStrategy, ListAvailableDeviceError, LoadCiphertextKeyError,
13-
LoadDeviceError, ReadFileError, RemoveDeviceError, SaveDeviceError, UpdateDeviceError,
14-
DEVICE_FILE_EXT,
13+
LoadDeviceError, ReadFileError, RemoveDeviceError, SaveDeviceError, SavePkiLocalPendingError,
14+
UpdateDeviceError, DEVICE_FILE_EXT,
1515
};
1616
use libparsec_platform_pki::{decrypt_message, encrypt_message};
1717
use libparsec_types::prelude::*;
@@ -522,3 +522,13 @@ pub(super) fn is_keyring_available() -> bool {
522522
}
523523
}
524524
}
525+
526+
pub async fn save_pki_local_pending(
527+
local_pending: LocalPendingEnrollment,
528+
local_file: PathBuf,
529+
) -> Result<(), SavePkiLocalPendingError> {
530+
let file_content = local_pending.dump();
531+
save_content(&local_file, &file_content)
532+
.await
533+
.map_err(Into::into)
534+
}

libparsec/crates/platform_device_loader/src/web/error.rs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,20 +94,20 @@ error_set::error_set! {
9494
GetFile(GetFileHandleError),
9595
ReadFile(ReadToEndError),
9696
}
97-
SaveDeviceError := SaveDeviceFileError || {
97+
SaveDeviceError := SaveRawDataError || {
9898
RemoteOpaqueKeyUploadFailed(anyhow::Error),
9999
RemoteOpaqueKeyUploadOffline(ConnectionError),
100100
Internal(anyhow::Error),
101101
}
102-
SaveDeviceFileError := GetFileHandleError || WriteAllError
102+
SaveRawDataError := GetFileHandleError || WriteAllError
103103
RemoveEntryError := NotFoundError || DomExceptionError || AwaitPromiseError || GetDirectoryHandleError
104104
ArchiveDeviceError := RemoveEntryError || {
105105
GetDeviceToArchive(GetFileHandleError),
106106
ReadDeviceToArchive(ReadToEndError),
107107
CreateArchiveDevice(GetFileHandleError),
108108
WriteArchiveDevice(WriteAllError),
109109
}
110-
RemoveDeviceError := SaveDeviceFileError
110+
RemoveDeviceError := SaveRawDataError
111111
}
112112

113113
macro_rules! impl_from_new_storage_error {
@@ -211,3 +211,21 @@ impl From<ArchiveDeviceError> for crate::ArchiveDeviceError {
211211
Self::Internal(anyhow::anyhow!("{value}"))
212212
}
213213
}
214+
215+
impl From<SaveRawDataError> for crate::SavePkiLocalPendingError {
216+
fn from(value: SaveRawDataError) -> Self {
217+
match value {
218+
SaveRawDataError::NotAFile { .. } => Self::InvalidPath(anyhow::anyhow!("{value}")),
219+
SaveRawDataError::NotADirectory { .. } => Self::InvalidPath(anyhow::anyhow!("{value}")),
220+
SaveRawDataError::NotFound { .. } => Self::InvalidPath(anyhow::anyhow!("{value}")),
221+
SaveRawDataError::CreateWritable { .. } => Self::Internal(anyhow::anyhow!("{value}")),
222+
SaveRawDataError::CannotEdit { .. } => Self::Internal(anyhow::anyhow!("{value}")),
223+
SaveRawDataError::Write { .. } => Self::Internal(anyhow::anyhow!("{value}")),
224+
SaveRawDataError::Close { .. } => Self::Internal(anyhow::anyhow!("{value}")),
225+
SaveRawDataError::DomException { .. } => Self::Internal(anyhow::anyhow!("{value}")),
226+
SaveRawDataError::Cast { .. } => Self::Internal(anyhow::anyhow!("{value}")),
227+
SaveRawDataError::Promise { .. } => Self::Internal(anyhow::anyhow!("{value}")),
228+
SaveRawDataError::NoSpaceLeft { .. } => Self::Internal(anyhow::anyhow!("{value}")),
229+
}
230+
}
231+
}

libparsec/crates/platform_device_loader/src/web/internal.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,10 +192,18 @@ impl Storage {
192192
&self,
193193
key: &Path,
194194
file_data: &DeviceFile,
195-
) -> Result<(), SaveDeviceFileError> {
195+
) -> Result<(), SaveRawDataError> {
196196
let data = file_data.dump();
197-
log::trace!("Saving device file at {}", key.display());
198-
let parent = if let Some(parent) = key.parent() {
197+
self.save_raw_data(key, &data).await
198+
}
199+
200+
pub(crate) async fn save_raw_data(
201+
&self,
202+
path: &Path,
203+
data: &[u8],
204+
) -> Result<(), SaveRawDataError> {
205+
log::trace!("Saving device file at {}", path.display());
206+
let parent = if let Some(parent) = path.parent() {
199207
Some(self.root_dir.create_dir_all(parent).await?)
200208
} else {
201209
None
@@ -204,7 +212,7 @@ impl Storage {
204212
.as_ref()
205213
.unwrap_or(&self.root_dir)
206214
.get_file(
207-
key.file_name()
215+
path.file_name()
208216
.and_then(std::ffi::OsStr::to_str)
209217
.expect("Missing filename"),
210218
Some(OpenOptions::create()),
@@ -247,6 +255,15 @@ impl Storage {
247255
.await
248256
.map_err(Into::into)
249257
}
258+
259+
pub(crate) async fn save_pki_local_pending(
260+
&self,
261+
local_file: PathBuf,
262+
file_data: LocalPendingEnrollment,
263+
) -> Result<(), SaveRawDataError> {
264+
let data = file_data.dump();
265+
self.save_raw_data(&local_file, &data).await
266+
}
250267
}
251268

252269
async fn load_available_device(file: &File) -> Result<AvailableDevice, LoadAvailableDeviceError> {

libparsec/crates/platform_device_loader/src/web/mod.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use libparsec_types::prelude::*;
1111
use crate::{
1212
AccountVaultOperationsFetchOpaqueKeyError, ArchiveDeviceError, AvailableDevice,
1313
DeviceAccessStrategy, DeviceSaveStrategy, ListAvailableDeviceError, LoadCiphertextKeyError,
14-
ReadFileError, RemoveDeviceError, SaveDeviceError, UpdateDeviceError,
14+
ReadFileError, RemoveDeviceError, SaveDeviceError, SavePkiLocalPendingError, UpdateDeviceError,
1515
};
1616
use internal::Storage;
1717

@@ -174,3 +174,18 @@ pub(super) async fn remove_device(device_path: &Path) -> Result<(), RemoveDevice
174174
};
175175
storage.remove_device(device_path).await.map_err(Into::into)
176176
}
177+
178+
pub(super) async fn save_pki_local_pending(
179+
local_pending: LocalPendingEnrollment,
180+
local_file: PathBuf,
181+
) -> Result<(), SavePkiLocalPendingError> {
182+
let Ok(storage) = Storage::new().await.inspect_err(|e| {
183+
log::error!("Failed to access storage: {e}");
184+
}) else {
185+
return Err(SavePkiLocalPendingError::StorageNotAvailable);
186+
};
187+
storage
188+
.save_pki_local_pending(local_file, local_pending)
189+
.await
190+
.map_err(Into::into)
191+
}

libparsec/crates/platform_device_loader/tests/units/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ libparsec_tests_lite::platform::wasm_bindgen_test_configure!(run_in_browser run_
66
mod archive;
77
mod list;
88
mod load;
9+
mod pki;
910
mod recovery;
1011
mod remove;
1112
mod save;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Parsec Cloud (https://parsec.cloud) Copyright (c) BUSL-1.1 2016-present Scille SAS
2+
3+
mod save;
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Parsec Cloud (https://parsec.cloud) Copyright (c) BUSL-1.1 2016-present Scille SAS
2+
3+
use libparsec_testbed::TestbedEnv;
4+
use libparsec_tests_fixtures::prelude::*;
5+
use libparsec_tests_lite::parsec_test;
6+
use libparsec_types::{
7+
Bytes, EnrollmentID, LocalPendingEnrollment, ParsecPkiEnrollmentAddr,
8+
PkiEnrollmentSubmitPayload, X509CertificateHash,
9+
};
10+
11+
use crate::save_pki_local_pending;
12+
13+
#[parsec_test(testbed = "minimal")]
14+
async fn ok_simple(tmp_path: TmpPath, env: &TestbedEnv) {
15+
let path = tmp_path.join("local_pki_enrol.keys");
16+
17+
let alice_device = env.local_device("alice@dev1");
18+
let pki_addr = ParsecPkiEnrollmentAddr::new(
19+
alice_device.organization_addr.clone(),
20+
alice_device.organization_id().clone(),
21+
);
22+
let local_pending = LocalPendingEnrollment {
23+
cert_ref: X509CertificateHash::fake_sha256().into(),
24+
addr: pki_addr,
25+
submitted_on: "2000-01-01T00:00:00Z".parse().unwrap(),
26+
enrollment_id: EnrollmentID::default(),
27+
payload: PkiEnrollmentSubmitPayload {
28+
// We reuse `alice_device` attribute for simplicity sake.
29+
// IRL, those values are RNG and provided by the user.
30+
verify_key: alice_device.signing_key.verify_key(),
31+
public_key: alice_device.private_key.public_key(),
32+
device_label: alice_device.device_label.clone(),
33+
human_handle: alice_device.human_handle.clone(),
34+
},
35+
encrypted_key: Bytes::from_static(b"encrypted key"),
36+
encrypted_key_algo: libparsec_types::EncryptionAlgorithm::RsaesOaepSha256,
37+
ciphertext: Bytes::from_static(b"encrypted secret part"),
38+
};
39+
save_pki_local_pending(local_pending, path).await.unwrap();
40+
}

0 commit comments

Comments
 (0)