Compare commits

..

12 Commits

Author SHA1 Message Date
Jade Ellis
4c01274886 feat: Typing notifications in simplified sliding sync
What's missing? Being able to use separate rooms & lists for typing
indicators.
At the moment, we use the same ones as we use for the timeline, as
todo_rooms is quite intertwined. We need to disentangle this to get that
functionality, although I'm not sure if clients use it.
2025-06-14 19:41:00 +01:00
Jade Ellis
5d44653e3a fix: Incorrect command descriptions 2025-06-14 16:51:24 +01:00
Jade Ellis
44e60d0ea6 docs: Tiny phrasing changes to the security policy 2025-06-14 16:34:58 +01:00
Jade Ellis
d7514178ab ci: Fix extra bracket in commit shorthash 2025-06-13 14:30:26 +01:00
Jade Ellis
1d45e0b68c feat: Add warning when admin users will be exposed as support contacts 2025-06-13 13:39:50 +01:00
Jade Ellis
3c44dccd65 ci: HACK, disable saving to actions cache 2025-05-26 19:16:50 +01:00
Jade Ellis
b57be072c7 build: Don't rerun on git changes 2025-05-26 19:16:05 +01:00
Jade Ellis
ea5dc8e09d fix: Use correct brand in clap version string 2025-05-26 19:16:05 +01:00
Jade Ellis
b9d60c64e5 ci: Don't specify container for image builder 2025-05-26 19:16:04 +01:00
Jade Ellis
94ae824149 ci: Don't install rustup if it's already there 2025-05-26 19:16:03 +01:00
Jade Ellis
640714922b feat: For knock_restricted rooms, automatically join rooms we meet
restrictions for rather than knocking
2025-05-26 19:16:03 +01:00
Jade Ellis
2b268fdaf3 fix: Allow joining via invite for knock_restricted rooms 2025-05-26 19:16:01 +01:00
20 changed files with 269 additions and 109 deletions

View File

@@ -19,11 +19,20 @@ outputs:
rustc_version:
description: The rustc version installed
value: ${{ steps.rustc-version.outputs.version }}
rustup_version:
description: The rustup version installed
value: ${{ steps.rustup-version.outputs.version }}
runs:
using: composite
steps:
- name: Check if rustup is already installed
shell: bash
id: rustup-version
run: |
echo "version=$(rustup --version)" >> $GITHUB_OUTPUT
- name: Cache rustup toolchains
if: steps.rustup-version.outputs.version == ''
uses: actions/cache@v3
with:
path: |
@@ -33,6 +42,7 @@ runs:
# Requires repo to be cloned if toolchain is not specified
key: ${{ runner.os }}-rustup-${{ inputs.toolchain || hashFiles('**/rust-toolchain.toml') }}
- name: Install Rust toolchain
if: steps.rustup-version.outputs.version == ''
shell: bash
run: |
if ! command -v rustup &> /dev/null ; then

View File

@@ -57,7 +57,6 @@ jobs:
build-image:
runs-on: dind
container: ghcr.io/catthehacker/ubuntu:act-latest
needs: define-variables
permissions:
contents: read
@@ -181,14 +180,14 @@ jobs:
file: "docker/Dockerfile"
build-args: |
GIT_COMMIT_HASH=${{ github.sha }})
GIT_COMMIT_HASH_SHORT=${{ env.COMMIT_SHORT_SHA }})
GIT_COMMIT_HASH_SHORT=${{ env.COMMIT_SHORT_SHA }}
GIT_REMOTE_URL=${{github.event.repository.html_url }}
GIT_REMOTE_COMMIT_URL=${{github.event.head_commit.url }}
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
annotations: ${{ steps.meta.outputs.annotations }}
cache-from: type=gha
cache-to: type=gha,mode=max
# cache-to: type=gha,mode=max
sbom: true
outputs: type=image,"name=${{ needs.define-variables.outputs.images_list }}",push-by-digest=true,name-canonical=true,push=true
env:
@@ -211,7 +210,6 @@ jobs:
merge:
runs-on: dind
container: ghcr.io/catthehacker/ubuntu:act-latest
needs: [define-variables, build-image]
steps:
- name: Download digests

View File

@@ -1,5 +1,5 @@
[files]
extend-exclude = ["*.csr"]
extend-exclude = ["*.csr", "*.lock"]
[default.extend-words]
"allocatedp" = "allocatedp"

22
Cargo.lock generated
View File

