use core::time::Duration;
use crate::authenticator::direct::AccountAuthorityInfo;
use crate::authenticator::AuthorityMembersRepository;
use ockam::identity::models::{CredentialAndPurposeKey, CredentialSchemaIdentifier};
use ockam::identity::utils::AttributesBuilder;
use ockam::identity::{Attributes, Credentials, Identifier, IdentitiesAttributes};
use ockam_core::compat::sync::Arc;
use ockam_core::Result;
pub const TRUST_CONTEXT_ID: &[u8] = b"trust_context_id";
pub const PROJECT_MEMBER_SCHEMA: CredentialSchemaIdentifier = CredentialSchemaIdentifier(1);
pub const DEFAULT_CREDENTIAL_VALIDITY: Duration = Duration::from_secs(30 * 24 * 3600);
pub struct CredentialIssuer {
members: Arc<dyn AuthorityMembersRepository>,
identities_attributes: Arc<IdentitiesAttributes>,
credentials: Arc<Credentials>,
issuer: Identifier,
subject_attributes: Attributes,
credential_ttl: Duration,
account_authority: Option<AccountAuthorityInfo>,
}
impl CredentialIssuer {
#[allow(clippy::too_many_arguments)]
#[instrument(skip_all, fields(issuer = %issuer, project_identifier = project_identifier.clone(), credential_ttl = credential_ttl.map_or("n/a".to_string(), |d| d.as_secs().to_string())))]
pub fn new(
members: Arc<dyn AuthorityMembersRepository>,
identities_attributes: Arc<IdentitiesAttributes>,
credentials: Arc<Credentials>,
issuer: &Identifier,
project_identifier: String,
credential_ttl: Option<Duration>,
account_authority: Option<AccountAuthorityInfo>,
disable_trust_context_id: bool,
) -> Self {
let subject_attributes = AttributesBuilder::with_schema(PROJECT_MEMBER_SCHEMA);
let subject_attributes = if !disable_trust_context_id {
subject_attributes.with_attribute(
TRUST_CONTEXT_ID.to_vec(),
project_identifier.as_bytes().to_vec(),
)
} else {
subject_attributes
};
let subject_attributes = subject_attributes.build();
Self {
members,
identities_attributes,
credentials,
issuer: issuer.clone(),
subject_attributes,
credential_ttl: credential_ttl.unwrap_or(DEFAULT_CREDENTIAL_VALIDITY),
account_authority,
}
}
#[instrument(skip_all, fields(subject = %subject))]
pub async fn issue_credential(
&self,
subject: &Identifier,
) -> Result<Option<CredentialAndPurposeKey>> {
if let Some(info) = self.account_authority.as_ref() {
if let Some(attrs) = self
.identities_attributes
.get_attributes(subject, info.account_authority())
.await?
{
if attrs.attrs().get("project".as_bytes())
== Some(&info.project_identifier().as_bytes().to_vec())
{
let mut subject_attributes = self.subject_attributes.clone();
subject_attributes
.map
.insert(b"ockam-relay".to_vec().into(), b"*".to_vec().into());
subject_attributes.map.insert(
b"ockam-tls-certificate".to_vec().into(),
b"true".to_vec().into(),
);
let credential = self
.credentials
.credentials_creation()
.issue_credential(
&self.issuer,
subject,
subject_attributes,
self.credential_ttl,
)
.await?;
info!("Successfully issued a credential for admin {}", subject);
return Ok(Some(credential));
}
}
}
let member = match self.members.get_member(&self.issuer, subject).await? {
Some(member) => member,
None => return Ok(None),
};
let mut subject_attributes = self.subject_attributes.clone();
for (key, value) in member.attributes().iter() {
subject_attributes
.map
.insert(key.clone().into(), value.clone().into());
}
let credential = self
.credentials
.credentials_creation()
.issue_credential(
&self.issuer,
subject,
subject_attributes,
self.credential_ttl,
)
.await?;
info!("Successfully issued a credential for {}", subject);
Ok(Some(credential))
}
}