uni_plugin/traits/connector.rs
1//! Wire-protocol connector plugins.
2
3use crate::errors::FnError;
4
5/// Free-form connector configuration (JSON-encoded).
6#[derive(Clone, Debug, Default)]
7pub struct ConnectorConfig {
8 /// JSON config payload.
9 pub config_json: String,
10}
11
12/// Opaque handle returned by [`Connector::start`].
13#[derive(Clone, Copy, Debug)]
14pub struct ConnectorHandle(pub u64);
15
16/// A wire-protocol connector — Bolt, GraphQL, REST, etc.
17pub trait Connector: Send + Sync {
18 /// Protocol name (`"bolt"`, `"graphql"`, …).
19 fn protocol(&self) -> &str;
20
21 /// Start the connector with the given configuration.
22 ///
23 /// # Errors
24 ///
25 /// Returns [`FnError`] if the connector cannot start (bind failure,
26 /// missing dependency).
27 fn start(&self, cfg: ConnectorConfig) -> Result<ConnectorHandle, FnError>;
28
29 /// Stop the connector.
30 ///
31 /// # Errors
32 ///
33 /// Returns [`FnError`] if the shutdown fails.
34 fn stop(&self, handle: ConnectorHandle) -> Result<(), FnError>;
35}
36
37/// Authentication credentials presented to an `AuthProvider`.
38#[derive(Clone, Debug)]
39pub enum Credentials {
40 /// Username + password pair.
41 Basic {
42 /// Username.
43 username: String,
44 /// Password.
45 password: String,
46 },
47 /// Bearer token.
48 Bearer(String),
49 /// mTLS client cert (DER-encoded).
50 MtlsCert(Vec<u8>),
51}
52
53/// Successfully-authenticated identity.
54#[derive(Clone, Debug)]
55pub struct Principal {
56 /// Identity string (subject id, username, etc.).
57 pub id: String,
58 /// Group memberships.
59 pub groups: Vec<String>,
60 /// Capabilities held by this principal.
61 ///
62 /// Populated by the host's authentication/authorization layer at
63 /// principal-construction time — typically the
64 /// [`AuthProvider`] / [`AuthzPolicy`] pair resolves group
65 /// memberships to a [`crate::CapabilitySet`]. Procedure invocation
66 /// paths (e.g.
67 /// `uni.plugin.declareProcedure` for `WRITE` mode) gate on
68 /// `principal.capabilities.contains_variant(&Capability::...)`.
69 pub capabilities: crate::CapabilitySet,
70}
71
72impl Principal {
73 /// Construct an anonymous principal with no capabilities — the
74 /// safe default for unauthenticated paths.
75 #[must_use]
76 pub fn anonymous() -> Self {
77 Self {
78 id: "anonymous".to_owned(),
79 groups: Vec::new(),
80 capabilities: crate::CapabilitySet::new(),
81 }
82 }
83}
84
85/// Authentication failure cause.
86#[derive(Clone, Debug, thiserror::Error)]
87#[error("authentication failure: {0}")]
88pub struct AuthError(pub String);
89
90/// Authentication provider — `AuthProvider::authenticate(creds) -> Principal`.
91pub trait AuthProvider: Send + Sync {
92 /// Authentication scheme name (`"basic"`, `"bearer"`, `"mtls"`).
93 fn scheme(&self) -> &str;
94
95 /// Authenticate the given credentials.
96 ///
97 /// # Errors
98 ///
99 /// Returns [`AuthError`] if the credentials are invalid.
100 fn authenticate(&self, credentials: &Credentials) -> Result<Principal, AuthError>;
101}
102
103/// Authorization action under check.
104#[derive(Clone, Debug)]
105pub struct Action {
106 /// Action verb (`"read"`, `"write"`, `"delete"`, …).
107 pub verb: String,
108}
109
110/// Authorization resource under check.
111#[derive(Clone, Debug)]
112pub struct Resource {
113 /// Resource path / identifier.
114 pub path: String,
115}
116
117/// Authorization decision.
118#[derive(Clone, Debug)]
119pub enum Decision {
120 /// Permit the action.
121 Allow,
122 /// Deny with reason.
123 Deny {
124 /// Human-readable reason.
125 reason: String,
126 },
127}
128
129/// Authorization failure (policy errored out, not "denied").
130#[derive(Clone, Debug, thiserror::Error)]
131#[error("authorization policy failure: {0}")]
132pub struct AuthzError(pub String);
133
134/// Authorization policy plugin.
135pub trait AuthzPolicy: Send + Sync {
136 /// Check whether `principal` may perform `action` on `resource`.
137 ///
138 /// # Errors
139 ///
140 /// Returns [`AuthzError`] if the policy fails to evaluate (e.g.,
141 /// external policy server unreachable).
142 fn check(
143 &self,
144 principal: &Principal,
145 action: &Action,
146 resource: &Resource,
147 ) -> Result<Decision, AuthzError>;
148}