soaprs-auth 0.4.0

Protocol-neutral authentication and authorization contracts for soaprs
Documentation
//! Authenticated principal contracts.

use std::collections::BTreeSet;

use soaprs_core::SoapResult;

use crate::{AuthorizationName, PrincipalId};

/// Identity and grants consumed by portable authorization policies.
pub trait Principal: Send + Sync {
    /// Returns the stable subject identity.
    fn principal_id(&self) -> &PrincipalId;

    /// Reports whether the principal owns one role.
    fn has_role(&self, role: &AuthorizationName) -> bool;

    /// Reports whether the principal owns one permission.
    fn has_permission(&self, permission: &AuthorizationName) -> bool;
}

/// Basic principal implementation suitable for role/permission services.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StandardPrincipal {
    id: PrincipalId,
    roles: BTreeSet<AuthorizationName>,
    permissions: BTreeSet<AuthorizationName>,
}

impl StandardPrincipal {
    /// Creates a principal without roles or permissions.
    pub fn new(id: impl Into<String>) -> SoapResult<Self> {
        Ok(Self {
            id: PrincipalId::new(id)?,
            roles: BTreeSet::new(),
            permissions: BTreeSet::new(),
        })
    }

    /// Adds a role once.
    pub fn role(mut self, role: impl Into<String>) -> SoapResult<Self> {
        self.roles.insert(AuthorizationName::new(role)?);
        Ok(self)
    }

    /// Adds a permission once.
    pub fn permission(mut self, permission: impl Into<String>) -> SoapResult<Self> {
        self.permissions.insert(AuthorizationName::new(permission)?);
        Ok(self)
    }

    /// Returns roles in deterministic order.
    pub fn roles(&self) -> &BTreeSet<AuthorizationName> {
        &self.roles
    }

    /// Returns permissions in deterministic order.
    pub fn permissions(&self) -> &BTreeSet<AuthorizationName> {
        &self.permissions
    }
}

impl Principal for StandardPrincipal {
    fn principal_id(&self) -> &PrincipalId {
        &self.id
    }

    fn has_role(&self, role: &AuthorizationName) -> bool {
        self.roles.contains(role)
    }

    fn has_permission(&self, permission: &AuthorizationName) -> bool {
        self.permissions.contains(permission)
    }
}