292 lines
11 KiB
Rust
292 lines
11 KiB
Rust
use std::collections::HashMap;
|
|
|
|
use anyhow::{anyhow, Context, Result};
|
|
use async_trait::async_trait;
|
|
use cynic::{MutationBuilder, QueryBuilder};
|
|
use galaxy_graphql::mutations::issue_task_identity_token::{
|
|
IssueTaskIdentityToken, IssueTaskIdentityTokenInput, IssueTaskIdentityTokenResult,
|
|
IssueTaskIdentityTokenVariables,
|
|
};
|
|
use galaxy_graphql::object_permissions::OwnerType;
|
|
use galaxy_graphql::queries::list_managed_secrets::{
|
|
ListManagedSecrets, ListManagedSecretsVariables, ManagedSecretsInput, ManagedSecretsResult,
|
|
};
|
|
use galaxy_graphql::queries::managed_secret_config::{
|
|
GetManagedSecretConfig, GetManagedSecretConfigVariables, UserResult,
|
|
};
|
|
use galaxy_graphql::queries::task_secrets::{
|
|
ManagedSecretValue, TaskSecrets, TaskSecretsInput, TaskSecretsResult, TaskSecretsVariables,
|
|
};
|
|
use galaxy_graphql::{
|
|
managed_secrets::{ManagedSecret, ManagedSecretType},
|
|
mutations::{
|
|
create_managed_secret::{
|
|
CreateManagedSecret, CreateManagedSecretInput, CreateManagedSecretResult,
|
|
CreateManagedSecretVariables,
|
|
},
|
|
delete_managed_secret::{
|
|
DeleteManagedSecret, DeleteManagedSecretInput, DeleteManagedSecretResult,
|
|
DeleteManagedSecretVariables,
|
|
},
|
|
update_managed_secret::{
|
|
UpdateManagedSecret, UpdateManagedSecretInput, UpdateManagedSecretResult,
|
|
UpdateManagedSecretVariables,
|
|
},
|
|
},
|
|
object_permissions::Owner,
|
|
};
|
|
use galaxy_managed_secrets::client::{SecretOwner, TaskIdentityToken};
|
|
|
|
use super::ServerApi;
|
|
use crate::server::graphql::{get_request_context, get_user_facing_error_message};
|
|
|
|
pub use galaxy_managed_secrets::client::{ManagedSecretConfigs, ManagedSecretsClient};
|
|
|
|
#[cfg_attr(not(target_family = "wasm"), async_trait)]
|
|
#[cfg_attr(target_family = "wasm", async_trait(?Send))]
|
|
impl ManagedSecretsClient for ServerApi {
|
|
async fn get_managed_secret_configs(&self) -> Result<ManagedSecretConfigs> {
|
|
let variables = GetManagedSecretConfigVariables {
|
|
request_context: get_request_context(),
|
|
};
|
|
let operation = GetManagedSecretConfig::build(variables);
|
|
let response = self.send_graphql_request(operation, None).await?;
|
|
|
|
match response.user {
|
|
UserResult::UserOutput(output) => {
|
|
let mut team_configs = HashMap::new();
|
|
for workspace in output.user.workspaces {
|
|
for team in workspace.teams {
|
|
if let Some(config) = team.managed_secrets {
|
|
// DO NOT inline the `insert` call into the `debug_assert!` macro. It will get compiled out in release builds.
|
|
let prior_config = team_configs.insert(team.uid.into_inner(), config);
|
|
debug_assert!(
|
|
prior_config.is_none(),
|
|
"Duplicate team UID returned from server"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
Ok(ManagedSecretConfigs {
|
|
user_secrets: output.user.managed_secrets,
|
|
team_secrets: team_configs,
|
|
})
|
|
}
|
|
UserResult::UserFacingError(error) => {
|
|
Err(anyhow!(get_user_facing_error_message(error)))
|
|
}
|
|
UserResult::Unknown => Err(anyhow!(
|
|
"Unknown error while getting managed secret configs"
|
|
)),
|
|
}
|
|
}
|
|
|
|
async fn create_managed_secret(
|
|
&self,
|
|
owner: SecretOwner,
|
|
name: String,
|
|
secret_type: ManagedSecretType,
|
|
encrypted_value: String,
|
|
description: Option<String>,
|
|
) -> Result<ManagedSecret> {
|
|
let graphql_owner = match owner {
|
|
SecretOwner::CurrentUser => Owner {
|
|
type_: OwnerType::User,
|
|
uid: None,
|
|
},
|
|
SecretOwner::Team { team_uid } => Owner {
|
|
type_: OwnerType::Team,
|
|
uid: Some(cynic::Id::new(team_uid)),
|
|
},
|
|
};
|
|
|
|
let variables = CreateManagedSecretVariables {
|
|
input: CreateManagedSecretInput {
|
|
description,
|
|
encrypted_value,
|
|
name,
|
|
owner: graphql_owner,
|
|
type_: secret_type,
|
|
},
|
|
request_context: get_request_context(),
|
|
};
|
|
let operation = CreateManagedSecret::build(variables);
|
|
let response = self.send_graphql_request(operation, None).await?;
|
|
|
|
match response.create_managed_secret {
|
|
CreateManagedSecretResult::CreateManagedSecretOutput(output) => {
|
|
Ok(output.managed_secret)
|
|
}
|
|
CreateManagedSecretResult::UserFacingError(error) => {
|
|
Err(anyhow!(get_user_facing_error_message(error)))
|
|
}
|
|
CreateManagedSecretResult::Unknown => {
|
|
Err(anyhow!("Unknown error while creating managed secret"))
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn delete_managed_secret(&self, owner: SecretOwner, name: String) -> Result<()> {
|
|
let graphql_owner = match owner {
|
|
SecretOwner::CurrentUser => Owner {
|
|
type_: OwnerType::User,
|
|
uid: None,
|
|
},
|
|
SecretOwner::Team { team_uid } => Owner {
|
|
type_: OwnerType::Team,
|
|
uid: Some(cynic::Id::new(team_uid)),
|
|
},
|
|
};
|
|
|
|
let variables = DeleteManagedSecretVariables {
|
|
input: DeleteManagedSecretInput {
|
|
name,
|
|
owner: graphql_owner,
|
|
},
|
|
request_context: get_request_context(),
|
|
};
|
|
let operation = DeleteManagedSecret::build(variables);
|
|
let response = self.send_graphql_request(operation, None).await?;
|
|
|
|
match response.delete_managed_secret {
|
|
DeleteManagedSecretResult::DeleteManagedSecretOutput(_) => Ok(()),
|
|
DeleteManagedSecretResult::UserFacingError(error) => {
|
|
Err(anyhow!(get_user_facing_error_message(error)))
|
|
}
|
|
DeleteManagedSecretResult::Unknown => {
|
|
Err(anyhow!("Unknown error while deleting managed secret"))
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn update_managed_secret(
|
|
&self,
|
|
owner: SecretOwner,
|
|
name: String,
|
|
encrypted_value: Option<String>,
|
|
description: Option<String>,
|
|
) -> Result<ManagedSecret> {
|
|
let graphql_owner = match owner {
|
|
SecretOwner::CurrentUser => Owner {
|
|
type_: OwnerType::User,
|
|
uid: None,
|
|
},
|
|
SecretOwner::Team { team_uid } => Owner {
|
|
type_: OwnerType::Team,
|
|
uid: Some(cynic::Id::new(team_uid)),
|
|
},
|
|
};
|
|
|
|
let variables = UpdateManagedSecretVariables {
|
|
input: UpdateManagedSecretInput {
|
|
name,
|
|
owner: graphql_owner,
|
|
encrypted_value,
|
|
description,
|
|
},
|
|
request_context: get_request_context(),
|
|
};
|
|
let operation = UpdateManagedSecret::build(variables);
|
|
let response = self.send_graphql_request(operation, None).await?;
|
|
|
|
match response.update_managed_secret {
|
|
UpdateManagedSecretResult::UpdateManagedSecretOutput(output) => {
|
|
Ok(output.managed_secret)
|
|
}
|
|
UpdateManagedSecretResult::UserFacingError(error) => {
|
|
Err(anyhow!(get_user_facing_error_message(error)))
|
|
}
|
|
UpdateManagedSecretResult::Unknown => {
|
|
Err(anyhow!("Unknown error while updating managed secret"))
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn list_secrets(&self) -> Result<Vec<ManagedSecret>> {
|
|
let variables = ListManagedSecretsVariables {
|
|
// Pagination over managed secrets is not yet supported.
|
|
input: ManagedSecretsInput { cursor: None },
|
|
request_context: get_request_context(),
|
|
};
|
|
let operation = ListManagedSecrets::build(variables);
|
|
let response = self.send_graphql_request(operation, None).await?;
|
|
|
|
match response.managed_secrets {
|
|
ManagedSecretsResult::ManagedSecretsOutput(output) => Ok(output.managed_secrets),
|
|
ManagedSecretsResult::UserFacingError(error) => {
|
|
Err(anyhow!(get_user_facing_error_message(error)))
|
|
}
|
|
ManagedSecretsResult::Unknown => {
|
|
Err(anyhow!("Unknown error while listing managed secrets"))
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn get_task_secrets(
|
|
&self,
|
|
task_id: String,
|
|
workload_token: String,
|
|
) -> Result<HashMap<String, ManagedSecretValue>> {
|
|
let variables = TaskSecretsVariables {
|
|
input: TaskSecretsInput {
|
|
task_id: cynic::Id::new(task_id),
|
|
workload_token,
|
|
},
|
|
request_context: get_request_context(),
|
|
};
|
|
let operation = TaskSecrets::build(variables);
|
|
let response = self.send_graphql_request(operation, None).await?;
|
|
|
|
match response.task_secrets {
|
|
TaskSecretsResult::TaskSecretsOutput(output) => {
|
|
let mut secrets = HashMap::new();
|
|
for entry in output.secrets {
|
|
secrets.insert(entry.name, entry.value);
|
|
}
|
|
Ok(secrets)
|
|
}
|
|
TaskSecretsResult::UserFacingError(error) => {
|
|
Err(anyhow!(get_user_facing_error_message(error)))
|
|
}
|
|
TaskSecretsResult::Unknown => Err(anyhow!("Unknown error while getting task secrets")),
|
|
}
|
|
}
|
|
|
|
async fn issue_task_identity_token(
|
|
&self,
|
|
options: galaxy_managed_secrets::client::IdentityTokenOptions,
|
|
) -> Result<TaskIdentityToken> {
|
|
let requested_duration_seconds = options
|
|
.requested_duration
|
|
.as_secs()
|
|
.try_into()
|
|
.context("Requested duration out of bounds")?;
|
|
let variables = IssueTaskIdentityTokenVariables {
|
|
input: IssueTaskIdentityTokenInput {
|
|
audience: options.audience,
|
|
requested_duration_seconds,
|
|
subject_template: Some(options.subject_template.into_vec()),
|
|
},
|
|
request_context: get_request_context(),
|
|
};
|
|
let operation = IssueTaskIdentityToken::build(variables);
|
|
let response = self.send_graphql_request(operation, None).await?;
|
|
|
|
match response.issue_task_identity_token {
|
|
IssueTaskIdentityTokenResult::IssueTaskIdentityTokenOutput(output) => {
|
|
Ok(TaskIdentityToken {
|
|
token: output.token,
|
|
expires_at: output.expires_at.utc(),
|
|
issuer: output.issuer,
|
|
})
|
|
}
|
|
IssueTaskIdentityTokenResult::UserFacingError(error) => {
|
|
Err(anyhow!(get_user_facing_error_message(error)))
|
|
}
|
|
IssueTaskIdentityTokenResult::Unknown => {
|
|
Err(anyhow!("Unknown error while issuing task identity token"))
|
|
}
|
|
}
|
|
}
|
|
}
|