aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Somerville <matthew@mysociety.org>2019-10-30 16:46:17 +0000
committerDave Arter <davea@mysociety.org>2019-12-09 12:48:13 +0000
commitcc93a1601580c9a7f4f6a2cc0980d886b10e5785 (patch)
tree9d054d7c64fce41d8219195956948de4a21a9808
parent440c33c1c41dab2df8786ce43141ff12c59eb6ac (diff)
[TfL] Mandate 2FA for non-internal-IP staff users.
-rw-r--r--cpanfile2
-rw-r--r--cpanfile.snapshot13
-rw-r--r--perllib/FixMyStreet/Cobrand/FixMyStreet.pm1
-rw-r--r--perllib/FixMyStreet/Cobrand/TfL.pm14
-rw-r--r--t/app/controller/admin/templates.t1
-rw-r--r--t/cobrand/tfl.t73
6 files changed, 102 insertions, 2 deletions
diff --git a/cpanfile b/cpanfile
index 64caa4b6a..c4df511fc 100644
--- a/cpanfile
+++ b/cpanfile
@@ -131,6 +131,8 @@ requires 'YAML', '1.28';
feature 'uk', 'FixMyStreet.com specific requirements' => sub {
# East Hampshire
requires 'SOAP::Lite', '1.20';
+ # TfL
+ requires 'Net::Subnet';
};
feature 'zurich', 'Zueri wie neu specific requirements' => sub {
diff --git a/cpanfile.snapshot b/cpanfile.snapshot
index ccb8bf1f6..85132bbb6 100644
--- a/cpanfile.snapshot
+++ b/cpanfile.snapshot
@@ -5424,6 +5424,13 @@ DISTRIBUTIONS
POSIX 0
Socket 0
Time::HiRes 0
+ Net-Subnet-1.03
+ pathname: J/JU/JUERD/Net-Subnet-1.03.tar.gz
+ provides:
+ Net::Subnet 1.03
+ requirements:
+ ExtUtils::MakeMaker 0
+ Socket6 0.23
Net-Twitter-Lite-0.12008
pathname: M/MM/MMIMS/Net-Twitter-Lite-0.12008.tar.gz
provides:
@@ -6771,6 +6778,12 @@ DISTRIBUTIONS
ExtUtils::Constant 0.23
ExtUtils::MakeMaker 0
perl 5.006001
+ Socket6-0.29
+ pathname: U/UM/UMEMOTO/Socket6-0.29.tar.gz
+ provides:
+ Socket6 0.29
+ requirements:
+ ExtUtils::MakeMaker 0
Sort-Key-1.32
pathname: S/SA/SALVA/Sort-Key-1.32.tar.gz
provides:
diff --git a/perllib/FixMyStreet/Cobrand/FixMyStreet.pm b/perllib/FixMyStreet/Cobrand/FixMyStreet.pm
index 5f5389e0d..f2551a16d 100644
--- a/perllib/FixMyStreet/Cobrand/FixMyStreet.pm
+++ b/perllib/FixMyStreet/Cobrand/FixMyStreet.pm
@@ -313,6 +313,7 @@ sub suppress_reporter_alerts {
sub must_have_2fa {
my ($self, $user) = @_;
return 1 if $user->is_superuser;
+ return 1 if $user->from_body && $user->from_body->name eq 'TfL';
return 0;
}
diff --git a/perllib/FixMyStreet/Cobrand/TfL.pm b/perllib/FixMyStreet/Cobrand/TfL.pm
index f4e7fb14a..4543c1751 100644
--- a/perllib/FixMyStreet/Cobrand/TfL.pm
+++ b/perllib/FixMyStreet/Cobrand/TfL.pm
@@ -127,4 +127,18 @@ sub available_permissions {
return $perms;
}
+sub must_have_2fa {
+ my ($self, $user) = @_;
+
+ require Net::Subnet;
+ my $ips = $self->feature('internal_ips');
+ my $is_internal_network = Net::Subnet::subnet_matcher(@$ips);
+
+ my $ip = $self->{c}->req->address;
+ return 'skip' if $is_internal_network->($ip);
+ return 1 if $user->is_superuser;
+ return 1 if $user->from_body && $user->from_body->name eq 'TfL';
+ return 0;
+}
+
1;
diff --git a/t/app/controller/admin/templates.t b/t/app/controller/admin/templates.t
index ac915af94..ee477de20 100644
--- a/t/app/controller/admin/templates.t
+++ b/t/app/controller/admin/templates.t
@@ -330,6 +330,7 @@ subtest "category groups are shown" => sub {
subtest "TfL cobrand only shows TfL templates" => sub {
FixMyStreet::override_config {
ALLOWED_COBRANDS => [ 'tfl' ],
+ COBRAND_FEATURES => { internal_ips => { tfl => [ '127.0.0.1' ] } },
}, sub {
$report->update({
category => $tflcontact->category,
diff --git a/t/cobrand/tfl.t b/t/cobrand/tfl.t
index c7db294ac..6e6920c60 100644
--- a/t/cobrand/tfl.t
+++ b/t/cobrand/tfl.t
@@ -10,7 +10,7 @@ my $mech = FixMyStreet::TestMech->new;
my $body = $mech->create_body_ok(2482, 'TfL');
my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1);
-my $staffuser = $mech->create_user_ok('counciluser@example.com', name => 'Council User', from_body => $body);
+my $staffuser = $mech->create_user_ok('counciluser@example.com', name => 'Council User', from_body => $body, password => 'password');
$staffuser->user_body_permissions->create({
body => $body,
permission_type => 'contribute_as_body',
@@ -38,7 +38,8 @@ my $contact2 = $mech->create_contact_ok(
FixMyStreet::override_config {
ALLOWED_COBRANDS => 'tfl',
- MAPIT_URL => 'http://mapit.uk/'
+ MAPIT_URL => 'http://mapit.uk/',
+ COBRAND_FEATURES => { internal_ips => { tfl => [ '127.0.0.1' ] } },
}, sub {
subtest "test report creation and reference number" => sub {
@@ -188,6 +189,74 @@ subtest 'Bromley staff cannot access TfL admin' => sub {
};
FixMyStreet::override_config {
+ ALLOWED_COBRANDS => 'tfl',
+ MAPIT_URL => 'http://mapit.uk/',
+ COBRAND_FEATURES => { internal_ips => { tfl => [ '127.0.0.1' ] } },
+}, sub {
+ subtest 'On internal network, user not asked to sign up for 2FA' => sub {
+ $mech->get_ok('/auth');
+ $mech->submit_form_ok(
+ { with_fields => { username => $staffuser->email, password_sign_in => 'password' } },
+ "sign in using form" );
+ $mech->content_contains('Your account');
+ };
+ subtest 'On internal network, user with 2FA not asked to enter it' => sub {
+ use Auth::GoogleAuth;
+ my $auth = Auth::GoogleAuth->new;
+ my $code = $auth->code;
+
+ $staffuser->set_extra_metadata('2fa_secret', $auth->secret32);
+ $staffuser->update;
+ $mech->get_ok('/auth');
+ $mech->submit_form_ok(
+ { with_fields => { username => $staffuser->email, password_sign_in => 'password' } },
+ "sign in using form" );
+ $mech->content_lacks('generate a two-factor code');
+ $mech->content_contains('Your account');
+ };
+ subtest 'On internal network, cannot disable 2FA' => sub {
+ $mech->get_ok('/auth/generate_token');
+ $mech->content_contains('Change two-factor');
+ $mech->content_lacks('Deactivate two-factor');
+ $staffuser->unset_extra_metadata('2fa_secret');
+ $staffuser->update;
+ };
+};
+FixMyStreet::override_config {
+ ALLOWED_COBRANDS => 'tfl',
+ MAPIT_URL => 'http://mapit.uk/',
+}, sub {
+ subtest 'On external network, user asked to sign up for 2FA' => sub {
+ $mech->get_ok('/auth');
+ $mech->submit_form_ok(
+ { with_fields => { username => $staffuser->email, password_sign_in => 'password' } },
+ "sign in using form" );
+ $mech->content_contains('requires two-factor authentication');
+ };
+ subtest 'On external network, user with 2FA asked to enter it' => sub {
+ use Auth::GoogleAuth;
+ my $auth = Auth::GoogleAuth->new;
+ my $code = $auth->code;
+
+ $staffuser->set_extra_metadata('2fa_secret', $auth->secret32);
+ $staffuser->update;
+ $mech->get_ok('/auth');
+ $mech->submit_form_ok(
+ { with_fields => { username => $staffuser->email, password_sign_in => 'password' } },
+ "sign in using form" );
+ $mech->content_contains('Please generate a two-factor code');
+ $mech->submit_form_ok({ with_fields => { '2fa_code' => $code } }, "provide correct 2FA code" );
+ };
+ subtest 'On external network, cannot disable 2FA' => sub {
+ $mech->get_ok('/auth/generate_token');
+ $mech->content_contains('Change two-factor');
+ $mech->content_lacks('Deactivate two-factor');
+ $staffuser->unset_extra_metadata('2fa_secret');
+ $staffuser->update;
+ };
+};
+
+FixMyStreet::override_config {
ALLOWED_COBRANDS => 'bromley',
MAPIT_URL => 'http://mapit.uk/'
}, sub {