coil-auth 0.1.1

Authorisation models and auth package support for the Coil framework.
Documentation
use super::*;

#[derive(Clone)]
pub struct CoilAuth<E> {
    engine: E,
    tenant_id: i64,
}

impl<E> CoilAuth<E> {
    pub fn new(engine: E, tenant_id: i64) -> Self {
        Self { engine, tenant_id }
    }

    pub fn tenant_id(&self) -> i64 {
        self.tenant_id
    }

    pub fn engine(&self) -> &E {
        &self.engine
    }

    pub fn into_inner(self) -> E {
        self.engine
    }
}

impl<E> CoilAuth<E>
where
    E: RebacEngine,
{
    pub async fn apply_default_schema(&self) -> Result<(), RebacError> {
        self.apply_model_package(&DefaultAuthModelPackage::default())
            .await
    }

    pub async fn apply_model_package<P>(&self, package: &P) -> Result<(), RebacError>
    where
        P: AuthModelPackage + ?Sized,
    {
        self.engine
            .apply_schema(self.tenant_id, package.schema().clone())
            .await
    }

    pub async fn write(
        &self,
        updates: impl IntoIterator<Item = DefaultTupleUpdate>,
    ) -> Result<(), RebacError> {
        self.engine
            .write_tuples(
                self.tenant_id,
                updates.into_iter().map(Into::into).collect(),
            )
            .await
    }

    pub async fn check(
        &self,
        subject: &DefaultSubject,
        relation: Relation,
        object: &Entity,
    ) -> Result<bool, RebacError> {
        let subject = subject.to_subject();
        let object = object.to_object();

        self.engine
            .check(self.tenant_id, &subject, relation.as_str(), &object)
            .await
    }

    pub async fn check_capability<P>(
        &self,
        package: &P,
        subject: &DefaultSubject,
        capability: Capability,
        object: &Entity,
    ) -> Result<bool, CoilAuthError>
    where
        P: AuthModelPackage + ?Sized,
    {
        let binding = package.resolve_binding(capability, object)?;
        let subject = subject.to_subject();
        let object = object.to_object();

        Ok(self
            .engine
            .check(self.tenant_id, &subject, binding.relation.as_str(), &object)
            .await?)
    }

    pub async fn check_default_capability(
        &self,
        subject: &DefaultSubject,
        capability: Capability,
        object: &Entity,
    ) -> Result<bool, CoilAuthError> {
        self.check_capability(
            &DefaultAuthModelPackage::default(),
            subject,
            capability,
            object,
        )
        .await
    }

    pub async fn explain_capability<P>(
        &self,
        package: &P,
        subject: &DefaultSubject,
        capability: Capability,
        object: &Entity,
    ) -> Result<CapabilityExplanation, CoilAuthError>
    where
        P: AuthModelPackage + ?Sized,
    {
        self.explain_capability_with_options(
            package,
            subject,
            capability,
            object,
            ExplainOptions::default(),
        )
        .await
    }

    pub async fn explain_capability_with_options<P>(
        &self,
        package: &P,
        subject: &DefaultSubject,
        capability: Capability,
        object: &Entity,
        options: ExplainOptions,
    ) -> Result<CapabilityExplanation, CoilAuthError>
    where
        P: AuthModelPackage + ?Sized,
    {
        let tuples = self
            .engine
            .read_tuples(self.tenant_id, None, None, None)
            .await?;

        build_capability_explanation(package, &tuples, subject, capability, object, options)
    }

    pub async fn explain_default_capability(
        &self,
        subject: &DefaultSubject,
        capability: Capability,
        object: &Entity,
    ) -> Result<CapabilityExplanation, CoilAuthError> {
        self.explain_default_capability_with_options(
            subject,
            capability,
            object,
            ExplainOptions::default(),
        )
        .await
    }

    pub async fn explain_default_capability_with_options(
        &self,
        subject: &DefaultSubject,
        capability: Capability,
        object: &Entity,
        options: ExplainOptions,
    ) -> Result<CapabilityExplanation, CoilAuthError> {
        self.explain_capability_with_options(
            &DefaultAuthModelPackage::default(),
            subject,
            capability,
            object,
            options,
        )
        .await
    }

    pub async fn check_many(
        &self,
        requests: impl IntoIterator<Item = AccessCheck>,
    ) -> Result<Vec<bool>, RebacError> {
        self.engine
            .check_many(
                self.tenant_id,
                requests.into_iter().map(Into::into).collect(),
            )
            .await
    }

    pub async fn list_objects(
        &self,
        subject: &DefaultSubject,
        relation: Relation,
        namespace: Namespace,
    ) -> Result<Vec<String>, RebacError> {
        let subject = subject.to_subject();

        self.engine
            .list_objects(
                self.tenant_id,
                &subject,
                relation.as_str(),
                namespace.as_str(),
            )
            .await
    }

    pub async fn list_subject_ids(
        &self,
        object: &Entity,
        relation: Relation,
        namespace: Namespace,
    ) -> Result<Vec<String>, RebacError> {
        let object = object.to_object();

        self.engine
            .list_subjects(
                self.tenant_id,
                &object,
                relation.as_str(),
                namespace.as_str(),
            )
            .await
    }
}