1212// See the License for the specific language governing permissions and
1313// limitations under the License.
1414
15+ use std:: borrow:: Cow ;
1516use std:: collections:: BTreeMap ;
1617
18+ use crate :: push:: { PushRule , PushRules } ;
1719use anyhow:: { Context , Error } ;
1820use lazy_static:: lazy_static;
1921use log:: warn;
@@ -32,7 +34,30 @@ lazy_static! {
3234
3335 /// Used to determine which MSC3931 room version feature flags are actually known to
3436 /// the push evaluator.
35- static ref KNOWN_RVER_FLAGS : Vec <String > = vec![ ] ;
37+ static ref KNOWN_RVER_FLAGS : Vec <String > = vec![
38+ RoomVersionFeatures :: ExtensibleEvents . as_str( ) . to_string( ) ,
39+ ] ;
40+
41+ /// The "safe" rule IDs which are not affected by MSC3932's behaviour (room versions which
42+ /// declare Extensible Events support ultimately *disable* push rules which do not declare
43+ /// *any* MSC3931 room_version_supports condition).
44+ static ref SAFE_EXTENSIBLE_EVENTS_RULE_IDS : Vec <String > = vec![
45+ "global/override/.m.rule.master" . to_string( ) ,
46+ "global/override/.m.rule.roomnotif" . to_string( ) ,
47+ "global/content/.m.rule.contains_user_name" . to_string( ) ,
48+ ] ;
49+ }
50+
51+ enum RoomVersionFeatures {
52+ ExtensibleEvents ,
53+ }
54+
55+ impl RoomVersionFeatures {
56+ fn as_str ( & self ) -> & ' static str {
57+ match self {
58+ RoomVersionFeatures :: ExtensibleEvents => "org.matrix.msc3932.extensible_events" ,
59+ }
60+ }
3661}
3762
3863/// Allows running a set of push rules against a particular event.
@@ -121,7 +146,22 @@ impl PushRuleEvaluator {
121146 continue ;
122147 }
123148
149+ let rule_id = & push_rule. rule_id ( ) . to_string ( ) ;
150+ let extev_flag = & RoomVersionFeatures :: ExtensibleEvents . as_str ( ) . to_string ( ) ;
151+ let supports_extensible_events = self . room_version_feature_flags . contains ( extev_flag) ;
152+ let safe_from_rver_condition = SAFE_EXTENSIBLE_EVENTS_RULE_IDS . contains ( rule_id) ;
153+ let mut has_rver_condition = false ;
154+
124155 for condition in push_rule. conditions . iter ( ) {
156+ has_rver_condition = has_rver_condition
157+ || match condition {
158+ Condition :: Known ( known) => match known {
159+ // per MSC3932, we just need *any* room version condition to match
160+ KnownCondition :: RoomVersionSupports { feature : _ } => true ,
161+ _ => false ,
162+ } ,
163+ _ => false ,
164+ } ;
125165 match self . match_condition ( condition, user_id, display_name) {
126166 Ok ( true ) => { }
127167 Ok ( false ) => continue ' outer,
@@ -132,6 +172,13 @@ impl PushRuleEvaluator {
132172 }
133173 }
134174
175+ // MSC3932: Disable push rules in extensible event-supporting room versions if they
176+ // don't describe *any* MSC3931 room version condition, unless the rule is on the
177+ // safe list.
178+ if !has_rver_condition && !safe_from_rver_condition && supports_extensible_events {
179+ continue ;
180+ }
181+
135182 let actions = push_rule
136183 . actions
137184 . iter ( )
@@ -394,3 +441,51 @@ fn push_rule_evaluator() {
394441 let result = evaluator. run ( & FilteredPushRules :: default ( ) , None , Some ( "bob" ) ) ;
395442 assert_eq ! ( result. len( ) , 3 ) ;
396443}
444+
445+ #[ test]
446+ fn test_requires_room_version_supports_condition ( ) {
447+ let mut flattened_keys = BTreeMap :: new ( ) ;
448+ flattened_keys. insert ( "content.body" . to_string ( ) , "foo bar bob hello" . to_string ( ) ) ;
449+ let flags = vec ! [ RoomVersionFeatures :: ExtensibleEvents . as_str( ) . to_string( ) ] ;
450+ let evaluator = PushRuleEvaluator :: py_new (
451+ flattened_keys,
452+ 10 ,
453+ Some ( 0 ) ,
454+ BTreeMap :: new ( ) ,
455+ BTreeMap :: new ( ) ,
456+ false ,
457+ flags,
458+ true ,
459+ )
460+ . unwrap ( ) ;
461+
462+ // first test: are the master and contains_user_name rules excluded from the "requires room
463+ // version condition" check?
464+ let mut result = evaluator. run (
465+ & FilteredPushRules :: default ( ) ,
466+ Some ( "@bob:example.org" ) ,
467+ None ,
468+ ) ;
469+ assert_eq ! ( result. len( ) , 3 ) ;
470+
471+ // second test: if an appropriate push rule is in play, does it get handled?
472+ let custom_rule = PushRule {
473+ rule_id : Cow :: from ( "global/underride/.org.example.extensible" ) ,
474+ priority_class : 1 , // underride
475+ conditions : Cow :: from ( vec ! [ Condition :: Known (
476+ KnownCondition :: RoomVersionSupports {
477+ feature: Cow :: from( RoomVersionFeatures :: ExtensibleEvents . as_str( ) . to_string( ) ) ,
478+ } ,
479+ ) ] ) ,
480+ actions : Cow :: from ( vec ! [ Action :: Notify ] ) ,
481+ default : false ,
482+ default_enabled : true ,
483+ } ;
484+ let rules = PushRules :: new ( vec ! [ custom_rule] ) ;
485+ result = evaluator. run (
486+ & FilteredPushRules :: py_new ( rules, BTreeMap :: new ( ) , true ) ,
487+ None ,
488+ None ,
489+ ) ;
490+ assert_eq ! ( result. len( ) , 1 ) ;
491+ }
0 commit comments