Skip to content
Merged
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
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion policies/dotnet/Devolutions.Now.Policy.Api/Enums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@ public enum ErrorCode
UnsupportedEndpoint,
MalformedDraft,
InvalidPolicy,
WarningConfirmationRequired,
Unauthenticated,
AdministratorRequired,
UnsafePolicyPath,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -359,10 +359,6 @@ public string RequestKind
[JsonRequired]
public PolicyConflictHandling ConflictHandling { get; set; }

[JsonPropertyName("WarningsAcknowledged")]
[JsonRequired]
public bool WarningsAcknowledged { get; set; }

/// <summary>Raw draft JSON retained for transaction-time reparsing and revalidation.</summary>
[JsonPropertyName("Draft")]
[JsonRequired]
Expand Down
3 changes: 3 additions & 0 deletions policies/dotnet/Devolutions.Now.Policy.Api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ Opaque policy store tokens and validation receipts are restricted to safe printa
HTTP body of policy validation and replacement requests. It is separate from the package-operation
limit advertised by broker capabilities.

Policy replacement retains the validation receipt, expected store token, operation, and explicit overwrite-conflict handling.
Validation warnings are advisory findings for inline client UX and do not require a wire-level acknowledgement to save a valid draft.

Because policy documents are JSON-only, configured `.yaml`, `.yml`, extensionless, and other
non-JSON paths use `PolicyReadOnlyReason.UnsupportedFormat` in management snapshots and
`ErrorCode.UnsupportedPolicyFormat` for structured HTTP 422 errors.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,17 @@ public async Task ReplacePolicy_sends_every_operation_intent(
Assert.Equal("store:active:8", response.Management.StoreToken);
}

[Fact]
public async Task PolicyReplacement_rejects_legacy_warning_acknowledgement()
{
var request = JsonNode.Parse(await ReadFixture("requests", "policy-replacement.update.request.json"))!;
request["WarningsAcknowledged"] = true;

var json = request.ToJsonString();
Assert.Throws<JsonException>(() => BrokerSerializer.DeserializeStrict<PolicyReplacementRequest>(json));
Assert.NotEmpty((await TestData.SchemaAsync("PolicyReplacementRequest")).Validate(json));
}

