use silicon_iam_client::{Client, Credential, EnvironmentKey, Mutation};
use crate::{
error::{CliError, Result},
output::Format,
store::{self, Profile, Session},
};
pub const DEFAULT_PROFILE: &str = "default";
pub const DEFAULT_URL: &str = "https://backend.iam.teamofsilicons.com";
pub struct Context {
pub format: Format,
pub profile_name: String,
pub profile: Profile,
pub step_up: Option<String>,
organization: Option<String>,
environment: Option<String>,
client: Client,
}
impl Context {
pub fn new(
format: Format,
profile: Option<String>,
url: Option<String>,
organization: Option<String>,
environment: Option<String>,
step_up: Option<String>,
) -> Result<Self> {
let config = store::load_config()?;
let profile_name = profile
.or_else(|| std::env::var("SILICON_IAM_PROFILE").ok())
.or_else(|| config.current_profile.clone())
.unwrap_or_else(|| DEFAULT_PROFILE.to_owned());
let mut stored = config
.profiles
.get(&profile_name)
.cloned()
.unwrap_or_default();
if stored.url.is_empty() {
DEFAULT_URL.clone_into(&mut stored.url);
}
if let Some(url) = url.or_else(|| std::env::var("SILICON_IAM_URL").ok()) {
stored.url = url;
}
let environment = environment
.or_else(|| std::env::var("SILICON_IAM_ENVIRONMENT").ok())
.or_else(|| stored.environment.clone());
let organization = organization
.or_else(|| std::env::var("SILICON_IAM_ORG").ok())
.or_else(|| stored.org.clone());
let mut builder =
Client::builder(&stored.url)?.user_agent(concat!("siam/", env!("CARGO_PKG_VERSION")));
if let Some(key) = &environment {
builder = builder.environment(EnvironmentKey::new(key.clone())?);
}
Ok(Self {
format,
profile_name,
profile: stored,
step_up,
organization,
environment,
client: builder.build()?,
})
}
#[must_use]
pub const fn anonymous(&self) -> &Client {
&self.client
}
#[must_use]
pub fn environment(&self) -> Option<&str> {
self.environment.as_deref()
}
pub async fn authenticated(&self) -> Result<Client> {
let session = self.session()?;
let session = if session.needs_refresh() {
self.renew(&session).await?
} else {
session
};
Ok(self
.client
.with_credential(Credential::bearer(session.access_token)))
}
pub fn session(&self) -> Result<Session> {
store::load_credentials()?
.sessions
.get(&self.profile_name)
.cloned()
.ok_or(CliError::NotSignedIn)
}
pub fn remember(&self, session: Session) -> Result<()> {
let mut credentials = store::load_credentials()?;
credentials
.sessions
.insert(self.profile_name.clone(), session);
store::save_credentials(&credentials)
}
pub fn forget(&self) -> Result<bool> {
let mut credentials = store::load_credentials()?;
let existed = credentials.sessions.remove(&self.profile_name).is_some();
store::save_credentials(&credentials)?;
Ok(existed)
}
pub fn organization(&self) -> Result<&str> {
self.organization.as_deref().ok_or(CliError::NoOrganization)
}
pub fn organization_or<'a>(&'a self, explicit: Option<&'a str>) -> Result<&'a str> {
match explicit {
Some(org) => Ok(org),
None => self.organization(),
}
}
#[must_use]
pub fn mutation(&self) -> Mutation {
let mutation = Mutation::new();
match &self.step_up {
Some(token) => mutation.step_up(token.clone()),
None => mutation,
}
}
async fn renew(&self, session: &Session) -> Result<Session> {
let tokens = self
.client
.auth()
.refresh(&session.refresh_token, &Mutation::new())
.await?;
let renewed = crate::commands::auth::session_from(&tokens, &session.carbon_id);
self.remember(renewed.clone())?;
Ok(renewed)
}
}