Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

do requestToken before adding a 3pid #706

Merged
merged 3 commits into from
Sep 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions lib/SyTest/Identity/Server.pm
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ use base qw( Net::Async::HTTP::Server );

use Crypt::NaCl::Sodium;
use List::Util qw( any );
use List::UtilsBy qw( extract_first_by );
use Protocol::Matrix qw( encode_base64_unpadded sign_json );
use MIME::Base64 qw ( encode_base64url );
use SyTest::HTTPServer::Request;
use HTTP::Response;
use Digest::SHA qw( sha256 );
use Struct::Dumb qw( struct );

my $crypto_sign = Crypt::NaCl::Sodium->sign;

Expand All @@ -22,6 +24,8 @@ my $next_token = 0;
# `access_token` parameter
my $ID_ACCESS_TOKEN = "swordfish";

struct Awaiter => [qw( future path_match )];

sub _init
{
my $self = shift;
Expand All @@ -34,6 +38,7 @@ sub _init

$self->{bindings} = {};
$self->{invites} = {};
$self->{awaiters} = [];

# String for peppering hashed lookup requests
$self->{lookup_pepper} = "matrixrocks";
Expand Down Expand Up @@ -68,6 +73,34 @@ sub rotate_keys
};
}

=head2 await_request

$server->await_request(
path => "/_matrix/identity/...",
)->then( sub {
my ( $req ) = @_;
});

Wait for a request to arrive at the identity server.

=cut

sub await_request
{
my ( $self, %params ) = @_;

my $path_match = $params{path};
my $f = $self->loop->new_future;
my $awaiter = Awaiter( $f, $path_match );
push @{$self->{awaiters}}, $awaiter;

$f->on_cancel( sub {
extract_first_by { $_ == $awaiter } @{$self->{awaiters}};
});

return $f;
}

=head2 on_request

Handles incoming HTTP requests to this server.
Expand All @@ -80,6 +113,17 @@ sub on_request
my ( $req ) = @_;

my $path = $req->path;

# first see if we've got an awaiter for this request
my $awaiter = extract_first_by {
$_->path_match eq $path
} @{$self->{awaiters}};

if( $awaiter ) {
$awaiter->future->done( $req );
return;
}

my $key_name;

