Merge branch 'MDL-61389-master' of git://github.com/junpataleta/moodle
authorEloy Lafuente (stronk7) <stronk7@moodle.org>
Mon, 17 Feb 2020 22:29:28 +0000 (23:29 +0100)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Mon, 17 Feb 2020 22:29:28 +0000 (23:29 +0100)
GruntfileComponents.js
admin/settings/security.php
lang/en/admin.php
lang/en/deprecated.txt
lang/en/moodle.php
lang/en/timezones.php
lib/classes/event/user_password_policy_failed.php [new file with mode: 0644]
lib/moodlelib.php
lib/tests/authlib_test.php
lib/tests/date_test.php

index 06ed999..54a431c 100644 (file)
@@ -163,8 +163,10 @@ const getComponentFromPath = path => {
 const getOwningComponentDirectory = checkPath => {
     const path = require('path');
 
-    const pathList = fetchComponentData().components;
-    for (const componentPath of Object.keys(pathList)) {
+    // Fetch all components into a reverse sorted array.
+    // This ensures that components which are within the directory of another component match first.
+    const pathList = Object.keys(fetchComponentData().components).sort().reverse();
+    for (const componentPath of pathList) {
         if (checkPath === componentPath) {
             return componentPath;
         }
index 1b91d6a..d8dc48a 100644 (file)
@@ -100,6 +100,9 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
     $temp->add(new admin_setting_configtext('minpasswordupper', new lang_string('minpasswordupper', 'admin'), new lang_string('configminpasswordupper', 'admin'), 1, PARAM_INT));
     $temp->add(new admin_setting_configtext('minpasswordnonalphanum', new lang_string('minpasswordnonalphanum', 'admin'), new lang_string('configminpasswordnonalphanum', 'admin'), 1, PARAM_INT));
     $temp->add(new admin_setting_configtext('maxconsecutiveidentchars', new lang_string('maxconsecutiveidentchars', 'admin'), new lang_string('configmaxconsecutiveidentchars', 'admin'), 0, PARAM_INT));
+    $temp->add(new admin_setting_configcheckbox('passwordpolicycheckonlogin',
+        new lang_string('passwordpolicycheckonlogin', 'admin'),
+        new lang_string('configpasswordpolicycheckonlogin', 'admin'), 0));
 
     $temp->add(new admin_setting_configtext('passwordreuselimit',
         new lang_string('passwordreuselimit', 'admin'),
index 473305b..f4d3e7d 100644 (file)
@@ -307,7 +307,9 @@ $string['confignotifyloginthreshold'] = 'If notifications about failed logins ar
 $string['confignotloggedinroleid'] = 'Users who are not logged in to the site will be treated as if they have this role granted to them at the site context.  Guest is almost always what you want here, but you might want to create roles that are less or more restrictive.  Things like creating posts still require the user to log in properly.';
 $string['configopentowebcrawlers'] = 'If you enable this setting, then search engines will be allowed to enter your site as a guest.  In addition, people coming in to your site via a search engine will automatically be logged in as a guest.  Note that this only provides transparent access to courses that already allow guest access.';
 $string['configoverride'] = 'Defined in config.php';
-$string['configpasswordpolicy'] = 'If enabled, user passwords will be checked against the password policy as specified in the settings below. Enabling the password policy will not affect existing users until they decide to, or are required to, change their password.';
+$string['configpasswordpolicy'] = 'If enabled, user passwords will be checked against the password policy as specified in the settings below. Enabling the password policy will not affect existing users until they decide to, or are required to, change their password, or the \'Check password on login\' setting is enabled.';
+$string['configpasswordpolicycheckonlogin'] = 'If enabled, user passwords will be checked against the password policy each time users log in. If the check fails, the user will be required to change their password before proceeding.
+It is useful to enable this setting after updating the password policy.';
 $string['configpasswordresettime'] = 'This specifies the amount of time people have to validate a password reset request before it expires. Usually 30 minutes is a good value.';
 $string['configpathtodu'] = 'Path to du. Probably something like /usr/bin/du. If you enter this, pages that display directory contents will run much faster for directories with a lot of files.';
 $string['configpathtophp'] = 'Path to PHP CLI. Probably something like /usr/bin/php. If you enter this, cron scripts can be executed from admin web interface.';
@@ -895,6 +897,7 @@ $string['passwordchangelogout_desc'] = 'If enabled, when a password is changed,
 $string['passwordchangetokendeletion'] = 'Remove web service access tokens after password change';
 $string['passwordchangetokendeletion_desc'] = 'If enabled, when a password is changed, all the user web service access tokens are deleted.';
 $string['passwordpolicy'] = 'Password policy';
+$string['passwordpolicycheckonlogin'] = 'Check password on login';
 $string['passwordresettime'] = 'Maximum time to validate password reset request';
 $string['passwordreuselimit'] = 'Password rotation limit';
 $string['passwordreuselimit_desc'] = 'Number of times a user must change their password before they are allowed to reuse a password. Hashes of previously used passwords are stored in local database table. This feature might not be compatible with some external authentication plugins.';
index 7753147..4bd15ab 100644 (file)
@@ -121,3 +121,21 @@ globalevent,core_calendar
 globalevents,core_calendar
 eventtypeglobal,core_calendar
 documentation,core_webservice
+africa/asmera,core_timezones
+africa/timbuktu,core_timezones
+america/argentina/comodrivadavia,core_timezones
+america/indianapolis,core_timezones
+america/louisville,core_timezones
+america/montreal,core_timezones
+asia/calcutta,core_timezones
+asia/chongqing,core_timezones
+asia/harbin,core_timezones
+asia/kashgar,core_timezones
+asia/katmandu,core_timezones
+asia/rangoon,core_timezones
+asia/saigon,core_timezones
+atlantic/faeroe,core_timezones
+europe/belfast,core_timezones
+pacific/ponape,core_timezones
+pacific/truk,core_timezones
+pacific/yap,core_timezones
index 5010450..22682b5 100644 (file)
@@ -799,6 +799,7 @@ $string['eventusercreated'] = 'User created';
 $string['eventuserdeleted'] = 'User deleted';
 $string['eventuserlistviewed'] = 'User list viewed';
 $string['eventuserloggedout'] = 'User logged out';
+$string['eventuserpasswordpolicyfailed'] = 'User password failed password policy';
 $string['eventuserpasswordupdated'] = 'User password updated';
 $string['eventuserprofileviewed'] = 'User profile viewed';
 $string['eventuserupdated'] = 'User updated';
@@ -859,6 +860,10 @@ $string['forcepasswordchange_help'] = 'If this checkbox is ticked, the user will
 $string['forcepasswordchangecheckfull'] = 'Are you absolutely sure you want to force a password change to {$a} ?';
 $string['forcepasswordchangenot'] = 'Could not force a password change to {$a}';
 $string['forcepasswordchangenotice'] = 'You must change your password to proceed.';
+$string['forcepasswordresetfailurenotice'] = 'Your current password no longer passes the set password policy. Please contact your Moodle administrator for assistance.
+   {$a}';
+$string['forcepasswordresetnotice'] = 'Your current password no longer passes the set password policy, you must reset your password to login.
+   {$a}';
 $string['forcetheme'] = 'Force theme';
 $string['forgotaccount'] = 'Lost password?';
 $string['forgotten'] = 'Forgotten your username or password?';
@@ -1517,6 +1522,8 @@ $string['passwordforgotteninstructions'] = 'Your details must first be found in
 $string['passwordforgotteninstructions2'] = 'To reset your password, submit your username or your email address below. If we can find you in the database, an email will be sent to your email address, with instructions how to get access again.';
 $string['passwordchanged'] = 'Password has been changed';
 $string['passwordnohelp'] = 'No help is available to find your lost password. Please contact your Moodle administrator.';
+$string['passwordpolicynomatch'] = 'Your current password no longer matches the set password policy.
+   {$a}';
 $string['passwordrecovery'] = 'Yes, help me log in';
 $string['passwordsdiffer'] = 'These passwords do not match';
 $string['passwordsent'] = 'Password has been sent';
index 01a1fea..417a4fb 100644 (file)
@@ -27,7 +27,7 @@ $string['africa/abidjan'] = 'Africa/Abidjan';
 $string['africa/accra'] = 'Africa/Accra';
 $string['africa/addis_ababa'] = 'Africa/Addis_Ababa';
 $string['africa/algiers'] = 'Africa/Algiers';
-$string['africa/asmera'] = 'Africa/Asmera';
+$string['africa/asmara'] = 'Africa/Asmara';
 $string['africa/bamako'] = 'Africa/Bamako';
 $string['africa/bangui'] = 'Africa/Bangui';
 $string['africa/banjul'] = 'Africa/Banjul';
@@ -48,6 +48,7 @@ $string['africa/freetown'] = 'Africa/Freetown';
 $string['africa/gaborone'] = 'Africa/Gaborone';
 $string['africa/harare'] = 'Africa/Harare';
 $string['africa/johannesburg'] = 'Africa/Johannesburg';
+$string['africa/juba'] = 'Africa/Juba';
 $string['africa/kampala'] = 'Africa/Kampala';
 $string['africa/khartoum'] = 'Africa/Khartoum';
 $string['africa/kigali'] = 'Africa/Kigali';
@@ -71,32 +72,35 @@ $string['africa/nouakchott'] = 'Africa/Nouakchott';
 $string['africa/ouagadougou'] = 'Africa/Ouagadougou';
 $string['africa/porto-novo'] = 'Africa/Porto-Novo';
 $string['africa/sao_tome'] = 'Africa/Sao_Tome';
-$string['africa/timbuktu'] = 'Africa/Timbuktu';
 $string['africa/tripoli'] = 'Africa/Tripoli';
 $string['africa/tunis'] = 'Africa/Tunis';
 $string['africa/windhoek'] = 'Africa/Windhoek';
 $string['america/adak'] = 'America/Adak';
-$string['america/anguilla'] = 'America/Anguilla';
 $string['america/anchorage'] = 'America/Anchorage';
+$string['america/anguilla'] = 'America/Anguilla';
 $string['america/antigua'] = 'America/Antigua';
 $string['america/araguaina'] = 'America/Araguaina';
 $string['america/argentina/buenos_aires'] = 'America/Argentina/Buenos_Aires';
 $string['america/argentina/catamarca'] = 'America/Argentina/Catamarca';
-$string['america/argentina/comodrivadavia'] = 'America/Argentina/ComodRivadavia';
 $string['america/argentina/cordoba'] = 'America/Argentina/Cordoba';
 $string['america/argentina/jujuy'] = 'America/Argentina/Jujuy';
 $string['america/argentina/la_rioja'] = 'America/Argentina/La_Rioja';
 $string['america/argentina/mendoza'] = 'America/Argentina/Mendoza';
 $string['america/argentina/rio_gallegos'] = 'America/Argentina/Rio_Gallegos';
+$string['america/argentina/salta'] = 'America/Argentina/Salta';
 $string['america/argentina/san_juan'] = 'America/Argentina/San_Juan';
+$string['america/argentina/san_luis'] = 'America/Argentina/San_Luis';
 $string['america/argentina/tucuman'] = 'America/Argentina/Tucuman';
 $string['america/argentina/ushuaia'] = 'America/Argentina/Ushuaia';
 $string['america/aruba'] = 'America/Aruba';
 $string['america/asuncion'] = 'America/Asuncion';
+$string['america/atikokan'] = 'America/Atikokan';
 $string['america/bahia'] = 'America/Bahia';
+$string['america/bahia_banderas'] = 'America/Bahia_Banderas';
 $string['america/barbados'] = 'America/Barbados';
 $string['america/belem'] = 'America/Belem';
 $string['america/belize'] = 'America/Belize';
+$string['america/blanc-sablon'] = 'America/Blanc-Sablon';
 $string['america/boa_vista'] = 'America/Boa_Vista';
 $string['america/bogota'] = 'America/Bogota';
 $string['america/boise'] = 'America/Boise';
@@ -106,7 +110,10 @@ $string['america/cancun'] = 'America/Cancun';
 $string['america/caracas'] = 'America/Caracas';
 $string['america/cayenne'] = 'America/Cayenne';
 $string['america/cayman'] = 'America/Cayman';
+$string['america/chicago'] = 'America/Chicago';
+$string['america/chihuahua'] = 'America/Chihuahua';
 $string['america/costa_rica'] = 'America/Costa_Rica';
+$string['america/creston'] = 'America/Creston';
 $string['america/cuiaba'] = 'America/Cuiaba';
 $string['america/curacao'] = 'America/Curacao';
 $string['america/danmarkshavn'] = 'America/Danmarkshavn';
@@ -118,6 +125,7 @@ $string['america/dominica'] = 'America/Dominica';
 $string['america/edmonton'] = 'America/Edmonton';
 $string['america/eirunepe'] = 'America/Eirunepe';
 $string['america/el_salvador'] = 'America/El_Salvador';
+$string['america/fort_nelson'] = 'America/Fort_Nelson';
 $string['america/fortaleza'] = 'America/Fortaleza';
 $string['america/glace_bay'] = 'America/Glace_Bay';
 $string['america/godthab'] = 'America/Godthab';
@@ -131,40 +139,50 @@ $string['america/guyana'] = 'America/Guyana';
 $string['america/halifax'] = 'America/Halifax';
 $string['america/havana'] = 'America/Havana';
 $string['america/hermosillo'] = 'America/Hermosillo';
-$string['america/chicago'] = 'America/Chicago';
-$string['america/chihuahua'] = 'America/Chihuahua';
+$string['america/indiana/indianapolis'] = 'America/Indiana/Indianapolis';
 $string['america/indiana/knox'] = 'America/Indiana/Knox';
 $string['america/indiana/marengo'] = 'America/Indiana/Marengo';
-$string['america/indianapolis'] = 'America/Indianapolis';
+$string['america/indiana/petersburg'] = 'America/Indiana/Petersburg';
+$string['america/indiana/tell_city'] = 'America/Indiana/Tell_City';
 $string['america/indiana/vevay'] = 'America/Indiana/Vevay';
+$string['america/indiana/vincennes'] = 'America/Indiana/Vincennes';
+$string['america/indiana/winamac'] = 'America/Indiana/Winamac';
 $string['america/inuvik'] = 'America/Inuvik';
 $string['america/iqaluit'] = 'America/Iqaluit';
 $string['america/jamaica'] = 'America/Jamaica';
 $string['america/juneau'] = 'America/Juneau';
+$string['america/kentucky/louisville'] = 'America/Kentucky/Louisville';
 $string['america/kentucky/monticello'] = 'America/Kentucky/Monticello';
+$string['america/kralendijk'] = 'America/Kralendijk';
 $string['america/la_paz'] = 'America/La_Paz';
 $string['america/lima'] = 'America/Lima';
 $string['america/los_angeles'] = 'America/Los_Angeles';
-$string['america/louisville'] = 'America/Louisville';
+$string['america/lower_princes'] = 'America/Lower_Princes';
 $string['america/maceio'] = 'America/Maceio';
 $string['america/managua'] = 'America/Managua';
 $string['america/manaus'] = 'America/Manaus';
+$string['america/marigot'] = 'America/Marigot';
 $string['america/martinique'] = 'America/Martinique';
+$string['america/matamoros'] = 'America/Matamoros';
 $string['america/mazatlan'] = 'America/Mazatlan';
 $string['america/menominee'] = 'America/Menominee';
 $string['america/merida'] = 'America/Merida';
+$string['america/metlakatla'] = 'America/Metlakatla';
 $string['america/mexico_city'] = 'America/Mexico_City';
 $string['america/miquelon'] = 'America/Miquelon';
+$string['america/moncton'] = 'America/Moncton';
 $string['america/monterrey'] = 'America/Monterrey';
 $string['america/montevideo'] = 'America/Montevideo';
-$string['america/montreal'] = 'America/Montreal';
 $string['america/montserrat'] = 'America/Montserrat';
 $string['america/nassau'] = 'America/Nassau';
 $string['america/new_york'] = 'America/New_York';
 $string['america/nipigon'] = 'America/Nipigon';
 $string['america/nome'] = 'America/Nome';
 $string['america/noronha'] = 'America/Noronha';
+$string['america/north_dakota/beulah'] = 'America/North_Dakota/Beulah';
 $string['america/north_dakota/center'] = 'America/North_Dakota/Center';
+$string['america/north_dakota/new_salem'] = 'America/North_Dakota/New_Salem';
+$string['america/ojinaga'] = 'America/Ojinaga';
 $string['america/panama'] = 'America/Panama';
 $string['america/pangnirtung'] = 'America/Pangnirtung';
 $string['america/paramaribo'] = 'America/Paramaribo';
@@ -173,15 +191,20 @@ $string['america/port-au-prince'] = 'America/Port-au-Prince';
 $string['america/port_of_spain'] = 'America/Port_of_Spain';
 $string['america/porto_velho'] = 'America/Porto_Velho';
 $string['america/puerto_rico'] = 'America/Puerto_Rico';
+$string['america/punta_arenas'] = 'America/Punta_Arenas';
 $string['america/rainy_river'] = 'America/Rainy_River';
 $string['america/rankin_inlet'] = 'America/Rankin_Inlet';
 $string['america/recife'] = 'America/Recife';
 $string['america/regina'] = 'America/Regina';
+$string['america/resolute'] = 'America/Resolute';
 $string['america/rio_branco'] = 'America/Rio_Branco';
+$string['america/santarem'] = 'America/Santarem';
 $string['america/santiago'] = 'America/Santiago';
 $string['america/santo_domingo'] = 'America/Santo_Domingo';
 $string['america/sao_paulo'] = 'America/Sao_Paulo';
 $string['america/scoresbysund'] = 'America/Scoresbysund';
+$string['america/sitka'] = 'America/Sitka';
+$string['america/st_barthelemy'] = 'America/St_Barthelemy';
 $string['america/st_johns'] = 'America/St_Johns';
 $string['america/st_kitts'] = 'America/St_Kitts';
 $string['america/st_lucia'] = 'America/St_Lucia';
@@ -202,12 +225,15 @@ $string['america/yellowknife'] = 'America/Yellowknife';
 $string['antarctica/casey'] = 'Antarctica/Casey';
 $string['antarctica/davis'] = 'Antarctica/Davis';
 $string['antarctica/dumontdurville'] = 'Antarctica/DumontDUrville';
+$string['antarctica/macquarie'] = 'Antarctica/Macquarie';
 $string['antarctica/mawson'] = 'Antarctica/Mawson';
 $string['antarctica/mcmurdo'] = 'Antarctica/McMurdo';
 $string['antarctica/palmer'] = 'Antarctica/Palmer';
 $string['antarctica/rothera'] = 'Antarctica/Rothera';
 $string['antarctica/syowa'] = 'Antarctica/Syowa';
+$string['antarctica/troll'] = 'Antarctica/Troll';
 $string['antarctica/vostok'] = 'Antarctica/Vostok';
+$string['arctic/longyearbyen'] = 'Arctic/Longyearbyen';
 $string['asia/aden'] = 'Asia/Aden';
 $string['asia/almaty'] = 'Asia/Almaty';
 $string['asia/amman'] = 'Asia/Amman';
@@ -215,34 +241,39 @@ $string['asia/anadyr'] = 'Asia/Anadyr';
 $string['asia/aqtau'] = 'Asia/Aqtau';
 $string['asia/aqtobe'] = 'Asia/Aqtobe';
 $string['asia/ashgabat'] = 'Asia/Ashgabat';
+$string['asia/atyrau'] = 'Asia/Atyrau';
 $string['asia/baghdad'] = 'Asia/Baghdad';
 $string['asia/bahrain'] = 'Asia/Bahrain';
 $string['asia/baku'] = 'Asia/Baku';
 $string['asia/bangkok'] = 'Asia/Bangkok';
+$string['asia/barnaul'] = 'Asia/Barnaul';
 $string['asia/beirut'] = 'Asia/Beirut';
 $string['asia/bishkek'] = 'Asia/Bishkek';
 $string['asia/brunei'] = 'Asia/Brunei';
-$string['asia/calcutta'] = 'Asia/Calcutta';
+$string['asia/chita'] = 'Asia/Chita';
+$string['asia/choibalsan'] = 'Asia/Choibalsan';
 $string['asia/colombo'] = 'Asia/Colombo';
 $string['asia/damascus'] = 'Asia/Damascus';
 $string['asia/dhaka'] = 'Asia/Dhaka';
 $string['asia/dili'] = 'Asia/Dili';
 $string['asia/dubai'] = 'Asia/Dubai';
 $string['asia/dushanbe'] = 'Asia/Dushanbe';
+$string['asia/famagusta'] = 'Asia/Famagusta';
 $string['asia/gaza'] = 'Asia/Gaza';
-$string['asia/harbin'] = 'Asia/Harbin';
+$string['asia/hebron'] = 'Asia/Hebron';
+$string['asia/ho_chi_minh'] = 'Asia/Ho_Chi_Minh';
 $string['asia/hong_kong'] = 'Asia/Hong_Kong';
 $string['asia/hovd'] = 'Asia/Hovd';
-$string['asia/choibalsan'] = 'Asia/Choibalsan';
-$string['asia/chongqing'] = 'Asia/Chongqing';
 $string['asia/irkutsk'] = 'Asia/Irkutsk';
+$string['asia/jakarta'] = 'Asia/Jakarta';
 $string['asia/jayapura'] = 'Asia/Jayapura';
 $string['asia/jerusalem'] = 'Asia/Jerusalem';
 $string['asia/kabul'] = 'Asia/Kabul';
 $string['asia/kamchatka'] = 'Asia/Kamchatka';
 $string['asia/karachi'] = 'Asia/Karachi';
-$string['asia/kashgar'] = 'Asia/Kashgar';
-$string['asia/katmandu'] = 'Asia/Katmandu';
+$string['asia/kathmandu'] = 'Asia/Kathmandu';
+$string['asia/khandyga'] = 'Asia/Khandyga';
+$string['asia/kolkata'] = 'Asia/Kolkata';
 $string['asia/krasnoyarsk'] = 'Asia/Krasnoyarsk';
 $string['asia/kuala_lumpur'] = 'Asia/Kuala_Lumpur';
 $string['asia/kuching'] = 'Asia/Kuching';
@@ -253,6 +284,7 @@ $string['asia/makassar'] = 'Asia/Makassar';
 $string['asia/manila'] = 'Asia/Manila';
 $string['asia/muscat'] = 'Asia/Muscat';
 $string['asia/nicosia'] = 'Asia/Nicosia';
+$string['asia/novokuznetsk'] = 'Asia/Novokuznetsk';
 $string['asia/novosibirsk'] = 'Asia/Novosibirsk';
 $string['asia/omsk'] = 'Asia/Omsk';
 $string['asia/oral'] = 'Asia/Oral';
@@ -260,42 +292,47 @@ $string['asia/phnom_penh'] = 'Asia/Phnom_Penh';
 $string['asia/pontianak'] = 'Asia/Pontianak';
 $string['asia/pyongyang'] = 'Asia/Pyongyang';
 $string['asia/qatar'] = 'Asia/Qatar';
+$string['asia/qostanay'] = 'Asia/Qostanay';
 $string['asia/qyzylorda'] = 'Asia/Qyzylorda';
-$string['asia/rangoon'] = 'Asia/Rangoon';
 $string['asia/riyadh'] = 'Asia/Riyadh';
-$string['asia/saigon'] = 'Asia/Saigon';
 $string['asia/sakhalin'] = 'Asia/Sakhalin';
 $string['asia/samarkand'] = 'Asia/Samarkand';
 $string['asia/seoul'] = 'Asia/Seoul';
 $string['asia/shanghai'] = 'Asia/Shanghai';
 $string['asia/singapore'] = 'Asia/Singapore';
+$string['asia/srednekolymsk'] = 'Asia/Srednekolymsk';
 $string['asia/taipei'] = 'Asia/Taipei';
 $string['asia/tashkent'] = 'Asia/Tashkent';
 $string['asia/tbilisi'] = 'Asia/Tbilisi';
 $string['asia/tehran'] = 'Asia/Tehran';
 $string['asia/thimphu'] = 'Asia/Thimphu';
 $string['asia/tokyo'] = 'Asia/Tokyo';
+$string['asia/tomsk'] = 'Asia/Tomsk';
 $string['asia/ulaanbaatar'] = 'Asia/Ulaanbaatar';
 $string['asia/urumqi'] = 'Asia/Urumqi';
+$string['asia/ust-nera'] = 'Asia/Ust-Nera';
 $string['asia/vientiane'] = 'Asia/Vientiane';
 $string['asia/vladivostok'] = 'Asia/Vladivostok';
 $string['asia/yakutsk'] = 'Asia/Yakutsk';
+$string['asia/yangon'] = 'Asia/Yangon';
 $string['asia/yekaterinburg'] = 'Asia/Yekaterinburg';
 $string['asia/yerevan'] = 'Asia/Yerevan';
 $string['atlantic/azores'] = 'Atlantic/Azores';
 $string['atlantic/bermuda'] = 'Atlantic/Bermuda';
 $string['atlantic/canary'] = 'Atlantic/Canary';
 $string['atlantic/cape_verde'] = 'Atlantic/Cape_Verde';
-$string['atlantic/faeroe'] = 'Atlantic/Faeroe';
+$string['atlantic/faroe'] = 'Atlantic/Faroe';
 $string['atlantic/madeira'] = 'Atlantic/Madeira';
 $string['atlantic/reykjavik'] = 'Atlantic/Reykjavik';
 $string['atlantic/south_georgia'] = 'Atlantic/South_Georgia';
-$string['atlantic/stanley'] = 'Atlantic/Stanley';
 $string['atlantic/st_helena'] = 'Atlantic/St_Helena';
+$string['atlantic/stanley'] = 'Atlantic/Stanley';
 $string['australia/adelaide'] = 'Australia/Adelaide';
 $string['australia/brisbane'] = 'Australia/Brisbane';
 $string['australia/broken_hill'] = 'Australia/Broken_Hill';
+$string['australia/currie'] = 'Australia/Currie';
 $string['australia/darwin'] = 'Australia/Darwin';
+$string['australia/eucla'] = 'Australia/Eucla';
 $string['australia/hobart'] = 'Australia/Hobart';
 $string['australia/lindeman'] = 'Australia/Lindeman';
 $string['australia/lord_howe'] = 'Australia/Lord_Howe';
@@ -304,51 +341,69 @@ $string['australia/perth'] = 'Australia/Perth';
 $string['australia/sydney'] = 'Australia/Sydney';
 $string['europe/amsterdam'] = 'Europe/Amsterdam';
 $string['europe/andorra'] = 'Europe/Andorra';
+$string['europe/astrakhan'] = 'Europe/Astrakhan';
 $string['europe/athens'] = 'Europe/Athens';
-$string['europe/belfast'] = 'Europe/Belfast';
 $string['europe/belgrade'] = 'Europe/Belgrade';
 $string['europe/berlin'] = 'Europe/Berlin';
+$string['europe/bratislava'] = 'Europe/Bratislava';
 $string['europe/brussels'] = 'Europe/Brussels';
-$string['europe/budapest'] = 'Europe/Budapest';
 $string['europe/bucharest'] = 'Europe/Bucharest';
+$string['europe/budapest'] = 'Europe/Budapest';
+$string['europe/busingen'] = 'Europe/Busingen';
+$string['europe/chisinau'] = 'Europe/Chisinau';
 $string['europe/copenhagen'] = 'Europe/Copenhagen';
 $string['europe/dublin'] = 'Europe/Dublin';
 $string['europe/gibraltar'] = 'Europe/Gibraltar';
+$string['europe/guernsey'] = 'Europe/Guernsey';
 $string['europe/helsinki'] = 'Europe/Helsinki';
-$string['europe/chisinau'] = 'Europe/Chisinau';
+$string['europe/isle_of_man'] = 'Europe/Isle_of_Man';
 $string['europe/istanbul'] = 'Europe/Istanbul';
+$string['europe/jersey'] = 'Europe/Jersey';
 $string['europe/kaliningrad'] = 'Europe/Kaliningrad';
 $string['europe/kiev'] = 'Europe/Kiev';
+$string['europe/kirov'] = 'Europe/Kirov';
 $string['europe/lisbon'] = 'Europe/Lisbon';
+$string['europe/ljubljana'] = 'Europe/Ljubljana';
 $string['europe/london'] = 'Europe/London';
 $string['europe/luxembourg'] = 'Europe/Luxembourg';
 $string['europe/madrid'] = 'Europe/Madrid';
 $string['europe/malta'] = 'Europe/Malta';
+$string['europe/mariehamn'] = 'Europe/Mariehamn';
 $string['europe/minsk'] = 'Europe/Minsk';
 $string['europe/monaco'] = 'Europe/Monaco';
 $string['europe/moscow'] = 'Europe/Moscow';
 $string['europe/oslo'] = 'Europe/Oslo';
 $string['europe/paris'] = 'Europe/Paris';
+$string['europe/podgorica'] = 'Europe/Podgorica';
 $string['europe/prague'] = 'Europe/Prague';
 $string['europe/riga'] = 'Europe/Riga';
 $string['europe/rome'] = 'Europe/Rome';
 $string['europe/samara'] = 'Europe/Samara';
+$string['europe/san_marino'] = 'Europe/San_Marino';
+$string['europe/sarajevo'] = 'Europe/Sarajevo';
+$string['europe/saratov'] = 'Europe/Saratov';
 $string['europe/simferopol'] = 'Europe/Simferopol';
+$string['europe/skopje'] = 'Europe/Skopje';
 $string['europe/sofia'] = 'Europe/Sofia';
 $string['europe/stockholm'] = 'Europe/Stockholm';
 $string['europe/tallinn'] = 'Europe/Tallinn';
 $string['europe/tirane'] = 'Europe/Tirane';
+$string['europe/ulyanovsk'] = 'Europe/Ulyanovsk';
 $string['europe/uzhgorod'] = 'Europe/Uzhgorod';
 $string['europe/vaduz'] = 'Europe/Vaduz';
+$string['europe/vatican'] = 'Europe/Vatican';
 $string['europe/vienna'] = 'Europe/Vienna';
 $string['europe/vilnius'] = 'Europe/Vilnius';
+$string['europe/volgograd'] = 'Europe/Volgograd';
 $string['europe/warsaw'] = 'Europe/Warsaw';
+$string['europe/zagreb'] = 'Europe/Zagreb';
 $string['europe/zaporozhye'] = 'Europe/Zaporozhye';
 $string['europe/zurich'] = 'Europe/Zurich';
 $string['indian/antananarivo'] = 'Indian/Antananarivo';
-$string['indian/comoro'] = 'Indian/Comoro';
 $string['indian/chagos'] = 'Indian/Chagos';
 $string['indian/christmas'] = 'Indian/Christmas';
+$string['indian/cocos'] = 'Indian/Cocos';
+$string['indian/comoro'] = 'Indian/Comoro';
 $string['indian/kerguelen'] = 'Indian/Kerguelen';
 $string['indian/mahe'] = 'Indian/Mahe';
 $string['indian/maldives'] = 'Indian/Maldives';
@@ -357,6 +412,9 @@ $string['indian/mayotte'] = 'Indian/Mayotte';
 $string['indian/reunion'] = 'Indian/Reunion';
 $string['pacific/apia'] = 'Pacific/Apia';
 $string['pacific/auckland'] = 'Pacific/Auckland';
+$string['pacific/bougainville'] = 'Pacific/Bougainville';
+$string['pacific/chatham'] = 'Pacific/Chatham';
+$string['pacific/chuuk'] = 'Pacific/Chuuk';
 $string['pacific/easter'] = 'Pacific/Easter';
 $string['pacific/efate'] = 'Pacific/Efate';
 $string['pacific/enderbury'] = 'Pacific/Enderbury';
@@ -368,7 +426,6 @@ $string['pacific/gambier'] = 'Pacific/Gambier';
 $string['pacific/guadalcanal'] = 'Pacific/Guadalcanal';
 $string['pacific/guam'] = 'Pacific/Guam';
 $string['pacific/honolulu'] = 'Pacific/Honolulu';
-$string['pacific/chatham'] = 'Pacific/Chatham';
 $string['pacific/kiritimati'] = 'Pacific/Kiritimati';
 $string['pacific/kosrae'] = 'Pacific/Kosrae';
 $string['pacific/kwajalein'] = 'Pacific/Kwajalein';
@@ -382,14 +439,33 @@ $string['pacific/noumea'] = 'Pacific/Noumea';
 $string['pacific/pago_pago'] = 'Pacific/Pago_Pago';
 $string['pacific/palau'] = 'Pacific/Palau';
 $string['pacific/pitcairn'] = 'Pacific/Pitcairn';
-$string['pacific/ponape'] = 'Pacific/Ponape';
+$string['pacific/pohnpei'] = 'Pacific/Pohnpei';
 $string['pacific/port_moresby'] = 'Pacific/Port_Moresby';
 $string['pacific/rarotonga'] = 'Pacific/Rarotonga';
 $string['pacific/saipan'] = 'Pacific/Saipan';
 $string['pacific/tahiti'] = 'Pacific/Tahiti';
 $string['pacific/tarawa'] = 'Pacific/Tarawa';
 $string['pacific/tongatapu'] = 'Pacific/Tongatapu';
-$string['pacific/truk'] = 'Pacific/Truk';
 $string['pacific/wake'] = 'Pacific/Wake';
 $string['pacific/wallis'] = 'Pacific/Wallis';
+$string['utc'] = 'UTC';
+// The following identifiers have been previous removed from TimeDateZone::listIdentifiers and are no longer used.
+// Deprecated since Moodle 3.9.
+$string['africa/asmera'] = 'Africa/Asmera';
+$string['africa/timbuktu'] = 'Africa/Timbuktu';
+$string['america/argentina/comodrivadavia'] = 'America/Argentina/ComodRivadavia';
+$string['america/indianapolis'] = 'America/Indianapolis';
+$string['america/louisville'] = 'America/Louisville';
+$string['america/montreal'] = 'America/Montreal';
+$string['asia/calcutta'] = 'Asia/Calcutta';
+$string['asia/chongqing'] = 'Asia/Chongqing';
+$string['asia/harbin'] = 'Asia/Harbin';
+$string['asia/kashgar'] = 'Asia/Kashgar';
+$string['asia/katmandu'] = 'Asia/Katmandu';
+$string['asia/rangoon'] = 'Asia/Rangoon';
+$string['asia/saigon'] = 'Asia/Saigon';
+$string['atlantic/faeroe'] = 'Atlantic/Faeroe';
+$string['europe/belfast'] = 'Europe/Belfast';
+$string['pacific/ponape'] = 'Pacific/Ponape';
+$string['pacific/truk'] = 'Pacific/Truk';
 $string['pacific/yap'] = 'Pacific/Yap';
diff --git a/lib/classes/event/user_password_policy_failed.php b/lib/classes/event/user_password_policy_failed.php
new file mode 100644 (file)
index 0000000..18bee65
--- /dev/null
@@ -0,0 +1,89 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Password policy failed event.
+ *
+ * @package    core
+ * @copyright  2020 Peter Burnett <peterburnett@catalyst-au.net>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Event when user's current password fails the password policy
+ *
+ * @package    core
+ * @since      Moodle 3.9
+ * @copyright  2020 Peter Burnett <peterburnett@catalyst-au.net>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class user_password_policy_failed extends base {
+    /**
+     * Create event for user's current password failing password policy.
+     *
+     * @param \stdClass $user
+     * @return user_password_updated
+     */
+    public static function create_from_user(\stdClass $user) {
+        $data = array(
+            'context' => \context_user::instance($user->id),
+            'userid' => $user->id,
+            'relateduserid' => $user->id,
+        );
+        $event = self::create($data);
+        $event->add_record_snapshot('user', $user);
+        return $event;
+    }
+
+    /**
+     * Initialise required event data properties.
+     */
+    protected function init() {
+        $this->data['crud'] = 'r';
+        $this->data['edulevel'] = self::LEVEL_OTHER;
+    }
+
+    /**
+     * Returns localised event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventuserpasswordpolicyfailed');
+    }
+
+    /**
+     * Returns non-localised event description with id's for admin use only.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "The password for user with id '$this->userid' failed the current password policy.";
+    }
+
+    /**
+     * Returns relevant URL.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        return new \moodle_url('/user/profile.php', array('id' => $this->userid));
+    }
+}
index 8f86592..078190b 100644 (file)
@@ -4398,7 +4398,7 @@ function guest_user() {
  * @return stdClass|false A {@link $USER} object or false if error
  */
 function authenticate_user_login($username, $password, $ignorelockout=false, &$failurereason=null, $logintoken=false) {
-    global $CFG, $DB;
+    global $CFG, $DB, $PAGE;
     require_once("$CFG->libdir/authlib.php");
 
     if ($user = get_complete_user_data('username', $username, $CFG->mnet_localhost_id)) {
@@ -4512,6 +4512,42 @@ function authenticate_user_login($username, $password, $ignorelockout=false, &$f
             continue;
         }
 
+        // Before performing login actions, check if user still passes password policy, if admin setting is enabled.
+        if (!empty($CFG->passwordpolicycheckonlogin)) {
+            $errmsg = '';
+            $passed = check_password_policy($password, $errmsg, $user);
+            if (!$passed) {
+                // First trigger event for failure.
+                $failedevent = \core\event\user_password_policy_failed::create_from_user($user);
+                $failedevent->trigger();
+
+                // If able to change password, set flag and move on.
+                if ($authplugin->can_change_password()) {
+                    // Check if we are on internal change password page, or service is external, don't show notification.
+                    $internalchangeurl = new moodle_url('/login/change_password.php');
+                    if (!($PAGE->has_set_url() && $internalchangeurl->compare($PAGE->url)) && $authplugin->is_internal()) {
+                        \core\notification::error(get_string('passwordpolicynomatch', '', $errmsg));
+                    }
+                    set_user_preference('auth_forcepasswordchange', 1, $user);
+                } else if ($authplugin->can_reset_password()) {
+                    // Else force a reset if possible.
+                    \core\notification::error(get_string('forcepasswordresetnotice', '', $errmsg));
+                    redirect(new moodle_url('/login/forgot_password.php'));
+                } else {
+                    $notifymsg = get_string('forcepasswordresetfailurenotice', '', $errmsg);
+                    // If support page is set, add link for help.
+                    if (!empty($CFG->supportpage)) {
+                        $link = \html_writer::link($CFG->supportpage, $CFG->supportpage);
+                        $link = \html_writer::tag('p', $link);
+                        $notifymsg .= $link;
+                    }
+
+                    // If no change or reset is possible, add a notification for user.
+                    \core\notification::error($notifymsg);
+                }
+            }
+        }
+
         // Successful authentication.
         if ($user->id) {
             // User already exists in database.
@@ -4980,7 +5016,8 @@ function get_complete_user_data($field, $value, $mnethostid = null, $throwexcept
  *
  * @param string $password the password to be checked against the password policy
  * @param string $errmsg the error message to display when the password doesn't comply with the policy.
- * @param stdClass $user the user object to perform password validation against. Defaults to null if not provided
+ * @param stdClass $user the user object to perform password validation against. Defaults to null if not provided.
+ *
  * @return bool true if the password is valid according to the policy. false otherwise.
  */
 function check_password_policy($password, &$errmsg, $user = null) {
index e59e383..f0a3d67 100644 (file)
@@ -335,6 +335,75 @@ class core_authlib_testcase extends advanced_testcase {
         $this->assertEquals(AUTH_LOGIN_OK, $reason);
 
         ini_set('error_log', $oldlog);
+
+        // Test password policy check on login.
+        $CFG->passwordpolicy = 0;
+        $CFG->passwordpolicycheckonlogin = 1;
+
+        // First test with password policy disabled.
+        $user4 = $this->getDataGenerator()->create_user(array('username' => 'username4', 'password' => 'a'));
+        $sink = $this->redirectEvents();
+        $reason = null;
+        $result = authenticate_user_login('username4', 'a', false, $reason);
+        $events = $sink->get_events();
+        $sink->close();
+        $notifications = \core\notification::fetch();
+        $this->assertInstanceOf('stdClass', $result);
+        $this->assertEquals(AUTH_LOGIN_OK, $reason);
+        $this->assertEquals(get_user_preferences('auth_forcepasswordchange', false, $result), false);
+        // Check no events.
+        $this->assertEquals(count($events), 0);
+        // Check no notifications.
+        $this->assertEquals(count($notifications), 0);
+
+        // Now test with the password policy enabled, flip reset flag.
+        $sink = $this->redirectEvents();
+        $reason = null;
+        $CFG->passwordpolicy = 1;
+        $result = authenticate_user_login('username4', 'a', false, $reason);
+        $events = $sink->get_events();
+        $sink->close();
+        $this->assertInstanceOf('stdClass', $result);
+        $this->assertEquals(AUTH_LOGIN_OK, $reason);
+        $this->assertEquals(get_user_preferences('auth_forcepasswordchange', true, $result), true);
+        // Check that an event was emitted for the policy failure.
+        $this->assertEquals(count($events), 1);
+        $this->assertEquals(reset($events)->eventname, '\core\event\user_password_policy_failed');
+        // Check notification fired.
+        $notifications = \core\notification::fetch();
+        $this->assertEquals(count($notifications), 1);
+
+        // Now the same tests with a user that passes the password policy.
+        $user5 = $this->getDataGenerator()->create_user(array('username' => 'username5', 'password' => 'ThisPassword1sSecure!'));
+        $reason = null;
+        $CFG->passwordpolicy = 0;
+        $sink = $this->redirectEvents();
+        $result = authenticate_user_login('username5', 'ThisPassword1sSecure!', false, $reason);
+        $events = $sink->get_events();
+        $sink->close();
+        $notifications = \core\notification::fetch();
+        $this->assertInstanceOf('stdClass', $result);
+        $this->assertEquals(AUTH_LOGIN_OK, $reason);
+        $this->assertEquals(get_user_preferences('auth_forcepasswordchange', false, $result), false);
+        // Check no events.
+        $this->assertEquals(count($events), 0);
+        // Check no notifications.
+        $this->assertEquals(count($notifications), 0);
+
+        $reason = null;
+        $CFG->passwordpolicy = 1;
+        $sink = $this->redirectEvents();
+        $result = authenticate_user_login('username5', 'ThisPassword1sSecure!', false, $reason);
+        $events = $sink->get_events();
+        $sink->close();
+        $notifications = \core\notification::fetch();
+        $this->assertInstanceOf('stdClass', $result);
+        $this->assertEquals(AUTH_LOGIN_OK, $reason);
+        $this->assertEquals(get_user_preferences('auth_forcepasswordchange', false, $result), false);
+        // Check no events.
+        $this->assertEquals(count($events), 0);
+        // Check no notifications.
+        $this->assertEquals(count($notifications), 0);
     }
 
     public function test_user_loggedin_event_exceptions() {
index 2cd0d9f..702124e 100644 (file)
@@ -166,6 +166,14 @@ class core_date_testcase extends advanced_testcase {
         }
     }
 
+    public function test_timezone_lang_strings() {
+        $phpzones = DateTimeZone::listIdentifiers();
+        $manager = get_string_manager();
+        foreach ($phpzones as $tz) {
+            $this->assertTrue($manager->string_exists(strtolower($tz), 'core_timezones'));
+        }
+    }
+
     public function test_get_localised_timezone() {
         $this->resetAfterTest();