merge: clarify feature host authorities
This commit is contained in:
commit
b46ea65fdd
|
|
@ -1,7 +1,7 @@
|
||||||
//! Feature contribution registry for Pod-hosted builtin/plugin modules.
|
//! Feature contribution registry for Pod-hosted builtin/plugin modules.
|
||||||
//!
|
//!
|
||||||
//! This module defines the Pod-side feature boundary used to collect
|
//! This module defines the Pod-side feature boundary used to collect
|
||||||
//! descriptor metadata, authority requests, tool contributions, safe hook
|
//! descriptor metadata, host authority requests, tool contributions, safe hook
|
||||||
//! contributions, background task declarations, and service declarations before
|
//! contributions, background task declarations, and service declarations before
|
||||||
//! installing them into the existing Worker/HookRegistry host surfaces.
|
//! installing them into the existing Worker/HookRegistry host surfaces.
|
||||||
//!
|
//!
|
||||||
|
|
@ -74,8 +74,8 @@ pub enum FeatureRuntimeKind {
|
||||||
///
|
///
|
||||||
/// Contribution declarations such as tools, hooks, background tasks, and
|
/// Contribution declarations such as tools, hooks, background tasks, and
|
||||||
/// services are descriptor/package-approved host-visible contributions, not
|
/// services are descriptor/package-approved host-visible contributions, not
|
||||||
/// sandbox authorities. Grants are additive and do not replace manifest/tool
|
/// host authorities. Host authority grants are additive and do not replace
|
||||||
/// permission checks.
|
/// manifest/tool permission checks.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum HostAuthority {
|
pub enum HostAuthority {
|
||||||
|
|
@ -98,15 +98,15 @@ pub enum FeatureHookPoint {
|
||||||
TurnEnd,
|
TurnEnd,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Authority request declared by a feature descriptor.
|
/// Host authority request declared by a feature descriptor.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct AuthorityRequest {
|
pub struct HostAuthorityRequest {
|
||||||
pub authority: HostAuthority,
|
pub authority: HostAuthority,
|
||||||
pub required: bool,
|
pub required: bool,
|
||||||
pub reason: String,
|
pub reason: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AuthorityRequest {
|
impl HostAuthorityRequest {
|
||||||
pub fn required(authority: HostAuthority, reason: impl Into<String>) -> Self {
|
pub fn required(authority: HostAuthority, reason: impl Into<String>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
authority,
|
authority,
|
||||||
|
|
@ -124,15 +124,15 @@ impl AuthorityRequest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Authority grants resolved by the host for one feature installation.
|
/// Host authority grants resolved by the host for one feature installation.
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct AuthorityGrantSet {
|
pub struct HostAuthorityGrantSet {
|
||||||
granted: HashSet<HostAuthority>,
|
granted: HashSet<HostAuthority>,
|
||||||
denied: Vec<AuthorityDenial>,
|
denied: Vec<HostAuthorityDenial>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AuthorityGrantSet {
|
impl HostAuthorityGrantSet {
|
||||||
pub fn grant_all(requests: &[AuthorityRequest]) -> Self {
|
pub fn grant_all(requests: &[HostAuthorityRequest]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
granted: requests
|
granted: requests
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -150,7 +150,7 @@ impl AuthorityGrantSet {
|
||||||
self.granted.contains(authority)
|
self.granted.contains(authority)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn denied(&self) -> &[AuthorityDenial] {
|
pub fn denied(&self) -> &[HostAuthorityDenial] {
|
||||||
&self.denied
|
&self.denied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -160,16 +160,16 @@ impl AuthorityGrantSet {
|
||||||
|
|
||||||
pub fn deny(&mut self, authority: HostAuthority, reason: impl Into<String>) {
|
pub fn deny(&mut self, authority: HostAuthority, reason: impl Into<String>) {
|
||||||
self.granted.remove(&authority);
|
self.granted.remove(&authority);
|
||||||
self.denied.push(AuthorityDenial {
|
self.denied.push(HostAuthorityDenial {
|
||||||
authority,
|
authority,
|
||||||
reason: reason.into(),
|
reason: reason.into(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Host-side denial of a requested feature authority.
|
/// Host-side denial of a requested feature host authority.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct AuthorityDenial {
|
pub struct HostAuthorityDenial {
|
||||||
pub authority: HostAuthority,
|
pub authority: HostAuthority,
|
||||||
pub reason: String,
|
pub reason: String,
|
||||||
}
|
}
|
||||||
|
|
@ -191,11 +191,12 @@ impl ToolDeclaration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executable tool contribution wrapper.
|
/// Executable tool contribution wrapper. Host-authority requirements are optional
|
||||||
|
/// per-tool gates for privileged host APIs, not permission to contribute a tool.
|
||||||
pub struct ToolContribution {
|
pub struct ToolContribution {
|
||||||
name: String,
|
name: String,
|
||||||
definition: ToolDefinition,
|
definition: ToolDefinition,
|
||||||
required_authorities: Vec<HostAuthority>,
|
required_host_authorities: Vec<HostAuthority>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToolContribution {
|
impl ToolContribution {
|
||||||
|
|
@ -203,12 +204,15 @@ impl ToolContribution {
|
||||||
Self {
|
Self {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
definition,
|
definition,
|
||||||
required_authorities: Vec::new(),
|
required_host_authorities: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_required_authorities(mut self, required_authorities: Vec<HostAuthority>) -> Self {
|
pub fn with_required_host_authorities(
|
||||||
self.required_authorities = required_authorities;
|
mut self,
|
||||||
|
required_host_authorities: Vec<HostAuthority>,
|
||||||
|
) -> Self {
|
||||||
|
self.required_host_authorities = required_host_authorities;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -323,7 +327,7 @@ impl ServiceDeclaration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Feature service requirement used for host-mediated dependency resolution.
|
/// Feature service requirement used for contribution dependency resolution.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct ServiceRequirement {
|
pub struct ServiceRequirement {
|
||||||
pub id: ServiceId,
|
pub id: ServiceId,
|
||||||
|
|
@ -352,7 +356,7 @@ impl ServiceRequirement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Host-mediated service registry skeleton used during feature installation.
|
/// Contribution service registry skeleton used during feature installation.
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct FeatureServiceRegistry {
|
pub struct FeatureServiceRegistry {
|
||||||
providers: HashMap<ServiceId, FeatureServiceProvider>,
|
providers: HashMap<ServiceId, FeatureServiceProvider>,
|
||||||
|
|
@ -405,7 +409,7 @@ pub struct FeatureDescriptor {
|
||||||
pub display_name: String,
|
pub display_name: String,
|
||||||
pub version: String,
|
pub version: String,
|
||||||
pub description: String,
|
pub description: String,
|
||||||
pub requested_authorities: Vec<AuthorityRequest>,
|
pub requested_host_authorities: Vec<HostAuthorityRequest>,
|
||||||
pub tools: Vec<ToolDeclaration>,
|
pub tools: Vec<ToolDeclaration>,
|
||||||
pub hooks: Vec<HookDeclaration>,
|
pub hooks: Vec<HookDeclaration>,
|
||||||
pub background_tasks: Vec<BackgroundTaskDeclaration>,
|
pub background_tasks: Vec<BackgroundTaskDeclaration>,
|
||||||
|
|
@ -421,7 +425,7 @@ impl FeatureDescriptor {
|
||||||
display_name: display_name.into(),
|
display_name: display_name.into(),
|
||||||
version: env!("CARGO_PKG_VERSION").into(),
|
version: env!("CARGO_PKG_VERSION").into(),
|
||||||
description: String::new(),
|
description: String::new(),
|
||||||
requested_authorities: Vec::new(),
|
requested_host_authorities: Vec::new(),
|
||||||
tools: Vec::new(),
|
tools: Vec::new(),
|
||||||
hooks: Vec::new(),
|
hooks: Vec::new(),
|
||||||
background_tasks: Vec::new(),
|
background_tasks: Vec::new(),
|
||||||
|
|
@ -435,8 +439,8 @@ impl FeatureDescriptor {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_authority(mut self, request: AuthorityRequest) -> Self {
|
pub fn with_host_authority(mut self, request: HostAuthorityRequest) -> Self {
|
||||||
self.requested_authorities.push(request);
|
self.requested_host_authorities.push(request);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -538,7 +542,7 @@ pub struct FeatureInstallReport {
|
||||||
pub feature_id: FeatureId,
|
pub feature_id: FeatureId,
|
||||||
pub runtime: FeatureRuntimeKind,
|
pub runtime: FeatureRuntimeKind,
|
||||||
pub installed: bool,
|
pub installed: bool,
|
||||||
pub granted_authorities: AuthorityGrantSet,
|
pub host_authority_grants: HostAuthorityGrantSet,
|
||||||
pub installed_tools: Vec<String>,
|
pub installed_tools: Vec<String>,
|
||||||
pub installed_hooks: Vec<HookDeclaration>,
|
pub installed_hooks: Vec<HookDeclaration>,
|
||||||
pub declared_background_tasks: Vec<BackgroundTaskDeclaration>,
|
pub declared_background_tasks: Vec<BackgroundTaskDeclaration>,
|
||||||
|
|
@ -549,12 +553,12 @@ pub struct FeatureInstallReport {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FeatureInstallReport {
|
impl FeatureInstallReport {
|
||||||
fn new(descriptor: &FeatureDescriptor, granted_authorities: AuthorityGrantSet) -> Self {
|
fn new(descriptor: &FeatureDescriptor, host_authority_grants: HostAuthorityGrantSet) -> Self {
|
||||||
Self {
|
Self {
|
||||||
feature_id: descriptor.id.clone(),
|
feature_id: descriptor.id.clone(),
|
||||||
runtime: descriptor.runtime.clone(),
|
runtime: descriptor.runtime.clone(),
|
||||||
installed: false,
|
installed: false,
|
||||||
granted_authorities,
|
host_authority_grants,
|
||||||
installed_tools: Vec::new(),
|
installed_tools: Vec::new(),
|
||||||
installed_hooks: Vec::new(),
|
installed_hooks: Vec::new(),
|
||||||
declared_background_tasks: Vec::new(),
|
declared_background_tasks: Vec::new(),
|
||||||
|
|
@ -648,33 +652,33 @@ fn reject_undeclared_contribution(
|
||||||
error
|
error
|
||||||
}
|
}
|
||||||
|
|
||||||
fn require_authority(
|
fn require_host_authority(
|
||||||
grants: &AuthorityGrantSet,
|
host_authority_grants: &HostAuthorityGrantSet,
|
||||||
report: &mut FeatureInstallReport,
|
report: &mut FeatureInstallReport,
|
||||||
kind: FeatureContributionKind,
|
kind: FeatureContributionKind,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
authority: &HostAuthority,
|
authority: &HostAuthority,
|
||||||
) -> Result<(), FeatureInstallError> {
|
) -> Result<(), FeatureInstallError> {
|
||||||
if grants.contains(authority) {
|
if host_authority_grants.contains(authority) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let reason = format!("required authority was not granted: {authority:?}");
|
let reason = format!("required host authority was not granted: {authority:?}");
|
||||||
report.mark_skipped(kind, name, reason.clone());
|
report.mark_skipped(kind, name, reason.clone());
|
||||||
Err(FeatureInstallError::AuthorityDenied(reason))
|
Err(FeatureInstallError::HostAuthorityDenied(reason))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Model-visible durable notification sink skeleton. The first slice exposes
|
/// Model-visible durable notification sink skeleton. The first slice exposes
|
||||||
/// the boundary without implementing a new event channel.
|
/// the boundary without implementing a new event channel.
|
||||||
pub struct FeatureNotificationSink<'a> {
|
pub struct FeatureNotificationSink<'a> {
|
||||||
grants: &'a AuthorityGrantSet,
|
host_authority_grants: &'a HostAuthorityGrantSet,
|
||||||
report: &'a mut FeatureInstallReport,
|
report: &'a mut FeatureInstallReport,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FeatureNotificationSink<'_> {
|
impl FeatureNotificationSink<'_> {
|
||||||
pub fn notify_model(&mut self, message: impl Into<String>) -> Result<(), FeatureInstallError> {
|
pub fn notify_model(&mut self, message: impl Into<String>) -> Result<(), FeatureInstallError> {
|
||||||
require_authority(
|
require_host_authority(
|
||||||
self.grants,
|
self.host_authority_grants,
|
||||||
self.report,
|
self.report,
|
||||||
FeatureContributionKind::Notification,
|
FeatureContributionKind::Notification,
|
||||||
"notify_model",
|
"notify_model",
|
||||||
|
|
@ -739,7 +743,7 @@ impl FeatureDiagnosticSink<'_> {
|
||||||
pub struct ToolContributionRegistrar<'a> {
|
pub struct ToolContributionRegistrar<'a> {
|
||||||
feature_id: &'a FeatureId,
|
feature_id: &'a FeatureId,
|
||||||
declarations: &'a FeatureContributionDeclarations,
|
declarations: &'a FeatureContributionDeclarations,
|
||||||
grants: &'a AuthorityGrantSet,
|
host_authority_grants: &'a HostAuthorityGrantSet,
|
||||||
pending_tools: &'a mut Vec<ToolDefinition>,
|
pending_tools: &'a mut Vec<ToolDefinition>,
|
||||||
installed_tool_names: &'a mut HashMap<String, FeatureId>,
|
installed_tool_names: &'a mut HashMap<String, FeatureId>,
|
||||||
report: &'a mut FeatureInstallReport,
|
report: &'a mut FeatureInstallReport,
|
||||||
|
|
@ -771,9 +775,9 @@ impl ToolContributionRegistrar<'_> {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
for authority in &contribution.required_authorities {
|
for authority in &contribution.required_host_authorities {
|
||||||
require_authority(
|
require_host_authority(
|
||||||
self.grants,
|
self.host_authority_grants,
|
||||||
self.report,
|
self.report,
|
||||||
FeatureContributionKind::Tool,
|
FeatureContributionKind::Tool,
|
||||||
model_visible_name.clone(),
|
model_visible_name.clone(),
|
||||||
|
|
@ -946,7 +950,7 @@ impl FeatureServiceRegistrar<'_> {
|
||||||
pub struct FeatureInstallContext<'a> {
|
pub struct FeatureInstallContext<'a> {
|
||||||
feature_id: &'a FeatureId,
|
feature_id: &'a FeatureId,
|
||||||
declarations: &'a FeatureContributionDeclarations,
|
declarations: &'a FeatureContributionDeclarations,
|
||||||
grants: &'a AuthorityGrantSet,
|
host_authority_grants: &'a HostAuthorityGrantSet,
|
||||||
pending_tools: &'a mut Vec<ToolDefinition>,
|
pending_tools: &'a mut Vec<ToolDefinition>,
|
||||||
installed_tool_names: &'a mut HashMap<String, FeatureId>,
|
installed_tool_names: &'a mut HashMap<String, FeatureId>,
|
||||||
hook_builder: &'a mut HookRegistryBuilder,
|
hook_builder: &'a mut HookRegistryBuilder,
|
||||||
|
|
@ -959,15 +963,15 @@ impl FeatureInstallContext<'_> {
|
||||||
self.feature_id
|
self.feature_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn grants(&self) -> &AuthorityGrantSet {
|
pub fn host_authority_grants(&self) -> &HostAuthorityGrantSet {
|
||||||
self.grants
|
self.host_authority_grants
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tools(&mut self) -> ToolContributionRegistrar<'_> {
|
pub fn tools(&mut self) -> ToolContributionRegistrar<'_> {
|
||||||
ToolContributionRegistrar {
|
ToolContributionRegistrar {
|
||||||
feature_id: self.feature_id,
|
feature_id: self.feature_id,
|
||||||
declarations: self.declarations,
|
declarations: self.declarations,
|
||||||
grants: self.grants,
|
host_authority_grants: self.host_authority_grants,
|
||||||
pending_tools: self.pending_tools,
|
pending_tools: self.pending_tools,
|
||||||
installed_tool_names: self.installed_tool_names,
|
installed_tool_names: self.installed_tool_names,
|
||||||
report: self.report,
|
report: self.report,
|
||||||
|
|
@ -1002,7 +1006,7 @@ impl FeatureInstallContext<'_> {
|
||||||
|
|
||||||
pub fn notifications(&mut self) -> FeatureNotificationSink<'_> {
|
pub fn notifications(&mut self) -> FeatureNotificationSink<'_> {
|
||||||
FeatureNotificationSink {
|
FeatureNotificationSink {
|
||||||
grants: self.grants,
|
host_authority_grants: self.host_authority_grants,
|
||||||
report: self.report,
|
report: self.report,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1102,9 +1106,10 @@ impl FeatureRegistryBuilder {
|
||||||
let mut seen_features = HashSet::new();
|
let mut seen_features = HashSet::new();
|
||||||
|
|
||||||
for (module, descriptor) in self.modules.into_iter().zip(descriptors.into_iter()) {
|
for (module, descriptor) in self.modules.into_iter().zip(descriptors.into_iter()) {
|
||||||
let grants = AuthorityGrantSet::grant_all(&descriptor.requested_authorities);
|
let host_authority_grants =
|
||||||
|
HostAuthorityGrantSet::grant_all(&descriptor.requested_host_authorities);
|
||||||
let declarations = FeatureContributionDeclarations::from_descriptor(&descriptor);
|
let declarations = FeatureContributionDeclarations::from_descriptor(&descriptor);
|
||||||
let mut report = FeatureInstallReport::new(&descriptor, grants.clone());
|
let mut report = FeatureInstallReport::new(&descriptor, host_authority_grants.clone());
|
||||||
|
|
||||||
if !seen_features.insert(descriptor.id.clone()) {
|
if !seen_features.insert(descriptor.id.clone()) {
|
||||||
report.diagnostics.push(FeatureDiagnostic::error(format!(
|
report.diagnostics.push(FeatureDiagnostic::error(format!(
|
||||||
|
|
@ -1120,9 +1125,9 @@ impl FeatureRegistryBuilder {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for authority in grants.denied() {
|
for authority in host_authority_grants.denied() {
|
||||||
report.diagnostics.push(FeatureDiagnostic::warning(format!(
|
report.diagnostics.push(FeatureDiagnostic::warning(format!(
|
||||||
"authority denied: {:?}: {}",
|
"host authority denied: {:?}: {}",
|
||||||
authority.authority, authority.reason
|
authority.authority, authority.reason
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
@ -1186,7 +1191,7 @@ impl FeatureRegistryBuilder {
|
||||||
let mut context = FeatureInstallContext {
|
let mut context = FeatureInstallContext {
|
||||||
feature_id: &descriptor.id,
|
feature_id: &descriptor.id,
|
||||||
declarations: &declarations,
|
declarations: &declarations,
|
||||||
grants: &grants,
|
host_authority_grants: &host_authority_grants,
|
||||||
pending_tools,
|
pending_tools,
|
||||||
installed_tool_names: &mut installed_tool_names,
|
installed_tool_names: &mut installed_tool_names,
|
||||||
hook_builder,
|
hook_builder,
|
||||||
|
|
@ -1250,8 +1255,8 @@ pub enum FeatureInstallError {
|
||||||
first_feature: String,
|
first_feature: String,
|
||||||
duplicate_feature: String,
|
duplicate_feature: String,
|
||||||
},
|
},
|
||||||
#[error("feature authority denied: {0}")]
|
#[error("feature host authority denied: {0}")]
|
||||||
AuthorityDenied(String),
|
HostAuthorityDenied(String),
|
||||||
#[error("feature install failed: {0}")]
|
#[error("feature install failed: {0}")]
|
||||||
Install(String),
|
Install(String),
|
||||||
}
|
}
|
||||||
|
|
@ -1325,7 +1330,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn descriptor_authorities_and_install_report_are_recorded() {
|
fn descriptor_contributions_and_empty_host_authority_grants_are_recorded() {
|
||||||
let descriptor = FeatureDescriptor::builtin("dummy", "Dummy")
|
let descriptor = FeatureDescriptor::builtin("dummy", "Dummy")
|
||||||
.with_tool(ToolDeclaration::new("Dummy", "dummy tool"))
|
.with_tool(ToolDeclaration::new("Dummy", "dummy tool"))
|
||||||
.with_background_task(BackgroundTaskDeclaration::descriptor_only(
|
.with_background_task(BackgroundTaskDeclaration::descriptor_only(
|
||||||
|
|
@ -1348,7 +1353,7 @@ mod tests {
|
||||||
assert!(feature_report.installed);
|
assert!(feature_report.installed);
|
||||||
assert_eq!(feature_report.installed_tools, vec!["Dummy"]);
|
assert_eq!(feature_report.installed_tools, vec!["Dummy"]);
|
||||||
assert_eq!(feature_report.declared_background_tasks[0].name, "daily");
|
assert_eq!(feature_report.declared_background_tasks[0].name, "daily");
|
||||||
assert!(feature_report.granted_authorities.denied().is_empty());
|
assert!(feature_report.host_authority_grants.denied().is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -1411,6 +1416,79 @@ mod tests {
|
||||||
assert_eq!(report.reports[0].skipped[0].name, "Actual");
|
assert_eq!(report.reports[0].skipped[0].name, "Actual");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tool_host_authority_requirements_use_host_authority_grants_not_contribution_declarations() {
|
||||||
|
struct HostAuthorityToolFeature {
|
||||||
|
descriptor: FeatureDescriptor,
|
||||||
|
required_host_authorities: Vec<HostAuthority>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FeatureModule for HostAuthorityToolFeature {
|
||||||
|
fn descriptor(&self) -> FeatureDescriptor {
|
||||||
|
self.descriptor.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn install(
|
||||||
|
&self,
|
||||||
|
context: &mut FeatureInstallContext<'_>,
|
||||||
|
) -> Result<(), FeatureInstallError> {
|
||||||
|
context.tools().register(
|
||||||
|
ToolContribution::new("NetworkTool", dummy_tool("NetworkTool"))
|
||||||
|
.with_required_host_authorities(self.required_host_authorities.clone()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut hook_builder = HookRegistryBuilder::default();
|
||||||
|
let mut pending_tools = Vec::new();
|
||||||
|
let missing_grant = FeatureDescriptor::builtin("missing-host-authority", "Missing")
|
||||||
|
.with_tool(ToolDeclaration::new("NetworkTool", "network host API tool"));
|
||||||
|
let missing_report = FeatureRegistryBuilder::new()
|
||||||
|
.with_module(HostAuthorityToolFeature {
|
||||||
|
descriptor: missing_grant,
|
||||||
|
required_host_authorities: vec![HostAuthority::Network],
|
||||||
|
})
|
||||||
|
.install_into_pending(&mut pending_tools, &mut hook_builder);
|
||||||
|
|
||||||
|
assert!(pending_tools.is_empty());
|
||||||
|
assert!(!missing_report.reports[0].installed);
|
||||||
|
assert!(
|
||||||
|
missing_report.reports[0]
|
||||||
|
.diagnostics
|
||||||
|
.iter()
|
||||||
|
.any(|diagnostic| {
|
||||||
|
diagnostic
|
||||||
|
.message
|
||||||
|
.contains("required host authority was not granted")
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
missing_report.reports[0].skipped[0].kind,
|
||||||
|
FeatureContributionKind::Tool
|
||||||
|
);
|
||||||
|
|
||||||
|
let granted = FeatureDescriptor::builtin("granted-host-authority", "Granted")
|
||||||
|
.with_host_authority(HostAuthorityRequest::required(
|
||||||
|
HostAuthority::Network,
|
||||||
|
"uses a host network API",
|
||||||
|
))
|
||||||
|
.with_tool(ToolDeclaration::new("NetworkTool", "network host API tool"));
|
||||||
|
let granted_report = FeatureRegistryBuilder::new()
|
||||||
|
.with_module(HostAuthorityToolFeature {
|
||||||
|
descriptor: granted,
|
||||||
|
required_host_authorities: vec![HostAuthority::Network],
|
||||||
|
})
|
||||||
|
.install_into_pending(&mut pending_tools, &mut hook_builder);
|
||||||
|
|
||||||
|
assert!(granted_report.reports[0].installed);
|
||||||
|
assert!(
|
||||||
|
granted_report.reports[0]
|
||||||
|
.host_authority_grants
|
||||||
|
.contains(&HostAuthority::Network)
|
||||||
|
);
|
||||||
|
assert_eq!(pending_tools.len(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn stateful_tool_definition_is_materialized_once_for_report_and_worker() {
|
fn stateful_tool_definition_is_materialized_once_for_report_and_worker() {
|
||||||
struct StatefulToolFeature {
|
struct StatefulToolFeature {
|
||||||
|
|
@ -1707,7 +1785,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn background_task_declaration_is_not_sandbox_authority_gated() {
|
fn background_task_declaration_is_not_host_authority_gated() {
|
||||||
let descriptor = FeatureDescriptor::builtin("background", "Background")
|
let descriptor = FeatureDescriptor::builtin("background", "Background")
|
||||||
.with_background_task(BackgroundTaskDeclaration::descriptor_only(
|
.with_background_task(BackgroundTaskDeclaration::descriptor_only(
|
||||||
"declared-task",
|
"declared-task",
|
||||||
|
|
@ -1728,7 +1806,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn service_provider_declaration_is_not_sandbox_authority_gated() {
|
fn service_provider_declaration_is_not_host_authority_gated() {
|
||||||
let service = ServiceId::builtin("declared-service");
|
let service = ServiceId::builtin("declared-service");
|
||||||
let descriptor = FeatureDescriptor::builtin("service", "Service").with_provided_service(
|
let descriptor = FeatureDescriptor::builtin("service", "Service").with_provided_service(
|
||||||
ServiceDeclaration::new(service.clone(), "1", "descriptor contribution"),
|
ServiceDeclaration::new(service.clone(), "1", "descriptor contribution"),
|
||||||
|
|
@ -1746,7 +1824,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn builtin_internal_task_feature_descriptor_has_exact_tools_hooks_and_no_authorities() {
|
fn builtin_internal_task_feature_descriptor_has_exact_tools_hooks_and_no_host_authorities() {
|
||||||
let descriptor = builtin::task_tools_feature().descriptor();
|
let descriptor = builtin::task_tools_feature().descriptor();
|
||||||
let tool_names: Vec<_> = descriptor
|
let tool_names: Vec<_> = descriptor
|
||||||
.tools
|
.tools
|
||||||
|
|
@ -1762,7 +1840,7 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(descriptor.id.as_str(), "builtin:task-tools");
|
assert_eq!(descriptor.id.as_str(), "builtin:task-tools");
|
||||||
assert_eq!(descriptor.runtime, FeatureRuntimeKind::Builtin);
|
assert_eq!(descriptor.runtime, FeatureRuntimeKind::Builtin);
|
||||||
assert!(descriptor.requested_authorities.is_empty());
|
assert!(descriptor.requested_host_authorities.is_empty());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
hook_points,
|
hook_points,
|
||||||
vec![FeatureHookPoint::PreRequest, FeatureHookPoint::PreToolCall]
|
vec![FeatureHookPoint::PreRequest, FeatureHookPoint::PreToolCall]
|
||||||
|
|
@ -1800,8 +1878,8 @@ mod tests {
|
||||||
assert_eq!(report.reports.len(), 1);
|
assert_eq!(report.reports.len(), 1);
|
||||||
assert!(report.reports[0].installed);
|
assert!(report.reports[0].installed);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
report.reports[0].granted_authorities,
|
report.reports[0].host_authority_grants,
|
||||||
AuthorityGrantSet::empty()
|
HostAuthorityGrantSet::empty()
|
||||||
);
|
);
|
||||||
assert!(report.reports[0].skipped.is_empty());
|
assert!(report.reports[0].skipped.is_empty());
|
||||||
assert!(report.reports[0].diagnostics.is_empty());
|
assert!(report.reports[0].diagnostics.is_empty());
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user