@@ -2,6 +2,7 @@ use std::cmp;
22use std:: collections:: BTreeMap ;
33use std:: num:: NonZeroU32 ;
44use std:: ops:: { Index , IndexMut } ;
5+ use std:: str:: FromStr ;
56use std:: sync:: { Arc , OnceLock } ;
67use std:: time:: { Instant , SystemTime } ;
78
@@ -21,6 +22,7 @@ use tracing::Span;
2122
2223use rumqttd_protocol:: { QoS , RetainForwardRule , SubscribeReasonCode , UnsubAckReason } ;
2324
25+ use crate :: config:: acl:: AclConfig ;
2426use crate :: map_join_error;
2527use crate :: mqtt:: mailbox:: MailSender ;
2628use crate :: mqtt:: packets:: PacketId ;
@@ -72,6 +74,7 @@ impl MqttRouter {
7274 tce_platform : Option < Arc < Platform > > ,
7375 tce_messages : Option < MessageStream > ,
7476 token : CancellationToken ,
77+ acl : AclConfig ,
7578 ) -> Self {
7679 let ( command_tx, command_rx) = mpsc:: channel ( COMMAND_CAPACITY ) ;
7780
@@ -90,6 +93,7 @@ impl MqttRouter {
9093
9194 let state = RouterState {
9295 token,
96+ acl,
9397 clients : SecondaryMap :: new ( ) ,
9498 dead_clients : HashSet :: default ( ) ,
9599 subscriptions : Subscriptions :: default ( ) ,
@@ -161,6 +165,7 @@ impl RouterHandle {
161165 connection_id : ConnectionId ,
162166 client_index : ClientIndex ,
163167 client_id : ClientId ,
168+ user : String ,
164169 mail_tx : MailSender ,
165170 clean_session : bool ,
166171 ) -> crate :: Result < RouterConnection > {
@@ -172,6 +177,7 @@ impl RouterHandle {
172177 RouterCommand :: NewConnection {
173178 connection_id,
174179 client_id,
180+ user,
175181 message_tx,
176182 mail_tx,
177183 clean_session,
@@ -286,6 +292,7 @@ enum RouterCommand {
286292 connection_id : ConnectionId ,
287293 client_id : ClientId ,
288294 mail_tx : MailSender ,
295+ user : String ,
289296 message_tx : mpsc:: UnboundedSender < RouterMessage > ,
290297 clean_session : bool ,
291298 } ,
@@ -336,6 +343,8 @@ struct RouterState {
336343 clients : SecondaryMap < ClientIndex , ClientState > ,
337344 dead_clients : HashSet < ClientIndex > ,
338345
346+ acl : AclConfig ,
347+
339348 subscriptions : Subscriptions ,
340349 command_rx : mpsc:: Receiver < ( ClientIndex , RouterCommand ) > ,
341350 system_rx : mpsc:: UnboundedReceiver < SystemCommand > ,
@@ -358,6 +367,7 @@ struct Tce {
358367struct ClientState {
359368 client_id : ClientId ,
360369 mail_tx : MailSender ,
370+ user : String ,
361371 subscriptions : ClientSubscriptions ,
362372 current_connection : Option < ConnectionState > ,
363373 clean_session : bool ,
@@ -421,6 +431,15 @@ enum PublishOrigin<'a> {
421431 Consensus ( & ' a CreatorId ) ,
422432}
423433
434+ impl PublishOrigin < ' _ > {
435+ pub fn get_client_index ( & self ) -> Option < ClientIndex > {
436+ match self {
437+ PublishOrigin :: Local ( client_index) => Some ( * client_index) ,
438+ _ => None ,
439+ }
440+ }
441+ }
442+
424443impl Index < SubscriptionKind > for Subscriptions {
425444 type Output = SubscriptionMap ;
426445
@@ -565,6 +584,7 @@ fn handle_command(state: &mut RouterState, client_idx: ClientIndex, command: Rou
565584 RouterCommand :: NewConnection {
566585 connection_id,
567586 client_id,
587+ user,
568588 message_tx,
569589 mail_tx,
570590 clean_session,
@@ -591,6 +611,7 @@ fn handle_command(state: &mut RouterState, client_idx: ClientIndex, command: Rou
591611 client_idx,
592612 ClientState {
593613 client_id,
614+ user,
594615 mail_tx,
595616 subscriptions : Default :: default ( ) ,
596617 current_connection : Some ( ConnectionState {
@@ -657,9 +678,11 @@ fn handle_subscribe(state: &mut RouterState, client_idx: ClientIndex, request: S
657678 publish : Arc < PublishTrasaction > ,
658679 }
659680
660- if ! state. clients . contains_key ( client_idx) {
681+ let Some ( client ) = state. clients . get ( client_idx) else {
661682 return ;
662- }
683+ } ;
684+
685+ let permissions = state. acl . get_topics_acl_config ( & client. user ) ;
663686
664687 // if state.connections[conn_id].message_tx.is_closed() {
665688 // return;
@@ -677,6 +700,14 @@ fn handle_subscribe(state: &mut RouterState, client_idx: ClientIndex, request: S
677700 // as they would have failed validation on the frontend.
678701 . ok_or ( SubscribeReasonCode :: Unspecified )
679702 . and_then ( |( filter, props) | {
703+ if !state. acl . check_acl_config (
704+ permissions,
705+ & filter,
706+ crate :: config:: acl:: TransactionType :: Subscribe ,
707+ ) {
708+ Err ( SubscribeReasonCode :: NotAuthorized ) ?
709+ }
710+
680711 let sub_kind = SubscriptionKind :: from_filter ( & filter)
681712 . map_err ( |_| SubscribeReasonCode :: NotAuthorized ) ?;
682713
@@ -904,6 +935,25 @@ fn dispatch(state: &mut RouterState, publish: Arc<PublishTrasaction>, origin: Pu
904935 } ,
905936 } ;
906937
938+ // Check if user has permission to publish
939+ if let Some ( client_index) = origin. get_client_index ( ) {
940+ let Some ( client) = state. clients . get ( client_index) else {
941+ return ;
942+ } ;
943+
944+ let topic_filter = Filter :: from ( & topic) ;
945+
946+ let topics_config = state. acl . get_topics_acl_config ( & client. user ) ;
947+
948+ if !state. acl . check_acl_config (
949+ topics_config,
950+ & topic_filter,
951+ crate :: config:: acl:: TransactionType :: Publish ,
952+ ) {
953+ return ;
954+ }
955+ }
956+
907957 // Only run this if TCE is not available.
908958 if state. tce . is_none ( ) && publish. meta . retain ( ) {
909959 let time_now = state. startup_time + state. startup_instant . elapsed ( ) ;
0 commit comments