[Fact]
public async Task ReplacePolicy_preserves_structured_stale_token_findings()
{
Expand Down
2 changes: 1 addition & 1 deletion policies/rust/now-policy-api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "now-policy-api"
version = "0.6.0"
version = "0.7.0"
edition = "2024"
license.workspace = true
homepage.workspace = true
Expand Down
3 changes: 3 additions & 0 deletions policies/rust/now-policy-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ The generated document contains the unchanged policy inspection route, the manag

Opaque store tokens and validation receipts use safe printable ASCII (`A-Z`, `a-z`, `0-9`, `.`, `_`, `~`, `:`, `-`) and begin with an ASCII alphanumeric character. This keeps length and validation behavior identical across Rust UTF-8 and .NET UTF-16 implementations.

Replacement requests retain their validation receipt, expected store token, operation, and explicit overwrite-conflict handling.
Validation warnings are advisory findings that clients present inline and never require a protocol-level acknowledgement to save a valid draft.

Validation
----------

Expand Down
5 changes: 0 additions & 5 deletions policies/rust/now-policy-api/openapi/now-policy-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,6 @@ components:
- UnsupportedEndpoint
- MalformedDraft
- InvalidPolicy
- WarningConfirmationRequired
- Unauthenticated
- AdministratorRequired
- UnsafePolicyPath
Expand Down Expand Up @@ -1419,17 +1418,13 @@ components:
$ref: '#/components/schemas/ApiVersion'
ValidationReceipt:
$ref: '#/components/schemas/PolicyValidationReceipt'
WarningsAcknowledged:
description: Explicit acknowledgement of every warning bound into the validation receipt.
type: boolean
additionalProperties: false
required:
- RequestKind
- RequestVersion
- ExpectedStoreToken
- Operation
- ConflictHandling
- WarningsAcknowledged
- Draft
- ValidationReceipt
PolicyReplacementRequestKind:
Expand Down
1 change: 0 additions & 1 deletion policies/rust/now-policy-api/src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ pub enum ErrorCode {
UnsupportedEndpoint,
MalformedDraft,
InvalidPolicy,
WarningConfirmationRequired,
Unauthenticated,
AdministratorRequired,
UnsafePolicyPath,
Expand Down
3 changes: 0 additions & 3 deletions policies/rust/now-policy-api/src/management.rs
Original file line number Diff line number Diff line change
Expand Up @@ -698,9 +698,6 @@ pub struct PolicyReplacementRequest {
pub operation: PolicyReplacementOperation,
pub conflict_handling: PolicyConflictHandling,

/// Explicit acknowledgement of every warning bound into the validation receipt.
pub warnings_acknowledged: bool,

/// Raw draft JSON retained for transaction-time reparsing and revalidation.
pub draft: serde_json::Value,

Expand Down
4 changes: 2 additions & 2 deletions policies/rust/now-policy-server-template/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "now-policy-server-template"
version = "0.6.0"
version = "0.7.0"
edition = "2024"
license.workspace = true
homepage.workspace = true
Expand All @@ -17,7 +17,7 @@ workspace = true
aide = { version = "0.15", features = ["axum", "axum-json"] }
async-trait = "0.1"
axum = { version = "0.8", default-features = false, features = ["json"] }
now-policy-api = { version = "0.6", path = "../now-policy-api" }
now-policy-api = { version = "0.7", path = "../now-policy-api" }
now-policy = { version = "0.5", path = "../now-policy" }
schemars = "0.9"
serde = { version = "1", features = ["derive"] }
Expand Down
5 changes: 1 addition & 4 deletions policies/rust/now-policy-server-template/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,10 +406,7 @@ fn error_status(code: ErrorCode) -> StatusCode {
ErrorCode::Unauthorized | ErrorCode::Unauthenticated => StatusCode::UNAUTHORIZED,
ErrorCode::Forbidden | ErrorCode::AdministratorRequired => StatusCode::FORBIDDEN,
ErrorCode::NotFound => StatusCode::NOT_FOUND,
ErrorCode::Conflict
| ErrorCode::WarningConfirmationRequired
| ErrorCode::UnsafePolicyPath
| ErrorCode::StalePolicyStoreToken => StatusCode::CONFLICT,
ErrorCode::Conflict | ErrorCode::UnsafePolicyPath | ErrorCode::StalePolicyStoreToken => StatusCode::CONFLICT,
ErrorCode::PayloadTooLarge => StatusCode::PAYLOAD_TOO_LARGE,
ErrorCode::UnsupportedMediaType => StatusCode::UNSUPPORTED_MEDIA_TYPE,
ErrorCode::ValidationFailed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@ fn all_sample_requests_deserialize() {
}
}

#[test]
fn policy_replacement_rejects_legacy_warning_acknowledgement() {
let mut request = load_json_file(&samples_dir().join("requests/policy-replacement.update.request.json"));
request["WarningsAcknowledged"] = true.into();

assert!(serde_json::from_value::<PolicyReplacementRequest>(request).is_err());
}

#[test]
fn all_sample_responses_deserialize() {
for path in json_files(&samples_dir().join("responses")) {
Expand Down Expand Up @@ -587,7 +595,6 @@ async fn policy_management_error_codes_use_stable_http_statuses() {
(ErrorCode::UnsupportedEndpoint, StatusCode::NOT_IMPLEMENTED),
(ErrorCode::MalformedDraft, StatusCode::BAD_REQUEST),
(ErrorCode::InvalidPolicy, StatusCode::UNPROCESSABLE_ENTITY),
(ErrorCode::WarningConfirmationRequired, StatusCode::CONFLICT),
(ErrorCode::Unauthenticated, StatusCode::UNAUTHORIZED),
(ErrorCode::AdministratorRequired, StatusCode::FORBIDDEN),
(ErrorCode::UnsafePolicyPath, StatusCode::CONFLICT),
Expand Down Expand Up @@ -745,7 +752,7 @@ async fn policy_management_routes_accept_exact_limit_and_reject_one_byte_over()
(
"PUT",
"/v1/policy",
r#"{"RequestKind":"PolicyReplacementRequest","RequestVersion":"1.0","ExpectedStoreToken":"store:active:7","Operation":"Update","ConflictHandling":"Reject","WarningsAcknowledged":true,"Draft":{"Padding":""#,
r#"{"RequestKind":"PolicyReplacementRequest","RequestVersion":"1.0","ExpectedStoreToken":"store:active:7","Operation":"Update","ConflictHandling":"Reject","Draft":{"Padding":""#,
r#""},"ValidationReceipt":"receipt:sha256:test"}"#,
),
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"ExpectedStoreToken": "store:missing:0",
"Operation": "Create",
"ConflictHandling": "Reject",
"WarningsAcknowledged": false,
"Draft": {
"PolicyFormatVersion": "1.0.0",
"Metadata": { "Id": "contoso.package-policy", "Publisher": "Contoso IT" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"ExpectedStoreToken": "store:active:newly-observed-8",
"Operation": "Update",
"ConflictHandling": "ConfirmOverwrite",
"WarningsAcknowledged": true,
"Draft": {
"PolicyFormatVersion": "1.0.0",
"Metadata": { "Id": "contoso.package-policy", "Publisher": "Contoso IT" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"ExpectedStoreToken": "store:invalid:4",
"Operation": "Repair",
"ConflictHandling": "Reject",
"WarningsAcknowledged": false,
"Draft": {
"PolicyFormatVersion": "1.0.0",
"Metadata": { "Id": "contoso.package-policy", "Publisher": "Contoso IT" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"ExpectedStoreToken": "store:active:7",
"Operation": "ReplaceIdentity",
"ConflictHandling": "Reject",
"WarningsAcknowledged": false,
"Draft": {
"PolicyFormatVersion": "1.0.0",
"Metadata": { "Id": "fabrikam.package-policy", "Publisher": "Fabrikam IT" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"ExpectedStoreToken": "store:active:7",
"Operation": "Update",
"ConflictHandling": "Reject",
"WarningsAcknowledged": true,
"Draft": {
"PolicyFormatVersion": "1.0.0",
"Metadata": { "Id": "contoso.package-policy", "Publisher": "Contoso IT" },
Expand Down
Loading