79 lines
2.4 KiB
Rust
79 lines
2.4 KiB
Rust
use anyhow::Result;
|
|
use iam_client::client::IamClientConfig;
|
|
use iam_client::IamClient;
|
|
use iam_types::{PolicyBinding, Principal, PrincipalRef, Scope};
|
|
use tonic::metadata::MetadataValue;
|
|
use tonic::Request;
|
|
|
|
pub fn authorized_request<T>(message: T, token: &str) -> Request<T> {
|
|
let mut req = Request::new(message);
|
|
let header = format!("Bearer {}", token);
|
|
let value = MetadataValue::try_from(header.as_str()).expect("valid bearer token metadata");
|
|
req.metadata_mut().insert("authorization", value);
|
|
req
|
|
}
|
|
|
|
pub async fn issue_controller_token(
|
|
iam_server_addr: &str,
|
|
principal_id: &str,
|
|
org_id: &str,
|
|
project_id: &str,
|
|
) -> Result<String> {
|
|
let mut config = IamClientConfig::new(iam_server_addr).with_timeout(5000);
|
|
if iam_server_addr.starts_with("http://") || !iam_server_addr.starts_with("https://") {
|
|
config = config.without_tls();
|
|
}
|
|
|
|
let client = IamClient::connect(config).await?;
|
|
let principal_ref = PrincipalRef::service_account(principal_id);
|
|
let principal = match client.get_principal(&principal_ref).await? {
|
|
Some(existing) => existing,
|
|
None => {
|
|
client
|
|
.create_service_account(principal_id, principal_id, project_id)
|
|
.await?
|
|
}
|
|
};
|
|
|
|
ensure_project_admin_binding(&client, &principal, org_id, project_id).await?;
|
|
|
|
let scope = Scope::project(project_id, org_id);
|
|
client
|
|
.issue_token(
|
|
&principal,
|
|
vec!["roles/ProjectAdmin".to_string()],
|
|
scope,
|
|
3600,
|
|
)
|
|
.await
|
|
.map_err(Into::into)
|
|
}
|
|
|
|
async fn ensure_project_admin_binding(
|
|
client: &IamClient,
|
|
principal: &Principal,
|
|
org_id: &str,
|
|
project_id: &str,
|
|
) -> Result<()> {
|
|
let scope = Scope::project(project_id, org_id);
|
|
let bindings = client
|
|
.list_bindings_for_principal(&principal.to_ref())
|
|
.await?;
|
|
|
|
let already_bound = bindings
|
|
.iter()
|
|
.any(|binding| binding.role_ref == "roles/ProjectAdmin" && binding.scope == scope);
|
|
if already_bound {
|
|
return Ok(());
|
|
}
|
|
|
|
let binding = PolicyBinding::new(
|
|
format!("{}-project-admin-{}-{}", principal.id, org_id, project_id),
|
|
principal.to_ref(),
|
|
"roles/ProjectAdmin",
|
|
scope,
|
|
)
|
|
.with_created_by("fleet-scheduler");
|
|
client.create_binding(&binding).await?;
|
|
Ok(())
|
|
}
|