Skip to content

Commit

Permalink
Ensure that you are on the latest revision if s service is created
Browse files Browse the repository at this point in the history
  • Loading branch information
Robin Doer committed Sep 27, 2024
1 parent 58fafcc commit 0f8ad4b
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 1 deletion.
14 changes: 14 additions & 0 deletions nuts-container/src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ use crate::ossl;
use crate::password::{PasswordError, PasswordStore};
use crate::svec::SecureVec;

const LATEST_REVISION: u32 = 1;

/// Header related errors.
#[derive(Debug, Error)]
pub enum HeaderError {
Expand All @@ -61,6 +63,10 @@ pub enum HeaderError {
#[error("the password is wrong")]
WrongPassword,

/// Invalid header revision
#[error("invalid header revision, expected {0} but got {1}")]
InvalidRevision(u32, u32),

/// Invalid header, could not validate magic
#[error("invalid header")]
InvalidHeader,
Expand Down Expand Up @@ -202,6 +208,14 @@ impl<'a, B: Backend> Header<'a, B> {
self.revision
}

pub fn latest_revision_or_err(&self) -> Result<(), HeaderError> {
if self.revision == LATEST_REVISION {
Ok(())
} else {
Err(HeaderError::InvalidRevision(LATEST_REVISION, self.revision))
}
}

pub fn cipher(&self) -> Cipher {
self.cipher
}
Expand Down
25 changes: 24 additions & 1 deletion nuts-container/src/header/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use nuts_memory::{MemoryBackend, Settings};

use crate::cipher::Cipher;
use crate::header::plain_secret::{PlainRev0, PlainRev1, PlainSecret};
use crate::header::Header;
use crate::header::{Header, HeaderError};
use crate::kdf::Kdf;
use crate::migrate::Migrator;
use crate::options::CreateOptionsBuilder;
Expand Down Expand Up @@ -161,6 +161,29 @@ fn write_rev1() {
assert_eq!(buf, REV1);
}

#[test]
fn latest_revision_or_err_rev0() {
let header = Header {
revision: 0,
..header(rev0(None))
};

let err = header.latest_revision_or_err().unwrap_err();

assert!(matches!(err, HeaderError::InvalidRevision(expected, got)
if expected == 1 && got == 0))
}

#[test]
fn latest_revision_or_err_rev1() {
let header = Header {
revision: 1,
..header(rev0(None))
};

header.latest_revision_or_err().unwrap();
}

#[test]
fn settings_rev0() {
let header = header(rev0(None));
Expand Down
7 changes: 7 additions & 0 deletions nuts-container/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,13 @@ impl<B: Backend> Container<B> {
pub fn create_service<F: ServiceFactory<B>>(
mut container: Container<B>,
) -> Result<F::Service, F::Err> {
// ensure that you are on the current revision
container
.header
.latest_revision_or_err()
.map_err(Error::<B>::Header)?;

// aquire top-id (if requested)
if F::Service::need_top_id() {
let id = container.aquire()?;
container.update_header(|header| header.set_top_id(id))?;
Expand Down
69 changes: 69 additions & 0 deletions nuts-container/tests/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,75 @@ fn create() {
assert_eq!(info.kdf, kdf);
}

#[test]
fn create_inval_revision() {
use nuts_container::*;
use nuts_directory::{DirectoryBackend, OpenOptions};
use std::path::PathBuf;
use thiserror::Error;

#[derive(Debug, Error)]
#[error(transparent)]
struct SampleError(#[from] Error<DirectoryBackend<PathBuf>>);

#[derive(Debug)]
struct SampleService;

impl Migration for SampleService {
fn migrate_rev0(&self, _userdata: &[u8]) -> Result<Vec<u8>, String> {
unimplemented!()
}
}

impl Service<DirectoryBackend<PathBuf>> for SampleService {
type Migration = Self;

fn need_top_id() -> bool {
false
}

fn migration() -> Self {
Self
}
}

impl ServiceFactory<DirectoryBackend<PathBuf>> for SampleService {
type Service = Self;
type Err = SampleError;

fn create(
_container: Container<DirectoryBackend<PathBuf>>,
) -> Result<Self::Service, Self::Err> {
unimplemented!()
}

fn open(
_container: Container<DirectoryBackend<PathBuf>>,
) -> Result<Self::Service, Self::Err> {
unimplemented!()
}
}

const MANIFEST_DIR: &str = env!("CARGO_MANIFEST_DIR");

// you need a rev0-container
let path: PathBuf = [MANIFEST_DIR, "data", "0.6.8-none"].iter().collect();
let backend_options = OpenOptions::for_path(path);
let container_options = OpenOptionsBuilder::new()
.build::<DirectoryBackend<PathBuf>>()
.unwrap();

let container = Container::open(backend_options, container_options).unwrap();

assert_eq!(container.info().unwrap().revision, 0);

let err = Container::create_service::<SampleService>(container).unwrap_err();

assert!(matches!(err.0, Error::Header(cause)
if matches!(cause,HeaderError::InvalidRevision(expected, got)
if expected == 1 && got == 0)));
}

#[test]
fn open() {
use nuts_container::*;
Expand Down

0 comments on commit 0f8ad4b

Please sign in to comment.