Skip to main content

uni_plugin/traits/
connector.rs

1//! Authentication and authorization plugins.
2
3/// Authentication credentials presented to an `AuthProvider`.
4#[derive(Clone, Debug)]
5pub enum Credentials {
6    /// Username + password pair.
7    Basic {
8        /// Username.
9        username: String,
10        /// Password.
11        password: String,
12    },
13    /// Bearer token.
14    Bearer(String),
15    /// mTLS client cert (DER-encoded).
16    MtlsCert(Vec<u8>),
17}
18
19/// Successfully-authenticated identity.
20#[derive(Clone, Debug)]
21pub struct Principal {
22    /// Identity string (subject id, username, etc.).
23    pub id: String,
24    /// Group memberships.
25    pub groups: Vec<String>,
26    /// Capabilities held by this principal.
27    ///
28    /// Populated by the host's authentication/authorization layer at
29    /// principal-construction time — typically the
30    /// [`AuthProvider`] / [`AuthzPolicy`] pair resolves group
31    /// memberships to a [`crate::CapabilitySet`]. Procedure invocation
32    /// paths (e.g.
33    /// `uni.plugin.declareProcedure` for `WRITE` mode) gate on
34    /// `principal.capabilities.contains_variant(&Capability::...)`.
35    pub capabilities: crate::CapabilitySet,
36}
37
38impl Principal {
39    /// Construct an anonymous principal with no capabilities — the
40    /// safe default for unauthenticated paths.
41    #[must_use]
42    pub fn anonymous() -> Self {
43        Self {
44            id: "anonymous".to_owned(),
45            groups: Vec::new(),
46            capabilities: crate::CapabilitySet::new(),
47        }
48    }
49}
50
51/// Authentication failure cause.
52#[derive(Clone, Debug, thiserror::Error)]
53#[error("authentication failure: {0}")]
54pub struct AuthError(pub String);
55
56/// Authentication provider — `AuthProvider::authenticate(creds) -> Principal`.
57pub trait AuthProvider: Send + Sync {
58    /// Authentication scheme name (`"basic"`, `"bearer"`, `"mtls"`).
59    fn scheme(&self) -> &str;
60
61    /// Authenticate the given credentials.
62    ///
63    /// # Errors
64    ///
65    /// Returns [`AuthError`] if the credentials are invalid.
66    fn authenticate(&self, credentials: &Credentials) -> Result<Principal, AuthError>;
67}
68
69/// Authorization action under check.
70#[derive(Clone, Debug)]
71pub struct Action {
72    /// Action verb (`"read"`, `"write"`, `"delete"`, …).
73    pub verb: String,
74}
75
76/// Authorization resource under check.
77///
78/// `path` is the raw query text (retained for backward compatibility); the
79/// structured fields are extracted from the parsed query so a policy can gate on
80/// vertex labels, relationship types, touched properties, and operations rather
81/// than string-matching the Cypher. Empty structured fields mean "none found" or
82/// "not extracted" — a policy should treat empties conservatively.
83#[derive(Clone, Debug, Default)]
84pub struct Resource {
85    /// Raw query text / resource identifier.
86    pub path: String,
87    /// Vertex labels the query references.
88    pub labels: Vec<String>,
89    /// Relationship types the query references.
90    pub rel_types: Vec<String>,
91    /// Property keys the query reads or writes.
92    pub properties: Vec<String>,
93    /// Operations the query performs (`"read"`, `"write"`, `"delete"`, …).
94    pub operations: Vec<String>,
95}
96
97/// Authorization decision.
98#[derive(Clone, Debug)]
99pub enum Decision {
100    /// Permit the action.
101    Allow,
102    /// Deny with reason.
103    Deny {
104        /// Human-readable reason.
105        reason: String,
106    },
107}
108
109/// Authorization failure (policy errored out, not "denied").
110#[derive(Clone, Debug, thiserror::Error)]
111#[error("authorization policy failure: {0}")]
112pub struct AuthzError(pub String);
113
114/// Authorization policy plugin.
115pub trait AuthzPolicy: Send + Sync {
116    /// Check whether `principal` may perform `action` on `resource`.
117    ///
118    /// # Errors
119    ///
120    /// Returns [`AuthzError`] if the policy fails to evaluate (e.g.,
121    /// external policy server unreachable).
122    fn check(
123        &self,
124        principal: &Principal,
125        action: &Action,
126        resource: &Resource,
127    ) -> Result<Decision, AuthzError>;
128}