made-core 0.7.1

Domain core of MADE: entities, value objects, events, ports. No IO.
Documentation
use serde::{Deserialize, Serialize};

use super::{AuthenticationMethod, PrincipalId, PrincipalKind};
use crate::DomainError;

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AuthenticatedPrincipal {
    id: PrincipalId,
    kind: PrincipalKind,
    method: AuthenticationMethod,
}

impl AuthenticatedPrincipal {
    pub fn new(
        id: PrincipalId,
        kind: PrincipalKind,
        method: AuthenticationMethod,
    ) -> Result<Self, DomainError> {
        let principal = Self { id, kind, method };
        principal.validate()?;
        Ok(principal)
    }

    #[must_use]
    pub fn id(&self) -> &PrincipalId {
        &self.id
    }

    #[must_use]
    pub const fn kind(&self) -> PrincipalKind {
        self.kind
    }

    #[must_use]
    pub const fn method(&self) -> AuthenticationMethod {
        self.method
    }

    pub fn validate(&self) -> Result<(), DomainError> {
        let trusted_host = self.kind == PrincipalKind::TrustedHost;
        let local_policy = self.method == AuthenticationMethod::LocalHostPolicy;
        if local_policy && !trusted_host {
            return Err(DomainError::InvariantViolated {
                reason: "local host policy authenticates only trusted-host principals",
            });
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn trusted_host_may_be_authenticated_by_mutual_tls() {
        let principal = AuthenticatedPrincipal::new(
            PrincipalId::new("remote-owner").unwrap(),
            PrincipalKind::TrustedHost,
            AuthenticationMethod::MutualTls,
        );

        assert!(principal.is_ok());
    }

    #[test]
    fn local_host_policy_rejects_non_host_principals() {
        for kind in [PrincipalKind::Human, PrincipalKind::Worker] {
            let result = AuthenticatedPrincipal::new(
                PrincipalId::new("not-a-host").unwrap(),
                kind,
                AuthenticationMethod::LocalHostPolicy,
            );

            assert!(matches!(result, Err(DomainError::InvariantViolated { .. })));
        }
    }
}