@@ -3695,7 +3695,7 @@ dependencies = [
[[package]]
name = "ruma"
version = "0.10.1"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=d6870a7fb7f6cccff63f7fd0ff6c581bad80e983#d6870a7fb7f6cccff63f7fd0ff6c581bad80e983"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a48665b682be1016cea53ea5e7787442dfe7c1de#a48665b682be1016cea53ea5e7787442dfe7c1de"
dependencies = [
"assign",
"js_int",
@@ -3715,7 +3715,7 @@ dependencies = [
[[package]]
name = "ruma-appservice-api"
version = "0.10.0"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=d6870a7fb7f6cccff63f7fd0ff6c581bad80e983#d6870a7fb7f6cccff63f7fd0ff6c581bad80e983"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a48665b682be1016cea53ea5e7787442dfe7c1de#a48665b682be1016cea53ea5e7787442dfe7c1de"
dependencies = [
"js_int",
"ruma-common",
@@ -3727,7 +3727,7 @@ dependencies = [
[[package]]
name = "ruma-client-api"
version = "0.18.0"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=d6870a7fb7f6cccff63f7fd0ff6c581bad80e983#d6870a7fb7f6cccff63f7fd0ff6c581bad80e983"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a48665b682be1016cea53ea5e7787442dfe7c1de#a48665b682be1016cea53ea5e7787442dfe7c1de"
dependencies = [
"as_variant",
"assign",
@@ -3750,7 +3750,7 @@ dependencies = [
[[package]]
name = "ruma-common"
version = "0.13.0"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=d6870a7fb7f6cccff63f7fd0ff6c581bad80e983#d6870a7fb7f6cccff63f7fd0ff6c581bad80e983"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a48665b682be1016cea53ea5e7787442dfe7c1de#a48665b682be1016cea53ea5e7787442dfe7c1de"
dependencies = [
"as_variant",
"base64 0.22.1",
@@ -3782,7 +3782,7 @@ dependencies = [
[[package]]
name = "ruma-events"
version = "0.28.1"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=d6870a7fb7f6cccff63f7fd0ff6c581bad80e983#d6870a7fb7f6cccff63f7fd0ff6c581bad80e983"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a48665b682be1016cea53ea5e7787442dfe7c1de#a48665b682be1016cea53ea5e7787442dfe7c1de"
dependencies = [
"as_variant",
"indexmap 2.9.0",
@@ -3807,7 +3807,7 @@ dependencies = [
[[package]]
name = "ruma-federation-api"
version = "0.9.0"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=d6870a7fb7f6cccff63f7fd0ff6c581bad80e983#d6870a7fb7f6cccff63f7fd0ff6c581bad80e983"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a48665b682be1016cea53ea5e7787442dfe7c1de#a48665b682be1016cea53ea5e7787442dfe7c1de"
dependencies = [
"bytes",
"headers",
@@ -3829,7 +3829,7 @@ dependencies = [
[[package]]
name = "ruma-identifiers-validation"
version = "0.9.5"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=d6870a7fb7f6cccff63f7fd0ff6c581bad80e983#d6870a7fb7f6cccff63f7fd0ff6c581bad80e983"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a48665b682be1016cea53ea5e7787442dfe7c1de#a48665b682be1016cea53ea5e7787442dfe7c1de"
dependencies = [
"js_int",
"thiserror 2.0.12",
@@ -3838,7 +3838,7 @@ dependencies = [
[[package]]
name = "ruma-identity-service-api"
version = "0.9.0"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=d6870a7fb7f6cccff63f7fd0ff6c581bad80e983#d6870a7fb7f6cccff63f7fd0ff6c581bad80e983"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a48665b682be1016cea53ea5e7787442dfe7c1de#a48665b682be1016cea53ea5e7787442dfe7c1de"
dependencies = [
"js_int",
"ruma-common",
@@ -3848,7 +3848,7 @@ dependencies = [
[[package]]
name = "ruma-macros"
version = "0.13.0"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=d6870a7fb7f6cccff63f7fd0ff6c581bad80e983#d6870a7fb7f6cccff63f7fd0ff6c581bad80e983"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a48665b682be1016cea53ea5e7787442dfe7c1de#a48665b682be1016cea53ea5e7787442dfe7c1de"
dependencies = [
"cfg-if",
"proc-macro-crate",
@@ -3863,7 +3863,7 @@ dependencies = [
[[package]]
name = "ruma-push-gateway-api"
version = "0.9.0"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=d6870a7fb7f6cccff63f7fd0ff6c581bad80e983#d6870a7fb7f6cccff63f7fd0ff6c581bad80e983"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a48665b682be1016cea53ea5e7787442dfe7c1de#a48665b682be1016cea53ea5e7787442dfe7c1de"
dependencies = [
"js_int",
"ruma-common",
@@ -3875,7 +3875,7 @@ dependencies = [
[[package]]
name = "ruma-signatures"
version = "0.15.0"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=d6870a7fb7f6cccff63f7fd0ff6c581bad80e983#d6870a7fb7f6cccff63f7fd0ff6c581bad80e983"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a48665b682be1016cea53ea5e7787442dfe7c1de#a48665b682be1016cea53ea5e7787442dfe7c1de"
dependencies = [
"base64 0.22.1",
"ed25519-dalek",

View File

@@ -350,7 +350,7 @@ version = "0.1.2"
[workspace.dependencies.ruma]
git = "https://forgejo.ellis.link/continuwuation/ruwuma"
#branch = "conduwuit-changes"
rev = "d6870a7fb7f6cccff63f7fd0ff6c581bad80e983"
rev = "a48665b682be1016cea53ea5e7787442dfe7c1de"
features = [
"compat",
"rand",
@@ -381,7 +381,7 @@ features = [
"unstable-msc4121",
"unstable-msc4125",
"unstable-msc4186",
"unstable-msc4203", # sending to-device events to appservices
"unstable-msc4203", # sending to-device events to appservices
"unstable-msc4210", # remove legacy mentions
"unstable-extensible-events",
"unstable-pdu",

View File

@@ -20,10 +20,10 @@ ### Responsible Disclosure
We appreciate the efforts of security researchers and the community in identifying and reporting vulnerabilities. To ensure that potential vulnerabilities are addressed properly, please follow these guidelines:
1. Contact members of the team over E2EE private message.
1. **Contact members of the team directly** over E2EE private message.
- [@jade:ellis.link](https://matrix.to/#/@jade:ellis.link)
- [@nex:nexy7574.co.uk](https://matrix.to/#/@nex:nexy7574.co.uk) <!-- ? -->
2. **Email the security team** directly at [security@continuwuity.org](mailto:security@continuwuity.org). This is not E2EE, so don't include sensitive details.
2. **Email the security team** at [security@continuwuity.org](mailto:security@continuwuity.org). This is not E2EE, so don't include sensitive details.
3. **Do not disclose the vulnerability publicly** until it has been addressed
4. **Provide detailed information** about the vulnerability, including:
- A clear description of the issue
@@ -48,7 +48,7 @@ ## Security Update Process
When security vulnerabilities are identified:
1. We will develop and test fixes in a private branch
1. We will develop and test fixes in a private fork
2. Security updates will be released as soon as possible
3. Release notes will include information about the vulnerabilities, avoiding details that could facilitate exploitation where possible
4. Critical security updates may be backported to the previous stable release

View File

@@ -125,13 +125,13 @@ pub(super) enum DebugCommand {
reset: bool,
},
/// - Verify json signatures
/// - Sign JSON blob
///
/// This command needs a JSON blob provided in a Markdown code block below
/// the command.
SignJson,
/// - Verify json signatures
/// - Verify JSON signatures
///
/// This command needs a JSON blob provided in a Markdown code block below
/// the command.

View File

@@ -1243,7 +1243,6 @@ async fn join_room_by_id_helper_remote(
services.rooms.timeline.get_pdu(event_id).await.ok()
};
debug!("running stateres check on send_join parsed PDU");
let auth_check = state_res::event_auth::auth_check(
&state_res::RoomVersion::new(&room_version_id)?,
&parsed_join_pdu,
@@ -2163,6 +2162,109 @@ async fn knock_room_by_id_helper(
}
}
// For knock_restricted rooms, check if the user meets the restricted conditions
// If they do, attempt to join instead of knock
// This is not mentioned in the spec, but should be allowable (we're allowed to
// auto-join invites to knocked rooms)
let join_rule = services.rooms.state_accessor.get_join_rules(room_id).await;
if let JoinRule::KnockRestricted(restricted) = &join_rule {
let restriction_rooms: Vec<_> = restricted
.allow
.iter()
.filter_map(|a| match a {
| AllowRule::RoomMembership(r) => Some(&r.room_id),
| _ => None,
})
.collect();
// Check if the user is in any of the allowed rooms
let mut user_meets_restrictions = false;
for restriction_room_id in &restriction_rooms {
if services
.rooms
.state_cache
.is_joined(sender_user, restriction_room_id)
.await
{
user_meets_restrictions = true;
break;
}
}
// If the user meets the restrictions, try joining instead
if user_meets_restrictions {
debug_info!(
"{sender_user} meets the restricted criteria in knock_restricted room \
{room_id}, attempting to join instead of knock"
);
// For this case, we need to drop the state lock and get a new one in
// join_room_by_id_helper We need to release the lock here and let
// join_room_by_id_helper acquire it again
drop(state_lock);
match join_room_by_id_helper(
services,
sender_user,
room_id,
reason.clone(),
servers,
None,
&None,
)
.await
{
| Ok(_) => return Ok(knock_room::v3::Response::new(room_id.to_owned())),
| Err(e) => {
debug_warn!(
"Failed to convert knock to join for {sender_user} in {room_id}: {e:?}"
);
// Get a new state lock for the remaining knock logic
let new_state_lock = services.rooms.state.mutex.lock(room_id).await;
let server_in_room = services
.rooms
.state_cache
.server_in_room(services.globals.server_name(), room_id)
.await;
let local_knock = server_in_room
|| servers.is_empty()
|| (servers.len() == 1 && services.globals.server_is_ours(&servers[0]));
if local_knock {
knock_room_helper_local(
services,
sender_user,
room_id,
reason,
servers,
new_state_lock,
)
.boxed()
.await?;
} else {
knock_room_helper_remote(
services,
sender_user,
room_id,
reason,
servers,
new_state_lock,
)
.boxed()
.await?;
}
return Ok(knock_room::v3::Response::new(room_id.to_owned()));
},
}
}
} else if !matches!(join_rule, JoinRule::Knock | JoinRule::KnockRestricted(_)) {
debug_warn!(
"{sender_user} attempted to knock on room {room_id} but its join rule is \
{join_rule:?}, not knock or knock_restricted"
);
}
let server_in_room = services
.rooms
.state_cache
@@ -2210,6 +2312,12 @@ async fn knock_room_helper_local(
return Err!(Request(Forbidden("This room does not support knocking.")));
}
// Verify that this room has a valid knock or knock_restricted join rule
let join_rule = services.rooms.state_accessor.get_join_rules(room_id).await;
if !matches!(join_rule, JoinRule::Knock | JoinRule::KnockRestricted(_)) {
return Err!(Request(Forbidden("This room's join rule does not allow knocking.")));
}
let content = RoomMemberEventContent {
displayname: services.users.displayname(sender_user).await.ok(),
avatar_url: services.users.avatar_url(sender_user).await.ok(),

View File

@@ -808,7 +808,7 @@ async fn load_joined_room(
let typings = services
.rooms
.typing
.typings_all(room_id, sender_user)
.typings_event_for_user(room_id, sender_user)
.await?;
Ok(vec![serde_json::from_str(&serde_json::to_string(&typings)?)?])

View File

@@ -33,6 +33,7 @@
events::{
AnyRawAccountDataEvent, AnySyncEphemeralRoomEvent, StateEventType, TimelineEventType,
room::member::{MembershipState, RoomMemberEventContent},
typing::TypingEventContent,
},
serde::Raw,
uint,
@@ -205,6 +206,9 @@ pub(crate) async fn sync_events_v5_route(
_ = tokio::time::timeout(duration, watcher).await;
}
let typing = collect_typing_events(services, sender_user, &body, &todo_rooms).await?;
response.extensions.typing = typing;
trace!(
rooms = ?response.rooms.len(),
account_data = ?response.extensions.account_data.rooms.len(),
@@ -288,6 +292,8 @@ async fn handle_lists<'a, Rooms, AllRooms>(
Rooms: Iterator<Item = &'a RoomId> + Clone + Send + 'a,
AllRooms: Iterator<Item = &'a RoomId> + Clone + Send + 'a,
{
// TODO MSC4186: Implement remaining list filters: is_dm, is_encrypted,
// room_types.
for (list_id, list) in &body.lists {
let active_rooms: Vec<_> = match list.filters.as_ref().and_then(|f| f.is_invite) {
| None => all_rooms.clone().collect(),
@@ -665,6 +671,62 @@ async fn process_rooms<'a, Rooms>(
}
Ok(rooms)
}
async fn collect_typing_events(
services: &Services,
sender_user: &UserId,
body: &sync_events::v5::Request,
todo_rooms: &TodoRooms,
) -> Result<sync_events::v5::response::Typing> {
if !body.extensions.typing.enabled.unwrap_or(false) {
return Ok(sync_events::v5::response::Typing::default());
}
let rooms: Vec<_> = body.extensions.typing.rooms.clone().unwrap_or_else(|| {
body.room_subscriptions
.keys()
.map(ToOwned::to_owned)
.collect()
});
let lists: Vec<_> = body
.extensions
.typing
.lists
.clone()
.unwrap_or_else(|| body.lists.keys().map(ToOwned::to_owned).collect::<Vec<_>>());
if rooms.is_empty() && lists.is_empty() {
return Ok(sync_events::v5::response::Typing::default());
}
let mut typing_response = sync_events::v5::response::Typing::default();
for (room_id, (required_state_request, timeline_limit, roomsince)) in todo_rooms {
if services.rooms.typing.last_typing_update(room_id).await? <= *roomsince {
continue;
}
match services
.rooms
.typing
.typing_users_for_user(room_id, sender_user)
.await
{
| Ok(typing_users) => {
typing_response.rooms.insert(
room_id.to_owned(), // Already OwnedRoomId
Raw::new(&sync_events::v5::response::SyncTypingEvent {
content: TypingEventContent::new(typing_users),
})?,
);
},
| Err(e) => {
warn!(%room_id, "Failed to get typing events for room: {}", e);
},
}
}
Ok(typing_response)
}
async fn collect_account_data(
services: &Services,
(sender_user, _, globalsince, body): (&UserId, &DeviceId, u64, &sync_events::v5::Request),

View File

@@ -79,12 +79,12 @@ fn main() {
// --- Rerun Triggers ---
// TODO: The git rerun triggers seem to always run
// Rerun if the git HEAD changes
println!("cargo:rerun-if-changed=.git/HEAD");
// Rerun if the ref pointed to by HEAD changes (e.g., new commit on branch)
if let Some(ref_path) = run_git_command(&["symbolic-ref", "--quiet", "HEAD"]) {
println!("cargo:rerun-if-changed=.git/{ref_path}");
}
// // Rerun if the git HEAD changes
// println!("cargo:rerun-if-changed=.git/HEAD");
// // Rerun if the ref pointed to by HEAD changes (e.g., new commit on branch)
// if let Some(ref_path) = run_git_command(&["symbolic-ref", "--quiet", "HEAD"])
// { println!("cargo:rerun-if-changed=.git/{ref_path}");
// }
println!("cargo:rerun-if-env-changed=GIT_COMMIT_HASH");
println!("cargo:rerun-if-env-changed=GIT_COMMIT_HASH_SHORT");

View File

@@ -219,6 +219,15 @@ pub fn check(config: &Config) -> Result {
));
}
// Check if support contact information is configured
if config.well_known.support_email.is_none() && config.well_known.support_mxid.is_none() {
warn!(
"No support contact information (support_email or support_mxid) is configured in \
the well_known section. Users in the admin room will be automatically listed as \
support contacts in the /.well-known/matrix/support endpoint."
);
}
if config
.url_preview_domain_contains_allowlist
.contains(&"*".to_owned())

View File

@@ -13,7 +13,6 @@
power_levels::RoomPowerLevelsEventContent,
third_party_invite::RoomThirdPartyInviteEventContent,
},
EventId,
int,
serde::{Base64, Raw},
};
@@ -22,6 +21,7 @@
de::{Error as _, IgnoredAny},
};
use serde_json::{from_str as from_json_str, value::RawValue as RawJsonValue};
use super::{
Error, Event, Result, StateEventType, StateKey, TimelineEventType,
power_levels::{
@@ -217,9 +217,8 @@ struct RoomCreateContentFields {
}
/*
// TODO: In the past this code was commented as it caused problems with Synapse. This is no
// longer the case. This needs to be implemented.
// See also: https://github.com/ruma/ruma/pull/2064
// TODO: In the past this code caused problems federating with synapse, maybe this has been
// resolved already. Needs testing.
//
// 2. Reject if auth_events
// a. auth_events cannot have duplicate keys since it's a BTree
@@ -251,33 +250,11 @@ struct RoomCreateContentFields {
let room_create_event = match room_create_event {
| None => {
error!(
create_event = room_create_event.as_ref().map(Event::event_id).unwrap_or(<&EventId>::try_from("$unknown").unwrap()).as_str(),
power_levels = power_levels_event.as_ref().map(Event::event_id).unwrap_or(<&EventId>::try_from("$unknown").unwrap()).as_str(),
member_event = sender_member_event.as_ref().map(Event::event_id).unwrap_or(<&EventId>::try_from("$unknown").unwrap()).as_str(),
"no m.room.create event found for {} ({})!",
incoming_event.event_id().as_str(),
incoming_event.room_id().as_str()
);
warn!("no m.room.create event in auth chain");
return Ok(false);
},
| Some(e) => e,
};
// just re-check 1.2 to work around a bug
let Some(room_id_server_name) = incoming_event.room_id().server_name() else {
warn!("room ID has no servername");
return Ok(false);
};
if room_id_server_name != room_create_event.sender().server_name() {
warn!(
"servername of room ID origin ({}) does not match servername of m.room.create \
sender ({})",
room_id_server_name,
room_create_event.sender().server_name()
);
return Ok(false);
}
// 3. If event does not have m.room.create in auth_events reject
if !incoming_event
@@ -661,7 +638,7 @@ struct GetThirdPartyInvite {
warn!(?target_user_membership_event_id, "Banned user can't join");
false
} else if (join_rules == JoinRule::Invite
|| room_version.allow_knocking && join_rules == JoinRule::Knock)
|| room_version.allow_knocking && (join_rules == JoinRule::Knock || matches!(join_rules, JoinRule::KnockRestricted(_))))
// If the join_rule is invite then allow if membership state is invite or join
&& (target_user_current_membership == MembershipState::Join
|| target_user_current_membership == MembershipState::Invite)

View File

@@ -609,7 +609,7 @@ async fn iterative_auth_check<'a, E, F, Fut, S>(
let fetch_state = |ty: &StateEventType, key: &str| {
future::ready(auth_state.get(&ty.with_state_key(key)))
};
debug!("running auth check on {:?}", event.event_id());
let auth_result =
auth_check(room_version, &event, current_third_party.as_ref(), fetch_state).await;
@@ -726,12 +726,8 @@ async fn get_mainline_depth<E, F, Fut>(
Fut: Future<Output = Option<E>> + Send,
E: Event + Send + Sync,
{
let mut room_id = None;
while let Some(sort_ev) = event {
trace!(event_id = sort_ev.event_id().as_str(), "mainline");
if room_id.is_none() {
room_id = Some(sort_ev.room_id().to_owned());
}
debug!(event_id = sort_ev.event_id().as_str(), "mainline");
let id = sort_ev.event_id();
if let Some(depth) = mainline_map.get(id) {
@@ -750,7 +746,7 @@ async fn get_mainline_depth<E, F, Fut>(
}
}
}
warn!("could not find a power event in the mainline map for {room_id:?}, defaulting to zero depth");
// Did not find a power level event so we default to zero
Ok(0)
}

View File

@@ -21,7 +21,10 @@
pub use ::tracing;
pub use config::Config;
pub use error::Error;
pub use info::{rustc_flags_capture, version, version::version};
pub use info::{
rustc_flags_capture, version,
version::{name, version},
};
pub use matrix::{Event, EventTypeExt, PduCount, PduEvent, PduId, RoomVersion, pdu, state_res};
pub use server::Server;
pub use utils::{ctor, dtor, implement, result, result::Result};

View File

@@ -15,7 +15,7 @@
#[clap(
about,
long_about = None,
name = "conduwuit",
name = conduwuit_core::name(),
version = conduwuit_core::version(),
)]
pub(crate) struct Args {

View File

@@ -76,7 +76,7 @@ pub(super) async fn handle_outlier_pdu<'a>(
// 5. Reject "due to auth events" if can't get all the auth events or some of
// the auth events are also rejected "due to auth events"
// NOTE: Step 5 is not applied anymore because it failed too often
debug!("Fetching auth events for {}", incoming_pdu.event_id);
debug!("Fetching auth events");
Box::pin(self.fetch_and_handle_outliers(
origin,
&incoming_pdu.auth_events,
@@ -88,12 +88,12 @@ pub(super) async fn handle_outlier_pdu<'a>(
// 6. Reject "due to auth events" if the event doesn't pass auth based on the
// auth events
debug!("Checking {} based on auth events", incoming_pdu.event_id);
debug!("Checking based on auth events");
// Build map of auth events
let mut auth_events = HashMap::with_capacity(incoming_pdu.auth_events.len());
for id in &incoming_pdu.auth_events {
let Ok(auth_event) = self.services.timeline.get_pdu(id).await else {
warn!("Could not find auth event {id} for {}", incoming_pdu.event_id);
warn!("Could not find auth event {id}");
continue;
};
@@ -119,7 +119,10 @@ pub(super) async fn handle_outlier_pdu<'a>(
}
// The original create event must be in the auth events
if !auth_events.contains_key(&(StateEventType::RoomCreate, String::new().into())) {
if !matches!(
auth_events.get(&(StateEventType::RoomCreate, String::new().into())),
Some(_) | None
) {
return Err!(Request(InvalidParam("Incoming event refers to wrong create event.")));
}
@@ -128,7 +131,6 @@ pub(super) async fn handle_outlier_pdu<'a>(
ready(auth_events.get(&key))
};
debug!("running auth check to handle outlier pdu {:?}", incoming_pdu.event_id);
let auth_check = state_res::event_auth::auth_check(
&to_room_version(&room_version_id),
&incoming_pdu,

View File

@@ -1,6 +1,12 @@
use std::{borrow::Borrow, collections::BTreeMap, iter::once, sync::Arc, time::Instant};
use conduwuit::{Err, Result, debug, debug_info, err, implement, matrix::{EventTypeExt, PduEvent, StateKey, state_res}, trace, utils::stream::{BroadbandExt, ReadyExt}, warn, info};
use conduwuit::{
Err, Result, debug, debug_info, err, implement,
matrix::{EventTypeExt, PduEvent, StateKey, state_res},
trace,
utils::stream::{BroadbandExt, ReadyExt},
warn,
};
use futures::{FutureExt, StreamExt, future::ready};
use ruma::{CanonicalJsonValue, RoomId, ServerName, events::StateEventType};
@@ -38,7 +44,7 @@ pub(super) async fn upgrade_outlier_to_timeline_pdu(
return Err!(Request(InvalidParam("Event has been soft failed")));
}
debug!("Upgrading pdu {} from outlier to timeline pdu", incoming_pdu.event_id);
debug!("Upgrading to timeline pdu");
let timer = Instant::now();
let room_version_id = get_room_version_id(create_event)?;
@@ -46,7 +52,7 @@ pub(super) async fn upgrade_outlier_to_timeline_pdu(
// backwards extremities doing all the checks in this list starting at 1.
// These are not timeline events.
debug!("Resolving state at event {}", incoming_pdu.event_id);
debug!("Resolving state at event");
let mut state_at_incoming_event = if incoming_pdu.prev_events.len() == 1 {
self.state_at_incoming_degree_one(&incoming_pdu).await?
} else {
@@ -64,7 +70,7 @@ pub(super) async fn upgrade_outlier_to_timeline_pdu(
state_at_incoming_event.expect("we always set this to some above");
let room_version = to_room_version(&room_version_id);
debug!("Performing auth check to upgrade {}", incoming_pdu.event_id);
debug!("Performing auth check");
// 11. Check the auth of the event passes based on the state of the event
let state_fetch_state = &state_at_incoming_event;
let state_fetch = |k: StateEventType, s: StateKey| async move {
@@ -74,7 +80,6 @@ pub(super) async fn upgrade_outlier_to_timeline_pdu(
self.services.timeline.get_pdu(event_id).await.ok()
};
debug!("running auth check on {}", incoming_pdu.event_id);
let auth_check = state_res::event_auth::auth_check(
&room_version,
&incoming_pdu,
@@ -88,7 +93,7 @@ pub(super) async fn upgrade_outlier_to_timeline_pdu(
return Err!(Request(Forbidden("Event has failed auth check with state at the event.")));
}
debug!("Gathering auth events for {}", incoming_pdu.event_id);
debug!("Gathering auth events");
let auth_events = self
.services
.state
@@ -106,7 +111,6 @@ pub(super) async fn upgrade_outlier_to_timeline_pdu(
ready(auth_events.get(&key).cloned())
};
debug!("running auth check on {} with claimed state auth", incoming_pdu.event_id);
let auth_check = state_res::event_auth::auth_check(
&room_version,
&incoming_pdu,
@@ -117,7 +121,7 @@ pub(super) async fn upgrade_outlier_to_timeline_pdu(
.map_err(|e| err!(Request(Forbidden("Auth check failed: {e:?}"))))?;
// Soft fail check before doing state res
debug!("Performing soft-fail check on {}", incoming_pdu.event_id);
debug!("Performing soft-fail check");
let soft_fail = match (auth_check, incoming_pdu.redacts_id(&room_version_id)) {
| (false, _) => true,
| (true, None) => false,
@@ -214,8 +218,7 @@ pub(super) async fn upgrade_outlier_to_timeline_pdu(
// 14. Check if the event passes auth based on the "current state" of the room,
// if not soft fail it
if soft_fail {
info!("Soft failing event {}", incoming_pdu.event_id);
assert!(extremities.is_empty(), "soft_fail extremities empty");
debug!("Soft failing event");
let extremities = extremities.iter().map(Borrow::borrow);
self.services

View File

@@ -698,20 +698,6 @@ pub async fn create_hash_and_sign_event(
.await
.saturating_add(uint!(1));
if state_key.is_none() {
if prev_events.is_empty() {
warn!("Timeline event had zero prev_events, something broke.");
return Err!(Request(Unknown("Timeline event had zero prev_events.")));
}
if depth.le(&uint!(2)) {
warn!(
"Had unsafe depth of {depth} in {room_id} when creating non-state event. \
Bad!"
);
return Err!(Request(Unknown("Unsafe depth for non-state event.")));
}
};
let mut unsigned = unsigned.unwrap_or_default();
if let Some(state_key) = &state_key {
@@ -771,7 +757,6 @@ pub async fn create_hash_and_sign_event(
ready(auth_events.get(&key))
};
debug!("running auth check on new {} event by {} in {}", pdu.kind, pdu.sender, pdu.room_id);
let auth_check = state_res::auth_check(
&room_version,
&pdu,
@@ -976,9 +961,8 @@ pub async fn append_incoming_pdu<'a, Leaves>(
state_lock: &'a RoomMutexGuard,
) -> Result<Option<RawPduId>>
where
Leaves: Iterator<Item = &'a EventId> + Send + Clone + 'a,
Leaves: Iterator<Item = &'a EventId> + Send + 'a,
{
assert!(new_room_leaves.clone().count() > 0, "extremities are empty");
// We append to state before appending the pdu, so we don't have a moment in
// time with the pdu without it's state. This is okay because append_pdu can't
// fail.
@@ -1158,7 +1142,7 @@ pub async fn backfill_if_required(&self, room_id: &RoomId, from: PduCount) -> Re
.boxed();
while let Some(ref backfill_server) = servers.next().await {
info!("Asking {backfill_server} for backfill in {:?}", room_id.to_owned());
info!("Asking {backfill_server} for backfill");
let response = self
.services
.sending
@@ -1186,7 +1170,7 @@ pub async fn backfill_if_required(&self, room_id: &RoomId, from: PduCount) -> Re
}
}
warn!("No servers could backfill, but backfill was needed in room {room_id}");
info!("No servers could backfill, but backfill was needed in room {room_id}");
Ok(())
}

View File

@@ -179,18 +179,15 @@ pub async fn last_typing_update(&self, room_id: &RoomId) -> Result<u64> {
.unwrap_or(0))
}
/// Returns a new typing EDU.
pub async fn typings_all(
pub async fn typing_users_for_user(
&self,
room_id: &RoomId,
sender_user: &UserId,
) -> Result<SyncEphemeralRoomEvent<ruma::events::typing::TypingEventContent>> {
) -> Result<Vec<OwnedUserId>> {
let room_typing_indicators = self.typing.read().await.get(room_id).cloned();
let Some(typing_indicators) = room_typing_indicators else {
return Ok(SyncEphemeralRoomEvent {
content: ruma::events::typing::TypingEventContent { user_ids: Vec::new() },
});
return Ok(Vec::new());
};
let user_ids: Vec<_> = typing_indicators
@@ -207,8 +204,19 @@ pub async fn typings_all(
.collect()
.await;
Ok(user_ids)
}
/// Returns a new typing EDU.
pub async fn typings_event_for_user(
&self,
room_id: &RoomId,
sender_user: &UserId,
) -> Result<SyncEphemeralRoomEvent<ruma::events::typing::TypingEventContent>> {
Ok(SyncEphemeralRoomEvent {
content: ruma::events::typing::TypingEventContent { user_ids },
content: ruma::events::typing::TypingEventContent {
user_ids: self.typing_users_for_user(room_id, sender_user).await?,
},
})
}