Skip to content

Commit

Permalink
Verify plugin scopes on installation (#1106)
Browse files Browse the repository at this point in the history
vigoo authored Dec 3, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent a9a0e4f commit e619487
Showing 7 changed files with 109 additions and 22 deletions.
7 changes: 7 additions & 0 deletions golem-common/src/model/plugin.rs
Original file line number Diff line number Diff line change
@@ -39,6 +39,13 @@ impl DefaultPluginScope {
pub fn component(component_id: ComponentId) -> Self {
DefaultPluginScope::Component(ComponentPluginScope { component_id })
}

pub fn valid_in_component(&self, component_id: &ComponentId) -> bool {
match self {
DefaultPluginScope::Global(_) => true,
DefaultPluginScope::Component(scope) => &scope.component_id == component_id,
}
}
}

impl Default for DefaultPluginScope {
6 changes: 3 additions & 3 deletions golem-component-service-base/src/service/component.rs
Original file line number Diff line number Diff line change
@@ -1441,8 +1441,8 @@ impl<Owner: ComponentOwner, Scope: PluginScope> ComponentService<Owner>
installation: PluginInstallationCreation,
) -> Result<PluginInstallation, PluginError> {
let namespace = owner.to_string();
let owner: Owner::Row = owner.clone().into();
let plugin_owner = owner.into();
let owner_row: Owner::Row = owner.clone().into();
let plugin_owner_row = owner_row.into();

let latest = self
.component_repo
@@ -1464,7 +1464,7 @@ impl<Owner: ComponentOwner, Scope: PluginScope> ComponentService<Owner>
component_version: latest.version as u64,
}
.into(),
owner: plugin_owner,
owner: plugin_owner_row,
};

let new_component_version = self.component_repo.install_plugin(&record).await?;
23 changes: 23 additions & 0 deletions golem-component-service-base/src/service/plugin.rs
Original file line number Diff line number Diff line change
@@ -35,6 +35,17 @@ pub enum PluginError {
ComponentNotFound { component_id: ComponentId },
#[error("Failed to get available scopes: {error}")]
FailedToGetAvailableScopes { error: String },
#[error("Plugin not found: {plugin_name}@{plugin_version}")]
PluginNotFound {
plugin_name: String,
plugin_version: String,
},
#[error("Plugin {plugin_name}@{plugin_version} {details}")]
InvalidScope {
plugin_name: String,
plugin_version: String,
details: String,
},
}

impl PluginError {
@@ -54,6 +65,8 @@ impl SafeDisplay for PluginError {
Self::InternalComponentError(inner) => inner.to_safe_string(),
Self::ComponentNotFound { .. } => self.to_string(),
Self::FailedToGetAvailableScopes { .. } => self.to_string(),
Self::PluginNotFound { .. } => self.to_string(),
Self::InvalidScope { .. } => self.to_string(),
}
}
}
@@ -82,6 +95,16 @@ impl From<PluginError> for golem_api_grpc::proto::golem::component::v1::Componen
error: value.to_safe_string(),
})),
},
PluginError::PluginNotFound { .. } => Self {
error: Some(component_error::Error::NotFound(ErrorBody {
error: value.to_safe_string(),
})),
},
PluginError::InvalidScope { .. } => Self {
error: Some(component_error::Error::Unauthorized(ErrorBody {
error: value.to_safe_string(),
})),
},
}
}
}
45 changes: 33 additions & 12 deletions golem-component-service/src/api/component.rs
Original file line number Diff line number Diff line change
@@ -16,7 +16,8 @@ use crate::api::{ComponentError, Result};
use futures_util::TryStreamExt;
use golem_common::model::component::DefaultComponentOwner;
use golem_common::model::plugin::{
PluginInstallation, PluginInstallationCreation, PluginInstallationUpdate,
DefaultPluginOwner, DefaultPluginScope, PluginInstallation, PluginInstallationCreation,
PluginInstallationUpdate,
};
use golem_common::model::ComponentFilePathWithPermissionsList;
use golem_common::model::{ComponentId, ComponentType, Empty, PluginInstallationId};
@@ -25,6 +26,7 @@ use golem_component_service_base::model::{
InitialComponentFilesArchiveAndPermissions, UpdatePayload,
};
use golem_component_service_base::service::component::ComponentService;
use golem_component_service_base::service::plugin::{PluginError, PluginService};
use golem_service_base::api_tags::ApiTags;
use golem_service_base::model::*;
use golem_service_base::poem::TempFileUpload;
@@ -38,6 +40,8 @@ use tracing::Instrument;

pub struct ComponentApi {
pub component_service: Arc<dyn ComponentService<DefaultComponentOwner> + Sync + Send>,
pub plugin_service:
Arc<dyn PluginService<DefaultPluginOwner, DefaultPluginScope> + Sync + Send>,
}

#[OpenApi(prefix_path = "/v1/components", tag = ApiTags::Component)]
@@ -386,18 +390,35 @@ impl ComponentApi {
plugin_version = plugin.version.clone()
);

