Skip to content

Commit 92a351f

Browse files
committed
Gate BMF payload versions per project
Each project now declares the highest BMF payload version it accepts. The field is `bmf_version` on the project, it defaults to 0 everywhere, and it is visible to everyone that can see the project. Only a server admin can move it: a PATCH that carries the field from anyone else is forbidden, and a PATCH that does not carry it is the patch it has always been. The gate is a plain setting, so an admin can lower it again just as easily. The gate is a maximum, not an exact match. A project at version 1 still ingests a payload that declares version 0 and a payload that declares nothing at all, so raising the gate refuses nothing that ingested before it moved. Ingest checks the gate twice, once for each way a payload states its version, and both refusals are the same class and name both versions, the payload's and the project's. The `bmf_version` key a payload declares is checked before anything is created for the report, because later layers hang payload shapes off the declared version and none of them should reach a project that does not accept them. The version the results actually parsed as is checked after parsing, because a payload can reach a v1 leaf without declaring anything: the `json_v1` adapter names the leaf outright, and the `magic` and `json` nodes fall back to it. Without the second check the gate would be decorative. That second check is a deliberate behavior change for those two undeclared paths on a project still at version 0. They are not a documented way to send v1 results, and a gate a payload can walk around is not a gate. Report ingest, `/v0/run`, and a job based run share one check rather than three copies of it. A job declares nothing, since its results are the runner's own output rather than a submitted payload, but the project gate still applies to what those results parse as.
1 parent 478de4f commit 92a351f

28 files changed

Lines changed: 1027 additions & 64 deletions

File tree

lib/api_projects/src/projects.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use bencher_endpoint::{
33
};
44
use bencher_json::{
55
JsonDirection, JsonPagination, JsonProject, JsonProjects, ProjectResourceId, ResourceName,
6-
Search,
6+
Sanitize as _, Search,
77
project::{JsonUpdateProject, Visibility},
88
};
99
use bencher_rbac::project::Permission;
@@ -277,6 +277,7 @@ pub async fn get_one_inner(
277277
///
278278
/// Update a project.
279279
/// The user must have `edit` permissions for the project.
280+
/// Setting the `bmf_version` field requires a server admin.
280281
#[endpoint {
281282
method = PATCH,
282283
path = "/v0/projects/{project}",
@@ -321,6 +322,16 @@ async fn patch_inner(
321322
Permission::Edit,
322323
)?;
323324

325+
// Only a server admin can move the project's BMF payload version gate.
326+
// Every other field of the patch is unaffected by this check.
327+
if json_project.bmf_version().is_some() && !auth_user.is_admin(&context.rbac) {
328+
let mut auth_user = auth_user.clone();
329+
auth_user.sanitize();
330+
return Err(forbidden_error(format!(
331+
"Only admins can update the `bmf_version` field for a project. User is not an admin: {auth_user:?}",
332+
)));
333+
}
334+
324335
// Check project visibility
325336
if let Some(visibility) = json_project.visibility() {
326337
#[cfg(not(feature = "plus"))]

0 commit comments

Comments
 (0)