Skip to content

Commit

Permalink
optional expire flags + automated versioning & --force-version flag
Browse files Browse the repository at this point in the history
  • Loading branch information
SequeI committed Sep 5, 2024
1 parent c26e089 commit a6af040
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 210 deletions.
17 changes: 9 additions & 8 deletions tough/src/editor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,7 @@ const SPEC_VERSION: &str = "1.0.0";
/// A new repository may be started using the `new()` method.
///
/// An existing `tough::Repository` may be loaded and edited using the
/// `from_repo()` method. When a repo is loaded in this way, versions and
/// expirations are discarded. It is good practice to update these whenever
/// a repo is changed.
/// `from_repo()` method.
///
/// Targets, versions, and expirations may be added to their respective roles
/// via the provided "setter" methods. The final step in the process is the
Expand Down Expand Up @@ -142,8 +140,7 @@ impl RepositoryEditor {

/// Given a `tough::Repository` and the path to a valid root.json, create a
/// `RepositoryEditor`. This `RepositoryEditor` will include all of the targets
/// and bits of _extra metadata from the roles included. It will not, however,
/// include the versions or expirations and the user is expected to set them.
/// versions, expiration and _extra bits of data from the roles included.
pub async fn from_repo<P>(root_path: P, repo: Repository) -> Result<RepositoryEditor>
where
P: AsRef<Path>,
Expand Down Expand Up @@ -232,7 +229,7 @@ impl RepositoryEditor {
Ok(self)
}

/// Add an existing `Snapshot` to the repository. Only the `_extra` data
/// Add an existing `Snapshot` to the repository. Only the `_extra` and `expires` data
/// is preserved
pub fn snapshot(&mut self, snapshot: Snapshot) -> Result<&mut Self> {
ensure!(
Expand All @@ -243,11 +240,13 @@ impl RepositoryEditor {
}
);
self.snapshot_extra = Some(snapshot._extra);
self.snapshot_expires = Some(snapshot.expires);
self.snapshot_version = snapshot.version.checked_add(1);
Ok(self)
}

/// Add an existing `Timestamp` to the repository. Only the `_extra` data
/// is preserved
/// Add an existing `Timestamp` to the repository. Only the `_extra`, `version`, and `expires`
/// data is preserved
pub fn timestamp(&mut self, timestamp: Timestamp) -> Result<&mut Self> {
ensure!(
timestamp.spec_version == SPEC_VERSION,
Expand All @@ -257,6 +256,8 @@ impl RepositoryEditor {
}
);
self.timestamp_extra = Some(timestamp._extra);
self.timestamp_expires = Some(timestamp.expires);
self.timestamp_version = timestamp.version.checked_add(1);
Ok(self)
}

Expand Down
11 changes: 4 additions & 7 deletions tough/src/editor/targets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ const SPEC_VERSION: &str = "1.0.0";
/// expirations are discarded. It is good practice to update these whenever
/// a repo is changed.
///
/// A `Targets` from an existing repository can be loaded using the `from_repo()` method.
/// `Targets` loaded this way will have the versions and expirations removed, but the
/// A `Targets` from an existing repository can be loaded using the `from_repo()` method.The
/// proper keyholder to sign the targets and the `Transport` used to load the repo will be saved.
///
/// Targets, versions, and expirations may be added to their respective roles
Expand Down Expand Up @@ -103,15 +102,14 @@ impl TargetsEditor {
}

/// Creates a `TargetsEditor` with the provided targets and keyholder
/// `version` and `expires` are thrown out to encourage updating the version and expiration
pub fn from_targets(name: &str, targets: Targets, key_holder: KeyHolder) -> Self {
TargetsEditor {
key_holder: Some(key_holder),
delegations: targets.delegations,
new_targets: None,
existing_targets: Some(targets.targets),
version: None,
expires: None,
version: targets.version.checked_add(1),
expires: Some(targets.expires),
name: name.to_string(),
new_roles: None,
_extra: Some(targets._extra),
Expand All @@ -120,8 +118,7 @@ impl TargetsEditor {
}
}

/// Creates a `TargetsEditor` with the provided targets from an already loaded repo
/// `version` and `expires` are thrown out to encourage updating the version and expiration
/// Creates a `TargetsEditor` with the provided targets from an already loaded repo.
/// If a `Repository` has been loaded, use `from_repo()` to preserve the `Transport` and `Limits`.
pub fn from_repo(repo: Repository, name: &str) -> Result<Self> {
let (targets, key_holder) = if name == "targets" {
Expand Down
6 changes: 0 additions & 6 deletions tough/src/editor/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,5 @@ mod tests {
.unwrap()
.timestamp(timestamp.signed)
.unwrap();

assert!(editor.snapshot_version.is_none());
assert!(editor.timestamp_version.is_none());

assert!(editor.snapshot_expires.is_none());
assert!(editor.timestamp_expires.is_none());
}
}
19 changes: 3 additions & 16 deletions tuftool/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,17 +102,16 @@ ls "${WRK}/tuf-repo/targets"
# Change one of the target files
echo "1.1" > "${WRK}/input/1.txt"

# update tuf repo!
# update tuf repo! Version will be automatically calculated, and expiry flags are optional.
# If no expires flag is passed, existing values will be passed on to the new updated version.
# A user may forcefully change the versions by supplying the --force-version flag alongside the <metadata>-version flag.
tuftool update \
--root "${ROOT}" \
--key "${WRK}/keys/root.pem" \
--add-targets "${WRK}/input" \
--targets-expires 'in 3 weeks' \
--targets-version 2 \
--snapshot-expires 'in 3 weeks' \
--snapshot-version 2 \
--timestamp-expires 'in 1 week' \
--timestamp-version 2 \
--outdir "${WRK}/tuf-repo" \
--metadata-url file:///$WRK/tuf-repo

Expand All @@ -124,12 +123,6 @@ tuftool rhtas \
--key "${WRK}/keys/root.pem" \
--set-ctlog-target "${WRK}/input/ctfe.pub" \
--ctlog-uri "https://ctfe.sigstore.dev" \
--targets-expires 'in 3 weeks' \
--targets-version 3 \
--snapshot-expires 'in 3 weeks' \
--snapshot-version 3 \
--timestamp-expires 'in 1 week' \
--timestamp-version 3 \
--outdir "${WRK}/tuf-repo" \
--metadata-url file:///$WRK/tuf-repo/

Expand All @@ -138,12 +131,6 @@ tuftool rhtas \
--root "${ROOT}" \
--key "${WRK}/keys/root.pem" \
--delete-target "ctfe.pub" \
--targets-expires 'in 3 weeks' \
--targets-version 4 \
--snapshot-expires 'in 3 weeks' \
--snapshot-version 4 \
--timestamp-expires 'in 1 week' \
--timestamp-version 4 \
--outdir "${WRK}/tuf-repo" \
--metadata-url file:///$WRK/tuf-repo/
```
Expand Down
65 changes: 43 additions & 22 deletions tuftool/src/rhtas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,7 @@ pub(crate) struct RhtasArgs {
/// Expiration of snapshot.json file; can be in full RFC 3339 format, or something like 'in
/// 7 days'
#[arg(long, value_parser = parse_datetime)]
snapshot_expires: DateTime<Utc>,

/// Version of snapshot.json file
#[arg(long)]
snapshot_version: NonZeroU64,
snapshot_expires: Option<DateTime<Utc>>,

/// Behavior when a target exists with the same name and hash in the targets directory,
/// for example from another repository when they share a targets directory.
Expand Down Expand Up @@ -121,20 +117,27 @@ pub(crate) struct RhtasArgs {
/// Expiration of targets.json file; can be in full RFC 3339 format, or something like 'in
/// 7 days'
#[arg(long, value_parser = parse_datetime)]
targets_expires: DateTime<Utc>,

/// Version of targets.json file
#[arg(long)]
targets_version: NonZeroU64,
targets_expires: Option<DateTime<Utc>>,

/// Expiration of timestamp.json file; can be in full RFC 3339 format, or something like 'in
/// 7 days'
#[arg(long, value_parser = parse_datetime)]
timestamp_expires: DateTime<Utc>,
timestamp_expires: Option<DateTime<Utc>>,

#[arg(long)]
force_version: bool,

/// Version of snapshot.json
#[arg(long)]
snapshot_version: Option<NonZeroU64>,

/// Version of targets.json
#[arg(long)]
targets_version: Option<NonZeroU64>,

/// Version of timestamp.json file
/// Version of timestamp.json
#[arg(long)]
timestamp_version: NonZeroU64,
timestamp_version: Option<NonZeroU64>,
}

fn expired_repo_warning<P: AsRef<Path>>(path: P) {
Expand Down Expand Up @@ -181,16 +184,34 @@ impl RhtasArgs {
keys.push(key_source);
}

editor
.targets_version(self.targets_version)
.context(error::DelegationStructureSnafu)?
.targets_expires(self.targets_expires)
.context(error::DelegationStructureSnafu)?
.snapshot_version(self.snapshot_version)
.snapshot_expires(self.snapshot_expires)
.timestamp_version(self.timestamp_version)
.timestamp_expires(self.timestamp_expires);
if self.force_version {
if self.snapshot_version.is_some() {
let _ = editor.snapshot_version(self.snapshot_version.unwrap());
}
if self.targets_version.is_some() {
let _ = editor.targets_version(self.targets_version.unwrap());
}
if self.timestamp_version.is_some() {
let _ = editor.timestamp_version(self.timestamp_version.unwrap());
}
} else if self.snapshot_version.is_some()
|| self.targets_version.is_some()
|| self.timestamp_version.is_some()
{
println!("Missing force-version flag to change metadata version forcefully.");
};

if let Some(_expires) = self.targets_expires {
let _ = editor.targets_expires(self.targets_expires.unwrap());
}

if let Some(_expires) = self.snapshot_expires {
let _ = editor.snapshot_expires(self.snapshot_expires.unwrap());
}

if let Some(_expires) = self.timestamp_expires {
let _ = editor.timestamp_expires(self.timestamp_expires.unwrap());
};
// If the "remove-target" argument was passed, remove the target
// from the repository.
for target_name in &self.delete_targets {
Expand Down
66 changes: 44 additions & 22 deletions tuftool/src/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,7 @@ pub(crate) struct UpdateArgs {
/// Expiration of snapshot.json file; can be in full RFC 3339 format, or something like 'in
/// 7 days'
#[arg(long, value_parser = parse_datetime)]
snapshot_expires: DateTime<Utc>,

/// Version of snapshot.json file
#[arg(long)]
snapshot_version: NonZeroU64,
snapshot_expires: Option<DateTime<Utc>>,

/// Directory of targets
#[arg(short, long = "add-targets")]
Expand All @@ -81,20 +77,27 @@ pub(crate) struct UpdateArgs {
/// Expiration of targets.json file; can be in full RFC 3339 format, or something like 'in
/// 7 days'
#[arg(long, value_parser = parse_datetime)]
targets_expires: DateTime<Utc>,

/// Version of targets.json file
#[arg(long)]
targets_version: NonZeroU64,
targets_expires: Option<DateTime<Utc>>,

/// Expiration of timestamp.json file; can be in full RFC 3339 format, or something like 'in
/// 7 days'
#[arg(long, value_parser = parse_datetime)]
timestamp_expires: DateTime<Utc>,
timestamp_expires: Option<DateTime<Utc>>,

#[arg(long)]
force_version: bool,

/// Version of snapshot.json
#[arg(long)]
snapshot_version: Option<NonZeroU64>,

/// Version of targets.json
#[arg(long)]
targets_version: Option<NonZeroU64>,

/// Version of timestamp.json file
/// Version of timestamp.json
#[arg(long)]
timestamp_version: NonZeroU64,
timestamp_version: Option<NonZeroU64>,
}

fn expired_repo_warning<P: AsRef<Path>>(path: P) {
Expand Down Expand Up @@ -141,15 +144,34 @@ impl UpdateArgs {
keys.push(key_source);
}

editor
.targets_version(self.targets_version)
.context(error::DelegationStructureSnafu)?
.targets_expires(self.targets_expires)
.context(error::DelegationStructureSnafu)?
.snapshot_version(self.snapshot_version)
.snapshot_expires(self.snapshot_expires)
.timestamp_version(self.timestamp_version)
.timestamp_expires(self.timestamp_expires);
if self.force_version {
if self.snapshot_version.is_some() {
let _ = editor.snapshot_version(self.snapshot_version.unwrap());
}
if self.targets_version.is_some() {
let _ = editor.targets_version(self.targets_version.unwrap());
}
if self.timestamp_version.is_some() {
let _ = editor.timestamp_version(self.timestamp_version.unwrap());
}
} else if self.snapshot_version.is_some()
|| self.targets_version.is_some()
|| self.timestamp_version.is_some()
{
println!("Missing force-version flag to change metadata version forcefully.");
};

if let Some(_expires) = self.targets_expires {
let _ = editor.targets_expires(self.targets_expires.unwrap());
}

if let Some(_expires) = self.snapshot_expires {
let _ = editor.snapshot_expires(self.snapshot_expires.unwrap());
}

if let Some(_expires) = self.timestamp_expires {
let _ = editor.timestamp_expires(self.timestamp_expires.unwrap());
};

// If the "add-targets" argument was passed, build a list of targets
// and add them to the repository. If a user specifies job count we
Expand Down
Loading

0 comments on commit a6af040

Please sign in to comment.