From a694e6d6ed4af6a42363c9b5e73279e5dc81b71d Mon Sep 17 00:00:00 2001 From: Ilya Tregubov Date: Mon, 5 Feb 2024 15:22:32 +0800 Subject: [PATCH] MDL-80599 enrol_guest: Support password update in csv course upload --- .../uploadcourse/tests/behat/guest.feature | 104 ++++++++++++++++++ .../tests/fixtures/enrolment_guest.csv | 4 + enrol/guest/lib.php | 43 ++++++++ enrol/guest/tests/lib_test.php | 74 +++++++++++++ 4 files changed, 225 insertions(+) create mode 100644 admin/tool/uploadcourse/tests/behat/guest.feature create mode 100644 admin/tool/uploadcourse/tests/fixtures/enrolment_guest.csv diff --git a/admin/tool/uploadcourse/tests/behat/guest.feature b/admin/tool/uploadcourse/tests/behat/guest.feature new file mode 100644 index 0000000000000..b01892ffaa749 --- /dev/null +++ b/admin/tool/uploadcourse/tests/behat/guest.feature @@ -0,0 +1,104 @@ +@tool @tool_uploadcourse @_file_upload +Feature: An admin can create courses with guest enrolments using a CSV file + In order to create courses using a CSV file with guest enrolment + As an admin + I need to be able to upload a CSV file and navigate through the import process + + Background: + Given the following "categories" exist: + | name | category | idnumber | + | Cat 0 | 0 | CAT0 | + | Cat 1 | CAT0 | CAT1 | + And the following "courses" exist: + | fullname | shortname | category | + | Course 1 | C1 | CAT1 | + And I log in as "admin" + And I navigate to "Courses > Upload courses" in site administration + And I set the field "Upload mode" to "Create new courses, or update existing ones" + And I set the field "Update mode" to "Update with CSV data only" + + @javascript + Scenario: Validation of password for uploaded courses with guest enrolments + # usepasswordpolicy is not set. + Given I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_guest.csv" file to "File" filemanager + And I click on "Preview" "button" + And I click on "Upload courses" "button" + And I should see "Courses created: 2" + And I should see "Courses updated: 1" + And I should see "Courses errors: 0" + And I am on the "Course 1" "enrolment methods" page + And I click on "Edit" "link" in the "Guest access" "table_row" + And the field "Password" matches value "test" + And I press "Cancel" + And I click on "Delete" "link" in the "Guest access" "table_row" + And I press "Continue" + And I am on the "Course 2" "enrolment methods" page + And I click on "Edit" "link" in the "Guest access" "table_row" + And the field "Password" matches value "" + And I press "Cancel" + And I click on "Delete" "link" in the "Guest access" "table_row" + And I press "Continue" + And I am on the "Course 3" "enrolment methods" page + And I click on "Edit" "link" in the "Guest access" "table_row" + And the field "Password" matches value "Test123@" + And I press "Cancel" + And I click on "Delete" "link" in the "Guest access" "table_row" + And I press "Continue" + + # Policy is used, but password not required so it will not be generated if omitted. + And the following config values are set as admin: + | config | value | plugin | + | usepasswordpolicy | 1 | enrol_guest | + And I navigate to "Courses > Upload courses" in site administration + And I set the field "Upload mode" to "Create new courses, or update existing ones" + And I set the field "Update mode" to "Update with CSV data only" + And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_guest.csv" file to "File" filemanager + And I click on "Preview" "button" + And I should see "Passwords must be at least 8 characters long." + And I should see "Passwords must have at least 1 digit(s)." + And I should see "Passwords must have at least 1 upper case letter(s)." + And I should see "The password must have at least 1 special character(s) such as *, -, or #." + And I click on "Upload courses" "button" + And I should see "Courses created: 0" + And I should see "Courses updated: 2" + And I should see "Courses errors: 1" + And I am on the "Course 1" "enrolment methods" page + And "Guest access" "table_row" should not exist + And I am on the "Course 2" "enrolment methods" page + And I click on "Edit" "link" in the "Guest access" "table_row" + And the field "Password" matches value "" + And I press "Cancel" + And I click on "Delete" "link" in the "Guest access" "table_row" + And I press "Continue" + And I am on the "Course 3" "enrolment methods" page + And I click on "Edit" "link" in the "Guest access" "table_row" + And the field "Password" matches value "Test123@" + And I press "Cancel" + And I click on "Delete" "link" in the "Guest access" "table_row" + And I press "Continue" + + # Policy is used and password not required so it will be generated if omitted. + And the following config values are set as admin: + | config | value | plugin | + | requirepassword | 1 | enrol_guest | + And I navigate to "Courses > Upload courses" in site administration + And I set the field "Upload mode" to "Create new courses, or update existing ones" + And I set the field "Update mode" to "Update with CSV data only" + And I upload "admin/tool/uploadcourse/tests/fixtures/enrolment_guest.csv" file to "File" filemanager + And I click on "Preview" "button" + And I should see "Passwords must be at least 8 characters long." + And I should see "Passwords must have at least 1 digit(s)." + And I should see "Passwords must have at least 1 upper case letter(s)." + And I should see "The password must have at least 1 special character(s) such as *, -, or #." + And I click on "Upload courses" "button" + And I should see "Courses created: 0" + And I should see "Courses updated: 2" + And I should see "Courses errors: 1" + And I am on the "Course 1" "enrolment methods" page + And "Guest access" "table_row" should not exist + And I am on the "Course 2" "enrolment methods" page + And I click on "Edit" "link" in the "Guest access" "table_row" + And the field "Password" does not match value "" + And I am on the "Course 3" "enrolment methods" page + And I click on "Edit" "link" in the "Guest access" "table_row" + And the field "Password" matches value "Test123@" diff --git a/admin/tool/uploadcourse/tests/fixtures/enrolment_guest.csv b/admin/tool/uploadcourse/tests/fixtures/enrolment_guest.csv new file mode 100644 index 0000000000000..7fd3872d53761 --- /dev/null +++ b/admin/tool/uploadcourse/tests/fixtures/enrolment_guest.csv @@ -0,0 +1,4 @@ +shortname,fullname,category_idnumber,enrolment_1,enrolment_1_role,enrolment_1_password +C1,Course 1,CAT1,guest,student,test +C2,Course 2,CAT1,guest,student, +C3,Course 3,CAT1,guest,student,Test123@ diff --git a/enrol/guest/lib.php b/enrol/guest/lib.php index 66e2465e845ff..ae53617efb602 100644 --- a/enrol/guest/lib.php +++ b/enrol/guest/lib.php @@ -529,6 +529,49 @@ public function find_instance(array $enrolmentdata, int $courseid): ?stdClass { public function fill_enrol_custom_fields(array $enrolmentdata, int $courseid): array { return $enrolmentdata + ['password' => '']; } + + /** + * Updates enrol plugin instance with provided data. + * @param int $courseid Course ID. + * @param array $enrolmentdata enrolment data. + * @param stdClass $instance Instance to update. + * + * @return stdClass updated instance + */ + public function update_enrol_plugin_data(int $courseid, array $enrolmentdata, stdClass $instance): stdClass { + if (!empty($enrolmentdata['password'])) { + $instance->password = $enrolmentdata['password']; + } + return parent::update_enrol_plugin_data($courseid, $enrolmentdata, $instance); + } + + /** + * Check if data is valid for a given enrolment plugin + * + * @param array $enrolmentdata enrolment data to validate. + * @param int|null $courseid Course ID. + * @return array Errors + */ + public function validate_enrol_plugin_data(array $enrolmentdata, ?int $courseid = null): array { + global $CFG; + + // If password is omitted or empty in csv it will be generated automatically if it is a required policy. + + $errors = parent::validate_enrol_plugin_data($enrolmentdata, $courseid); + + $policy = $this->get_config('usepasswordpolicy'); + if (!empty($enrolmentdata['password']) && $policy) { + $errmsg = ''; + $errarray = []; + if (!check_password_policy($enrolmentdata['password'], $errmsg, null, $errarray)) { + foreach ($errarray as $err) { + $passwordconfig = str_replace('error', '', $err); + $errors[$err] = new lang_string($err, 'auth', $CFG->$passwordconfig); + } + } + } + return $errors; + } } /** diff --git a/enrol/guest/tests/lib_test.php b/enrol/guest/tests/lib_test.php index 1a35b5801b764..09666bd39276e 100644 --- a/enrol/guest/tests/lib_test.php +++ b/enrol/guest/tests/lib_test.php @@ -26,6 +26,80 @@ class lib_test extends \advanced_testcase { + /** + * Test the behaviour of validate_enrol_plugin_data(). + * + * @covers ::validate_enrol_plugin_data + */ + public function test_validate_enrol_plugin_data(): void { + global $CFG; + + $this->resetAfterTest(); + + $guestplugin = enrol_get_plugin('guest'); + + $guestplugin->set_config('usepasswordpolicy', false); + $enrolmentdata = []; + $errors = $guestplugin->validate_enrol_plugin_data($enrolmentdata); + $this->assertEmpty($errors); + + // Now enable some controls, and check that the policy responds with policy text. + $guestplugin->set_config('usepasswordpolicy', true); + $CFG->minpasswordlength = 8; + $CFG->minpassworddigits = 1; + $CFG->minpasswordlower = 1; + $CFG->minpasswordupper = 1; + $CFG->minpasswordnonalphanum = 1; + $CFG->maxconsecutiveidentchars = 1; + $errors = $guestplugin->validate_enrol_plugin_data($enrolmentdata); + // If password is omitted it will be autocreated so nothing to validate. + $this->assertEmpty($errors); + + $enrolmentdata = ['password' => 'test']; + $errors = $guestplugin->validate_enrol_plugin_data($enrolmentdata); + $this->assertArrayHasKey('errorminpasswordlength', $errors); + $this->assertArrayHasKey('errorminpassworddigits', $errors); + $this->assertArrayHasKey('errorminpasswordlength', $errors); + $this->assertArrayHasKey('errorminpasswordupper', $errors); + $this->assertArrayHasKey('errorminpasswordlength', $errors); + $this->assertArrayHasKey('errorminpasswordnonalphanum', $errors); + $this->assertArrayNotHasKey('errorminpasswordlower', $errors); + $this->assertArrayNotHasKey('errormaxconsecutiveidentchars', $errors); + + $enrolmentdata = ['password' => 'Testingtest123@']; + $errors = $guestplugin->validate_enrol_plugin_data($enrolmentdata); + $this->assertEmpty($errors); + } + + /** + * Test the behaviour of update_enrol_plugin_data(). + * + * @covers ::update_enrol_plugin_data + */ + public function test_update_enrol_plugin_data(): void { + global $DB; + $this->resetAfterTest(); + $manualplugin = enrol_get_plugin('guest'); + + $admin = get_admin(); + $this->setUser($admin); + + $enrolmentdata = []; + + $cat = $this->getDataGenerator()->create_category(); + $course = $this->getDataGenerator()->create_course(['category' => $cat->id, 'shortname' => 'ANON']); + $instance = $DB->get_record('enrol', ['courseid' => $course->id, 'enrol' => 'guest'], '*', MUST_EXIST); + + $expectedinstance = $instance; + $modifiedinstance = $manualplugin->update_enrol_plugin_data($course->id, $enrolmentdata, $instance); + $this->assertEquals($expectedinstance, $modifiedinstance); + + $enrolmentdata['password'] = 'test'; + $expectedinstance->password = 'test'; + $modifiedinstance = $manualplugin->update_enrol_plugin_data($course->id, $enrolmentdata, $instance); + $this->assertEquals($expectedinstance, $modifiedinstance); + } + /** * Test the behaviour of find_instance(). *