use js_int::int;
use ruma_common::{
RoomVersionId, owned_event_id, owned_room_id,
room_version_rules::{AuthorizationRules, RoomIdFormatVersion},
};
use ruma_events::TimelineEventType;
use serde_json::json;
use test_log::test;
mod room_power_levels;
use super::check_room_create;
use crate::{
check_state_dependent_auth_rules, check_state_independent_auth_rules,
event_auth::check_room_redaction,
events::{RoomCreateEvent, RoomPowerLevelsEvent},
test_utils::{
Pdu, PublicChatInitialPdu, RoomCreatePduBuilder, RoomMemberPduContent,
RoomPowerLevelsPduContent, RoomTimelineFactory, UserFactory, default_room_id,
},
};
#[test]
fn valid_room_create() {
let alice_id = UserFactory::Alice.user_id();
let mut pdu = RoomCreatePduBuilder::new(RoomVersionId::V6).build();
check_room_create(RoomCreateEvent::new(&pdu), &AuthorizationRules::V6).unwrap();
pdu.set_content(json!({ "creator": alice_id }));
check_room_create(RoomCreateEvent::new(&pdu), &AuthorizationRules::V1).unwrap();
let mut pdu = RoomCreatePduBuilder::new(RoomVersionId::V11).build();
check_room_create(RoomCreateEvent::new(&pdu), &AuthorizationRules::V11).unwrap();
let contents_to_check = vec![
json!({
"room_version": "11",
"predecessor": "!XPoLiaavxVgyMSiRwK:localhost",
}),
json!({
"room_version": "11",
"type": true,
}),
];
for content in contents_to_check {
pdu.set_content(content);
check_room_create(RoomCreateEvent::new(&pdu), &AuthorizationRules::V11).unwrap();
}
pdu.set_content(json!({
"room_version": "11",
"additional_creators": ["@::example.org"]
}));
check_room_create(RoomCreateEvent::new(&pdu), &AuthorizationRules::V11).unwrap();
let pdu =
RoomCreatePduBuilder::new(RoomVersionId::V12).additional_creators(vec![alice_id]).build();
check_room_create(RoomCreateEvent::new(pdu), &AuthorizationRules::V12).unwrap();
}
#[test]
fn invalid_room_create() {
let valid_v6_pdu = RoomCreatePduBuilder::new(RoomVersionId::V6).build();
let valid_v12_pdu = RoomCreatePduBuilder::new(RoomVersionId::V12).build();
let mut pdu = valid_v6_pdu.clone();
pdu.prev_events.insert(owned_event_id!("$other-room-create"));
assert_eq!(
check_room_create(RoomCreateEvent::new(pdu), &AuthorizationRules::V6).unwrap_err(),
"`m.room.create` event cannot have previous events"
);
let mut pdu = valid_v6_pdu.clone();
let zara_id = UserFactory::Zara.user_id();
pdu.set_content(json!({
"creator": zara_id,
"room_version": "6",
}));
pdu.sender = zara_id;
assert_eq!(
check_room_create(RoomCreateEvent::new(pdu), &AuthorizationRules::V6).unwrap_err(),
"invalid `room_id` field in `m.room.create` event: server name does not match sender's server name"
);
let mut pdu = valid_v6_pdu;
pdu.set_content(json!({ "room_version": "6" }));
assert_eq!(
check_room_create(RoomCreateEvent::new(pdu), &AuthorizationRules::V6).unwrap_err(),
"missing `creator` field in `m.room.create` event"
);
let mut pdu = valid_v12_pdu.clone();
pdu.room_id = Some(owned_room_id!("!room:matrix.local"));
assert_eq!(
check_room_create(RoomCreateEvent::new(pdu), &AuthorizationRules::V12).unwrap_err(),
"`m.room.create` event cannot have a `room_id` field"
);
let mut pdu = valid_v12_pdu;
pdu.set_content(json!({
"room_version": "12",
"additional_creators": ["@::example.org"]
}));
assert_eq!(
check_room_create(RoomCreateEvent::new(pdu), &AuthorizationRules::V12).unwrap_err(),
"invalid `additional_creators` field in `m.room.create` event: server name is not a valid IP address or domain name at line 1 column 41"
);
}
#[test]
fn redact_higher_power_level() {
let alice_id = UserFactory::Alice.user_id();
let mut room_redaction_event = Pdu::with_minimal_fields(
owned_event_id!("$redaction:matrix.local"),
alice_id.clone(),
TimelineEventType::RoomRedaction,
json!({}),
);
room_redaction_event.redacts = Some(owned_event_id!("$other-event:other.local"));
let room_power_levels_event = Pdu::with_minimal_state_fields(
owned_event_id!("$powerlevels:matrix.local"),
alice_id.clone(),
TimelineEventType::RoomPowerLevels,
String::new(),
json!({}),
);
assert_eq!(
check_room_redaction(
room_redaction_event,
Some(RoomPowerLevelsEvent::new(room_power_levels_event)),
&AuthorizationRules::V1,
int!(0).into(),
)
.unwrap_err(),
"`m.room.redaction` event did not pass any of the allow rules"
);
}
#[test]
fn redact_same_power_level() {
let alice_id = UserFactory::Alice.user_id();
let mut room_redaction_event = Pdu::with_minimal_fields(
owned_event_id!("$redaction:matrix.local"),
alice_id.clone(),
TimelineEventType::RoomRedaction,
json!({}),
);
room_redaction_event.redacts = Some(owned_event_id!("$other-event:other.local"));
let room_power_levels_event = Pdu::with_minimal_state_fields(
owned_event_id!("$powerlevels:matrix.local"),
alice_id.clone(),
TimelineEventType::RoomPowerLevels,
String::new(),
json!({ "users": { alice_id: 50 }}),
);
check_room_redaction(
room_redaction_event,
Some(RoomPowerLevelsEvent::new(room_power_levels_event)),
&AuthorizationRules::V1,
int!(50).into(),
)
.unwrap();
}
#[test]
fn redact_same_server() {
let alice_id = UserFactory::Alice.user_id();
let mut room_redaction_event = Pdu::with_minimal_fields(
owned_event_id!("$redaction:matrix.local"),
alice_id.clone(),
TimelineEventType::RoomRedaction,
json!({}),
);
room_redaction_event.redacts = Some(owned_event_id!("$other-event:matrix.local"));
let room_power_levels_event = Pdu::with_minimal_state_fields(
owned_event_id!("$powerlevels:matrix.local"),
alice_id.clone(),
TimelineEventType::RoomPowerLevels,
String::new(),
json!({}),
);
check_room_redaction(
room_redaction_event,
Some(RoomPowerLevelsEvent::new(room_power_levels_event)),
&AuthorizationRules::V1,
int!(0).into(),
)
.unwrap();
}
#[test]
fn reject_missing_room_create_auth_events() {
let mut factory = RoomTimelineFactory::with_public_chat_preset(RoomVersionId::V6);
let mut pdu = factory.create_text_message(
owned_event_id!("$hello"),
UserFactory::Alice.user_id(),
"Hello!",
);
pdu.auth_events.remove(&PublicChatInitialPdu::RoomCreate.event_id());
assert_eq!(
check_state_independent_auth_rules(&AuthorizationRules::V6, pdu, factory.get_fn())
.unwrap_err(),
"no `m.room.create` event in auth events"
);
}
#[test]
fn no_federate() {
let mut factory = RoomTimelineFactory::with_public_chat_preset(RoomVersionId::V6);
factory.get_mut(&PublicChatInitialPdu::RoomCreate.event_id()).unwrap().set_content(json!({
"creator": UserFactory::Alice.user_id(),
"m.federate": false,
}));
let pdu = factory.create_room_member(
owned_event_id!("$room-member-zara-join"),
UserFactory::Zara.user_id(),
RoomMemberPduContent::Join,
);
assert_eq!(
check_state_dependent_auth_rules(&AuthorizationRules::V6, pdu, factory.state_event_fn())
.unwrap_err(),
"room is not federated and event's sender domain does not match `m.room.create` event's sender domain"
);
let pdu = factory.create_room_member(
owned_event_id!("$room-member-charlie-join"),
UserFactory::Charlie.user_id(),
RoomMemberPduContent::Join,
);
check_state_dependent_auth_rules(&AuthorizationRules::V6, pdu, factory.state_event_fn())
.unwrap();
}
#[test]
fn room_aliases_no_state_key() {
let mut factory = RoomTimelineFactory::with_public_chat_preset(RoomVersionId::V6);
let mut pdu = Pdu::with_minimal_fields(
owned_event_id!("$room-aliases"),
UserFactory::Alice.user_id(),
"m.room.aliases".into(),
json!({
"aliases": [
"#alias:matrix.local",
"#other_alias:matrix.local",
],
}),
);
factory.prepare_to_add_pdu(&mut pdu);
assert_eq!(
check_state_dependent_auth_rules(&AuthorizationRules::V3, &pdu, factory.state_event_fn())
.unwrap_err(),
"server name of the `state_key` of `m.room.aliases` event does not match the server name of the sender"
);
check_state_dependent_auth_rules(&AuthorizationRules::V6, &pdu, factory.state_event_fn())
.unwrap();
}
#[test]
fn room_aliases_other_server() {
let mut factory = RoomTimelineFactory::with_public_chat_preset(RoomVersionId::V6);
let mut pdu = Pdu::with_minimal_state_fields(
owned_event_id!("$room-aliases"),
UserFactory::Alice.user_id(),
"m.room.aliases".into(),
"other.local".to_owned(),
json!({
"aliases": [
"#alias:matrix.local",
"#other_alias:matrix.local",
],
}),
);
factory.prepare_to_add_pdu(&mut pdu);
assert_eq!(
check_state_dependent_auth_rules(&AuthorizationRules::V3, &pdu, factory.state_event_fn())
.unwrap_err(),
"server name of the `state_key` of `m.room.aliases` event does not match the server name of the sender"
);
check_state_dependent_auth_rules(&AuthorizationRules::V8, &pdu, factory.state_event_fn())
.unwrap();
}
#[test]
fn room_aliases_same_server() {
let mut factory = RoomTimelineFactory::with_public_chat_preset(RoomVersionId::V6);
let mut pdu = Pdu::with_minimal_state_fields(
owned_event_id!("$room-aliases"),
UserFactory::Alice.user_id(),
"m.room.aliases".into(),
"matrix.local".to_owned(),
json!({
"aliases": [
"#alias:matrix.local",
"#other_alias:matrix.local",
],
}),
);
factory.prepare_to_add_pdu(&mut pdu);
check_state_dependent_auth_rules(&AuthorizationRules::V3, &pdu, factory.state_event_fn())
.unwrap();
check_state_dependent_auth_rules(&AuthorizationRules::V8, &pdu, factory.state_event_fn())
.unwrap();
}
#[test]
fn sender_not_in_room() {
let mut factory = RoomTimelineFactory::with_public_chat_preset(RoomVersionId::V6);
let pdu = factory.create_text_message(
owned_event_id!("$hello"),
UserFactory::Charlie.user_id(),
"Hello!",
);
assert_eq!(
check_state_dependent_auth_rules(&AuthorizationRules::V6, pdu, factory.state_event_fn())
.unwrap_err(),
"sender's membership is not `join`"
);
}
#[test]
fn room_third_party_invite() {
let mut factory = RoomTimelineFactory::with_public_chat_preset(RoomVersionId::V6);
let mut pdu = factory.create_room_third_party_invite();
check_state_dependent_auth_rules(&AuthorizationRules::V6, &pdu, factory.state_event_fn())
.unwrap();
factory.add_room_power_levels(
owned_event_id!("$room-power-levels-invite"),
UserFactory::Alice.user_id(),
RoomPowerLevelsPduContent::Invite { value: 50 },
);
factory.prepare_to_add_pdu(&mut pdu);
assert_eq!(
check_state_dependent_auth_rules(&AuthorizationRules::V6, &pdu, factory.state_event_fn())
.unwrap_err(),
"sender does not have enough power to send invites in this room"
);
}
#[test]
fn event_type_not_enough_power() {
let mut factory = RoomTimelineFactory::with_public_chat_preset(RoomVersionId::V6);
let mut pdu = factory.create_text_message(
owned_event_id!("$hello"),
UserFactory::Bob.user_id(),
"Hello!",
);
check_state_dependent_auth_rules(&AuthorizationRules::V6, &pdu, factory.state_event_fn())
.unwrap();
let alice_id = UserFactory::Alice.user_id();
factory.add_room_power_levels(
owned_event_id!("$room-power-levels-invite"),
alice_id.clone(),
RoomPowerLevelsPduContent::Events {
event_types: vec![TimelineEventType::RoomMessage],
value: 10,
},
);
factory.prepare_to_add_pdu(&mut pdu);
assert_eq!(
check_state_dependent_auth_rules(&AuthorizationRules::V6, pdu, factory.state_event_fn())
.unwrap_err(),
"sender does not have enough power to send event of type `m.room.message`"
);
}
#[test]
fn user_id_state_key_not_sender() {
let mut factory = RoomTimelineFactory::with_public_chat_preset(RoomVersionId::V6);
let mut pdu = Pdu::with_minimal_state_fields(
owned_event_id!("$fake-state-event"),
UserFactory::Alice.user_id(),
"dev.ruma.fake_state_event".into(),
UserFactory::Zara.user_id().into(),
json!({}),
);
factory.prepare_to_add_pdu(&mut pdu);
assert_eq!(
check_state_dependent_auth_rules(&AuthorizationRules::V6, pdu, factory.state_event_fn())
.unwrap_err(),
"sender cannot send event with `state_key` matching another user's ID"
);
}
#[test]
fn user_id_state_key_is_sender() {
let mut factory = RoomTimelineFactory::with_public_chat_preset(RoomVersionId::V6);
let alice_id = UserFactory::Alice.user_id();
let mut pdu = Pdu::with_minimal_state_fields(
owned_event_id!("$fake-state-event"),
alice_id.clone(),
"dev.ruma.fake_state_event".into(),
alice_id.into(),
json!({}),
);
factory.prepare_to_add_pdu(&mut pdu);
check_state_dependent_auth_rules(&AuthorizationRules::V6, pdu, factory.state_event_fn())
.unwrap();
}
#[test]
fn auth_event_in_different_room() {
let mut factory = RoomCreatePduBuilder::new(RoomVersionId::V6).build_factory();
let mut pdu = factory.create_room_member(
owned_event_id!("$room-member-alice-join"),
UserFactory::Alice.user_id(),
RoomMemberPduContent::Join,
);
pdu.room_id = Some(owned_room_id!("!wrongroom:matrix.local"));
assert_eq!(
check_state_independent_auth_rules(&AuthorizationRules::V6, pdu, factory.get_fn())
.unwrap_err(),
"auth event $room-create not in the same room"
);
}
#[test]
fn duplicate_auth_event_type() {
let mut factory = RoomTimelineFactory::with_public_chat_preset(RoomVersionId::V6);
let alice_id = UserFactory::Alice.user_id();
factory.add_room_member(
owned_event_id!("$room-member-alice-displayname"),
alice_id.clone(),
RoomMemberPduContent::DisplayName { displayname: "Alice".to_owned() },
);
let mut pdu = factory.create_text_message(owned_event_id!("$hello"), alice_id, "Hello!");
pdu.auth_events.insert(PublicChatInitialPdu::RoomMemberAliceJoin.event_id());
assert_eq!(
check_state_independent_auth_rules(&AuthorizationRules::V6, pdu, factory.get_fn())
.unwrap_err(),
"duplicate auth event $room-member-alice-join for (m.room.member, @alice:matrix.local) pair"
);
}
#[test]
fn unexpected_auth_event_type() {
let mut factory = RoomTimelineFactory::with_public_chat_preset(RoomVersionId::V6);
let mut pdu = factory.create_text_message(
owned_event_id!("$hello"),
UserFactory::Alice.user_id(),
"Hello!",
);
pdu.auth_events.insert(PublicChatInitialPdu::RoomJoinRules.event_id());
assert_eq!(
check_state_independent_auth_rules(&AuthorizationRules::V6, pdu, factory.get_fn())
.unwrap_err(),
"unexpected auth event $room-join-rules with (m.room.join_rules, ) pair"
);
}
#[test]
fn rejected_auth_event() {
let mut factory = RoomTimelineFactory::with_public_chat_preset(RoomVersionId::V6);
let charlie_id = UserFactory::Charlie.user_id();
let room_member_charlie_knock_pdu = factory.add_room_member(
owned_event_id!("$room-member-charlie-knock"),
charlie_id.clone(),
RoomMemberPduContent::Knock,
);
room_member_charlie_knock_pdu.rejected = true;
let room_member_charlie_invite_pdu = factory.create_room_member(
owned_event_id!("$room-member-charlie-invite"),
charlie_id,
RoomMemberPduContent::Invite { sender: UserFactory::Bob.user_id() },
);
assert_eq!(
check_state_independent_auth_rules(
&AuthorizationRules::V6,
room_member_charlie_invite_pdu,
factory.get_fn(),
)
.unwrap_err(),
"rejected auth event $room-member-charlie-knock"
);
}
#[test]
fn room_create_with_allowed_or_rejected_room_id() {
let room_create_v11 = RoomCreatePduBuilder::new(RoomVersionId::V11).build();
let room_create_v12 = RoomCreatePduBuilder::new(RoomVersionId::V12).build();
check_room_create(RoomCreateEvent::new(&room_create_v11), &AuthorizationRules::V11).unwrap();
assert_eq!(
check_room_create(RoomCreateEvent::new(&room_create_v12), &AuthorizationRules::V11)
.unwrap_err(),
"missing `room_id` field in `m.room.create` event"
);
assert_eq!(
check_room_create(RoomCreateEvent::new(&room_create_v11), &AuthorizationRules::V12)
.unwrap_err(),
"`m.room.create` event cannot have a `room_id` field"
);
check_room_create(RoomCreateEvent::new(&room_create_v12), &AuthorizationRules::V12).unwrap();
}
#[test]
fn event_without_room_id() {
let mut factory = RoomTimelineFactory::with_public_chat_preset(RoomVersionId::V11);
let mut pdu = factory.create_text_message(
owned_event_id!("$hello"),
UserFactory::Alice.user_id(),
"Hello!",
);
pdu.room_id.take();
assert_eq!(
check_state_independent_auth_rules(&AuthorizationRules::V11, pdu, factory.get_fn())
.unwrap_err(),
"missing `room_id` field for event"
);
}
#[test]
fn allow_missing_room_create_auth_events() {
let mut factory = RoomTimelineFactory::with_public_chat_preset(RoomVersionId::V12);
let pdu = factory.create_text_message(
owned_event_id!("$hello"),
UserFactory::Alice.user_id(),
"Hello!",
);
assert!(!pdu.auth_events.contains(&PublicChatInitialPdu::RoomCreate.event_id()));
check_state_independent_auth_rules(&AuthorizationRules::V12, pdu, factory.get_fn()).unwrap();
}
#[test]
fn reject_room_create_in_auth_events() {
let mut factory = RoomTimelineFactory::with_public_chat_preset(RoomVersionId::V12);
let mut pdu = factory.create_text_message(
owned_event_id!("$hello"),
UserFactory::Alice.user_id(),
"Hello!",
);
pdu.auth_events.insert(PublicChatInitialPdu::RoomCreate.event_id());
assert_eq!(
check_state_independent_auth_rules(&AuthorizationRules::V12, pdu, factory.get_fn())
.unwrap_err(),
"auth event $room-create not in the same room"
);
}
#[test]
fn missing_room_create_in_fetch_event() {
let mut factory = RoomTimelineFactory::with_public_chat_preset(RoomVersionId::V12);
let pdu = factory.create_text_message(
owned_event_id!("$hello"),
UserFactory::Alice.user_id(),
"Hello!",
);
factory.remove(&PublicChatInitialPdu::RoomCreate.event_id());
assert_eq!(
check_state_independent_auth_rules(&AuthorizationRules::V12, pdu, factory.get_fn())
.unwrap_err(),
"failed to find `m.room.create` event $room-create"
);
}
#[test]
fn rejected_room_create_in_fetch_event() {
let mut room_create_pdu = RoomCreatePduBuilder::new(RoomVersionId::V12).build();
room_create_pdu.rejected = true;
let room_create_event_id = room_create_pdu.event_id.clone();
let alice_id = UserFactory::Alice.user_id();
let mut room_member_pdu = Pdu::with_minimal_state_fields(
owned_event_id!("$room-member-alice-join"),
alice_id.clone(),
TimelineEventType::RoomMember,
alice_id.into(),
json!({ "membership": "join" }),
);
room_member_pdu.room_id = Some(default_room_id(&RoomIdFormatVersion::V2));
room_member_pdu.prev_events.insert(room_create_event_id.clone());
assert_eq!(
check_state_independent_auth_rules(&AuthorizationRules::V12, room_member_pdu, |event_id| {
(event_id == room_create_event_id).then_some(&room_create_pdu)
})
.unwrap_err(),
"rejected `m.room.create` event $room-create"
);
}