rocketmq_auth/
remoting_auth_context.rs1use std::net::SocketAddr;
16
17use rocketmq_transport::api::EmbeddedCaller;
18use rocketmq_transport::api::RemotingRequest;
19use rocketmq_transport::api::RequestOrigin;
20
21use crate::AuthFailureKind;
22use crate::AuthOperation;
23use crate::AuthServiceError;
24use crate::AuthServiceResult;
25use rocketmq_transport::api::SessionView;
26
27#[derive(Clone, Debug, Default, Eq, PartialEq)]
31pub struct RemotingAuthContext {
32 source_ip: Option<String>,
33 channel_id: Option<String>,
34 embedded: bool,
35}
36
37impl RemotingAuthContext {
38 #[must_use]
44 pub fn new(source_ip: Option<String>, channel_id: Option<String>) -> Self {
45 Self {
46 source_ip,
47 channel_id,
48 embedded: false,
49 }
50 }
51
52 #[must_use]
54 pub fn network(source_ip: impl Into<String>, channel_id: impl Into<String>) -> Self {
55 Self {
56 source_ip: Some(source_ip.into()),
57 channel_id: Some(channel_id.into()),
58 embedded: false,
59 }
60 }
61
62 #[must_use]
64 pub(crate) fn embedded(channel_id: impl Into<String>) -> Self {
65 Self {
66 source_ip: None,
67 channel_id: Some(channel_id.into()),
68 embedded: true,
69 }
70 }
71
72 pub fn from_request(request: &RemotingRequest) -> AuthServiceResult<Self> {
80 let channel_id = format!(
81 "transport-session-{}",
82 request.original_identity().request_id().owner_id()
83 );
84 let context = match (request.origin(), request.session()) {
85 (RequestOrigin::Network { peer }, SessionView::Network { remote_addr, .. }) => {
86 validate_network_origin(peer.address(), *remote_addr)?;
87 Self::network(remote_addr.ip().to_string(), channel_id)
88 }
89 (RequestOrigin::Embedded { caller }, SessionView::Embedded { .. }) => {
90 validate_embedded_caller(*caller)?;
91 Self::embedded(channel_id)
92 }
93 _ => {
94 return Err(invalid_remoting_context());
95 }
96 };
97 context.validate()?;
98 Ok(context)
99 }
100
101 #[must_use]
103 pub fn source_ip(&self) -> Option<&str> {
104 self.source_ip.as_deref()
105 }
106
107 #[must_use]
109 pub fn channel_id(&self) -> Option<&str> {
110 self.channel_id.as_deref()
111 }
112
113 #[must_use]
115 pub const fn is_embedded(&self) -> bool {
116 self.embedded
117 }
118
119 pub fn validate(&self) -> AuthServiceResult<()> {
127 let channel_id = self.channel_id().filter(|value| !value.trim().is_empty());
128 if channel_id.is_none() {
129 return Err(invalid_remoting_context());
130 }
131 if self.embedded {
132 if self.source_ip.is_some() {
133 return Err(invalid_remoting_context());
134 }
135 return Ok(());
136 }
137 if self.source_ip().filter(|value| !value.trim().is_empty()).is_none() {
138 return Err(invalid_remoting_context());
139 }
140 Ok(())
141 }
142}
143
144fn validate_network_origin(peer: SocketAddr, remote_addr: SocketAddr) -> AuthServiceResult<()> {
145 if peer != remote_addr {
146 return Err(invalid_remoting_context());
147 }
148 Ok(())
149}
150
151fn validate_embedded_caller(caller: EmbeddedCaller) -> AuthServiceResult<()> {
152 match caller {
153 EmbeddedCaller::BrokerProxy => Ok(()),
154 _ => Err(invalid_remoting_context()),
155 }
156}
157
158fn invalid_remoting_context() -> AuthServiceError {
159 AuthServiceError::new(AuthOperation::BuildContext, AuthFailureKind::Unauthenticated)
160}
161
162#[cfg(test)]
163mod tests {
164 use std::net::SocketAddr;
165
166 use rocketmq_transport::api::EmbeddedCaller;
167
168 use super::validate_embedded_caller;
169 use super::validate_network_origin;
170 use super::RemotingAuthContext;
171
172 #[test]
173 fn exposes_only_owned_authentication_facts() {
174 let context = RemotingAuthContext::network("192.0.2.10", "transport-session-17");
175
176 assert_eq!(context.source_ip(), Some("192.0.2.10"));
177 assert_eq!(context.channel_id(), Some("transport-session-17"));
178 }
179
180 #[test]
181 fn embedded_context_can_omit_network_facts() {
182 let context = RemotingAuthContext::embedded("embedded-session-1");
183
184 assert_eq!(context.source_ip(), None);
185 assert_eq!(context.channel_id(), Some("embedded-session-1"));
186 assert!(context.is_embedded());
187 assert!(context.validate().is_ok());
188 }
189
190 #[test]
191 fn missing_network_facts_fail_closed() {
192 assert!(RemotingAuthContext::default().validate().is_err());
193 assert!(RemotingAuthContext::new(Some("192.0.2.10".to_owned()), None)
194 .validate()
195 .is_err());
196 }
197
198 #[test]
199 fn mismatched_network_peer_and_session_source_fail_closed() {
200 let peer: SocketAddr = "192.0.2.10:10911".parse().unwrap();
201 let session_source: SocketAddr = "192.0.2.11:10911".parse().unwrap();
202
203 assert!(validate_network_origin(peer, session_source).is_err());
204 assert!(validate_network_origin(peer, peer).is_ok());
205 }
206
207 #[test]
208 fn broker_proxy_is_an_explicitly_trusted_embedded_caller() {
209 assert!(validate_embedded_caller(EmbeddedCaller::BrokerProxy).is_ok());
210 }
211}