Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Verify plugin scopes on installation #1106

Merged
merged 1 commit into from
Dec 3, 2024
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
7 changes: 7 additions & 0 deletions golem-common/src/model/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
6 changes: 3 additions & 3 deletions golem-component-service-base/src/service/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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?;
Expand Down
23 changes: 23 additions & 0 deletions golem-component-service-base/src/service/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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(),
}
}
}
Expand Down Expand Up @@ -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(),
})),
},
}
}
}
Expand Down
45 changes: 33 additions & 12 deletions golem-component-service/src/api/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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;
Expand All @@ -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)]
Expand Down Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions golem-component-service/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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(),
})),
}
}
}
Expand Down
42 changes: 35 additions & 7 deletions golem-component-service/src/grpcapi/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand All @@ -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 {
Expand Down Expand Up @@ -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())
}

Expand Down
1 change: 1 addition & 0 deletions golem-component-service/src/grpcapi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
Loading