Skip to main content

soaprs_auth/
principal.rs

1//! Authenticated principal contracts.
2
3use std::collections::BTreeSet;
4
5use soaprs_core::SoapResult;
6
7use crate::{AuthorizationName, PrincipalId};
8
9/// Identity and grants consumed by portable authorization policies.
10pub trait Principal: Send + Sync {
11    /// Returns the stable subject identity.
12    fn principal_id(&self) -> &PrincipalId;
13
14    /// Reports whether the principal owns one role.
15    fn has_role(&self, role: &AuthorizationName) -> bool;
16
17    /// Reports whether the principal owns one permission.
18    fn has_permission(&self, permission: &AuthorizationName) -> bool;
19}
20
21/// Basic principal implementation suitable for role/permission services.
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct StandardPrincipal {
24    id: PrincipalId,
25    roles: BTreeSet<AuthorizationName>,
26    permissions: BTreeSet<AuthorizationName>,
27}
28
29impl StandardPrincipal {
30    /// Creates a principal without roles or permissions.
31    pub fn new(id: impl Into<String>) -> SoapResult<Self> {
32        Ok(Self {
33            id: PrincipalId::new(id)?,
34            roles: BTreeSet::new(),
35            permissions: BTreeSet::new(),
36        })
37    }
38
39    /// Adds a role once.
40    pub fn role(mut self, role: impl Into<String>) -> SoapResult<Self> {
41        self.roles.insert(AuthorizationName::new(role)?);
42        Ok(self)
43    }
44
45    /// Adds a permission once.
46    pub fn permission(mut self, permission: impl Into<String>) -> SoapResult<Self> {
47        self.permissions.insert(AuthorizationName::new(permission)?);
48        Ok(self)
49    }
50
51    /// Returns roles in deterministic order.
52    pub fn roles(&self) -> &BTreeSet<AuthorizationName> {
53        &self.roles
54    }
55
56    /// Returns permissions in deterministic order.
57    pub fn permissions(&self) -> &BTreeSet<AuthorizationName> {
58        &self.permissions
59    }
60}
61
62impl Principal for StandardPrincipal {
63    fn principal_id(&self) -> &PrincipalId {
64        &self.id
65    }
66
67    fn has_role(&self, role: &AuthorizationName) -> bool {
68        self.roles.contains(role)
69    }
70
71    fn has_permission(&self, permission: &AuthorizationName) -> bool {
72        self.permissions.contains(permission)
73    }
74}