feat: add authorized Plugin Ticket data capabilities
This commit is contained in:
@@ -91,12 +91,25 @@ pub enum PluginSurface {
|
||||
Tool,
|
||||
}
|
||||
|
||||
/// Only capabilities implemented by the initial Tool Host are representable.
|
||||
/// Exact grant identities; new operations are never implied by old pins.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
|
||||
pub enum PluginGrant {
|
||||
Logging,
|
||||
Https { slot: String },
|
||||
Https {
|
||||
slot: String,
|
||||
},
|
||||
TicketRead,
|
||||
TicketDataRead {
|
||||
collection: String,
|
||||
schema_id: String,
|
||||
schema_version: String,
|
||||
},
|
||||
TicketDataWrite {
|
||||
collection: String,
|
||||
schema_id: String,
|
||||
schema_version: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
@@ -164,6 +177,7 @@ impl WorkerPluginPin {
|
||||
| plugin::PLUGIN_COMPONENT_HTTPS_TOOL_WORLD
|
||||
| plugin::PLUGIN_COMPONENT_LOGGED_TOOL_WORLD
|
||||
| plugin::PLUGIN_COMPONENT_HTTPS_LOGGED_TOOL_WORLD
|
||||
| plugin::PLUGIN_COMPONENT_TICKET_TOOL_WORLD
|
||||
)
|
||||
|| !valid_digest(&artifact.config_schema.digest)
|
||||
|| !valid_identity(&artifact.package_id)
|
||||
@@ -201,6 +215,27 @@ impl WorkerPluginPin {
|
||||
.any(|slot| *slot == binding.slot)
|
||||
})
|
||||
|| projection.grants.iter().any(|grant| match grant {
|
||||
PluginGrant::TicketRead
|
||||
| PluginGrant::TicketDataRead { .. }
|
||||
| PluginGrant::TicketDataWrite { .. } => {
|
||||
artifact.world != plugin::PLUGIN_COMPONENT_TICKET_TOOL_WORLD
|
||||
|| !artifact.requested_grants.contains(grant)
|
||||
|| match grant {
|
||||
PluginGrant::TicketDataRead {
|
||||
collection,
|
||||
schema_id,
|
||||
schema_version,
|
||||
}
|
||||
| PluginGrant::TicketDataWrite {
|
||||
collection,
|
||||
schema_id,
|
||||
schema_version,
|
||||
} => [collection, schema_id, schema_version]
|
||||
.iter()
|
||||
.any(|v| !valid_identity(v) || v.len() > 128),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
PluginGrant::Logging => !artifact.requested_grants.contains(&PluginGrant::Logging),
|
||||
PluginGrant::Https { slot } => {
|
||||
!valid_identity(slot)
|
||||
|
||||
@@ -202,3 +202,39 @@ fn deserialized_unknown_fields_versions_and_noncanonical_order_are_rejected() {
|
||||
unsorted.input.plugins.push(second);
|
||||
assert_eq!(unsorted.validate_canonical(), Err(PlanError::Invalid));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ticket_grants_require_exact_world_request_and_schema_namespace() {
|
||||
let mut a = artifact();
|
||||
let write = PluginGrant::TicketDataWrite {
|
||||
collection: "notes".into(),
|
||||
schema_id: "note".into(),
|
||||
schema_version: "1".into(),
|
||||
};
|
||||
let read = PluginGrant::TicketDataRead {
|
||||
collection: "notes".into(),
|
||||
schema_id: "note".into(),
|
||||
schema_version: "1".into(),
|
||||
};
|
||||
a.requested_grants
|
||||
.extend([PluginGrant::TicketRead, write.clone()]);
|
||||
let mut selected = projection();
|
||||
selected.grants.push(write.clone());
|
||||
// Even a fabricated metadata request cannot add Ticket operations to v2.0.
|
||||
assert_eq!(pin(&a, selected.clone()), Err(PlanError::Invalid));
|
||||
a.world = plugin::PLUGIN_COMPONENT_TICKET_TOOL_WORLD.into();
|
||||
let exact = pin(&a, selected.clone()).unwrap();
|
||||
assert!(exact.projection().grants.contains(&write));
|
||||
assert!(!exact.projection().grants.contains(&read)); // write is not read
|
||||
selected.grants.push(read);
|
||||
assert_eq!(pin(&a, selected), Err(PlanError::Invalid));
|
||||
let mut selected = projection();
|
||||
selected.grants.push(PluginGrant::TicketDataWrite {
|
||||
collection: "notes".into(),
|
||||
schema_id: "note".into(),
|
||||
schema_version: "2".into(),
|
||||
});
|
||||
assert_eq!(pin(&a, selected), Err(PlanError::Invalid));
|
||||
a.requested_grants.retain(|g| g != &write);
|
||||
assert_eq!(exact.matches_artifact(&a), Err(PlanError::Invalid));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user