use std::sync::Arc;
use reqwest::Method;
use uuid::Uuid;
use wyrd_spec::auth::{
CreateServicePrincipalRequest, CreateServicePrincipalResponse, CredentialListResponse,
IssuedCredential, PrincipalId, PrincipalPage, PrincipalQuery, PrincipalRoles,
RevokePrincipalRequest, RoleAssignmentChange,
};
use wyrd_spec::error::WyrdError;
use crate::client::WyrdClient;
use std::fmt::{Debug, Formatter, Result as FmtResult};
#[derive(Clone)]
pub struct Principals {
client: Arc<WyrdClient>,
}
impl Debug for Principals {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_struct("Principals").finish_non_exhaustive()
}
}
impl Principals {
pub async fn connect() -> Result<Self, WyrdError> {
Ok(Self {
client: Arc::new(WyrdClient::connect_ambient().await?),
})
}
pub async fn connect_with(client: &WyrdClient) -> Result<Self, WyrdError> {
Ok(Self {
client: Arc::new(client.connected().await?),
})
}
pub async fn create_service_principal(
&self,
request: &CreateServicePrincipalRequest,
) -> Result<CreateServicePrincipalResponse, WyrdError> {
self.client
.http
.request_json(Method::POST, "/v1/principals", Some(request))
.await
}
pub async fn issue_credential(
&self,
principal_id: &PrincipalId,
) -> Result<IssuedCredential, WyrdError> {
self.client
.http
.request_json::<(), _>(
Method::POST,
&format!("/v1/principals/{principal_id}/credentials"),
None,
)
.await
}
pub async fn list_credentials(
&self,
principal_id: &PrincipalId,
) -> Result<CredentialListResponse, WyrdError> {
self.client
.http
.request_json::<(), _>(
Method::GET,
&format!("/v1/principals/{principal_id}/credentials"),
None,
)
.await
}
pub async fn revoke_principal(
&self,
principal_id: &PrincipalId,
request: &RevokePrincipalRequest,
) -> Result<(), WyrdError> {
self.client
.http
.request_json(
Method::POST,
&format!("/v1/principals/{principal_id}/revoke"),
Some(request),
)
.await
}
pub async fn revoke_credential(
&self,
principal_id: &PrincipalId,
credential_id: Uuid,
) -> Result<(), WyrdError> {
self.client
.http
.request_json::<(), ()>(
Method::DELETE,
&format!("/v1/principals/{principal_id}/credentials/{credential_id}"),
None,
)
.await?;
Ok(())
}
pub async fn list(&self, query: &PrincipalQuery) -> Result<PrincipalPage, WyrdError> {
let query = serde_urlencoded::to_string(query)
.expect("a principal query of optional scalars always URL-encodes");
let path = if query.is_empty() {
"/v1/principals".to_owned()
} else {
format!("/v1/principals?{query}")
};
self.client
.http
.request_json::<(), _>(Method::GET, &path, None)
.await
}
pub async fn roles(&self, principal_id: &PrincipalId) -> Result<PrincipalRoles, WyrdError> {
self.client
.http
.request_json::<(), _>(
Method::GET,
&format!("/v1/principals/{principal_id}/roles"),
None,
)
.await
}
pub async fn grant_role(
&self,
principal_id: &PrincipalId,
role: &str,
) -> Result<RoleAssignmentChange, WyrdError> {
self.change_role(Method::PUT, principal_id, role).await
}
pub async fn revoke_role(
&self,
principal_id: &PrincipalId,
role: &str,
) -> Result<RoleAssignmentChange, WyrdError> {
self.change_role(Method::DELETE, principal_id, role).await
}
async fn change_role(
&self,
method: Method,
principal_id: &PrincipalId,
role: &str,
) -> Result<RoleAssignmentChange, WyrdError> {
self.client
.http
.request_json::<(), _>(
method,
&format!(
"/v1/principals/{principal_id}/roles/{}",
urlencoding::encode(role)
),
None,
)
.await
}
}