From 2d3a3a109682f32b2ef2c5cb99d7e7f2a3ef77b5 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Fri, 22 Jun 2018 13:54:03 +0100 Subject: [PATCH 1/5] Tests for erasure --- tests/14account/02deactivate.pl | 23 +++++----- tests/30rooms/32erasure.pl | 74 +++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 10 deletions(-) create mode 100644 tests/30rooms/32erasure.pl diff --git a/tests/14account/02deactivate.pl b/tests/14account/02deactivate.pl index 4909e7d27..98d2dbd8e 100644 --- a/tests/14account/02deactivate.pl +++ b/tests/14account/02deactivate.pl @@ -1,10 +1,11 @@ use JSON qw( decode_json ); -my $password = "my secure password"; - sub matrix_deactivate_account { - my ( $user, $password ) = @_; + my ( $user, %opts ) = @_; + + # use the user's password unless one was given in opts + my $password = (delete $opts{password}) // $user->password; do_request_json_for( $user, method => "POST", @@ -15,26 +16,28 @@ sub matrix_deactivate_account user => $user->user_id, password => $password, }, + %opts, }, ); } +push our @EXPORT, qw( matrix_deactivate_account ); test "Can deactivate account", - requires => [ local_user_fixture( password => $password ) ], + requires => [ local_user_fixture() ], check => sub { my ( $user ) = @_; - matrix_deactivate_account( $user, $password ); + matrix_deactivate_account( $user ); }; test "Can't deactivate account with wrong password", - requires => [ local_user_fixture( password => $password ) ], + requires => [ local_user_fixture() ], check => sub { my ( $user ) = @_; - matrix_deactivate_account( $user, "wrong password" ) + matrix_deactivate_account( $user, password=>"wrong password" ) ->main::expect_http_401->then( sub { my ( $resp ) = @_; @@ -52,12 +55,12 @@ sub matrix_deactivate_account }; test "After deactivating account, can't log in with password", - requires => [ local_user_fixture( password => $password ) ], + requires => [ local_user_fixture() ], check => sub { my ( $user ) = @_; - matrix_deactivate_account( $user, $password ) + matrix_deactivate_account( $user ) ->then( sub { do_request_json_for( $user, method => "POST", @@ -65,7 +68,7 @@ sub matrix_deactivate_account content => { type => "m.login.password", user => $user->user_id, - password => $password, + password => $user->password, } # We don't mandate the exact failure code here # (that should be done in the login test if diff --git a/tests/30rooms/32erasure.pl b/tests/30rooms/32erasure.pl new file mode 100644 index 000000000..8d5e974f9 --- /dev/null +++ b/tests/30rooms/32erasure.pl @@ -0,0 +1,74 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +use List::Util qw( first ); + +test "Only original members of the room can see messages from erased users", + requires => [ local_user_and_room_fixtures(), local_user_fixture(), local_user_fixture() ], + + do => sub { + my ( $creator, $room_id, $member, $joiner ) = @_; + + my $message_id; + matrix_join_room_synced( $member, $room_id ) + ->then( sub { + matrix_send_room_text_message( $creator, $room_id, body => "body1" ); + })->then( sub { + ( $message_id ) = @_; + matrix_join_room_synced( $joiner, $room_id ); + })->then( sub { + # now both users should see the message event + matrix_sync( $joiner, limit => 4 ); + })->then( sub { + my ( $body ) = @_; + + my $room = $body->{rooms}{join}{$room_id}; + my $events = $room->{timeline}->{events}; + log_if_fail "messages for joining user before erasure", $events; + + my $e = first { $_->{event_id} eq $message_id } @$events; + assert_eq( $e->{type}, "m.room.message", "event type" ); + assert_eq( $e->{content}->{body}, "body1", "event content body" ); + + matrix_deactivate_account( $creator, erase => JSON::true ); + })->then( sub { + # now the original member should see the message event, but the joiner + # should see a redacted version + matrix_sync( $member ); + })->then( sub { + my ( $body ) = @_; + + my $room = $body->{rooms}{join}{$room_id}; + my $events = $room->{timeline}->{events}; + log_if_fail "messages for original member after erasure", $events; + + my $e = first { $_->{event_id} eq $message_id } @$events; + assert_eq( $e->{type}, "m.room.message", "event type" ); + assert_eq( $e->{content}->{body}, "body1", "event content body" ); + + matrix_sync( $joiner ); + })->then( sub { + my ( $body ) = @_; + + my $room = $body->{rooms}{join}{$room_id}; + my $events = $room->{timeline}->{events}; + log_if_fail "messages for joining user after erasure", $events; + + my $e = first { $_->{event_id} eq $message_id } @$events; + assert_eq( $e->{type}, "m.room.message", "event type" ); + assert_deeply_eq( $e->{content}, {}, "event content" ); + + Future->done(1); + }); + }; From 1a81419048a6b4cfa81c9cebc643c5b12de424a3 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 16 Jul 2018 15:23:33 +0100 Subject: [PATCH 2/5] Ensure that events from erased users are redacted over federation --- tests/50federation/32room-getevent.pl | 72 +++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/tests/50federation/32room-getevent.pl b/tests/50federation/32room-getevent.pl index af52c1f7e..1549624cb 100644 --- a/tests/50federation/32room-getevent.pl +++ b/tests/50federation/32room-getevent.pl @@ -45,3 +45,75 @@ }); }; + +test "Inbound federation redacts events from erased users", + requires => [ $main::OUTBOUND_CLIENT, $main::HOMESERVER_INFO[0], + local_user_and_room_fixtures(), + federation_user_id_fixture() ], + + do => sub { + my ( $outbound_client, $info, $creator, $room_id, $user_id ) = @_; + my $first_home_server = $info->server_name; + + my $message_id; + + $outbound_client->join_room( + server_name => $first_home_server, + room_id => $room_id, + user_id => $user_id, + )->then( sub { + my ( $room ) = @_; + + # have the creator send a message into the room, which we will try to + # fetch. + matrix_send_room_text_message( $creator, $room_id, body => "body1" ); + })->then( sub { + ( $message_id ) = @_; + + $outbound_client->do_request_json( + method => "GET", + hostname => $first_home_server, + uri => "/event/$message_id/", + ); + })->then( sub { + my ( $body ) = @_; + log_if_fail "Fetched event before erasure", $body; + + assert_json_keys( $body, qw( origin origin_server_ts pdus )); + assert_json_list( my $events = $body->{pdus} ); + + @$events == 1 or + die "Expected 1 event, found " . scalar(@$events); + my ( $event ) = @$events; + + # Check that the content is right + assert_eq( $event->{content}->{body}, "body1" ); + + # now do the erasure + matrix_deactivate_account( $creator, erase => JSON::true ); + })->then( sub { + # re-fetch the event + $outbound_client->do_request_json( + method => "GET", + hostname => $first_home_server, + uri => "/event/$message_id/", + ); + })->then( sub { + my ( $body ) = @_; + log_if_fail "Fetched event after erasure", $body; + + assert_json_keys( $body, qw( origin origin_server_ts pdus )); + assert_json_list( my $events = $body->{pdus} ); + + @$events == 1 or + die "Expected 1 event, found " . scalar(@$events); + my ( $event ) = @$events; + + # Check that the content has been redacted + exists $event->{content}->{body} and + die "Event was not redacted"; + + Future->done(1); + }); + }; + From 49403c62b69bf5805299a3cfcb43dac51fdf6f26 Mon Sep 17 00:00:00 2001 From: Amber Brown Date: Fri, 20 Jul 2018 23:22:10 +1000 Subject: [PATCH 3/5] Shared secret registration moved to an admin API (#453) --- lib/SyTest/HTTPClient.pm | 2 +- tests/10apidoc/01register.pl | 96 ++++++++++-------------------------- 2 files changed, 26 insertions(+), 72 deletions(-) diff --git a/lib/SyTest/HTTPClient.pm b/lib/SyTest/HTTPClient.pm index ab3c5e069..4079b2acf 100644 --- a/lib/SyTest/HTTPClient.pm +++ b/lib/SyTest/HTTPClient.pm @@ -105,7 +105,7 @@ sub do_request my $message = $response->message; $message =~ s/\r?\n?$//; # because HTTP::Response doesn't do this - return Future->fail( "HTTP Request failed (${\$response->code} $message)", + return Future->fail( "HTTP Request failed ( ${\$response->code} $message $uri )", http => $response, $response->request ); } diff --git a/tests/10apidoc/01register.pl b/tests/10apidoc/01register.pl index bbb072f17..9f2f4e131 100644 --- a/tests/10apidoc/01register.pl +++ b/tests/10apidoc/01register.pl @@ -227,10 +227,9 @@ sub matrix_register_user }); } -shared_secret_tests( "/v1/register", \&matrix_v1_register_user_via_secret); -shared_secret_tests( "/r0/register", \&matrix_r0_register_user_via_secret); +shared_secret_tests( "/r0/admin/register", \&matrix_admin_register_user_via_secret); -sub matrix_v1_register_user_via_secret +sub matrix_admin_register_user_via_secret { my ( $http, $uid, %opts ) = @_; @@ -240,23 +239,30 @@ sub matrix_v1_register_user_via_secret defined $uid or croak "Require UID for matrix_register_user_via_secret"; - my $mac = hmac_sha1_hex( - join( "\0", $uid, $password, $is_admin ? "admin" : "notadmin" ), - "reg_secret" - ); - $http->do_request_json( - method => "POST", - uri => "/api/v1/register", + method => "GET", + uri => "/r0/admin/register", + )->then( sub{ + my ( $nonce ) = @_; + + my $mac = hmac_sha1_hex( + join( "\0", $nonce->{nonce}, $uid, $password, $is_admin ? "admin" : "notadmin" ), + "reg_secret" + ); - content => { - type => "org.matrix.login.shared_secret", - user => $uid, - password => $password, - admin => $is_admin ? JSON::true : JSON::false, - mac => $mac, - }, - )->then( sub { + return $http->do_request_json( + method => "POST", + uri => "/r0/admin/register", + + content => { + nonce => $nonce->{nonce}, + username => $uid, + password => $password, + admin => $is_admin ? JSON::true : JSON::false, + mac => $mac, + }, + ) + })->then( sub { my ( $body ) = @_; assert_json_keys( $body, qw( user_id access_token )); @@ -278,58 +284,6 @@ sub matrix_v1_register_user_via_secret }); } -sub matrix_r0_register_user_via_secret -{ - my ( $http, $uid, %opts ) = @_; - - my $password = $opts{password} // "an0th3r s3kr1t"; - my $is_admin = $opts{is_admin} // 0; - - defined $uid or - croak "Require UID for matrix_register_user_via_secret"; - - # for some reason the /r0/register endpoint only includes the - # uid in the hash. AFAICT it's equally valid, but one has to - # wonder why it is different to the /v1/ endpoint. - # (https://github.com/matrix-org/synapse/issues/2664) - my $mac = hmac_sha1_hex( - $uid, - "reg_secret" - ); - - $http->do_request_json( - method => "POST", - uri => "/r0/register", - - content => { - type => "org.matrix.login.shared_secret", - username => $uid, - password => $password, - admin => $is_admin ? JSON::true : JSON::false, - mac => $mac, - }, - )->then( sub { - my ( $body ) = @_; - - assert_json_keys( $body, qw( user_id access_token device_id )); - - my $uid = $body->{user_id}; - - log_if_fail "Registered new user (via secret) $uid"; - - my $access_token = $body->{access_token}; - - my $user = new_User( - http => $http, - user_id => $uid, - device_id => $body->{device_id}, - password => $password, - access_token => $access_token, - ); - return Future->done( $user ); - }); -} - sub shared_secret_tests { my ( $ep_name, $register_func ) = @_; @@ -428,7 +382,7 @@ sub local_admin_fixture setup => sub { my ( $http, $localpart ) = @_; - matrix_v1_register_user_via_secret( $http, $localpart, is_admin => 1, %args ); + matrix_admin_register_user_via_secret( $http, $localpart, is_admin => 1, %args ); }, ); } From 0389996ce7f62cc6076856fd502a3471dbc10a42 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 23 Jul 2018 23:51:52 +0100 Subject: [PATCH 4/5] test for lazyloaded /messages --- tests/10apidoc/34room-messages.pl | 40 +++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/10apidoc/34room-messages.pl b/tests/10apidoc/34room-messages.pl index 842df745a..b8092e972 100644 --- a/tests/10apidoc/34room-messages.pl +++ b/tests/10apidoc/34room-messages.pl @@ -158,6 +158,46 @@ sub matrix_send_room_text_message }); }; +test "GET /rooms/:room_id/messages lazy loads members correctly", + requires => [ local_user_and_room_fixtures(), + qw( can_send_message )], + + proves => [qw( can_get_messages )], + + check => sub { + my ( $user, $room_id ) = @_; + + matrix_send_room_text_message( $user, $room_id, + body => "Here is the message content", + )->then( sub { + do_request_json_for( $user, + method => "GET", + uri => "/r0/rooms/$room_id/messages", + + # With no params this does "forwards from END"; i.e. nothing useful + params => { + dir => "b", + filter => '{ "lazy_load_members" : true }', + }, + ) + })->then( sub { + my ( $body ) = @_; + + assert_json_keys( $body, qw( start end state chunk )); + assert_json_list( $body->{chunk} ); + assert_json_list( $body->{state} ); + + assert_eq( scalar @{$body->{state}}, 1); + assert_eq( $body->{state}[0]{type}, 'm.room.member'); + assert_eq( $body->{state}[0]{state_key}, $user->user_id); + + scalar @{ $body->{chunk} } > 0 or + die "Expected some messages but got none at all\n"; + + Future->done(1); + }); + }; + push @EXPORT, qw( matrix_get_room_messages matrix_send_room_text_message_synced matrix_send_room_message_synced matrix_send_filler_messages_synced From e2fc8f91a6a9e160773dacef3121022350495c65 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 24 Jul 2018 11:44:25 +0100 Subject: [PATCH 5/5] Remove overly-specific room-vorgetting test As per https://github.com/matrix-org/synapse/issues/3350, we shouldn't necessarily expect room history to be hidden when you rejoin a room you forgot. --- tests/30rooms/31forget.pl | 62 +-------------------------------------- 1 file changed, 1 insertion(+), 61 deletions(-) diff --git a/tests/30rooms/31forget.pl b/tests/30rooms/31forget.pl index 6755075f6..95980a944 100644 --- a/tests/30rooms/31forget.pl +++ b/tests/30rooms/31forget.pl @@ -123,7 +123,7 @@ })->main::expect_http_4xx; }; -test "Can re-join room if re-invited - history_visibility = shared", +test "Can re-join room if re-invited", requires => [ local_user_fixture(), local_user_fixture() ], do => sub { @@ -183,66 +183,6 @@ }); }; -test "Can re-join room if re-invited - history_visibility joined", - requires => [ local_user_fixture(), local_user_fixture() ], - - do => sub { - my ( $creator, $user ) = @_; - - my ( $room_id ); - - matrix_create_room( $creator, invite => [ $user->user_id ] )->then( sub { - ( $room_id ) = @_; - - log_if_fail "room_id", $room_id; - - matrix_put_room_state( $creator, $room_id, - type => "m.room.join_rules", - state_key => "", - content => { - join_rule => "invite", - } - ) - })->then( sub { - matrix_set_room_history_visibility( $creator, $room_id, "joined"); - })->then( sub { - matrix_join_room( $user, $room_id ); - })->then( sub { - matrix_send_room_text_message( $creator, $room_id, body => "before leave" ); - })->then( sub { - matrix_leave_room( $user, $room_id ); - })->then( sub { - matrix_forget_room( $user, $room_id ); - })->then( sub { - matrix_join_room( $user, $room_id )->main::expect_http_403; - })->then( sub { - matrix_invite_user_to_room( $creator, $user, $room_id ); - })->then( sub { - matrix_join_room( $user, $room_id ); - })->then( sub { - matrix_get_room_messages( $user, $room_id, limit => 100 ); - })->then( sub { - my ( $body ) = @_; - - none { $_->{type} eq "m.room.message" } @{ $body->{chunk} } - or die "Should not have seen any m.room.message events"; - - matrix_send_room_text_message( $creator, $room_id, body => "after rejoin" ); - })->then( sub { - matrix_get_room_messages( $user, $room_id, limit => 1 ); - })->then( sub { - my ( $body ) = @_; - - log_if_fail "body", $body; - - @{ $body->{chunk} } == 1 or die "Expected event"; - $body->{chunk}[0]->{type} eq "m.room.message" && $body->{chunk}[0]->{content}{body} eq "after rejoin" - or die "Got wrong event"; - - Future->done( 1 ); - }); - }; - push our @EXPORT, qw( matrix_forget_room ); sub matrix_forget_room