use crate::roles::ScopeEntry;
use anyhow::{bail, Result};
use reqwest::{blocking::Client, StatusCode};
use serde_json::Value;
use tracing::{debug, info};
use uuid::Uuid;
#[allow(clippy::indexing_slicing)]
fn check_error_response(body: &Value) -> Result<()> {
if body["error"]["code"].as_str() == Some("RoleAssignmentExists") {
info!("role already assigned");
return Ok(());
}
if body["error"]["code"].as_str() == Some("RoleAssignmentRequestExists") {
info!("role assignment request already exists");
return Ok(());
}
bail!("unable to elevate: {body:#?}");
}
pub fn activate_role(
principal_id: &str,
token: &str,
entry: &ScopeEntry,
justification: &str,
duration: u32,
) -> Result<Option<Uuid>> {
let ScopeEntry {
scope,
role_definition_id,
..
} = entry;
let request_id = Uuid::new_v4();
let url = format!("https://management.azure.com{scope}/providers/Microsoft.Authorization/roleAssignmentScheduleRequests/{request_id}");
let body = serde_json::json!({
"properties": {
"principalId": principal_id,
"roleDefinitionId": role_definition_id,
"requestType": "SelfActivate",
"justification": justification,
"scheduleInfo": {
"expiration": {
"duration": format!("PT{duration}M"),
"type": "AfterDuration",
}
}
}
});
let response = Client::new()
.put(url)
.query(&[("api-version", "2020-10-01")])
.bearer_auth(token)
.json(&body)
.send()?;
let status = response.status();
if status == StatusCode::BAD_REQUEST {
check_error_response(&response.json()?)?;
return Ok(None);
}
let body: Value = response.error_for_status()?.json()?;
debug!("body: {status:#?} - {body:#?}");
Ok(Some(request_id))
}