Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 53 additions & 2 deletions core/crates/ployz-core/src/authority/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,26 @@ impl PayloadDigest {
.mount_graph
.scope_to_namespace(namespace)
.map_err(|error| CanonicalError::Parse(error.to_string()))?;
let mut value = serde_json::to_value(&spec)
.map_err(|error| CanonicalError::Parse(error.to_string()))?;
Self::of_scoped(&spec, |_| {})
}

/// The digest of `spec` with its Volumes already scoped, after `edit` changed its JSON
/// form, such as a preview template's placeholders.
///
/// # Errors
/// Returns a [`CanonicalError`] when the spec has no canonical form.
pub(super) fn of_scoped(
spec: &RequestedServiceSpec,
edit: impl FnOnce(&mut Value),
) -> Result<Self, CanonicalError> {
let mut value =
serde_json::to_value(spec).map_err(|error| CanonicalError::Parse(error.to_string()))?;
if let Value::Object(fields) = &mut value {
fields.remove("mode");
fields.remove("update");
}
drop_nulls(&mut value);
edit(&mut value);
let body = canonical::to_canonical(&value)?;
Ok(Self(Sha256::digest(body.as_bytes()).into()))
}
Expand Down Expand Up @@ -544,13 +557,51 @@ pub fn verify(
})?;
}
super::validity::verify(body.issued_at, body.expires_at, facts.now)?;
if let Some(creation) = facts.creation {
refuse_preview_parts(facts.namespace, creation.spec)?;
}
let digest = facts
.creation
.map(|creation| creation_digest(&body, creation))
.transpose()?;
Ok(DeployApproved { body, by, digest })
}

/// Refuse a creation, outside a preview rule, in a reserved preview Namespace or carrying a
/// parent's value a preview inherits: only a preview rule may admit either, so preview code
/// shares a Namespace with nothing else and an inherited value opens nowhere else.
///
/// # Errors
/// Returns [`AuthorityRefusal::Mismatch`] naming the Namespace or the Sealed value.
pub fn refuse_preview_parts(
namespace: &Namespace,
spec: &ResolvedServiceSpec,
) -> Result<(), AuthorityRefusal> {
if super::preview::is_preview_namespace(namespace) {
return Err(AuthorityRefusal::Mismatch {
field: "a preview Namespace",
});
}
if sealed_values(spec).any(|value| value.environment.is_some()) {
return Err(AuthorityRefusal::Mismatch {
field: super::preview::SEALED_VALUE,
});
}
Ok(())
}

fn sealed_values(spec: &ResolvedServiceSpec) -> impl Iterator<Item = &crate::SealedValue> {
let hook = spec
.pre_deploy
.iter()
.flat_map(|hook| hook.environment.values());
spec.container
.environment
.values()
.chain(hook)
.flat_map(EnvValue::sealed_parts)
}

/// Whether `by` may approve a Container that opens the Sealed values `spec` carries, in its
/// environment or its hook's. A Device opens any. A browser key under a session grant
/// opens only values sealed for its grant's Environment, so a dashboard holding a session
Expand Down
11 changes: 8 additions & 3 deletions core/crates/ployz-core/src/authority/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub mod canonical;
pub mod deploy;
pub mod join;
pub mod keys;
pub mod preview;
pub mod proposal;
pub mod refusal;
pub mod restart;
Expand All @@ -29,13 +30,17 @@ pub use deploy::{
};
pub use join::JoinApproval;
pub use keys::{Approver, KeySignature, Purpose, Signed, Signer, SigningPublicKey, Statement};
pub use preview::{
CheckedReview, PreviewCreation, PreviewFacts, PreviewKey, PreviewProof, PreviewReview,
PreviewRule, PreviewService, Review, ReviewSigner, ReviewedService,
};
pub use proposal::{ProposalApproval, ProposalRef, ProposalStep, StepDigest, StepKind};
pub use refusal::AuthorityRefusal;
pub use restart::{Approved, RestartApproval, RestartFacts, RestartStep, SignerKey, WorkflowKind};
pub use rule::{
AttestKey, AutoDeployRule, BuildClaim, BuildPolicy, BuildStage, CommitAuthority, CommitSha,
GithubClaims, ImageDigest, PolicyDigest, RecipientKey, RuleFacts, RuleId, RuleProof,
RuleService, RuleSource, SealedDigest,
AttestKey, AutoDeployRule, BuildClaim, BuildPolicy, BuildStage, ClaimRule, CommitAuthority,
CommitSha, GithubClaims, ImageDigest, PolicyDigest, RecipientKey, ReviewDigest, RuleFacts,
RuleId, RuleIdentity, RuleProof, RuleService, RuleSource, SealedDigest,
};
pub use session::{ActionFamily, GrantBody, SessionGrant};
pub use starting_set::{Member, StartingSet};
Expand Down
Loading