let response = self
.component_service
.create_plugin_installation_for_component(
&DefaultComponentOwner,
&component_id.0,
plugin.0,
)
.await
.map_err(|e| e.into())
.map(Json);
let plugin_definition = self
.plugin_service
.get(&DefaultPluginOwner, &plugin.name, &plugin.version)
.await?;

let response = if let Some(plugin_definition) = plugin_definition {
if plugin_definition.scope.valid_in_component(&component_id.0) {
self.component_service
.create_plugin_installation_for_component(
&DefaultComponentOwner,
&component_id.0,
plugin.0,
)
.await
} else {
Err(PluginError::InvalidScope {
plugin_name: plugin.name.clone(),
plugin_version: plugin.version.clone(),
details: format!("not available for component {}", component_id.0),
})
}
} else {
Err(PluginError::PluginNotFound {
plugin_name: plugin.name.clone(),
plugin_version: plugin.version.clone(),
})
};

record.result(response)
record.result(response.map_err(|e| e.into()).map(Json))
}

/// Updates the priority or parameters of a plugin installation
7 changes: 7 additions & 0 deletions golem-component-service/src/api/mod.rs
Original file line number Diff line number Diff line change
@@ -54,6 +54,7 @@ pub fn make_open_api_service(services: &Services) -> OpenApiService<ApiServices,
(
component::ComponentApi {
component_service: services.component_service.clone(),
plugin_service: services.plugin_service.clone(),
},
healthcheck::HealthcheckApi,
plugin::PluginApi {
@@ -198,6 +199,12 @@ impl From<PluginError> for ComponentError {
error: value.to_safe_string(),
}))
}
PluginError::PluginNotFound { .. } => ComponentError::NotFound(Json(ErrorBody {
error: value.to_safe_string(),
})),
PluginError::InvalidScope { .. } => ComponentError::Unauthorized(Json(ErrorBody {
error: value.to_safe_string(),
})),
}
}
}
42 changes: 35 additions & 7 deletions golem-component-service/src/grpcapi/component.rs
Original file line number Diff line number Diff line change
@@ -47,12 +47,15 @@ use golem_api_grpc::proto::golem::component::{Component, PluginInstallation};
use golem_common::grpc::{proto_component_id_string, proto_plugin_installation_id_string};
use golem_common::model::component::DefaultComponentOwner;
use golem_common::model::component_constraint::FunctionConstraintCollection;
use golem_common::model::plugin::{PluginInstallationCreation, PluginInstallationUpdate};
use golem_common::model::plugin::{
DefaultPluginOwner, DefaultPluginScope, PluginInstallationCreation, PluginInstallationUpdate,
};
use golem_common::model::{ComponentId, ComponentType};
use golem_common::recorded_grpc_api_request;
use golem_component_service_base::api::common::ComponentTraceErrorKind;
use golem_component_service_base::model::ComponentConstraints;
use golem_component_service_base::service::component;
use golem_component_service_base::service::plugin::{PluginError, PluginService};
use tokio_stream::Stream;
use tonic::{Request, Response, Status, Streaming};

@@ -75,6 +78,8 @@ fn internal_error(error: &str) -> ComponentError {
pub struct ComponentGrpcApi {
pub component_service:
Arc<dyn component::ComponentService<DefaultComponentOwner> + Sync + Send>,
pub plugin_service:
Arc<dyn PluginService<DefaultPluginOwner, DefaultPluginScope> + Sync + Send>,
}

impl ComponentGrpcApi {
@@ -283,15 +288,38 @@ impl ComponentGrpcApi {
parameters: request.parameters.clone(),
};

let response = self
.component_service
.create_plugin_installation_for_component(
&DefaultComponentOwner,
&component_id,
plugin_installation_creation,
let plugin_definition = self
.plugin_service
.get(
&DefaultPluginOwner,
&plugin_installation_creation.name,
&plugin_installation_creation.version,
)
.await?;

let response = if let Some(plugin_definition) = plugin_definition {
if plugin_definition.scope.valid_in_component(&component_id) {
self.component_service
.create_plugin_installation_for_component(
&DefaultComponentOwner,
&component_id,
plugin_installation_creation.clone(),
)
.await
} else {
Err(PluginError::InvalidScope {
plugin_name: plugin_installation_creation.name,
plugin_version: plugin_installation_creation.version,
details: format!("not available for component {}", component_id),
})
}
} else {
Err(PluginError::PluginNotFound {
plugin_name: plugin_installation_creation.name,
plugin_version: plugin_installation_creation.version,
})
}?;

Ok(response.into())
}

1 change: 1 addition & 0 deletions golem-component-service/src/grpcapi/mod.rs
Original file line number Diff line number Diff line change
@@ -56,6 +56,7 @@ pub async fn start_grpc_server(
.add_service(
ComponentServiceServer::new(ComponentGrpcApi {
component_service: services.component_service.clone(),
plugin_service: services.plugin_service.clone(),
})
.accept_compressed(CompressionEncoding::Gzip)
.send_compressed(CompressionEncoding::Gzip),

0 comments on commit e619487

Please sign in to comment.