if(
Expand Down
138 changes: 125 additions & 13 deletions tests/12login/01threepid-and-password.pl
Original file line number Diff line number Diff line change
@@ -1,21 +1,116 @@
my $password = "my secure password";

=head2 validate_email_for_user

test "Can login with 3pid and password using m.login.password",
requires => [ local_user_fixture( password => $password ), id_server_fixture() ],
validate_email_for_user(
$user, $address, $id_server,
)->then( sub {
my ( $sid, $client_secret ) = @_;
});

check => sub {
my ( $user, $id_server ) = @_;
Runs through the `/r0/account/3pid/email/requestToken` flow for verifying
that an email address belongs to the user. Doesn't add the address to the
account.

my $http = $user->http;
Returns the session id and client secret which can then be used for binding
the address.

my $medium = "email";
my $address = 'bob@example.com';
my $client_secret = "a client secret";
my $id_access_token = $id_server->get_access_token();
=cut

sub validate_email_for_user {
my ( $user, $address, $id_server ) = @_;

# fixme: synapse screws up the escaping of non-alpha chars.
my $client_secret = join "", map { chr 65 + rand 26 } 1 .. 20;

my $sid;

return Future->needs_all(
do_request_json_for(
$user,
method => "POST",
uri => "/r0/account/3pid/email/requestToken",
content => {
client_secret => $client_secret,
email => $address,
send_attempt => 1,
id_server => $id_server->name,
id_access_token => $id_server->get_access_token(),
},
)->then( sub {
my ( $resp ) = @_;
log_if_fail "requestToken response", $resp;

$sid = $resp->{sid};
Future->done;
}),

# we now expect a callout to our test ID server.
await_id_validation( $id_server, $address ),
)->then( sub {
Future->done( $sid, $client_secret );
});
}

push our @EXPORT, qw( validate_email_for_user );

# wait for a call to /requestToken on the test IS, and act as if the
# email has been validated.
sub await_id_validation {
my ( $id_server, $address ) = @_;

$id_server->await_request(
path=>"/_matrix/identity/api/v1/validate/email/requestToken",
)->then( sub {
my ( $req ) = @_;
my $body = $req->body_from_json;

log_if_fail "ID server /requestToken request", $body;
assert_eq( $body->{email}, $address );
my $sid = $id_server->validate_identity( 'email', $address, $body->{client_secret} );
$req->respond_json({
sid => $sid,
});
Future->done();
});
}

=head2 add_email_for_user

add_email_for_user(
$user, $address, $id_server, %params
);

my $sid = $id_server->validate_identity( $medium, $address, $client_secret );
Add the given email address to the homeserver account, including the
verfication steps.

C<%params> may include:

=over

=item bind => SCALAR

If truthy, we will ask the server to ask the IS to bind the email
address. False by default.

=back

=cut

sub add_email_for_user {
my ( $user, $address, $id_server, %params ) = @_;

my $bind = $params{bind} // 0;

my $id_access_token = $id_server->get_access_token();

# start by requesting an email validation.
validate_email_for_user(
$user, $address, $id_server,
)->then( sub {
my ( $sid, $client_secret ) = @_;

# now tell the HS to add the 3pid
do_request_json_for( $user,
method => "POST",
uri => "/r0/account/3pid",
Expand All @@ -26,16 +121,33 @@
sid => $sid,
client_secret => $client_secret,
},
bind => JSON::false,
bind => $bind ? JSON::true : JSON::false,
},
)->then( sub {
);
});
}

push @EXPORT, qw( add_email_for_user );

test "Can login with 3pid and password using m.login.password",
requires => [ local_user_fixture( password => $password ), id_server_fixture() ],

check => sub {
my ( $user, $id_server ) = @_;

my $http = $user->http;

my $address = 'bob@example.com';

add_email_for_user( $user, $address, $id_server )
->then( sub {
$http->do_request_json(
method => "POST",
uri => "/r0/login",

content => {
type => "m.login.password",
medium => $medium,
medium => 'email',
address => $address,
password => $password,
}
Expand Down
96 changes: 15 additions & 81 deletions tests/54identity.pl
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,10 @@
my ( $http, $user, $id_server ) = @_;

my $medium = "email";
my $address = 'bob@example.com';
my $client_secret = "a client secret";
my $id_access_token = $id_server->get_access_token;
my $address = 'bob1@example.com';

my $sid = $id_server->validate_identity( $medium, $address, $client_secret );

do_request_json_for( $user,
method => "POST",
uri => "/r0/account/3pid",
content => {
three_pid_creds => {
id_server => $id_server->name,
id_access_token => $id_access_token,
sid => $sid,
client_secret => $client_secret,
},
bind => JSON::true,
},
add_email_for_user(
$user, $address, $id_server, bind => 1,
)->then( sub {
my $res = $id_server->lookup_identity( $medium, $address );

Expand All @@ -40,24 +26,10 @@
my ( $http, $user, $id_server ) = @_;

my $medium = "email";
my $address = 'bob@example.com';
my $client_secret = "a client secret";
my $id_access_token = $id_server->get_access_token;
my $address = 'bob2@example.com';

my $sid = $id_server->validate_identity( $medium, $address, $client_secret );

do_request_json_for( $user,
method => "POST",
uri => "/r0/account/3pid",
content => {
three_pid_creds => {
id_server => $id_server->name,
id_access_token => $id_access_token,
sid => $sid,
client_secret => $client_secret,
},
bind => JSON::true,
},
add_email_for_user(
$user, $address, $id_server, bind => 1,
)->then( sub {
my $res = $id_server->lookup_identity( $medium, $address );
assert_eq( $res, $user->user_id );
Expand Down Expand Up @@ -86,7 +58,7 @@
my ( $http, $user, $id_server ) = @_;

my $medium = "email";
my $address = 'bob@example.com';
my $address = 'bob3@example.com';

# Bind the 3PID out of band of the homeserver
$id_server->bind_identity( undef, $medium, $address, $user->user_id );
Expand Down Expand Up @@ -117,24 +89,10 @@
my ( $http, $user, $id_server ) = @_;

my $medium = "email";
my $address = 'bob@example.com';
my $client_secret = "a client secret";
my $id_access_token = $id_server->get_access_token;
my $address = 'bob4@example.com';

my $sid = $id_server->validate_identity( $medium, $address, $client_secret );

do_request_json_for( $user,
method => "POST",
uri => "/r0/account/3pid",
content => {
three_pid_creds => {
id_server => $id_server->name,
id_access_token => $id_access_token,
sid => $sid,
client_secret => $client_secret,
},
bind => JSON::true,
},
add_email_for_user(
$user, $address, $id_server, bind => 1,
)->then( sub {
my $res = $id_server->lookup_identity( $medium, $address );
assert_eq( $res, $user->user_id );
Expand All @@ -157,21 +115,9 @@

my $medium = "email";
my $address = 'bobby@example.com';
my $client_secret = "53kr3t";

my $sid = $id_server->validate_identity( $medium, $address, $client_secret );

do_request_json_for( $user,
method => "POST",
uri => "/r0/account/3pid",
content => {
three_pid_creds => {
id_server => $id_server->name,
sid => $sid,
client_secret => $client_secret,
},
bind => JSON::true,
},
add_email_for_user(
$user, $address, $id_server, bind => 1,
)->then( sub {
my $res = $id_server->lookup_identity( $medium, $address );
assert_eq( $res, $user->user_id );
Expand Down Expand Up @@ -201,22 +147,10 @@
my ( $http, $user, $id_server ) = @_;

my $medium = "email";
my $address = 'bobby@example.com';
my $client_secret = "53kr3t";

my $sid = $id_server->validate_identity( $medium, $address, $client_secret );
my $address = 'bobby2@example.com';

do_request_json_for( $user,
method => "POST",
uri => "/r0/account/3pid",
content => {
three_pid_creds => {
id_server => $id_server->name,
sid => $sid,
client_secret => $client_secret,
},
bind => JSON::true,
},
add_email_for_user(
$user, $address, $id_server, bind => 1,
)->then( sub {
my $res = $id_server->lookup_identity( $medium, $address );
assert_eq( $res, $user->user_id );
Expand Down