appcore_peer_rpc/
authentication.rs1use super::*;
12
13pub trait PeerRpcTokenIssuer: Send + Sync {
15 fn issue_peer_token(
17 &self,
18 request_id: &str,
19 request_hash: Option<&str>,
20 now_ms: u64,
21 ttl_ms: u64,
22 ) -> Result<String, PeerRpcError>;
23}
24
25#[derive(Debug, Clone)]
27pub struct HashTokenPeerTokenIssuer<P = HashTokenProvider> {
28 provider: P,
29 claims: TokenClaims,
30}
31
32#[cfg(any(test, feature = "insecure-testing"))]
36#[derive(Clone)]
37pub struct StaticPeerRpcTokenIssuer {
38 token: Vec<u8>,
39}
40
41#[cfg(any(test, feature = "insecure-testing"))]
42impl std::fmt::Debug for StaticPeerRpcTokenIssuer {
43 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 formatter
45 .debug_struct("StaticPeerRpcTokenIssuer")
46 .field("token", &"REDACTED")
47 .finish()
48 }
49}
50
51#[cfg(any(test, feature = "insecure-testing"))]
52impl Drop for StaticPeerRpcTokenIssuer {
53 fn drop(&mut self) {
54 use zeroize::Zeroize;
55 self.token.zeroize();
56 }
57}
58
59pub trait PeerRpcDispatcher: Send + Sync {
61 fn dispatch_peer_query(
63 &self,
64 envelope: PeerRpcEnvelope,
65 ) -> Result<PeerRpcResponse, PeerRpcError>;
66
67 fn dispatch_peer_command(
69 &self,
70 envelope: PeerRpcEnvelope,
71 ) -> Result<PeerRpcResponse, PeerRpcError>;
72}
73
74pub trait PeerRpcAuthenticator: Send + Sync {
76 fn authenticate(
78 &self,
79 token: Option<&str>,
80 expected_request_hash: Option<&str>,
81 now_ms: u64,
82 ) -> Result<(), PeerRpcError>;
83}
84
85#[cfg(any(test, feature = "insecure-testing"))]
87#[derive(Debug, Clone, Copy, Default)]
88pub struct AllowPeerAuthenticator;
89
90#[cfg(any(test, feature = "insecure-testing"))]
91impl PeerRpcAuthenticator for AllowPeerAuthenticator {
92 fn authenticate(
93 &self,
94 _token: Option<&str>,
95 _expected_request_hash: Option<&str>,
96 _now_ms: u64,
97 ) -> Result<(), PeerRpcError> {
98 Ok(())
99 }
100}
101
102#[derive(Debug, Clone)]
104pub struct HashTokenPeerAuthenticator<P = HashTokenProvider> {
105 provider: P,
106 claims: TokenClaims,
107}
108
109impl<P> HashTokenPeerAuthenticator<P>
110where
111 P: TokenProvider,
112{
113 pub fn new(provider: P, mut claims: TokenClaims) -> Self {
115 claims.salt = "peer".to_string();
116 Self { provider, claims }
117 }
118}
119
120impl<P> PeerRpcAuthenticator for HashTokenPeerAuthenticator<P>
121where
122 P: TokenProvider + Send + Sync,
123{
124 fn authenticate(
125 &self,
126 token: Option<&str>,
127 expected_request_hash: Option<&str>,
128 now_ms: u64,
129 ) -> Result<(), PeerRpcError> {
130 let token = token.ok_or(PeerRpcError::Unauthorized)?;
131 let token = token
132 .strip_prefix("Bearer ")
133 .or_else(|| token.strip_prefix("bearer "))
134 .unwrap_or(token);
135 let claims = CommandTokenValidator::new(&self.provider, self.claims.clone())
136 .validate_and_get_claims(token, "peer", None, now_ms, expected_request_hash)
137 .map_err(peer_auth_error)?;
138 if expected_request_hash.is_some() && claims.request_hash.is_none() {
139 return Err(PeerRpcError::Forbidden);
140 }
141 Ok(())
142 }
143}
144
145impl<P> HashTokenPeerTokenIssuer<P>
146where
147 P: TokenProvider,
148{
149 pub fn new(provider: P, mut claims: TokenClaims) -> Self {
151 claims.salt = "peer".to_string();
152 Self { provider, claims }
153 }
154}
155
156impl<P> PeerRpcTokenIssuer for HashTokenPeerTokenIssuer<P>
157where
158 P: TokenProvider + Send + Sync,
159{
160 fn issue_peer_token(
161 &self,
162 request_id: &str,
163 request_hash: Option<&str>,
164 now_ms: u64,
165 ttl_ms: u64,
166 ) -> Result<String, PeerRpcError> {
167 CommandTokenFactory::new(&self.provider, self.claims.clone())
168 .create_v1_with_jti_and_hash(
169 "peer",
170 None,
171 Some("*"),
172 Some(LOCAL_ADMIN_SUBJECT),
173 now_ms,
174 ttl_ms,
175 Some(request_id.to_string()),
176 request_hash.map(ToOwned::to_owned),
177 )
178 .map_err(peer_auth_error)
179 }
180}
181
182#[cfg(any(test, feature = "insecure-testing"))]
183impl StaticPeerRpcTokenIssuer {
184 pub fn new(token: impl Into<String>) -> Self {
186 Self {
187 token: token.into().into_bytes(),
188 }
189 }
190}
191
192#[cfg(any(test, feature = "insecure-testing"))]
193impl PeerRpcTokenIssuer for StaticPeerRpcTokenIssuer {
194 fn issue_peer_token(
195 &self,
196 _request_id: &str,
197 _request_hash: Option<&str>,
198 _now_ms: u64,
199 _ttl_ms: u64,
200 ) -> Result<String, PeerRpcError> {
201 String::from_utf8(self.token.clone()).map_err(|_| PeerRpcError::Unauthorized)
202 }
203}
204fn peer_auth_error(error: CommandTokenError) -> PeerRpcError {
205 match error {
206 CommandTokenError::Forbidden => PeerRpcError::Forbidden,
207 CommandTokenError::InvalidFormat | CommandTokenError::Unauthorized => {
208 PeerRpcError::Unauthorized
209 }
210 }
211}