From 7068dd3990b810975360c7779a628404e6f09a62 Mon Sep 17 00:00:00 2001 From: Andrew Hancox Date: Fri, 15 Jun 2018 13:09:33 +0100 Subject: [PATCH] MDL-62663 core_user: Enforce viewalldetails capability when editing user --- user/profile/lib.php | 94 +++++++++---- user/tests/behat/custom_profile_fields.feature | 188 +++++++++++++++++++++++++ 2 files changed, 253 insertions(+), 29 deletions(-) create mode 100644 user/tests/behat/custom_profile_fields.feature diff --git a/user/profile/lib.php b/user/profile/lib.php index 29cb7b73504..25b719d1640 100644 --- a/user/profile/lib.php +++ b/user/profile/lib.php @@ -22,9 +22,23 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define ('PROFILE_VISIBLE_ALL', '2'); // Only visible for users with moodle/user:update capability. -define ('PROFILE_VISIBLE_PRIVATE', '1'); // Either we are viewing our own profile or we have moodle/user:update capability. -define ('PROFILE_VISIBLE_NONE', '0'); // Only visible for moodle/user:update capability. +/** + * Visible to anyone who can view the user. + * Editable by the profile owner if they have the moodle/user:editownprofile capability + * or any user with the moodle/user:update capability. + */ +define('PROFILE_VISIBLE_ALL', '2'); +/** + * Visible to the profile owner or anyone with the moodle/user:viewalldetails capability. + * Editable by the profile owner if they have the moodle/user:editownprofile capability + * or any user with moodle/user:viewalldetails and moodle/user:update capabilities. + */ +define('PROFILE_VISIBLE_PRIVATE', '1'); +/** + * Only visible to users with the moodle/user:viewalldetails capability. + * Only editable by users with the moodle/user:viewalldetails and moodle/user:update capabilities. + */ +define('PROFILE_VISIBLE_NONE', '0'); /** * Base class for the customisable profile fields. @@ -131,15 +145,14 @@ class profile_field_base { * @return bool */ public function edit_field($mform) { - if ($this->field->visible != PROFILE_VISIBLE_NONE - or has_capability('moodle/user:update', context_system::instance())) { - - $this->edit_field_add($mform); - $this->edit_field_set_default($mform); - $this->edit_field_set_required($mform); - return true; + if (!$this->is_editable()) { + return false; } - return false; + + $this->edit_field_add($mform); + $this->edit_field_set_default($mform); + $this->edit_field_set_required($mform); + return true; } /** @@ -148,12 +161,12 @@ class profile_field_base { * @return bool */ public function edit_after_data($mform) { - if ($this->field->visible != PROFILE_VISIBLE_NONE - or has_capability('moodle/user:update', context_system::instance())) { - $this->edit_field_set_locked($mform); - return true; + if (!$this->is_editable()) { + return false; } - return false; + + $this->edit_field_set_locked($mform); + return true; } /** @@ -430,6 +443,31 @@ class profile_field_base { } /** + * Check if the field data is editable for the current user + * This method should not generally be overwritten by child classes. + * @return bool + */ + public function is_editable() { + global $USER; + + if (!$this->is_visible()) { + return false; + } + + $systemcontext = context_system::instance(); + + if ($this->userid == $USER->id && has_capability('moodle/user:editownprofile', $systemcontext)) { + return true; + } + + if (has_capability('moodle/user:update', $systemcontext)) { + return true; + } + + return false; + } + + /** * Check if the field data is considered empty * @internal This method should not generally be overwritten by child classes. * @return boolean @@ -565,27 +603,25 @@ function profile_load_data($user) { * @param int $userid id of user whose profile is being edited. */ function profile_definition($mform, $userid = 0) { - global $CFG, $DB; - - // If user is "admin" fields are displayed regardless. - $update = has_capability('moodle/user:update', context_system::instance()); - $categories = profile_get_user_fields_with_data_by_category($userid); foreach ($categories as $categoryid => $fields) { // Check first if *any* fields will be displayed. - $display = false; + $fieldstodisplay = []; + foreach ($fields as $formfield) { - if ($formfield->is_visible()) { - $display = true; + if ($formfield->is_editable()) { + $fieldstodisplay[] = $formfield; } } + if (empty($fieldstodisplay)) { + continue; + } + // Display the header and the fields. - if ($display or $update) { - $mform->addElement('header', 'category_'.$categoryid, format_string($formfield->get_category_name())); - foreach ($fields as $formfield) { - $formfield->edit_field($mform); - } + $mform->addElement('header', 'category_'.$categoryid, format_string($fields[0]->get_category_name())); + foreach ($fieldstodisplay as $formfield) { + $formfield->edit_field($mform); } } } diff --git a/user/tests/behat/custom_profile_fields.feature b/user/tests/behat/custom_profile_fields.feature new file mode 100644 index 00000000000..9359113dba9 --- /dev/null +++ b/user/tests/behat/custom_profile_fields.feature @@ -0,0 +1,188 @@ +@core @core_user +Feature: Custom profile fields should be visible and editable by those with the correct permissions. + + Background: Attempting to self-register as a new user with empty names + Given the following "users" exist: + | username | firstname | lastname | email | + | userwithinformation | userwithinformation | 1 | userwithinformation@example.com | + And the following "courses" exist: + | fullname | shortname | category | groupmode | + | Course 1 | C1 | 0 | 1 | + And the following "course enrolments" exist: + | user | course | role | + | userwithinformation | C1 | student | + + And I log in as "admin" + And I navigate to "User profile fields" node in "Site administration > Users > Accounts" + And I set the field "datatype" to "Text input" + And I set the following fields to these values: + | Short name | notvisible_field | + | Name | notvisible_field | + | Who is this field visible to? | Not visible | + And I click on "Save changes" "button" + + And I set the field "datatype" to "Text input" + And I set the following fields to these values: + | Short name | uservisible_field | + | Name | uservisible_field | + | Who is this field visible to? | Visible to user | + And I click on "Save changes" "button" + + And I set the field "datatype" to "Text input" + And I set the following fields to these values: + | Short name | everyonevisible_field | + | Name | everyonevisible_field | + | Who is this field visible to? | Visible to everyone | + And I click on "Save changes" "button" + + And I navigate to "Browse list of users" node in "Site administration > Users > Accounts" + And I click on ".icon[title=Edit]" "css_element" in the "userwithinformation@example.com" "table_row" + And I expand all fieldsets + And I set the field "notvisible_field" to "notvisible_field_information" + And I set the field "uservisible_field" to "uservisible_field_information" + And I set the field "everyonevisible_field" to "everyonevisible_field_information" + And I click on "Update profile" "button" + And I log out + + @javascript + Scenario: User with moodle/user:update but without moodle/user:viewalldetails can only update visible profile fields. + Given the following "roles" exist: + | name | shortname | description | archetype | + | Update Users | updateusers | updateusers | | + And the following "permission overrides" exist: + | capability | permission | role | contextlevel | reference | + | moodle/user:update | Allow | updateusers | System | | + And the following "users" exist: + | username | firstname | lastname | email | + | user_updateusers | updateusers | 1 | updateusers@example.com | + And the following "role assigns" exist: + | user | role | contextlevel | reference | + | user_updateusers | updateusers | System | | + And the following "course enrolments" exist: + | user | course | role | + | user_updateusers | C1 | editingteacher | + And I log in as "user_updateusers" + And I am on "Course 1" course homepage + And I navigate to course participants + And I follow "userwithinformation 1" + + Then I should see "everyonevisible_field" + And I should see "everyonevisible_field_information" + And I should not see "uservisible_field" + And I should not see "uservisible_field_information" + And I should not see "notvisible_field" + And I should not see "notvisible_field_information" + And I follow "Edit profile" + And the following fields match these values: + | everyonevisible_field | everyonevisible_field_information | + And I should not see "uservisible_field" + And I should not see "notvisible_field" + + @javascript + Scenario: User with moodle/user:viewalldetails but without moodle/user:update can view all profile fields. + Given the following "roles" exist: + | name | shortname | description | archetype | + | View All Details | viewalldetails | viewalldetails | | + And the following "permission overrides" exist: + | capability | permission | role | contextlevel | reference | + | moodle/user:viewalldetails | Allow | viewalldetails | System | | + And the following "users" exist: + | username | firstname | lastname | email | + | user_viewalldetails | viewalldetails | 1 | viewalldetails@example.com | + And the following "role assigns" exist: + | user | role | contextlevel | reference | + | user_viewalldetails | viewalldetails | System | | + And the following "course enrolments" exist: + | user | course | role | + | user_viewalldetails | C1 | editingteacher | + And I log in as "user_viewalldetails" + And I am on "Course 1" course homepage + And I navigate to course participants + And I follow "userwithinformation 1" + + Then I should see "everyonevisible_field" + And I should see "everyonevisible_field_information" + And I should see "uservisible_field" + And I should see "uservisible_field_information" + And I should see "notvisible_field" + And I should see "notvisible_field_information" + And I should not see "Edit profile" + + @javascript + Scenario: User with moodle/user:viewalldetails and moodle/user:update capabilities can view and edit all profile fields. + Given the following "roles" exist: + | name | shortname | description | archetype | + | View All Details and Update Users | viewalldetailsandupdateusers | viewalldetailsandupdateusers | | + And the following "permission overrides" exist: + | capability | permission | role | contextlevel | reference | + | moodle/user:viewalldetails | Allow | viewalldetailsandupdateusers | System | | + | moodle/user:update | Allow | viewalldetailsandupdateusers | System | | + And the following "users" exist: + | username | firstname | lastname | email | + | user_viewalldetailsandupdateusers | viewalldetailsandupdateusers | 1 | viewalldetailsandupdateusers@example.com | + And the following "role assigns" exist: + | user | role | contextlevel | reference | + | user_viewalldetailsandupdateusers | viewalldetailsandupdateusers | System | | + And the following "course enrolments" exist: + | user | course | role | + | user_viewalldetailsandupdateusers | C1 | editingteacher | + And I log in as "user_viewalldetailsandupdateusers" + And I am on "Course 1" course homepage + And I navigate to course participants + And I follow "userwithinformation 1" + + Then I should see "everyonevisible_field" + And I should see "everyonevisible_field_information" + And I should see "uservisible_field" + And I should see "uservisible_field_information" + And I should see "notvisible_field" + And I should see "notvisible_field_information" + And I follow "Edit profile" + And the following fields match these values: + | everyonevisible_field | everyonevisible_field_information | + | uservisible_field | uservisible_field_information | + | notvisible_field | notvisible_field_information | + + @javascript + Scenario: Users can view and edit custom profile fields except those marked as not visible. + Given I log in as "userwithinformation" + And I follow "Profile" in the user menu + + Then I should see "everyonevisible_field" + And I should see "everyonevisible_field_information" + And I should see "uservisible_field" + And I should see "uservisible_field_information" + And I should not see "notvisible_field" + And I should not see "notvisible_field_information" + + And I click on "Edit profile" "link" in the "region-main" "region" + Then the following fields match these values: + | everyonevisible_field | everyonevisible_field_information | + | uservisible_field | uservisible_field_information | + And I should not see "notvisible_field" + And I should not see "notvisible_field_information" + + @javascript + Scenario: Users can view but not edit custom profile fields when denied the edit own profile capability. + Given the following "roles" exist: + | name | shortname | description | archetype | + | Deny editownprofile | denyeditownprofile | denyeditownprofile | | + + And the following "permission overrides" exist: + | capability | permission | role | contextlevel | reference | + | moodle/user:editownprofile | Prohibit | denyeditownprofile | System | | + And the following "role assigns" exist: + | user | role | contextlevel | reference | + | userwithinformation | denyeditownprofile | System | | + + And I log in as "userwithinformation" + And I follow "Profile" in the user menu + + Then I should see "everyonevisible_field" + And I should see "everyonevisible_field_information" + And I should see "uservisible_field" + And I should see "uservisible_field_information" + And I should not see "notvisible_field" + And I should not see "notvisible_field_information" + + And I should not see "Edit profile" \ No newline at end of file -- 2.11.4.GIT