diff --git a/tests/30rooms/60anonymousaccess.pl b/tests/30rooms/60anonymousaccess.pl index 02c6b2a63..44339968c 100644 --- a/tests/30rooms/60anonymousaccess.pl +++ b/tests/30rooms/60anonymousaccess.pl @@ -1,396 +1,418 @@ use Future::Utils qw( try_repeat_until_success repeat ); use JSON qw( encode_json ); -test "Anonymous user cannot view non-world-readable rooms", - requires => [ anonymous_user_fixture(), local_user_fixture() ], +foreach my $i ( + [ "Anonymous", sub { anonymous_user_fixture() } ], + [ "Real", sub { local_user_fixture() } ] +) { + my ( $name, $fixture ) = @$i; - do => sub { - my ( $anonymous_user, $user ) = @_; + # /messages - my $room_id; + test( + "$name non-joined user cannot view non-world-readable rooms", - matrix_create_and_join_room( [ $user ] ) - ->then( sub { - ( $room_id ) = @_; + requires => [ $fixture->(), local_user_fixture() ], - matrix_set_room_history_visibility( $user, $room_id, "shared" ); - })->then( sub { - matrix_send_room_text_message( $user, $room_id, body => "mice" ) - })->then( sub { - matrix_get_room_messages( $anonymous_user, $room_id, limit => "1" ) - })->followed_by( \&expect_4xx_or_empty_chunk ); - }; + do => sub { + my ( $nonjoined_user, $creator_user ) = @_; -test "Anonymous user can view world-readable rooms", - requires => [ anonymous_user_fixture(), local_user_fixture() ], + my $room_id; - do => sub { - my ( $anonymous_user, $user ) = @_; - - my $room_id; + matrix_create_and_join_room( [ $creator_user ] ) + ->then( sub { + ( $room_id ) = @_; - matrix_create_and_join_room( [ $user ] ) - ->then( sub { - ( $room_id ) = @_; + matrix_set_room_history_visibility( $creator_user, $room_id, "shared" ); + })->then( sub { + matrix_send_room_text_message( $creator_user, $room_id, body => "mice" ) + })->then( sub { + matrix_get_room_messages( $nonjoined_user, $room_id, limit => "1" ) + })->followed_by( \&expect_4xx_or_empty_chunk ); + }, + ); - matrix_set_room_history_visibility( $user, $room_id, "world_readable" ); - })->then( sub { - matrix_send_room_text_message( $user, $room_id, body => "mice" ) - })->then( sub { - matrix_get_room_messages( $anonymous_user, $room_id, limit => "2" ) - }); - }; + test( + "$name non-joined user can view world-readable rooms", -test "Anonymous user cannot call /events globally", - requires => [ anonymous_user_fixture() ], + requires => [ $fixture->(), local_user_fixture() ], - do => sub { - my ( $anonymous_user ) = @_; + do => sub { + my ( $nonjoined_user, $creator_user ) = @_; - do_request_json_for( $anonymous_user, - method => "GET", - uri => "/api/v1/events", - )->followed_by( \&expect_4xx_or_empty_chunk ); - }; + my $room_id; -test "Anonymous user cannot call /events on non-world_readable room", - requires => [ anonymous_user_fixture(), local_user_fixture() ], + matrix_create_and_join_room( [ $creator_user ] ) + ->then( sub { + ( $room_id ) = @_; - do => sub { - my ( $anonymous_user, $user ) = @_; - - my $room_id; + matrix_set_room_history_visibility( $creator_user, $room_id, "world_readable" ); + })->then( sub { + matrix_send_room_text_message( $creator_user, $room_id, body => "mice" ) + })->then( sub { + matrix_get_room_messages( $nonjoined_user, $room_id, limit => "2" ) + }); + }, + ); - matrix_create_and_join_room( [ $user ] ) - ->then( sub { - ( $room_id ) = @_; + # /events - matrix_send_room_text_message( $user, $room_id, body => "mice" ) - })->then( sub { - do_request_json_for( $anonymous_user, - method => "GET", - uri => "/api/v1/events", - params => { - room_id => $room_id, - }, - ); - })->followed_by( \&expect_4xx_or_empty_chunk ); - }; + test( + "$name non-joined user cannot call /events on non-world_readable room", -sub await_event_not_presence_for -{ - my ( $user, $room_id, $allowed_users ) = @_; - await_event_for( $user, - room_id => $room_id, - filter => sub { - my ( $event ) = @_; + requires => [ $fixture->(), local_user_fixture() ], - # Include all events where the type is not m.presence. - # If the type is m.presence, then only include it if it is for one of - # the allowed users - return ((not $event->{type} eq "m.presence") or - any { $event->{content}{user_id} eq $_->user_id } @$allowed_users); - }, - )->on_done( sub { - my ( $event ) = @_; - log_if_fail "event", $event - }); -} + do => sub { + my ( $nonjoined_user, $creator_user ) = @_; -test "Anonymous user can call /events on world_readable room", - requires => [ anonymous_user_fixture(), local_user_fixture(), local_user_fixture() ], + my $room_id; - do => sub { - my ( $anonymous_user, $user, $user_not_in_room ) = @_; + matrix_create_and_join_room( [ $creator_user ] ) + ->then( sub { + ( $room_id ) = @_; - my ( $room_id, $sent_event_id ); + matrix_send_room_text_message( $creator_user, $room_id, body => "mice" ) + })->then( sub { + do_request_json_for( $nonjoined_user, + method => "GET", + uri => "/api/v1/events", + params => { + room_id => $room_id, + }, + ); + })->followed_by( \&expect_4xx_or_empty_chunk ); + }, + ); - matrix_create_and_join_room( [ $user ] ) - ->then( sub { - ( $room_id ) = @_; + test( + "$name non-joined user can call /events on world_readable room", - matrix_set_room_history_visibility( $user, $room_id, "world_readable" ); - })->then( sub { - Future->needs_all( - matrix_send_room_text_message( $user, $room_id, body => "mice" ) - ->on_done( sub { - ( $sent_event_id ) = @_; - }), + requires => [ $fixture->(), local_user_fixture(), local_user_fixture() ], - await_event_not_presence_for( $anonymous_user, $room_id, [] ) - ->then( sub { - my ( $event ) = @_; + do => sub { + my ( $nonjoined_user, $user, $user_not_in_room ) = @_; - assert_json_keys( $event, qw( content ) ); - my $content = $event->{content}; - assert_json_keys( $content, qw( body ) ); - $content->{body} eq "mice" or die "Want content body to be mice"; + my ( $room_id, $sent_event_id ); - Future->done( 1 ); - }), - )->then( sub { - my ( $stream_token ) = @_; + matrix_create_and_join_room( [ $user ] ) + ->then( sub { + ( $room_id ) = @_; + matrix_set_room_history_visibility( $user, $room_id, "world_readable" ); + })->then( sub { Future->needs_all( - matrix_set_presence_status( $user_not_in_room, "online", - status_msg => "Worshiping lemurs' tails", - ), - matrix_set_presence_status( $user, "online", - status_msg => "Worshiping lemurs' tails", - ), + matrix_send_room_text_message( $user, $room_id, body => "mice" ) + ->on_done( sub { + ( $sent_event_id ) = @_; + }), - await_event_not_presence_for( $anonymous_user, $room_id, [ $user ] )->then( sub { + await_event_not_history_visibility_or_presence_for( $nonjoined_user, $room_id, [] ) + ->then( sub { my ( $event ) = @_; - assert_eq( $event->{type}, "m.presence", - "event type" ); - assert_eq( $event->{content}{user_id}, $user->user_id, - "event content.user_id" ); + assert_json_keys( $event, qw( content ) ); + my $content = $event->{content}; + assert_json_keys( $content, qw( body ) ); + assert_eq( $content->{body}, "mice", "content body" ); Future->done( 1 ); }), - ), - })->then( sub { - my ( $stream_token ) = @_; - - Future->needs_all( - do_request_json_for( $user, - method => "POST", - uri => "/v2_alpha/rooms/$room_id/receipt/m.read/$sent_event_id", - content => {}, + )->then( sub { + my ( $stream_token ) = @_; + + Future->needs_all( + matrix_set_presence_status( $user_not_in_room, "online", + status_msg => "Worshiping lemurs' tails", + ), + matrix_set_presence_status( $user, "online", + status_msg => "Worshiping lemurs' tails", + ), + + await_event_not_history_visibility_or_presence_for( $nonjoined_user, $room_id, [ $user ] )->then( sub { + my ( $event ) = @_; + + assert_eq( $event->{type}, "m.presence", + "event type" ); + assert_eq( $event->{content}{user_id}, $user->user_id, + "event content.user_id" ); + + Future->done( 1 ); + }), ), + })->then( sub { + my ( $stream_token ) = @_; + + Future->needs_all( + do_request_json_for( $user, + method => "POST", + uri => "/v2_alpha/rooms/$room_id/receipt/m.read/$sent_event_id", + content => {}, + ), + + await_event_not_history_visibility_or_presence_for( $nonjoined_user, $room_id, [] )->then( sub { + my ( $event ) = @_; + + assert_eq( $event->{type}, "m.receipt", + "event type" ); + assert_ok( $event->{content}{$sent_event_id}{"m.read"}{ $user->user_id }, + "receipt event ID for user" ); + + Future->done( 1 ); + }), + ); + })->then( sub { + my ( $stream_token ) = @_; + + Future->needs_all( + do_request_json_for( $user, + method => "PUT", + uri => "/api/v1/rooms/$room_id/typing/:user_id", + content => { + typing => JSON::true, + timeout => 5000, + }, + ), + + await_event_not_history_visibility_or_presence_for( $nonjoined_user, $room_id, [] )->then( sub { + my ( $event ) = @_; + + assert_eq( $event->{type}, "m.typing", + "event type" ); + assert_eq( $event->{room_id}, $room_id, + "event room_id" ); + assert_eq( $event->{content}{user_ids}[0], $user->user_id, + "event content user_ids[0]" ); + + Future->done( 1 ); + }), + ); + }); + }); + }, + ); - await_event_not_presence_for( $anonymous_user, $room_id, [] )->then( sub { - my ( $event ) = @_; - - assert_eq( $event->{type}, "m.receipt", - "event type" ); - assert_ok( $event->{content}{$sent_event_id}{"m.read"}{ $user->user_id }, - "receipt event ID for user" ); + test( + "$name non-joined user doesn't get events before room made world_readable", - Future->done( 1 ); - }), - ); - })->then( sub { - my ( $stream_token ) = @_; + requires => [ $fixture->(), local_user_fixture() ], - Future->needs_all( - do_request_json_for( $user, - method => "PUT", - uri => "/api/v1/rooms/$room_id/typing/:user_id", - content => { - typing => JSON::true, - timeout => 5000, - }, - ), + do => sub { + my ( $nonjoined_user, $user ) = @_; - await_event_not_presence_for( $anonymous_user, $room_id, [] )->then( sub { - my ( $event ) = @_; + my $room_id; - assert_eq( $event->{type}, "m.typing", - "event type" ); - assert_eq( $event->{room_id}, $room_id, - "event room_id" ); - assert_eq( $event->{content}{user_ids}[0], $user->user_id, - "event content user_ids[0]" ); + matrix_create_and_join_room( [ $user ] ) + ->then( sub { + ( $room_id ) = @_; + matrix_send_room_text_message( $user, $room_id, body => "private" ); + })->then( sub { + matrix_set_room_history_visibility( $user, $room_id, "world_readable" ); + })->then( sub { + Future->needs_all( + matrix_send_room_text_message( $user, $room_id, body => "public" ), + + # The client is allowed to see exactly two events, the + # m.room.history_visibility event and the public message. + # The server is free to return these in separate calls to + # /events, so we try at most two times to get the events we expect. + check_events( $nonjoined_user, $room_id ) + ->then( sub { Future->done( 1 ); + }, sub { + check_events( $nonjoined_user, $room_id ); }), ); }); - }); - }; + }, + ); -test "Annonymous user can call /sync on a world readable room", - requires => [ anonymous_user_fixture(), local_user_fixture() ], + # /state - do => sub { - my ( $anonymous_user, $user ) = @_; + test( + "$name non-joined users can get state for world_readable rooms", - my ( $room_id, $sent_event_id ); + requires => [ local_user_and_room_fixtures(), $fixture->() ], - matrix_create_and_join_room( [ $user ] ) - ->then( sub { - ( $room_id ) = @_; + do => sub { + my ( $user, $room_id ) = @_; matrix_set_room_history_visibility( $user, $room_id, "world_readable" ); - })->then( sub { - matrix_set_room_guest_access( $user, $room_id, "can_join" ); - })->then( sub { - matrix_send_room_text_message( $user, $room_id, body => "mice" ); - })->then( sub { - ( $sent_event_id ) = @_; - - matrix_join_room( $anonymous_user, $room_id ); - })->then( sub { + }, - matrix_sync( $anonymous_user, filter => encode_json({ - room => { - rooms => [ $room_id ], - ephemeral => { types => [] }, - state => { types => [] }, - timeline => { types => ["m.room.message"] }, - }, - presence => { types => [] } - })); - })->then( sub { - my ( $sync_body ) = @_; + check => sub { + my ( $user, $room_id, $nonjoined_user ) = @_; - assert_json_object( my $room = $sync_body->{rooms}{join}{$room_id} ); - assert_json_list( my $events = $room->{timeline}{events} ); - assert_eq( $events->[0]{event_id}, $sent_event_id, 'event id' ); + do_request_json_for( $nonjoined_user, + method => "GET", + uri => "/api/v1/rooms/$room_id/state", + ); + }, + ); - Future->done( 1 ); - }); - }; + test( + "$name non-joined users can get individual state for world_readable rooms", -test "Anonymous user doesn't get events before room made world_readable", - requires => [ anonymous_user_fixture(), local_user_fixture() ], + requires => [ local_user_and_room_fixtures(), $fixture->() ], - do => sub { - my ( $anonymous_user, $user ) = @_; + do => sub { + my ( $user, $room_id ) = @_; - my $room_id; + matrix_set_room_history_visibility( $user, $room_id, "world_readable" ); + }, - matrix_create_and_join_room( [ $user ] ) - ->then( sub { - ( $room_id ) = @_; + check => sub { + my ( $user, $room_id, $nonjoined_user ) = @_; - matrix_send_room_text_message( $user, $room_id, body => "private" ); - })->then( sub { - matrix_set_room_history_visibility( $user, $room_id, "world_readable" ); - })->then( sub { - Future->needs_all( - matrix_send_room_text_message( $user, $room_id, body => "public" ), - - # The client is allowed to see exactly two events, the - # m.room.history_visibility event and the public message. - # The server is free to return these in separate calls to - # /events, so we try at most two times to get the events we expect. - check_events( $anonymous_user, $room_id ) - ->then(sub { - Future->done( 1 ); - }, sub { - check_events( $anonymous_user, $room_id ); - }), + do_request_json_for( $nonjoined_user, + method => "GET", + uri => "/api/v1/rooms/$room_id/state/m.room.member/".$user->user_id, ); - }); - }; + }, + ); -test "Anonymous users can get state for world_readable rooms", - requires => [ local_user_and_room_fixtures(), anonymous_user_fixture() ], + # room /initialSync - do => sub { - my ( $user, $room_id ) = @_; + test( + "$name non-joined users cannot room initalSync for non-world_readable rooms", - matrix_set_room_history_visibility( $user, $room_id, "world_readable" ); - }, + requires => [ anonymous_user_fixture(), local_user_fixture() ], - check => sub { - my ( $user, $room_id, $anonymous_user ) = @_; + do => sub { + my ( $non_joined_user, $creating_user ) = @_; - do_request_json_for( $anonymous_user, - method => "GET", - uri => "/api/v1/rooms/$room_id/state", - ); - }; + my $room_id; -test "Real users can get state for world_readable rooms", - requires => [ local_user_and_room_fixtures(), local_user_fixture() ], + matrix_create_and_join_room( [ $creating_user ] ) + ->then( sub { + ( $room_id ) = @_; - do => sub { - my ( $user, $room_id ) = @_; + matrix_send_room_text_message( $creating_user, $room_id, body => "private" ) + })->then( sub { + matrix_initialsync_room( $non_joined_user, $room_id ) + ->main::expect_http_403; + }); + }, + ); - matrix_set_room_history_visibility( $user, $room_id, "world_readable" ); - }, + test( + "$name non-joined users can room initialSync for world_readable rooms", - check => sub { - my ( $user, $room_id, $non_joined_user ) = @_; + requires => [ anonymous_user_fixture(), local_user_fixture() ], - do_request_json_for( $non_joined_user, - method => "GET", - uri => "/api/v1/rooms/$room_id/state", - ); - }; + do => sub { + my ( $syncing_user, $creating_user ) = @_; -test "Anonymous users can get individual state for world_readable rooms", - requires => [ local_user_and_room_fixtures(), anonymous_user_fixture() ], + my $room_id; - do => sub { - my ( $user, $room_id ) = @_; + matrix_create_and_join_room( [ $creating_user ] ) + ->then( sub { + ( $room_id ) = @_; - matrix_set_room_history_visibility( $user, $room_id, "world_readable" ); - }, + matrix_send_room_text_message( $creating_user, $room_id, body => "private" ) + })->then( sub { + matrix_set_room_history_visibility( $creating_user, $room_id, "world_readable" ); + })->then( sub { + matrix_send_room_text_message( $creating_user, $room_id, body => "public" ); + })->then( sub { + matrix_initialsync_room( $syncing_user, $room_id ); + })->then( sub { + my ( $body ) = @_; - check => sub { - my ( $user, $room_id, $anonymous_user ) = @_; + assert_json_keys( $body, qw( room_id state messages presence )); + assert_json_keys( $body->{messages}, qw( chunk start end )); + assert_json_list( $body->{messages}{chunk} ); + assert_json_list( $body->{state} ); - do_request_json_for( $anonymous_user, - method => "GET", - uri => "/api/v1/rooms/$room_id/state/m.room.member/".$user->user_id, - ); - }; + log_if_fail "room initialSync body", $body; -test "Anonymous user cannot room initalSync for non-world_readable rooms", - requires => [ anonymous_user_fixture(), local_user_fixture() ], + my $chunk = $body->{messages}{chunk}; - do => sub { - my ( $anonymous_user, $user ) = @_; + @{ $chunk } == 2 or die "Wrong number of chunks"; + assert_eq( $chunk->[0]->{type}, "m.room.history_visibility", "event 0 type" ); + assert_eq( $chunk->[0]->{content}->{history_visibility}, "world_readable", "history_visibility content" ); + assert_eq( $chunk->[1]->{type}, "m.room.message", "event 1 type" ); + assert_eq( $chunk->[1]->{content}->{body}, "public", "message content body" ); - my $room_id; + Future->done( 1 ); + }); + }, + ); - matrix_create_and_join_room( [ $user ] ) - ->then( sub { - ( $room_id ) = @_; + test( + "$name non-joined users can get individual state for world_readable rooms after leaving", - matrix_send_room_text_message( $user, $room_id, body => "private" ) - })->then( sub { - matrix_initialsync_room( $anonymous_user, $room_id ) - ->main::expect_http_403; - }); - }; + requires => [ local_user_and_room_fixtures(), $fixture->() ], + do => sub { + my ( $user, $room_id, $nonjoined_user ) = @_; -test "Anonymous user can room initialSync for world_readable rooms", - requires => [ anonymous_user_fixture(), local_user_fixture() ], + Future->needs_all( + matrix_set_room_history_visibility( $user, $room_id, "world_readable" ), + matrix_set_room_guest_access( $user, $room_id, "can_join" ), + )->then( sub { + matrix_join_room( $nonjoined_user, $room_id ); + })->then( sub { + matrix_leave_room( $nonjoined_user, $room_id ); + })->then( sub { + do_request_json_for( $nonjoined_user, + method => "GET", + uri => "/api/v1/rooms/$room_id/state/m.room.member/".$user->user_id, + ); + }); + }, + ); - do => sub { - my ( $anonymous_user, $user ) = @_; + test( + "$name non-joined users cannot send messages to guest_access rooms if not joined", - my $room_id; + requires => [ local_user_and_room_fixtures(), $fixture->() ], - matrix_create_and_join_room( [ $user ] ) - ->then( sub { - ( $room_id ) = @_; + do => sub { + my ( $user, $room_id, $nonjoined_user ) = @_; - matrix_send_room_text_message( $user, $room_id, body => "private" ) - })->then(sub { - matrix_set_room_history_visibility( $user, $room_id, "world_readable" ); - })->then( sub { - matrix_send_room_text_message( $user, $room_id, body => "public" ); - })->then( sub { - matrix_initialsync_room( $anonymous_user, $room_id ); - })->then( sub { - my ( $body ) = @_; + matrix_set_room_guest_access( $user, $room_id, "can_join" ) + ->then( sub { + matrix_send_room_text_message( $nonjoined_user, $room_id, body => "sup" ) + ->main::expect_http_403; + }); + }, + ); +} - assert_json_keys( $body, qw( room_id state messages presence )); - assert_json_keys( $body->{messages}, qw( chunk start end )); - assert_json_list( $body->{messages}{chunk} ); - assert_json_list( $body->{state} ); +test "Anonymous user cannot call /events globally", + requires => [ anonymous_user_fixture() ], - log_if_fail "room initialSync body", $body; + do => sub { + my ( $anonymous_user ) = @_; - my $chunk = $body->{messages}{chunk}; + do_request_json_for( $anonymous_user, + method => "GET", + uri => "/api/v1/events", + )->followed_by( \&expect_4xx_or_empty_chunk ); + }; - @{ $chunk } == 2 or die "Wrong number of chunks"; - $chunk->[0]->{type} eq "m.room.history_visibility" or die "Want m.room.history_visibility"; - $chunk->[0]->{content}->{history_visibility} eq "world_readable" or die "Wrong history_visibility value"; - $chunk->[1]->{type} eq "m.room.message" or die "Want m.room.message"; - $chunk->[1]->{content}->{body} eq "public" or die "Wrong message body"; +sub await_event_not_history_visibility_or_presence_for +{ + my ( $user, $room_id, $allowed_users ) = @_; + await_event_for( $user, + room_id => $room_id, + filter => sub { + my ( $event ) = @_; - Future->done( 1 ); - }); - }; + return 0 if defined $event->{type} and $event->{type} eq "m.room.history_visibility"; + + # Include all events where the type is not m.presence. + # If the type is m.presence, then only include it if it is for one of + # the allowed users + return ((not $event->{type} eq "m.presence") or + any { $event->{content}{user_id} eq $_->user_id } @$allowed_users); + }, + )->on_done( sub { + my ( $event ) = @_; + log_if_fail "event", $event + }); +} test "Anonymous users can join guest_access rooms", requires => [ local_user_and_room_fixtures(), anonymous_user_fixture() ], @@ -444,40 +466,6 @@ sub await_event_not_presence_for }) }; -test "Anonymous users cannot send messages to guest_access rooms if not joined", - requires => [ local_user_and_room_fixtures(), anonymous_user_fixture() ], - - do => sub { - my ( $user, $room_id, $anonymous_user ) = @_; - - matrix_set_room_guest_access( $user, $room_id, "can_join" ) - ->then( sub { - matrix_send_room_text_message( $anonymous_user, $room_id, body => "sup" ) - ->main::expect_http_403; - }); - }; - -test "Anonymous users can get individual state for world_readable rooms after leaving", - requires => [ local_user_and_room_fixtures(), anonymous_user_fixture() ], - - do => sub { - my ( $user, $room_id, $anonymous_user ) = @_; - - Future->needs_all( - matrix_set_room_history_visibility( $user, $room_id, "world_readable" ), - matrix_set_room_guest_access( $user, $room_id, "can_join" ), - )->then( sub { - matrix_join_room( $anonymous_user, $room_id ); - })->then( sub { - matrix_leave_room( $anonymous_user, $room_id ); - })->then( sub { - do_request_json_for( $anonymous_user, - method => "GET", - uri => "/api/v1/rooms/$room_id/state/m.room.member/".$user->user_id, - ); - }); - }; - test "Annonymous user calling /events doesn't tightloop", requires => [ anonymous_user_fixture(), local_user_fixture() ],