aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md5
-rw-r--r--cpanfile3
-rw-r--r--cpanfile.snapshot519
-rw-r--r--perllib/Catalyst/Plugin/FixMyStreet/Session/StoreSessions.pm23
-rw-r--r--perllib/FixMyStreet/App.pm3
-rw-r--r--perllib/FixMyStreet/App/Controller/Admin.pm35
-rw-r--r--perllib/FixMyStreet/App/Controller/Auth.pm64
-rw-r--r--perllib/FixMyStreet/App/Controller/Auth/Phone.pm8
-rw-r--r--perllib/FixMyStreet/App/Controller/Auth/Profile.pm35
-rw-r--r--perllib/FixMyStreet/App/Controller/Report/New.pm6
-rw-r--r--perllib/FixMyStreet/App/Controller/Report/Update.pm7
-rw-r--r--perllib/FixMyStreet/Cobrand/Borsetshire.pm2
-rw-r--r--perllib/FixMyStreet/Cobrand/Default.pm8
-rw-r--r--perllib/FixMyStreet/Cobrand/Oxfordshire.pm2
-rw-r--r--perllib/FixMyStreet/DB/Result/User.pm9
-rw-r--r--perllib/FixMyStreet/Test.pm13
-rw-r--r--t/app/controller/admin/users.t26
-rw-r--r--t/app/controller/auth.t25
-rw-r--r--t/app/controller/auth_profile.t79
-rw-r--r--t/app/controller/report_new.t8
-rw-r--r--t/app/controller/report_new_text.t4
-rw-r--r--t/app/controller/report_update_text.t2
-rw-r--r--templates/web/base/admin/user-form.html2
-rw-r--r--templates/web/base/auth/change_password.html20
-rw-r--r--templates/web/base/auth/change_phone.html2
-rw-r--r--templates/web/base/auth/general.html12
-rw-r--r--templates/web/base/js/translation_strings.html6
-rw-r--r--templates/web/base/report/_inspect.html2
-rw-r--r--templates/web/base/report/new/form_user_loggedin.html4
-rw-r--r--templates/web/base/report/new/form_user_loggedout_by_email.html11
-rw-r--r--templates/web/base/report/update/form_user_loggedout_by_email.html9
-rw-r--r--templates/web/bromley/report/new/form_user.html10
-rw-r--r--templates/web/bromley/report/update-form.html10
-rw-r--r--templates/web/fixamingata/report/new/form_user_loggedout.html13
-rw-r--r--templates/web/zurich/auth/general.html21
-rw-r--r--web/cobrands/fixmystreet/fixmystreet.js3
-rw-r--r--web/js/validation_rules.js8
37 files changed, 700 insertions, 319 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 37b12d997..065ad72e4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,9 @@
- Front end improvements:
- Zoom out as much as necessary on body map page, even on mobile. #1958
- Show loading message on initial /around map load #1976
+ - Ask for current password/send email on password change. #1974
+ - Add minimum password length and common password checking.
+ - Nicer display of national phone numbers.
- Bugfixes:
- Fix bug specifying category in URL on /around. #1950
- Fix bug with multiple select-multiples on a page. #1951
@@ -18,6 +21,8 @@
- Fix error sending `requires_inspection` reports. #1961
- Admin improvements:
- Admin can anonymize/hide all a user's reports. #1942 #1943
+ - Admin can log a user out. #1975
+ - Admin can remove a user's account details. #1944
- Superusers can have optional two-factor authentication. #1973
- UK:
- Lazy load images in the footer.
diff --git a/cpanfile b/cpanfile
index acdec9e7c..707dcb006 100644
--- a/cpanfile
+++ b/cpanfile
@@ -36,6 +36,7 @@ requires 'Authen::SASL';
requires 'Cache::Memcached';
requires 'Carp';
requires 'Crypt::Eksblowfish::Bcrypt';
+requires 'Data::Password::Common';
requires 'DateTime';
requires 'DateTime::Format::HTTP';
requires 'DateTime::Format::ISO8601';
@@ -83,7 +84,7 @@ requires 'Net::Domain::TLD', '1.75';
requires 'Net::Facebook::Oauth2', '0.10';
requires 'Net::OAuth';
requires 'Net::Twitter::Lite::WithAPIv1_1', '0.12008';
-requires 'Number::Phone';
+requires 'Number::Phone', '3.4003';
requires 'Path::Class';
requires 'POSIX';
requires 'Readonly';
diff --git a/cpanfile.snapshot b/cpanfile.snapshot
index 7b3bb89f5..83d855c98 100644
--- a/cpanfile.snapshot
+++ b/cpanfile.snapshot
@@ -1433,6 +1433,20 @@ DISTRIBUTIONS
Module::Build 0.35
Test::Exception 0
Test::More 0
+ Data-Password-Common-0.004
+ pathname: D/DA/DAGOLDEN/Data-Password-Common-0.004.tar.gz
+ provides:
+ Data::Password::Common 0.004
+ requirements:
+ ExtUtils::MakeMaker 6.17
+ File::ShareDir 0
+ File::ShareDir::Install 0.03
+ IO::File 0
+ Search::Dict 0
+ Sub::Exporter 0
+ autodie 2.00
+ strict 0
+ warnings 0
Data-Visitor-0.28
pathname: D/DO/DOY/Data-Visitor-0.28.tar.gz
provides:
@@ -4534,15 +4548,18 @@ DISTRIBUTIONS
Carp 0
ExtUtils::MakeMaker 0
POSIX 0
- Number-Phone-3.4002
- pathname: D/DC/DCANTRELL/Number-Phone-3.4002.tar.gz
+ Number-Phone-3.4003
+ pathname: D/DC/DCANTRELL/Number-Phone-3.4003.tar.gz
provides:
- Number::Phone 3.4002
+ Number::Phone 3.4003
Number::Phone::Country 1.93
Number::Phone::Country::Data 1.6
+ Number::Phone::Formatter 1.0
+ Number::Phone::Formatter::National 1.0
+ Number::Phone::Formatter::NationallyPreferredIntl 1.0
Number::Phone::Formatter::Raw undef
Number::Phone::Lib 1.0
- Number::Phone::NANP 1.5
+ Number::Phone::NANP 1.6000
Number::Phone::NANP::AG 1.1
Number::Phone::NANP::AI 1.1
Number::Phone::NANP::AS 1.1
@@ -4552,7 +4569,7 @@ DISTRIBUTIONS
Number::Phone::NANP::CA 1.1
Number::Phone::NANP::DM 1.1
Number::Phone::NANP::DO 1.1
- Number::Phone::NANP::Data 1.20170908113144
+ Number::Phone::NANP::Data 1.20180203200229
Number::Phone::NANP::GD 1.1
Number::Phone::NANP::GU 1.1
Number::Phone::NANP::JM 1.1
@@ -4569,252 +4586,252 @@ DISTRIBUTIONS
Number::Phone::NANP::VC 1.1
Number::Phone::NANP::VG 1.1
Number::Phone::NANP::VI 1.1
- Number::Phone::StubCountry 1.3
- Number::Phone::StubCountry::AC 1.20170908113147
- Number::Phone::StubCountry::AD 1.20170908113147
- Number::Phone::StubCountry::AE 1.20170908113147
- Number::Phone::StubCountry::AF 1.20170908113147
- Number::Phone::StubCountry::AG 1.20170908113147
- Number::Phone::StubCountry::AI 1.20170908113147
- Number::Phone::StubCountry::AL 1.20170908113147
- Number::Phone::StubCountry::AM 1.20170908113147
- Number::Phone::StubCountry::AO 1.20170908113147
- Number::Phone::StubCountry::AR 1.20170908113147
- Number::Phone::StubCountry::AS 1.20170908113147
- Number::Phone::StubCountry::AT 1.20170908113147
- Number::Phone::StubCountry::AU 1.20170908113147
- Number::Phone::StubCountry::AW 1.20170908113147
- Number::Phone::StubCountry::AX 1.20170908113147
- Number::Phone::StubCountry::AZ 1.20170908113147
- Number::Phone::StubCountry::BA 1.20170908113147
- Number::Phone::StubCountry::BB 1.20170908113147
- Number::Phone::StubCountry::BD 1.20170908113147
- Number::Phone::StubCountry::BE 1.20170908113147
- Number::Phone::StubCountry::BF 1.20170908113147
- Number::Phone::StubCountry::BG 1.20170908113147
- Number::Phone::StubCountry::BH 1.20170908113147
- Number::Phone::StubCountry::BI 1.20170908113147
- Number::Phone::StubCountry::BJ 1.20170908113147
- Number::Phone::StubCountry::BL 1.20170908113147
- Number::Phone::StubCountry::BM 1.20170908113147
- Number::Phone::StubCountry::BN 1.20170908113147
- Number::Phone::StubCountry::BO 1.20170908113147
- Number::Phone::StubCountry::BQ 1.20170908113147
- Number::Phone::StubCountry::BR 1.20170908113147
- Number::Phone::StubCountry::BS 1.20170908113147
- Number::Phone::StubCountry::BT 1.20170908113147
- Number::Phone::StubCountry::BW 1.20170908113147
- Number::Phone::StubCountry::BY 1.20170908113147
- Number::Phone::StubCountry::BZ 1.20170908113147
- Number::Phone::StubCountry::CA 1.20170908113147
- Number::Phone::StubCountry::CC 1.20170908113147
- Number::Phone::StubCountry::CD 1.20170908113147
- Number::Phone::StubCountry::CF 1.20170908113147
- Number::Phone::StubCountry::CG 1.20170908113147
- Number::Phone::StubCountry::CH 1.20170908113147
- Number::Phone::StubCountry::CI 1.20170908113147
- Number::Phone::StubCountry::CK 1.20170908113147
- Number::Phone::StubCountry::CL 1.20170908113147
- Number::Phone::StubCountry::CM 1.20170908113147
- Number::Phone::StubCountry::CN 1.20170908113147
- Number::Phone::StubCountry::CO 1.20170908113148
- Number::Phone::StubCountry::CR 1.20170908113148
- Number::Phone::StubCountry::CU 1.20170908113148
- Number::Phone::StubCountry::CV 1.20170908113148
- Number::Phone::StubCountry::CW 1.20170908113148
- Number::Phone::StubCountry::CX 1.20170908113148
- Number::Phone::StubCountry::CY 1.20170908113148
- Number::Phone::StubCountry::CZ 1.20170908113148
- Number::Phone::StubCountry::DE 1.20170908113148
- Number::Phone::StubCountry::DJ 1.20170908113148
- Number::Phone::StubCountry::DK 1.20170908113148
- Number::Phone::StubCountry::DM 1.20170908113148
- Number::Phone::StubCountry::DO 1.20170908113148
- Number::Phone::StubCountry::DZ 1.20170908113148
- Number::Phone::StubCountry::EC 1.20170908113148
- Number::Phone::StubCountry::EE 1.20170908113148
- Number::Phone::StubCountry::EG 1.20170908113148
- Number::Phone::StubCountry::EH 1.20170908113148
- Number::Phone::StubCountry::ER 1.20170908113148
- Number::Phone::StubCountry::ES 1.20170908113148
- Number::Phone::StubCountry::ET 1.20170908113148
- Number::Phone::StubCountry::FI 1.20170908113148
- Number::Phone::StubCountry::FJ 1.20170908113148
- Number::Phone::StubCountry::FK 1.20170908113148
- Number::Phone::StubCountry::FM 1.20170908113148
- Number::Phone::StubCountry::FO 1.20170908113148
- Number::Phone::StubCountry::FR 1.20170908113148
- Number::Phone::StubCountry::GA 1.20170908113148
- Number::Phone::StubCountry::GB 1.20170908113148
- Number::Phone::StubCountry::GD 1.20170908113148
- Number::Phone::StubCountry::GE 1.20170908113148
- Number::Phone::StubCountry::GF 1.20170908113148
- Number::Phone::StubCountry::GG 1.20170908113148
- Number::Phone::StubCountry::GH 1.20170908113148
- Number::Phone::StubCountry::GI 1.20170908113148
- Number::Phone::StubCountry::GL 1.20170908113148
- Number::Phone::StubCountry::GM 1.20170908113148
- Number::Phone::StubCountry::GN 1.20170908113148
- Number::Phone::StubCountry::GP 1.20170908113148
- Number::Phone::StubCountry::GQ 1.20170908113148
- Number::Phone::StubCountry::GR 1.20170908113148
- Number::Phone::StubCountry::GT 1.20170908113148
- Number::Phone::StubCountry::GU 1.20170908113148
- Number::Phone::StubCountry::GW 1.20170908113148
- Number::Phone::StubCountry::GY 1.20170908113148
- Number::Phone::StubCountry::HK 1.20170908113148
- Number::Phone::StubCountry::HN 1.20170908113148
- Number::Phone::StubCountry::HR 1.20170908113148
- Number::Phone::StubCountry::HT 1.20170908113148
- Number::Phone::StubCountry::HU 1.20170908113148
- Number::Phone::StubCountry::ID 1.20170908113148
- Number::Phone::StubCountry::IE 1.20170908113148
- Number::Phone::StubCountry::IL 1.20170908113148
- Number::Phone::StubCountry::IM 1.20170908113148
- Number::Phone::StubCountry::IN 1.20170908113148
- Number::Phone::StubCountry::IO 1.20170908113148
- Number::Phone::StubCountry::IQ 1.20170908113148
- Number::Phone::StubCountry::IR 1.20170908113148
- Number::Phone::StubCountry::IS 1.20170908113148
- Number::Phone::StubCountry::IT 1.20170908113148
- Number::Phone::StubCountry::JE 1.20170908113148
- Number::Phone::StubCountry::JM 1.20170908113148
- Number::Phone::StubCountry::JO 1.20170908113148
- Number::Phone::StubCountry::JP 1.20170908113148
- Number::Phone::StubCountry::KE 1.20170908113148
- Number::Phone::StubCountry::KG 1.20170908113148
- Number::Phone::StubCountry::KH 1.20170908113148
- Number::Phone::StubCountry::KI 1.20170908113148
- Number::Phone::StubCountry::KM 1.20170908113148
- Number::Phone::StubCountry::KN 1.20170908113148
- Number::Phone::StubCountry::KP 1.20170908113148
- Number::Phone::StubCountry::KR 1.20170908113148
- Number::Phone::StubCountry::KW 1.20170908113148
- Number::Phone::StubCountry::KY 1.20170908113148
- Number::Phone::StubCountry::KZ 1.20170908113148
- Number::Phone::StubCountry::LA 1.20170908113148
- Number::Phone::StubCountry::LB 1.20170908113148
- Number::Phone::StubCountry::LC 1.20170908113148
- Number::Phone::StubCountry::LI 1.20170908113148
- Number::Phone::StubCountry::LK 1.20170908113148
- Number::Phone::StubCountry::LR 1.20170908113148
- Number::Phone::StubCountry::LS 1.20170908113148
- Number::Phone::StubCountry::LT 1.20170908113148
- Number::Phone::StubCountry::LU 1.20170908113148
- Number::Phone::StubCountry::LV 1.20170908113148
- Number::Phone::StubCountry::LY 1.20170908113148
- Number::Phone::StubCountry::MA 1.20170908113148
- Number::Phone::StubCountry::MC 1.20170908113148
- Number::Phone::StubCountry::MD 1.20170908113148
- Number::Phone::StubCountry::ME 1.20170908113148
- Number::Phone::StubCountry::MF 1.20170908113148
- Number::Phone::StubCountry::MG 1.20170908113148
- Number::Phone::StubCountry::MH 1.20170908113148
- Number::Phone::StubCountry::MK 1.20170908113148
- Number::Phone::StubCountry::ML 1.20170908113148
- Number::Phone::StubCountry::MM 1.20170908113148
- Number::Phone::StubCountry::MN 1.20170908113148
- Number::Phone::StubCountry::MO 1.20170908113148
- Number::Phone::StubCountry::MP 1.20170908113148
- Number::Phone::StubCountry::MQ 1.20170908113148
- Number::Phone::StubCountry::MR 1.20170908113148
- Number::Phone::StubCountry::MS 1.20170908113148
- Number::Phone::StubCountry::MT 1.20170908113148
- Number::Phone::StubCountry::MU 1.20170908113148
- Number::Phone::StubCountry::MV 1.20170908113148
- Number::Phone::StubCountry::MW 1.20170908113148
- Number::Phone::StubCountry::MX 1.20170908113148
- Number::Phone::StubCountry::MY 1.20170908113148
- Number::Phone::StubCountry::MZ 1.20170908113148
- Number::Phone::StubCountry::NA 1.20170908113148
- Number::Phone::StubCountry::NC 1.20170908113148
- Number::Phone::StubCountry::NE 1.20170908113148
- Number::Phone::StubCountry::NF 1.20170908113148
- Number::Phone::StubCountry::NG 1.20170908113148
- Number::Phone::StubCountry::NI 1.20170908113148
- Number::Phone::StubCountry::NL 1.20170908113148
- Number::Phone::StubCountry::NO 1.20170908113148
- Number::Phone::StubCountry::NP 1.20170908113148
- Number::Phone::StubCountry::NR 1.20170908113148
- Number::Phone::StubCountry::NU 1.20170908113148
- Number::Phone::StubCountry::NZ 1.20170908113148
- Number::Phone::StubCountry::OM 1.20170908113148
- Number::Phone::StubCountry::PA 1.20170908113148
- Number::Phone::StubCountry::PE 1.20170908113148
- Number::Phone::StubCountry::PF 1.20170908113148
- Number::Phone::StubCountry::PG 1.20170908113148
- Number::Phone::StubCountry::PH 1.20170908113148
- Number::Phone::StubCountry::PK 1.20170908113148
- Number::Phone::StubCountry::PL 1.20170908113148
- Number::Phone::StubCountry::PM 1.20170908113148
- Number::Phone::StubCountry::PR 1.20170908113149
- Number::Phone::StubCountry::PS 1.20170908113149
- Number::Phone::StubCountry::PT 1.20170908113149
- Number::Phone::StubCountry::PW 1.20170908113149
- Number::Phone::StubCountry::PY 1.20170908113149
- Number::Phone::StubCountry::QA 1.20170908113149
- Number::Phone::StubCountry::RE 1.20170908113149
- Number::Phone::StubCountry::RO 1.20170908113149
- Number::Phone::StubCountry::RS 1.20170908113149
- Number::Phone::StubCountry::RU 1.20170908113149
- Number::Phone::StubCountry::RW 1.20170908113149
- Number::Phone::StubCountry::SA 1.20170908113149
- Number::Phone::StubCountry::SB 1.20170908113149
- Number::Phone::StubCountry::SC 1.20170908113149
- Number::Phone::StubCountry::SD 1.20170908113149
- Number::Phone::StubCountry::SE 1.20170908113149
- Number::Phone::StubCountry::SG 1.20170908113149
- Number::Phone::StubCountry::SH 1.20170908113149
- Number::Phone::StubCountry::SI 1.20170908113149
- Number::Phone::StubCountry::SJ 1.20170908113149
- Number::Phone::StubCountry::SK 1.20170908113149
- Number::Phone::StubCountry::SL 1.20170908113149
- Number::Phone::StubCountry::SM 1.20170908113149
- Number::Phone::StubCountry::SN 1.20170908113149
- Number::Phone::StubCountry::SO 1.20170908113149
- Number::Phone::StubCountry::SR 1.20170908113149
- Number::Phone::StubCountry::SS 1.20170908113149
- Number::Phone::StubCountry::ST 1.20170908113149
- Number::Phone::StubCountry::SV 1.20170908113149
- Number::Phone::StubCountry::SX 1.20170908113149
- Number::Phone::StubCountry::SY 1.20170908113149
- Number::Phone::StubCountry::SZ 1.20170908113149
- Number::Phone::StubCountry::TA 1.20170908113149
- Number::Phone::StubCountry::TC 1.20170908113149
- Number::Phone::StubCountry::TD 1.20170908113149
- Number::Phone::StubCountry::TG 1.20170908113149
- Number::Phone::StubCountry::TH 1.20170908113149
- Number::Phone::StubCountry::TJ 1.20170908113149
- Number::Phone::StubCountry::TK 1.20170908113149
- Number::Phone::StubCountry::TL 1.20170908113149
- Number::Phone::StubCountry::TM 1.20170908113149
- Number::Phone::StubCountry::TN 1.20170908113149
- Number::Phone::StubCountry::TO 1.20170908113149
- Number::Phone::StubCountry::TR 1.20170908113149
- Number::Phone::StubCountry::TT 1.20170908113149
- Number::Phone::StubCountry::TV 1.20170908113149
- Number::Phone::StubCountry::TW 1.20170908113149
- Number::Phone::StubCountry::TZ 1.20170908113149
- Number::Phone::StubCountry::UA 1.20170908113149
- Number::Phone::StubCountry::UG 1.20170908113149
- Number::Phone::StubCountry::US 1.20170908113149
- Number::Phone::StubCountry::UY 1.20170908113149
- Number::Phone::StubCountry::UZ 1.20170908113149
- Number::Phone::StubCountry::VA 1.20170908113149
- Number::Phone::StubCountry::VC 1.20170908113149
- Number::Phone::StubCountry::VE 1.20170908113149
- Number::Phone::StubCountry::VG 1.20170908113149
- Number::Phone::StubCountry::VI 1.20170908113149
- Number::Phone::StubCountry::VN 1.20170908113149
- Number::Phone::StubCountry::VU 1.20170908113149
- Number::Phone::StubCountry::WF 1.20170908113149
- Number::Phone::StubCountry::WS 1.20170908113149
- Number::Phone::StubCountry::YE 1.20170908113149
- Number::Phone::StubCountry::YT 1.20170908113149
- Number::Phone::StubCountry::ZA 1.20170908113149
- Number::Phone::StubCountry::ZM 1.20170908113149
- Number::Phone::StubCountry::ZW 1.20170908113149
- Number::Phone::UK 1.67
+ Number::Phone::StubCountry 1.4000
+ Number::Phone::StubCountry::AC 1.20180203200231
+ Number::Phone::StubCountry::AD 1.20180203200231
+ Number::Phone::StubCountry::AE 1.20180203200232
+ Number::Phone::StubCountry::AF 1.20180203200232
+ Number::Phone::StubCountry::AG 1.20180203200232
+ Number::Phone::StubCountry::AI 1.20180203200232
+ Number::Phone::StubCountry::AL 1.20180203200232
+ Number::Phone::StubCountry::AM 1.20180203200232
+ Number::Phone::StubCountry::AO 1.20180203200232
+ Number::Phone::StubCountry::AR 1.20180203200232
+ Number::Phone::StubCountry::AS 1.20180203200232
+ Number::Phone::StubCountry::AT 1.20180203200232
+ Number::Phone::StubCountry::AU 1.20180203200232
+ Number::Phone::StubCountry::AW 1.20180203200232
+ Number::Phone::StubCountry::AX 1.20180203200232
+ Number::Phone::StubCountry::AZ 1.20180203200232
+ Number::Phone::StubCountry::BA 1.20180203200232
+ Number::Phone::StubCountry::BB 1.20180203200232
+ Number::Phone::StubCountry::BD 1.20180203200232
+ Number::Phone::StubCountry::BE 1.20180203200232
+ Number::Phone::StubCountry::BF 1.20180203200232
+ Number::Phone::StubCountry::BG 1.20180203200232
+ Number::Phone::StubCountry::BH 1.20180203200232
+ Number::Phone::StubCountry::BI 1.20180203200232
+ Number::Phone::StubCountry::BJ 1.20180203200232
+ Number::Phone::StubCountry::BL 1.20180203200232
+ Number::Phone::StubCountry::BM 1.20180203200232
+ Number::Phone::StubCountry::BN 1.20180203200232
+ Number::Phone::StubCountry::BO 1.20180203200232
+ Number::Phone::StubCountry::BQ 1.20180203200232
+ Number::Phone::StubCountry::BR 1.20180203200232
+ Number::Phone::StubCountry::BS 1.20180203200232
+ Number::Phone::StubCountry::BT 1.20180203200232
+ Number::Phone::StubCountry::BW 1.20180203200232
+ Number::Phone::StubCountry::BY 1.20180203200232
+ Number::Phone::StubCountry::BZ 1.20180203200232
+ Number::Phone::StubCountry::CA 1.20180203200232
+ Number::Phone::StubCountry::CC 1.20180203200232
+ Number::Phone::StubCountry::CD 1.20180203200233
+ Number::Phone::StubCountry::CF 1.20180203200233
+ Number::Phone::StubCountry::CG 1.20180203200233
+ Number::Phone::StubCountry::CH 1.20180203200233
+ Number::Phone::StubCountry::CI 1.20180203200233
+ Number::Phone::StubCountry::CK 1.20180203200233
+ Number::Phone::StubCountry::CL 1.20180203200233
+ Number::Phone::StubCountry::CM 1.20180203200233
+ Number::Phone::StubCountry::CN 1.20180203200233
+ Number::Phone::StubCountry::CO 1.20180203200234
+ Number::Phone::StubCountry::CR 1.20180203200234
+ Number::Phone::StubCountry::CU 1.20180203200234
+ Number::Phone::StubCountry::CV 1.20180203200234
+ Number::Phone::StubCountry::CW 1.20180203200234
+ Number::Phone::StubCountry::CX 1.20180203200234
+ Number::Phone::StubCountry::CY 1.20180203200234
+ Number::Phone::StubCountry::CZ 1.20180203200234
+ Number::Phone::StubCountry::DE 1.20180203200234
+ Number::Phone::StubCountry::DJ 1.20180203200234
+ Number::Phone::StubCountry::DK 1.20180203200234
+ Number::Phone::StubCountry::DM 1.20180203200234
+ Number::Phone::StubCountry::DO 1.20180203200234
+ Number::Phone::StubCountry::DZ 1.20180203200234
+ Number::Phone::StubCountry::EC 1.20180203200234
+ Number::Phone::StubCountry::EE 1.20180203200234
+ Number::Phone::StubCountry::EG 1.20180203200234
+ Number::Phone::StubCountry::EH 1.20180203200234
+ Number::Phone::StubCountry::ER 1.20180203200234
+ Number::Phone::StubCountry::ES 1.20180203200234
+ Number::Phone::StubCountry::ET 1.20180203200234
+ Number::Phone::StubCountry::FI 1.20180203200234
+ Number::Phone::StubCountry::FJ 1.20180203200234
+ Number::Phone::StubCountry::FK 1.20180203200234
+ Number::Phone::StubCountry::FM 1.20180203200234
+ Number::Phone::StubCountry::FO 1.20180203200234
+ Number::Phone::StubCountry::FR 1.20180203200234
+ Number::Phone::StubCountry::GA 1.20180203200234
+ Number::Phone::StubCountry::GB 1.20180203200234
+ Number::Phone::StubCountry::GD 1.20180203200234
+ Number::Phone::StubCountry::GE 1.20180203200234
+ Number::Phone::StubCountry::GF 1.20180203200234
+ Number::Phone::StubCountry::GG 1.20180203200234
+ Number::Phone::StubCountry::GH 1.20180203200234
+ Number::Phone::StubCountry::GI 1.20180203200234
+ Number::Phone::StubCountry::GL 1.20180203200234
+ Number::Phone::StubCountry::GM 1.20180203200234
+ Number::Phone::StubCountry::GN 1.20180203200234
+ Number::Phone::StubCountry::GP 1.20180203200234
+ Number::Phone::StubCountry::GQ 1.20180203200234
+ Number::Phone::StubCountry::GR 1.20180203200234
+ Number::Phone::StubCountry::GT 1.20180203200234
+ Number::Phone::StubCountry::GU 1.20180203200234
+ Number::Phone::StubCountry::GW 1.20180203200234
+ Number::Phone::StubCountry::GY 1.20180203200234
+ Number::Phone::StubCountry::HK 1.20180203200234
+ Number::Phone::StubCountry::HN 1.20180203200234
+ Number::Phone::StubCountry::HR 1.20180203200234
+ Number::Phone::StubCountry::HT 1.20180203200234
+ Number::Phone::StubCountry::HU 1.20180203200234
+ Number::Phone::StubCountry::ID 1.20180203200234
+ Number::Phone::StubCountry::IE 1.20180203200235
+ Number::Phone::StubCountry::IL 1.20180203200235
+ Number::Phone::StubCountry::IM 1.20180203200235
+ Number::Phone::StubCountry::IN 1.20180203200235
+ Number::Phone::StubCountry::IO 1.20180203200235
+ Number::Phone::StubCountry::IQ 1.20180203200235
+ Number::Phone::StubCountry::IR 1.20180203200235
+ Number::Phone::StubCountry::IS 1.20180203200235
+ Number::Phone::StubCountry::IT 1.20180203200235
+ Number::Phone::StubCountry::JE 1.20180203200235
+ Number::Phone::StubCountry::JM 1.20180203200235
+ Number::Phone::StubCountry::JO 1.20180203200235
+ Number::Phone::StubCountry::JP 1.20180203200235
+ Number::Phone::StubCountry::KE 1.20180203200235
+ Number::Phone::StubCountry::KG 1.20180203200235
+ Number::Phone::StubCountry::KH 1.20180203200235
+ Number::Phone::StubCountry::KI 1.20180203200235
+ Number::Phone::StubCountry::KM 1.20180203200235
+ Number::Phone::StubCountry::KN 1.20180203200235
+ Number::Phone::StubCountry::KP 1.20180203200235
+ Number::Phone::StubCountry::KR 1.20180203200235
+ Number::Phone::StubCountry::KW 1.20180203200235
+ Number::Phone::StubCountry::KY 1.20180203200235
+ Number::Phone::StubCountry::KZ 1.20180203200235
+ Number::Phone::StubCountry::LA 1.20180203200235
+ Number::Phone::StubCountry::LB 1.20180203200235
+ Number::Phone::StubCountry::LC 1.20180203200235
+ Number::Phone::StubCountry::LI 1.20180203200235
+ Number::Phone::StubCountry::LK 1.20180203200235
+ Number::Phone::StubCountry::LR 1.20180203200235
+ Number::Phone::StubCountry::LS 1.20180203200235
+ Number::Phone::StubCountry::LT 1.20180203200235
+ Number::Phone::StubCountry::LU 1.20180203200235
+ Number::Phone::StubCountry::LV 1.20180203200235
+ Number::Phone::StubCountry::LY 1.20180203200235
+ Number::Phone::StubCountry::MA 1.20180203200235
+ Number::Phone::StubCountry::MC 1.20180203200235
+ Number::Phone::StubCountry::MD 1.20180203200235
+ Number::Phone::StubCountry::ME 1.20180203200235
+ Number::Phone::StubCountry::MF 1.20180203200235
+ Number::Phone::StubCountry::MG 1.20180203200235
+ Number::Phone::StubCountry::MH 1.20180203200235
+ Number::Phone::StubCountry::MK 1.20180203200235
+ Number::Phone::StubCountry::ML 1.20180203200235
+ Number::Phone::StubCountry::MM 1.20180203200235
+ Number::Phone::StubCountry::MN 1.20180203200235
+ Number::Phone::StubCountry::MO 1.20180203200235
+ Number::Phone::StubCountry::MP 1.20180203200235
+ Number::Phone::StubCountry::MQ 1.20180203200235
+ Number::Phone::StubCountry::MR 1.20180203200235
+ Number::Phone::StubCountry::MS 1.20180203200235
+ Number::Phone::StubCountry::MT 1.20180203200235
+ Number::Phone::StubCountry::MU 1.20180203200235
+ Number::Phone::StubCountry::MV 1.20180203200235
+ Number::Phone::StubCountry::MW 1.20180203200235
+ Number::Phone::StubCountry::MX 1.20180203200235
+ Number::Phone::StubCountry::MY 1.20180203200235
+ Number::Phone::StubCountry::MZ 1.20180203200235
+ Number::Phone::StubCountry::NA 1.20180203200235
+ Number::Phone::StubCountry::NC 1.20180203200235
+ Number::Phone::StubCountry::NE 1.20180203200235
+ Number::Phone::StubCountry::NF 1.20180203200235
+ Number::Phone::StubCountry::NG 1.20180203200235
+ Number::Phone::StubCountry::NI 1.20180203200235
+ Number::Phone::StubCountry::NL 1.20180203200235
+ Number::Phone::StubCountry::NO 1.20180203200235
+ Number::Phone::StubCountry::NP 1.20180203200235
+ Number::Phone::StubCountry::NR 1.20180203200235
+ Number::Phone::StubCountry::NU 1.20180203200235
+ Number::Phone::StubCountry::NZ 1.20180203200235
+ Number::Phone::StubCountry::OM 1.20180203200235
+ Number::Phone::StubCountry::PA 1.20180203200235
+ Number::Phone::StubCountry::PE 1.20180203200235
+ Number::Phone::StubCountry::PF 1.20180203200235
+ Number::Phone::StubCountry::PG 1.20180203200235
+ Number::Phone::StubCountry::PH 1.20180203200235
+ Number::Phone::StubCountry::PK 1.20180203200235
+ Number::Phone::StubCountry::PL 1.20180203200235
+ Number::Phone::StubCountry::PM 1.20180203200235
+ Number::Phone::StubCountry::PR 1.20180203200235
+ Number::Phone::StubCountry::PS 1.20180203200235
+ Number::Phone::StubCountry::PT 1.20180203200235
+ Number::Phone::StubCountry::PW 1.20180203200235
+ Number::Phone::StubCountry::PY 1.20180203200235
+ Number::Phone::StubCountry::QA 1.20180203200235
+ Number::Phone::StubCountry::RE 1.20180203200235
+ Number::Phone::StubCountry::RO 1.20180203200235
+ Number::Phone::StubCountry::RS 1.20180203200235
+ Number::Phone::StubCountry::RU 1.20180203200235
+ Number::Phone::StubCountry::RW 1.20180203200235
+ Number::Phone::StubCountry::SA 1.20180203200236
+ Number::Phone::StubCountry::SB 1.20180203200236
+ Number::Phone::StubCountry::SC 1.20180203200236
+ Number::Phone::StubCountry::SD 1.20180203200236
+ Number::Phone::StubCountry::SE 1.20180203200236
+ Number::Phone::StubCountry::SG 1.20180203200236
+ Number::Phone::StubCountry::SH 1.20180203200236
+ Number::Phone::StubCountry::SI 1.20180203200236
+ Number::Phone::StubCountry::SJ 1.20180203200236
+ Number::Phone::StubCountry::SK 1.20180203200236
+ Number::Phone::StubCountry::SL 1.20180203200236
+ Number::Phone::StubCountry::SM 1.20180203200236
+ Number::Phone::StubCountry::SN 1.20180203200236
+ Number::Phone::StubCountry::SO 1.20180203200236
+ Number::Phone::StubCountry::SR 1.20180203200236
+ Number::Phone::StubCountry::SS 1.20180203200236
+ Number::Phone::StubCountry::ST 1.20180203200236
+ Number::Phone::StubCountry::SV 1.20180203200236
+ Number::Phone::StubCountry::SX 1.20180203200236
+ Number::Phone::StubCountry::SY 1.20180203200236
+ Number::Phone::StubCountry::SZ 1.20180203200236
+ Number::Phone::StubCountry::TA 1.20180203200236
+ Number::Phone::StubCountry::TC 1.20180203200236
+ Number::Phone::StubCountry::TD 1.20180203200236
+ Number::Phone::StubCountry::TG 1.20180203200236
+ Number::Phone::StubCountry::TH 1.20180203200236
+ Number::Phone::StubCountry::TJ 1.20180203200236
+ Number::Phone::StubCountry::TK 1.20180203200236
+ Number::Phone::StubCountry::TL 1.20180203200236
+ Number::Phone::StubCountry::TM 1.20180203200236
+ Number::Phone::StubCountry::TN 1.20180203200236
+ Number::Phone::StubCountry::TO 1.20180203200236
+ Number::Phone::StubCountry::TR 1.20180203200236
+ Number::Phone::StubCountry::TT 1.20180203200236
+ Number::Phone::StubCountry::TV 1.20180203200236
+ Number::Phone::StubCountry::TW 1.20180203200236
+ Number::Phone::StubCountry::TZ 1.20180203200236
+ Number::Phone::StubCountry::UA 1.20180203200236
+ Number::Phone::StubCountry::UG 1.20180203200236
+ Number::Phone::StubCountry::US 1.20180203200236
+ Number::Phone::StubCountry::UY 1.20180203200236
+ Number::Phone::StubCountry::UZ 1.20180203200236
+ Number::Phone::StubCountry::VA 1.20180203200236
+ Number::Phone::StubCountry::VC 1.20180203200236
+ Number::Phone::StubCountry::VE 1.20180203200236
+ Number::Phone::StubCountry::VG 1.20180203200236
+ Number::Phone::StubCountry::VI 1.20180203200236
+ Number::Phone::StubCountry::VN 1.20180203200236
+ Number::Phone::StubCountry::VU 1.20180203200236
+ Number::Phone::StubCountry::WF 1.20180203200236
+ Number::Phone::StubCountry::WS 1.20180203200236
+ Number::Phone::StubCountry::YE 1.20180203200236
+ Number::Phone::StubCountry::YT 1.20180203200236
+ Number::Phone::StubCountry::ZA 1.20180203200236
+ Number::Phone::StubCountry::ZM 1.20180203200236
+ Number::Phone::StubCountry::ZW 1.20180203200236
+ Number::Phone::UK 1.68
Number::Phone::UK::Data 2.0
Number::Phone::UK::Exchanges 1.20060823121334
Number::Phone::UK::GG 1
diff --git a/perllib/Catalyst/Plugin/FixMyStreet/Session/StoreSessions.pm b/perllib/Catalyst/Plugin/FixMyStreet/Session/StoreSessions.pm
new file mode 100644
index 000000000..5e7a3cede
--- /dev/null
+++ b/perllib/Catalyst/Plugin/FixMyStreet/Session/StoreSessions.pm
@@ -0,0 +1,23 @@
+package Catalyst::Plugin::FixMyStreet::Session::StoreSessions;
+use Moose::Role;
+use namespace::autoclean;
+
+after set_authenticated => sub {
+ my $c = shift;
+ my $sessions = $c->user->get_extra_metadata('sessions');
+ push @$sessions, $c->sessionid;
+ $c->user->set_extra_metadata('sessions', $sessions);
+ $c->user->update;
+};
+
+before logout => sub {
+ my $c = shift;
+ if (my $user = $c->user) {
+ my $sessions = $user->get_extra_metadata('sessions');
+ $sessions = [ grep { $_ ne $c->sessionid } @$sessions ];
+ @$sessions ? $user->set_extra_metadata('sessions', $sessions) : $user->unset_extra_metadata('sessions');
+ $user->update;
+ }
+};
+
+__PACKAGE__;
diff --git a/perllib/FixMyStreet/App.pm b/perllib/FixMyStreet/App.pm
index 3108c5c01..008aea595 100644
--- a/perllib/FixMyStreet/App.pm
+++ b/perllib/FixMyStreet/App.pm
@@ -18,13 +18,14 @@ use URI;
use URI::QueryParam;
use Catalyst (
- 'Static::Simple', #
+ 'Static::Simple',
'Unicode::Encoding',
'Session',
'Session::Store::DBIC',
'Session::State::Cookie', # FIXME - we're using our own override atm
'Authentication',
'SmartURI',
+ 'FixMyStreet::Session::StoreSessions',
);
extends 'Catalyst';
diff --git a/perllib/FixMyStreet/App/Controller/Admin.pm b/perllib/FixMyStreet/App/Controller/Admin.pm
index a1d301249..85b6204fc 100644
--- a/perllib/FixMyStreet/App/Controller/Admin.pm
+++ b/perllib/FixMyStreet/App/Controller/Admin.pm
@@ -1423,10 +1423,14 @@ sub user_edit : Path('user_edit') : Args(1) {
if ( $c->get_param('submit') and $c->get_param('unban') ) {
$c->forward('unban_user', [ $user ]);
+ } elsif ( $c->get_param('submit') and $c->get_param('logout_everywhere') ) {
+ $c->forward('user_logout_everywhere', [ $user ]);
} elsif ( $c->get_param('submit') and $c->get_param('anon_everywhere') ) {
$c->forward('user_anon_everywhere', [ $user ]);
} elsif ( $c->get_param('submit') and $c->get_param('hide_everywhere') ) {
$c->forward('user_hide_everywhere', [ $user ]);
+ } elsif ( $c->get_param('submit') and $c->get_param('remove_account') ) {
+ $c->forward('user_remove_account', [ $user ]);
} elsif ( $c->get_param('submit') ) {
my $edited = 0;
@@ -1756,6 +1760,15 @@ sub ban_user : Private {
return 1;
}
+sub user_logout_everywhere : Private {
+ my ( $self, $c, $user ) = @_;
+ my $sessions = $user->get_extra_metadata('sessions');
+ foreach (grep { $_ ne $c->sessionid } @$sessions) {
+ $c->delete_session_data("session:$_");
+ }
+ $c->stash->{status_message} = _('That user has been logged out.');
+}
+
sub user_anon_everywhere : Private {
my ( $self, $c, $user ) = @_;
$user->problems->update({anonymous => 1});
@@ -1777,6 +1790,28 @@ sub user_hide_everywhere : Private {
$c->stash->{status_message} = _('That user’s reports and updates have been hidden.');
}
+# Anonymize and remove name from all problems/updates, disable all alerts.
+# Remove their account's email address, phone number, password, etc.
+sub user_remove_account : Private {
+ my ( $self, $c, $user ) = @_;
+ $c->forward('user_logout_everywhere', [ $user ]);
+ $user->problems->update({ anonymous => 1, name => '', send_questionnaire => 0 });
+ $user->comments->update({ anonymous => 1, name => '' });
+ $user->alerts->update({ whendisabled => \'current_timestamp' });
+ $user->password('', 1);
+ $user->update({
+ email => 'removed-' . $user->id . '@' . FixMyStreet->config('EMAIL_DOMAIN'),
+ email_verified => 0,
+ name => '',
+ phone => '',
+ phone_verified => 0,
+ title => undef,
+ twitter_id => undef,
+ facebook_id => undef,
+ });
+ $c->stash->{status_message} = _('That user’s personal details have been removed.');
+}
+
sub unban_user : Private {
my ( $self, $c, $user ) = @_;
diff --git a/perllib/FixMyStreet/App/Controller/Auth.pm b/perllib/FixMyStreet/App/Controller/Auth.pm
index b2d909f9c..533e6a9be 100644
--- a/perllib/FixMyStreet/App/Controller/Auth.pm
+++ b/perllib/FixMyStreet/App/Controller/Auth.pm
@@ -5,6 +5,7 @@ use namespace::autoclean;
BEGIN { extends 'Catalyst::Controller'; }
use Email::Valid;
+use Data::Password::Common 'found';
use Digest::HMAC_SHA1 qw(hmac_sha1);
use JSON::MaybeXS;
use MIME::Base64;
@@ -84,6 +85,12 @@ sub sign_in : Private {
my $parsed = FixMyStreet::SMS->parse_username($username);
if ($parsed->{username} && $password && $c->forward('authenticate', [ $parsed->{type}, $parsed->{username}, $password ])) {
+ # Upgrade hash count if necessary
+ my $cost = sprintf("%02d", FixMyStreet::DB::Result::User->cost);
+ if ($c->user->password !~ /^\$2a\$$cost\$/) {
+ $c->user->update({ password => $password });
+ }
+
# unless user asked to be remembered limit the session to browser
$c->set_session_cookie_expire(0)
unless $remember_me;
@@ -143,6 +150,11 @@ sub email_sign_in : Private {
return;
}
+ my $password = $c->get_param('password_register');
+ if ($password) {
+ return unless $c->forward('/auth/test_password', [ $password ]);
+ }
+
# If user registration is disabled then bail out at this point
# if there's not already a user with this email address.
# NB this uses the same template as a successful sign in to stop
@@ -156,8 +168,7 @@ sub email_sign_in : Private {
}
my $user_params = {};
- $user_params->{password} = $c->get_param('password_register')
- if $c->get_param('password_register');
+ $user_params->{password} = $password if $password;
my $user = $c->model('DB::User')->new( $user_params );
my $token_data = {
@@ -356,6 +367,55 @@ sub no_csrf_token : Private {
$c->detach('/page_error_400_bad_request', []);
}
+=item common_password
+
+Returns 1/0 depending on if password is common or not.
+
+=cut
+
+sub common_password : Local : Args(0) {
+ my ($self, $c) = @_;
+
+ my $password = $c->get_param('password_register');
+
+ my $return = JSON->true;
+ if (!$c->cobrand->call_hook('bypass_password_checks') && found($password)) {
+ $return = _('Please choose a less commonly-used password');
+ }
+
+ my $body = JSON->new->utf8->allow_nonref->encode($return);
+ $c->res->content_type('application/json; charset=utf-8');
+ $c->res->body($body);
+}
+
+=item test_password
+
+Checks a password is not too weak; returns true if okay,
+false if weak (and sets stash error).
+
+=cut
+
+sub test_password : Private {
+ my ($self, $c, $password) = @_;
+
+ return 1 if $c->cobrand->call_hook('bypass_password_checks');
+
+ my @errors;
+
+ my $min_length = $c->cobrand->password_minimum_length;
+ push @errors, sprintf(_('Please make sure your password is at least %d characters long'), $min_length)
+ if length($password) < $min_length;
+
+ push @errors, _('Please choose a less commonly-used password')
+ if found($password);
+
+ if (@errors) {
+ $c->stash->{field_errors}->{password_register} = join('<br>', @errors);
+ return 0;
+ }
+ return 1;
+}
+
=head2 sign_out
Log the user out. Tell them we've done so.
diff --git a/perllib/FixMyStreet/App/Controller/Auth/Phone.pm b/perllib/FixMyStreet/App/Controller/Auth/Phone.pm
index 8387b9d64..8e3150df9 100644
--- a/perllib/FixMyStreet/App/Controller/Auth/Phone.pm
+++ b/perllib/FixMyStreet/App/Controller/Auth/Phone.pm
@@ -59,6 +59,11 @@ sub sign_in : Private {
return;
}
+ my $password = $c->get_param('password_register');
+ if ($password) {
+ return unless $c->forward('/auth/test_password', [ $password ]);
+ }
+
(my $number = $parsed->{phone}->format) =~ s/\s+//g;
if ( FixMyStreet->config('SIGNUPS_DISABLED')
@@ -70,8 +75,7 @@ sub sign_in : Private {
}
my $user_params = {};
- $user_params->{password} = $c->get_param('password_register')
- if $c->get_param('password_register');
+ $user_params->{password} = $password if $password;
my $user = $c->model('DB::User')->new( $user_params );
my $token_data = {
diff --git a/perllib/FixMyStreet/App/Controller/Auth/Profile.pm b/perllib/FixMyStreet/App/Controller/Auth/Profile.pm
index a58d2ddf6..87aff2261 100644
--- a/perllib/FixMyStreet/App/Controller/Auth/Profile.pm
+++ b/perllib/FixMyStreet/App/Controller/Auth/Profile.pm
@@ -19,7 +19,7 @@ verifying email, phone, password.
=cut
-sub auto {
+sub auto : Private {
my ( $self, $c ) = @_;
$c->detach( '/auth/redirect' ) unless $c->user;
@@ -49,10 +49,20 @@ sub change_password : Path('/auth/change_password') {
my $new = $c->get_param('new_password') // '';
my $confirm = $c->get_param('confirm') // '';
+ my $password_error;
+
+ # Check existing password, if available
+ if ($c->user->password) {
+ my $current = $c->get_param('current_password') // '';
+ $c->stash->{current_password} = $current;
+ $password_error = 'incorrect' unless $c->user->check_password($current);
+ }
+
# check for errors
- my $password_error =
+ $password_error ||=
!$new && !$confirm ? 'missing'
: $new ne $confirm ? 'mismatch'
+ : !$c->forward('/auth/test_password', [ $new ]) ? 'failed'
: '';
if ($password_error) {
@@ -62,10 +72,17 @@ sub change_password : Path('/auth/change_password') {
return;
}
- # we should have a usable password - save it to the user
- $c->user->obj->update( { password => $new } );
- $c->stash->{password_changed} = 1;
-
+ if ($c->user->password) {
+ # we should have a usable password - save it to the user
+ $c->user->obj->update( { password => $new } );
+ $c->stash->{password_changed} = 1;
+ } else {
+ # Set up arguments for code sign in
+ $c->set_param('username', $c->user->username);
+ $c->set_param('password_register', $new);
+ $c->set_param('r', 'auth/change_password/success');
+ $c->detach('/auth/code_sign_in');
+ }
}
=head2 change_email
@@ -148,6 +165,12 @@ sub change_phone_success : Path('/auth/change_phone/success') {
$c->res->redirect('/my');
}
+sub change_password_success : Path('/auth/change_password/success') {
+ my ( $self, $c ) = @_;
+ $c->flash->{flash_message} = _('Your password has been changed');
+ $c->res->redirect('/my');
+}
+
sub generate_token : Path('/auth/generate_token') {
my ($self, $c) = @_;
diff --git a/perllib/FixMyStreet/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm
index 1083f843b..85652450e 100644
--- a/perllib/FixMyStreet/App/Controller/Report/New.pm
+++ b/perllib/FixMyStreet/App/Controller/Report/New.pm
@@ -817,8 +817,10 @@ sub process_user : Private {
}
$c->forward('update_user', [ \%params ]);
- $report->user->password( Utils::trim_text( $params{password_register} ) )
- if $params{password_register};
+ if ($params{password_register}) {
+ $c->forward('/auth/test_password', [ $params{password_register} ]);
+ $report->user->password(Utils::trim_text($params{password_register}));
+ }
return 1;
}
diff --git a/perllib/FixMyStreet/App/Controller/Report/Update.pm b/perllib/FixMyStreet/App/Controller/Report/Update.pm
index 8b96b02d1..24b54d7b5 100644
--- a/perllib/FixMyStreet/App/Controller/Report/Update.pm
+++ b/perllib/FixMyStreet/App/Controller/Report/Update.pm
@@ -149,11 +149,14 @@ sub process_user : Private {
$update->user->name( Utils::trim_text( $params{name} ) )
if $params{name};
- $update->user->password( Utils::trim_text( $params{password_register} ) )
- if $params{password_register};
$update->user->title( Utils::trim_text( $params{fms_extra_title} ) )
if $params{fms_extra_title};
+ if ($params{password_register}) {
+ $c->forward('/auth/test_password', [ $params{password_register} ]);
+ $update->user->password(Utils::trim_text($params{password_register}));
+ }
+
return 1;
}
diff --git a/perllib/FixMyStreet/Cobrand/Borsetshire.pm b/perllib/FixMyStreet/Cobrand/Borsetshire.pm
index 7ddcff469..d9b018d69 100644
--- a/perllib/FixMyStreet/Cobrand/Borsetshire.pm
+++ b/perllib/FixMyStreet/Cobrand/Borsetshire.pm
@@ -29,4 +29,6 @@ sub send_questionnaires {
return 0;
}
+sub bypass_password_checks { 1 }
+
1;
diff --git a/perllib/FixMyStreet/Cobrand/Default.pm b/perllib/FixMyStreet/Cobrand/Default.pm
index 7888f8ccf..c6ca5c56b 100644
--- a/perllib/FixMyStreet/Cobrand/Default.pm
+++ b/perllib/FixMyStreet/Cobrand/Default.pm
@@ -59,6 +59,14 @@ sub path_to_email_templates {
return $paths;
}
+=item password_minimum_length
+
+Returns the minimum length a password can be set to.
+
+=cut
+
+sub password_minimum_length { 6 }
+
=item country
Returns the country that this cobrand operates in, as an ISO3166-alpha2 code.
diff --git a/perllib/FixMyStreet/Cobrand/Oxfordshire.pm b/perllib/FixMyStreet/Cobrand/Oxfordshire.pm
index 23324e763..00f099278 100644
--- a/perllib/FixMyStreet/Cobrand/Oxfordshire.pm
+++ b/perllib/FixMyStreet/Cobrand/Oxfordshire.pm
@@ -15,8 +15,6 @@ sub is_council_with_case_management {
return FixMyStreet->config('STAGING_SITE');
}
-sub map_type { 'OSM' }
-
sub base_url {
my $self = shift;
return $self->next::method() if FixMyStreet->config('STAGING_SITE');
diff --git a/perllib/FixMyStreet/DB/Result/User.pm b/perllib/FixMyStreet/DB/Result/User.pm
index 7e16308b9..a6b927ad1 100644
--- a/perllib/FixMyStreet/DB/Result/User.pm
+++ b/perllib/FixMyStreet/DB/Result/User.pm
@@ -125,11 +125,15 @@ with 'FixMyStreet::Roles::Extra';
__PACKAGE__->many_to_many( planned_reports => 'user_planned_reports', 'report' );
+sub cost {
+ FixMyStreet->test_mode ? 1 : 12;
+}
+
__PACKAGE__->add_columns(
"password" => {
encode_column => 1,
encode_class => 'Crypt::Eksblowfish::Bcrypt',
- encode_args => { cost => 8 },
+ encode_args => { cost => cost() },
encode_check_method => 'check_password',
},
);
@@ -150,8 +154,9 @@ sub username {
sub phone_display {
my $self = shift;
return $self->phone unless $self->phone;
+ my $country = FixMyStreet->config('PHONE_COUNTRY');
my $parsed = FixMyStreet::SMS->parse_username($self->phone);
- return $parsed->{phone} ? $parsed->{phone}->format : $self->phone;
+ return $parsed->{phone} ? $parsed->{phone}->format_for_country($country) : $self->phone;
}
sub latest_anonymity {
diff --git a/perllib/FixMyStreet/Test.pm b/perllib/FixMyStreet/Test.pm
index 572ae0a44..aa1a63c21 100644
--- a/perllib/FixMyStreet/Test.pm
+++ b/perllib/FixMyStreet/Test.pm
@@ -7,6 +7,13 @@ use warnings FATAL => 'all';
use utf8;
use Test::More;
use mySociety::Locale;
+
+BEGIN {
+ use FixMyStreet;
+ FixMyStreet->test_mode(1);
+ mySociety::Locale::gettext_domain('FixMyStreet', 1);
+}
+
use FixMyStreet::DB;
my $db = FixMyStreet::DB->schema->storage;
@@ -19,12 +26,6 @@ sub import {
$db->txn_begin;
}
-BEGIN {
- use FixMyStreet;
- FixMyStreet->test_mode(1);
- mySociety::Locale::gettext_domain('FixMyStreet', 1);
-}
-
END {
$db->txn_rollback if $db;
}
diff --git a/t/app/controller/admin/users.t b/t/app/controller/admin/users.t
index e6cf51449..63295e26d 100644
--- a/t/app/controller/admin/users.t
+++ b/t/app/controller/admin/users.t
@@ -410,4 +410,30 @@ subtest "Hiding user's reports from admin" => sub {
is $c, $count_u;
};
+subtest "Logging user out" => sub {
+ my $mech2 = FixMyStreet::TestMech->new;
+ $mech2->log_in_ok($user->email);
+ $mech2->logged_in_ok;
+
+ $mech->get_ok( '/admin/user_edit/' . $user->id );
+ $mech->submit_form_ok({ button => 'logout_everywhere' }, 'Logging user out');
+ $mech2->not_logged_in_ok;
+};
+
+subtest "Removing account from admin" => sub {
+ $mech->create_problems_for_body(4, 2237, 'Title');
+ my $count_p = FixMyStreet::DB->resultset('Problem')->search({ user_id => $user->id })->count;
+ my $count_u = FixMyStreet::DB->resultset('Comment')->search({ user_id => $user->id })->count;
+ $mech->get_ok( '/admin/user_edit/' . $user->id );
+ $mech->submit_form_ok({ button => 'remove_account' }, 'Removing account');
+ my $c = FixMyStreet::DB->resultset('Problem')->search({ user_id => $user->id, anonymous => 1, name => '' })->count;
+ is $c, $count_p, 'All reports anon/nameless';
+ $c = FixMyStreet::DB->resultset('Comment')->search({ user_id => $user->id, anonymous => 1, name => '' })->count;
+ is $c, $count_u, 'All updates anon/nameless';
+ $user->discard_changes;
+ is $user->name, '', 'Name gone';
+ is $user->password, '', 'Password gone';
+ is $user->email, 'removed-' . $user->id . '@example.org', 'Email gone'
+};
+
done_testing();
diff --git a/t/app/controller/auth.t b/t/app/controller/auth.t
index 70b970e2b..8cc7e4154 100644
--- a/t/app/controller/auth.t
+++ b/t/app/controller/auth.t
@@ -5,7 +5,7 @@ my $mech = FixMyStreet::TestMech->new;
my $test_email = 'test@example.com';
my $test_email3 = 'newuser@example.org';
-my $test_password = 'foobar';
+my $test_password = 'foobar123';
END {
done_testing();
@@ -277,6 +277,29 @@ subtest "check logging in with token" => sub {
$mech->delete_header('Authorization');
};
+subtest 'check password length/common' => sub {
+ $mech->get_ok('/auth');
+ $mech->submit_form_ok({
+ form_name => 'general_auth',
+ fields => { username => $test_email, password_register => 'short' },
+ button => 'sign_in_by_code',
+ });
+ $mech->content_contains("Please make sure your password is at least");
+ $mech->submit_form_ok({
+ form_name => 'general_auth',
+ fields => { username => $test_email, password_register => 'common' },
+ button => 'sign_in_by_code',
+ });
+ $mech->content_contains("Please choose a less commonly-used password");
+};
+
+subtest 'check common password AJAX call' => sub {
+ $mech->post_ok('/auth/common_password', { password_register => 'password' });
+ $mech->content_contains("Please choose a less commonly-used password");
+ $mech->post_ok('/auth/common_password', { password_register => 'squirblewirble' });
+ $mech->content_contains("true");
+};
+
subtest "Test two-factor authentication login" => sub {
use Auth::GoogleAuth;
my $auth = Auth::GoogleAuth->new;
diff --git a/t/app/controller/auth_profile.t b/t/app/controller/auth_profile.t
index c9daff7ae..4be1be12c 100644
--- a/t/app/controller/auth_profile.t
+++ b/t/app/controller/auth_profile.t
@@ -8,7 +8,7 @@ LWP::Protocol::PSGI->register($twilio->to_psgi_app, host => 'api.twilio.com');
my $test_email = 'test@example.com';
my $test_email2 = 'test@example.net';
-my $test_password = 'foobar';
+my $test_password = 'foobar123';
END {
done_testing();
@@ -75,9 +75,68 @@ subtest "Test change password page" => sub {
{
form_name => 'change_password',
fields =>
- { new_password => $test_password, confirm => $test_password, },
+ { new_password => 'new_password', confirm => 'new_password', },
},
- "change_password with '$test_password' and '$test_password'"
+ "change_password with 'new_password' and 'new_password'"
+ );
+ is $mech->uri->path, '/auth/change_password',
+ "still on change password page";
+ $mech->content_contains('check your email');
+
+ $link = $mech->get_link_from_email;
+ $mech->get_ok($link);
+ is $mech->uri->path, '/my', "redirected to /my";
+
+ $mech->content_contains( 'password has been changed',
+ "found password changed" );
+
+ $user->discard_changes();
+ ok $user->password, "user now has a password";
+};
+
+# Change password, when already got one
+subtest "Test change password page with current password" => sub {
+ $mech->get_ok('/auth/change_password');
+
+ ok my $form = $mech->form_name('change_password'),
+ "found change password form";
+ is_deeply [ sort grep { $_ } map { $_->name } $form->inputs ], #
+ [ 'confirm', 'current_password', 'new_password', 'token' ],
+ "check we got expected fields (ie not old_password)";
+
+ # check the various ways the form can be wrong
+ for my $test (
+ { current => '', new => '', conf => '', err => 'check the passwords', },
+ { current => 'new_password', new => '', conf => '', err => 'enter a password', },
+ { current => 'new_password', new => 'secret', conf => '', err => 'do not match', },
+ { current => 'new_password', new => '', conf => 'secret', err => 'do not match', },
+ { current => 'new_password', new => 'secret', conf => 'not_secret', err => 'do not match', },
+ )
+ {
+ $mech->get_ok('/auth/change_password');
+ $mech->content_lacks( $test->{err}, "did not find expected error" );
+ $mech->submit_form_ok(
+ {
+ form_name => 'change_password',
+ fields =>
+ { current_password => $test->{current}, new_password => $test->{new}, confirm => $test->{conf}, },
+ },
+ "change_password with '$test->{current}', '$test->{new}' and '$test->{conf}'"
+ );
+ $mech->content_contains( $test->{err}, "found expected error" );
+ }
+
+ my $user = FixMyStreet::App->model('DB::User')->find( { email => $test_email } );
+ ok $user, "got a user";
+
+ $mech->get_ok('/auth/change_password');
+ $mech->submit_form_ok(
+ {
+ form_name => 'change_password',
+ fields =>
+ { current_password => 'new_password', new_password => $test_password, confirm => $test_password },
+ },
+ "change_password with 'new_password' and '$test_password'"
);
is $mech->uri->path, '/auth/change_password',
"still on change password page";
@@ -88,6 +147,20 @@ subtest "Test change password page" => sub {
ok $user->password, "user now has a password";
};
+subtest 'check password length/common' => sub {
+ $mech->get_ok('/auth/change_password');
+ $mech->submit_form_ok({
+ form_name => 'change_password',
+ fields => { current_password => $test_password, new_password => 'short', confirm => 'short' },
+ });
+ $mech->content_contains("Please make sure your password is at least");
+ $mech->submit_form_ok({
+ form_name => 'change_password',
+ fields => { current_password => $test_password, new_password => 'common', confirm => 'common' },
+ });
+ $mech->content_contains("Please choose a less commonly-used password");
+};
+
subtest "Test change email page" => sub {
$mech->create_problems_for_body(1, 2514, 'Title1', { user => FixMyStreet::DB->resultset('User')->find( { email => $test_email } ) } );
diff --git a/t/app/controller/report_new.t b/t/app/controller/report_new.t
index f5af6f082..3c120b0b0 100644
--- a/t/app/controller/report_new.t
+++ b/t/app/controller/report_new.t
@@ -1262,9 +1262,7 @@ for my $test (
is $user->title, $test->{'user_title'}, 'user title correct';
is_deeply $extras, $test->{extra}, 'extra contains correct values';
- $user->problems->delete;
- $user->alerts->delete;
- $user->delete;
+ $mech->delete_user($user);
};
}
@@ -1731,9 +1729,7 @@ subtest "extra google analytics code displayed on email confirmation problem cre
$mech->content_contains( "'id': 'report/" . $report->id . "'", 'extra google code present' );
- $user->problems->delete;
- $user->alerts->delete;
- $user->delete;
+ $mech->delete_user($user);
};
};
diff --git a/t/app/controller/report_new_text.t b/t/app/controller/report_new_text.t
index 734b9dbb4..cb07e57ee 100644
--- a/t/app/controller/report_new_text.t
+++ b/t/app/controller/report_new_text.t
@@ -45,8 +45,8 @@ foreach my $test (
password_register => '', password_sign_in => '',
},
changes => {
- username => '+44 121 496 0000',
- phone => '+44 121 496 0000',
+ username => '0121 496 0000',
+ phone => '0121 496 0000',
},
errors => [ 'Please enter a mobile number', ],
},
diff --git a/t/app/controller/report_update_text.t b/t/app/controller/report_update_text.t
index 45b4e78c2..a3b767221 100644
--- a/t/app/controller/report_update_text.t
+++ b/t/app/controller/report_update_text.t
@@ -95,7 +95,7 @@ for my $test (
password_sign_in => '',
},
changes => {
- username => '+44 121 496 0000',
+ username => '0121 496 0000',
},
field_errors => [ 'Please enter a mobile number' ]
},
diff --git a/templates/web/base/admin/user-form.html b/templates/web/base/admin/user-form.html
index 63d4858ab..9dc14c98d 100644
--- a/templates/web/base/admin/user-form.html
+++ b/templates/web/base/admin/user-form.html
@@ -200,8 +200,10 @@
</p>
[% IF user AND NOT user.from_body %]
<ul class="no-bullets danger-zone">
+ <li><input class="btn-danger" type="submit" name="logout_everywhere" value="[% loc('Log out of all sessions') %]">
<li><input class="btn-danger" type="submit" name="anon_everywhere" value="[% loc('Make anonymous on all reports and updates') %]">
<li><input class="btn-danger" type="submit" name="hide_everywhere" value="[% loc('Hide all reports and updates') %]">
+ <li><input class="btn-danger" type="submit" name="remove_account" value="[% loc('Remove account details') %]">
</ul>
[% END %]
diff --git a/templates/web/base/auth/change_password.html b/templates/web/base/auth/change_password.html
index a32dbaf9c..7a38e0134 100644
--- a/templates/web/base/auth/change_password.html
+++ b/templates/web/base/auth/change_password.html
@@ -1,7 +1,9 @@
[%
SET bclass = 'authpage';
SET bclass = 'fullwidthpage' IF password_changed;
-INCLUDE 'header.html', title = loc('Change password'), bodyclass = bclass
+SET title = loc('Set password');
+SET title = loc('Change password') IF c.user.password;
+INCLUDE 'header.html', title = title bodyclass = bclass
%]
[% IF password_changed %]
@@ -13,13 +15,15 @@ INCLUDE 'header.html', title = loc('Change password'), bodyclass = bclass
[% ELSE %]
-<h1>[% loc('Change password') %]</h1>
+<h1>[% title %]</h1>
<form action="[% c.uri_for_action('/auth/profile/change_password') %]" method="post" name="change_password" class="fieldset">
<input type="hidden" name="token" value="[% csrf_token %]">
<fieldset>
- [% IF password_error;
+ [% IF password_error == 'failed' %]
+ <div class="form-error">[% field_errors.password_register %]</div>
+ [% ELSIF password_error;
errors = {
missing => loc('Please enter a password'),
@@ -31,6 +35,14 @@ INCLUDE 'header.html', title = loc('Change password'), bodyclass = bclass
<div class="form-error">[% loc_password_error %]</div>
[% END %]
+[% IF c.user.password %]
+ <div class="form-field">
+ <label for="current_password">[% loc('Current password:') %]</label>
+ <input class="form-control" type="password" name="current_password" value="[% current_password | html %]">
+ </div>
+ <hr>
+[% END %]
+
<div class="form-field">
<label for="new_password">[% loc('New password:') %]</label>
<input class="form-control" type="password" name="new_password" value="[% new_password | html %]">
@@ -40,7 +52,7 @@ INCLUDE 'header.html', title = loc('Change password'), bodyclass = bclass
<input class="form-control" type="password" name="confirm" value="[% confirm | html %]">
</div>
<div class="final-submit">
- <input type="submit" class="btn" value="[% loc('Change password') %]">
+ <input type="submit" class="btn" value="[% title %]">
</div>
</fieldset>
diff --git a/templates/web/base/auth/change_phone.html b/templates/web/base/auth/change_phone.html
index 27a2f63dd..62e9fa6d6 100644
--- a/templates/web/base/auth/change_phone.html
+++ b/templates/web/base/auth/change_phone.html
@@ -18,7 +18,7 @@ END
[% IF c.user.phone_verified OR (c.user.phone AND NOT verifying) %]
[% loc('Your phone number') %]: [% c.user.phone_display %]
[% ELSIF c.user.phone %]
-[% DEFAULT username = c.user.phone %]
+[% DEFAULT username = c.user.phone_display %]
[% END %]
<form method="post" name="change_phone">
diff --git a/templates/web/base/auth/general.html b/templates/web/base/auth/general.html
index 8fc5578c1..1e44bb68e 100644
--- a/templates/web/base/auth/general.html
+++ b/templates/web/base/auth/general.html
@@ -60,7 +60,9 @@
[% INCLUDE form_sign_in_yes %]
<input type="hidden" name="oauth_need_email" value="1">
[% ELSE %]
+ [% IF NOT field_errors.password_register %]
[% INCLUDE form_sign_in_yes %]
+ [% END %]
[% INCLUDE form_sign_in_no %]
[% END %]
</div>
@@ -114,14 +116,22 @@
<input class="form-control" type="text" name="name" value="" placeholder="[% loc('Your name') %]">
<label for="password_register">[% loc('Password (optional)') %]</label>
+ [% IF field_errors.password_register %]
+ <p class='form-error'>[% field_errors.password_register %]</p>
+ [% END %]
<div class="general-notes">
<p>[% loc('Providing a name and password is optional, but doing so will allow you to more easily report problems, leave updates and manage your reports.') %]</p>
</div>
<div class="form-txt-submit-box">
- <input class="form-control" type="password" name="password_register" id="password_register" value="" placeholder="[% loc('Enter a password') %]">
+ <input class="form-control js-password-validate" type="password" name="password_register" id="password_register" value="" placeholder="[% loc('Enter a password') %]">
<input class="green-btn" type="submit" name="sign_in_by_code" value="[% loc('Sign in') %]">
</div>
+
+ <div class="general-notes">
+ <p>[% tprintf(loc('Your password should include %d or more characters.'), c.cobrand.password_minimum_length) %]</p>
+ </div>
+
</div>
[% END %]
diff --git a/templates/web/base/js/translation_strings.html b/templates/web/base/js/translation_strings.html
index ed95335a6..a2e3c16c5 100644
--- a/templates/web/base/js/translation_strings.html
+++ b/templates/web/base/js/translation_strings.html
@@ -1,4 +1,7 @@
[% FILTER collapse %]
+var fixmystreet = fixmystreet || {};
+fixmystreet.password_minimum_length = [% c.cobrand.password_minimum_length %];
+
translation_strings = {
update: '[% loc('Please enter a message') | replace("'", "\\'") %]',
title: '[% loc('Please enter a subject') | replace("'", "\\'") %]',
@@ -19,6 +22,9 @@
password_sign_in: {
required: '[% loc('Please enter a password') | replace("'", "\\'") %]'
},
+ password_register: {
+ short: '[% tprintf(loc('Please make sure your password is at least %d characters long'), c.cobrand.password_minimum_length) | replace("'", "\\'") %]',
+ },
phone: {
required: '[% loc('Please enter your phone number') | replace("'", "\\'") %]'
},
diff --git a/templates/web/base/report/_inspect.html b/templates/web/base/report/_inspect.html
index 5088332ce..eb2564157 100644
--- a/templates/web/base/report/_inspect.html
+++ b/templates/web/base/report/_inspect.html
@@ -17,7 +17,7 @@
[% IF permissions.report_inspect AND problem.user.phone %]
<p>
<strong>[% loc('Phone Reporter:') %]</strong>
- <a href="tel:[% problem.user.phone | html %]">[% problem.user.phone | html %]</a>
+ <a href="tel:[% problem.user.phone | html %]">[% problem.user.phone_display | html %]</a>
</p>
[% END %]
<p>
diff --git a/templates/web/base/report/new/form_user_loggedin.html b/templates/web/base/report/new/form_user_loggedin.html
index bd4ce1cf7..04a93ef74 100644
--- a/templates/web/base/report/new/form_user_loggedin.html
+++ b/templates/web/base/report/new/form_user_loggedin.html
@@ -31,7 +31,7 @@
[% IF c.user.phone_verified %]
<label for="form_phone">[% loc('Phone number') %]</label>
- <input class="form-control" id="form_phone" name="phone" disabled type="text" value="[% c.user.phone | html %]">
+ <input class="form-control" id="form_phone" name="phone" disabled type="text" value="[% c.user.phone_display | html %]">
[% END %]
[% IF c.user.email_verified %]
@@ -65,7 +65,7 @@
[% IF NOT c.user.phone_verified %]
<label for="form_phone">[% loc('Phone number (optional)') %]</label>
- <input class="form-control" type="text" value="[% report.user.phone | html %]" name="phone" id="form_phone">
+ <input class="form-control" type="text" value="[% report.user.phone_display | html %]" name="phone" id="form_phone">
[% END %]
[% IF NOT c.user.email_verified %]
<label for="form_username">[% loc('Email address (optional)') %]</label>
diff --git a/templates/web/base/report/new/form_user_loggedout_by_email.html b/templates/web/base/report/new/form_user_loggedout_by_email.html
index e9519f573..975dbe704 100644
--- a/templates/web/base/report/new/form_user_loggedout_by_email.html
+++ b/templates/web/base/report/new/form_user_loggedout_by_email.html
@@ -36,13 +36,20 @@
</div>
<label class="form-focus-hidden" for="password_register">[% loc('Password (optional)') %]</label>
-
+ [% IF field_errors.password_register %]
+ <p class='form-error'>[% field_errors.password_register %]</p>
+ [% END %]
<div class="general-notes form-focus-hidden">
<p>[% loc('Providing a password is optional, but doing so will allow you to more easily report problems, leave updates and manage your reports.') %]</p>
</div>
<div class="form-txt-submit-box form-focus-hidden">
- <input class="form-control" type="password" name="password_register" id="password_register" value="" placeholder="[% loc('Enter a password') %]">
+ <input class="form-control js-password-validate" type="password" name="password_register" id="password_register" value="" placeholder="[% loc('Enter a password') %]">
<input class="green-btn js-submit_register" type="submit" name="submit_register" value="[% loc('Submit') %]">
</div>
+
+ <div class="general-notes">
+ <p>[% tprintf(loc('Your password should include %d or more characters.'), c.cobrand.password_minimum_length) %]</p>
+ </div>
+
</div>
diff --git a/templates/web/base/report/update/form_user_loggedout_by_email.html b/templates/web/base/report/update/form_user_loggedout_by_email.html
index 7d10fe391..d038cdb23 100644
--- a/templates/web/base/report/update/form_user_loggedout_by_email.html
+++ b/templates/web/base/report/update/form_user_loggedout_by_email.html
@@ -8,14 +8,21 @@
[% INCLUDE 'report/update/form_name.html' %]
<label for="password_register">[% loc('Password (optional)') %]</label>
+ [% IF field_errors.password_register %]
+ <p class='form-error'>[% field_errors.password_register %]</p>
+ [% END %]
<div class="general-notes">
<p>[% loc('Providing a password is optional, but doing so will allow you to more easily report problems, leave updates and manage your reports.') %]</p>
</div>
<div class="form-txt-submit-box">
- <input type="password" class="form-control" name="password_register" id="password_register" value="" placeholder="[% loc('Enter a password') %]">
+ <input type="password" class="form-control js-password-validate" name="password_register" id="password_register" value="" placeholder="[% loc('Enter a password') %]">
<input class="green-btn js-submit_register" type="submit" name="submit_register" value="[% loc('Post') %]">
</div>
+ <div class="general-notes">
+ <p>[% tprintf(loc('Your password should include %d or more characters.'), c.cobrand.password_minimum_length) %]</p>
+ </div>
+
</div>
diff --git a/templates/web/bromley/report/new/form_user.html b/templates/web/bromley/report/new/form_user.html
index e6749f5ab..cce985c95 100644
--- a/templates/web/bromley/report/new/form_user.html
+++ b/templates/web/bromley/report/new/form_user.html
@@ -91,15 +91,23 @@
</div>
<label class="form-focus-hidden" for="password_register">[% loc('Password (optional)') %]</label>
+ [% IF field_errors.password_register %]
+ <p class='form-error'>[% field_errors.password_register %]</p>
+ [% END %]
<div class="general-notes form-focus-hidden">
<p>[% loc('Providing a password is optional, but doing so will allow you to more easily report future problems, leave updates and manage your reports.') %]</p>
</div>
<div class="form-txt-submit-box form-focus-hidden">
- <input class="form-control" type="password" name="password_register" id="password_register" value="" placeholder="[% loc('Enter a password') %]">
+ <input class="form-control js-password-validate" type="password" name="password_register" id="password_register" value="" placeholder="[% loc('Enter a password') %]">
<input class="green-btn js-submit_register" type="submit" name="submit_register" value="[% loc('Submit') %]">
</div>
+
+ <div class="general-notes">
+ <p>[% tprintf(loc('Your password should include %d or more characters.'), c.cobrand.password_minimum_length) %]</p>
+ </div>
+
</div>
<div id="form_sign_in_yes" class="form-box">
diff --git a/templates/web/bromley/report/update-form.html b/templates/web/bromley/report/update-form.html
index 45d7aed5e..9778a0db3 100644
--- a/templates/web/bromley/report/update-form.html
+++ b/templates/web/bromley/report/update-form.html
@@ -98,15 +98,23 @@
[% INCLUDE 'report/update/form_name.html' %]
<label for="password_register">[% loc('Password (optional)') %]</label>
+ [% IF field_errors.password_register %]
+ <p class='form-error'>[% field_errors.password_register %]</p>
+ [% END %]
<div class="general-notes">
<p>[% loc('Providing a password is optional, but doing so will allow you to more easily report problems, leave updates and manage your reports.') %]</p>
</div>
<div class="form-txt-submit-box">
- <input type="password" class="form-control" name="password_register" id="password_register" value="" placeholder="[% loc('Enter a password') %]">
+ <input type="password" class="form-control js-password-validate" name="password_register" id="password_register" value="" placeholder="[% loc('Enter a password') %]">
<input class="green-btn js-submit_register" type="submit" name="submit_register" value="[% loc('Post') %]">
</div>
+
+ <div class="general-notes">
+ <p>[% tprintf(loc('Your password should include %d or more characters.'), c.cobrand.password_minimum_length) %]</p>
+ </div>
+
</div>
<div id="form_sign_in_yes" class="form-box">
<h5>Confirm my report with my FixMyStreet password</h5>
diff --git a/templates/web/fixamingata/report/new/form_user_loggedout.html b/templates/web/fixamingata/report/new/form_user_loggedout.html
index 1f7cf2aeb..bbb9864a3 100644
--- a/templates/web/fixamingata/report/new/form_user_loggedout.html
+++ b/templates/web/fixamingata/report/new/form_user_loggedout.html
@@ -27,23 +27,32 @@
</div>
<label for="form_phone">[% loc('Phone number (optional)') %]</label>
- <input type="text" class="form-control" value="[% report.user.phone | html %]" name="phone" id="form_phone" placeholder="[% loc('Your phone number') %]">
+ <input type="text" class="form-control" value="[% report.user.phone_display | html %]" name="phone" id="form_phone" placeholder="[% loc('Your phone number') %]">
<div class="general-notes form-focus-hidden">
<p>[% loc('We never show your email address or phone number.') %]</p>
</div>
<label for="password_register">[% loc('Password (optional)') %]</label>
+ [% IF field_errors.password_register %]
+ <p class='form-error'>[% field_errors.password_register %]</p>
+ [% END %]
<div class="general-notes form-focus-hidden">
<p>[% loc('Providing a password is optional, but doing so will allow you to more easily report problems, leave updates and manage your reports.') %]</p>
</div>
<div class="form-txt-submit-box">
- <input type="password" class="form-control" name="password_register" id="password_register" value="" placeholder="[% loc('Enter a password') %]">
+ <input type="password" class="form-control js-password-validate" name="password_register" id="password_register" value="" placeholder="[% loc('Enter a password') %]">
<input class="green-btn js-submit_register" type="submit" name="submit_register" value="[% loc('Submit') %]">
</div>
+
+ <div class="general-notes">
+ <p>[% tprintf(loc('Your password should include %d or more characters.'), c.cobrand.password_minimum_length) %]</p>
+ </div>
+
</div>
+
<div id="form_sign_in_yes" class="form-box">
<h5>Jag har ett lösenord sedan tidigare:</h5>
diff --git a/templates/web/zurich/auth/general.html b/templates/web/zurich/auth/general.html
index 899f0ca71..555a72374 100644
--- a/templates/web/zurich/auth/general.html
+++ b/templates/web/zurich/auth/general.html
@@ -1,16 +1,6 @@
[% INCLUDE 'header.html', title = loc('Sign in or create an account') %]
-[% IF username_error;
-
- # other keys include fqdn, mxcheck if you'd like to write a custom error message
-
- errors = {
- missing_email = loc('Please enter your email'),
- other_email = loc('Please check your email address is correct')
- };
-
- loc_username_error = errors.$username_error || errors.other_email;
-END %]
+[% loc_username_error = INCLUDE 'auth/_username_error.html' default='email' %]
<form action="/auth" method="post" name="general_auth_login" class="validate">
<fieldset>
@@ -61,11 +51,18 @@ END %]
<input type="text" class="required" name="name" value="" placeholder="[% loc('Your name') %]">
<label for="password_register">[% loc('Password (optional)') %]</label>
+ [% IF field_errors.password_register %]
+ <p class='form-error'>[% field_errors.password_register %]</p>
+ [% END %]
<div class="form-txt-submit-box">
- <input type="password" class="required" name="password_register" id="password_register" value="" placeholder="[% loc('Enter a password') %]">
+ <input type="password" class="required js-password-validate" name="password_register" id="password_register" value="" placeholder="[% loc('Enter a password') %]">
<input class="green-btn" type="submit" name="sign_in_by_code" value="Registrieren">
</div>
+ <div class="general-notes">
+ <p>[% tprintf(loc('Your password should include %d or more characters.'), c.cobrand.password_minimum_length) %]</p>
+ </div>
+
</div>
</fieldset>
</form>
diff --git a/web/cobrands/fixmystreet/fixmystreet.js b/web/cobrands/fixmystreet/fixmystreet.js
index a3ac5b71a..0aa01e483 100644
--- a/web/cobrands/fixmystreet/fixmystreet.js
+++ b/web/cobrands/fixmystreet/fixmystreet.js
@@ -285,6 +285,9 @@ $.extend(fixmystreet.set_up, {
if (jQuery.validator) {
jQuery.validator.addMethod('validCategory', function(value, element) {
return this.optional(element) || value != '-- Pick a category --'; }, translation_strings.category );
+ jQuery.validator.addMethod('js-password-validate', function(value, element) {
+ return !value || value.length >= fixmystreet.password_minimum_length;
+ }, translation_strings.password_register.short);
}
var submitted = false;
diff --git a/web/js/validation_rules.js b/web/js/validation_rules.js
index 5295a53ca..e6d745336 100644
--- a/web/js/validation_rules.js
+++ b/web/js/validation_rules.js
@@ -1,5 +1,11 @@
validation_rules = {
title: { required: true },
detail: { required: true },
- update: { required: true }
+ update: { required: true },
+ password_register: {
+ remote: {
+ url: '/auth/common_password',
+ type: 'post'
+ }
+ }
};