-
-
Notifications
You must be signed in to change notification settings - Fork 835
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
User edit permission split #2620
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also needs some integration tests for EditUserController
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code looks fine, but since it's dealing with API stuff (and quite important API stuff at that) it still needs integration tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note for reviewers: the vast majority of changes is tests
src/User/Command/EditUserHandler.php
Outdated
@@ -82,7 +82,8 @@ public function handle(EditUser $command) | |||
} | |||
} | |||
|
|||
if ($actor->isAdmin() && ! empty($attributes['isEmailConfirmed'])) { | |||
if (!empty($attributes['isEmailConfirmed'])) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change allows us to more cleanly test editing the user
$newGroupIds = []; | ||
foreach ($relationships['groups']['data'] as $group) { | ||
if ($id = Arr::get($group, 'id')) { | ||
$newGroupIds[] = $id; | ||
} | ||
} | ||
|
||
$adminChanged = in_array('1', array_diff($oldGroupIds, $newGroupIds)) || in_array('1', array_diff($newGroupIds, $oldGroupIds)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be moved out to a guard (like SelfDemotionGuard)? We decided to leave it here since that felt simpler. Alternatively, we could move SelfDemotionGuard here for simplicity?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't feel strongly about this, but I wouldn't remove the SelfDemotionGuard either, maybe just leave things as is ?
@@ -26,7 +27,24 @@ protected function setUp(): void | |||
$this->prepareDatabase([ | |||
'users' => [ | |||
$this->normalUser(), | |||
] | |||
[ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We decided to put this here and not in RetrievesAuthorizedUsers
since it's only used in one test case
We also added restrictions that stop non-admins from editing credentials of admins, and stop non-admins from promoting/demoting admins |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On a side note, when this is merged it should be squashed into 3 commits: one for the split, the other 2 for the other restrictions introduced.
'joinTime' => $this->formatDate($user->joined_at), | ||
'discussionCount' => (int) $user->discussion_count, | ||
'commentCount' => (int) $user->comment_count, | ||
'canEdit' => $this->actor->can('edit', $user), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be serialized as canEditAttributes? I think that might be more consistent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I didn't do that to prevent breaking extensions that might use canEdit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're not losing the method on the frontend user model, so we'd be preserving this for API consumers. But the meaning of the value has changed, so I'm not sure it's worth it to preserve; renaming would force consumers to adapt to the new system.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should rename this, it's quiet confusing right now what the difference between canEdit
and canEditAttributes
is, on the frontend canEdit
is true if the user has the ability to edit any of the three new permissions, on the backend (serialization) it just refers to the canEditAttributes
ability.
Are we gonna keep canEdit
the way it is on the frontend ? (ie any of the three abilities), I'm assuming we're deprecating it, if so, can we comment on the frontend model that it's deprecated please ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Temporarily removing approval as per #2620 (comment), we should either rename the value returned by the API to canEditAttributes
, or rename the frontend methods to canEdit
(and use something else for the combined method name).
The second option might actually be better; that way, we are consistent across permission name, frontend, and backend.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The backend code looks solid, but the frontend can be improved, here are some issues I noticed while testing the permissions:
-
The EditUserModal is empty if the only permission given to the user is edit attributes.
-
By giving the user only permission to edit credentials, they can open the EditUserModal on normal members, but can't actually change credentials. (haven't looked too deep into it, but the frontend seems to still send an array of groups relationship which naturally fails).
-
Giving a user edit credentials permission does not allow to edit an admin's credentials (as it should) but once the user is given edit attributes permission, they can open the admin's EditUserModal (it fails on edit of course, because the backend seems solid in the behaviour).
-
If a user have permission to edit groups only, and they try to edit groups (anything but the admin group ofc) they're not allowed to (because the username is included in the payload).
'joinTime' => $this->formatDate($user->joined_at), | ||
'discussionCount' => (int) $user->discussion_count, | ||
'commentCount' => (int) $user->comment_count, | ||
'canEdit' => $this->actor->can('edit', $user), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should rename this, it's quiet confusing right now what the difference between canEdit
and canEditAttributes
is, on the frontend canEdit
is true if the user has the ability to edit any of the three new permissions, on the backend (serialization) it just refers to the canEditAttributes
ability.
Are we gonna keep canEdit
the way it is on the frontend ? (ie any of the three abilities), I'm assuming we're deprecating it, if so, can we comment on the frontend model that it's deprecated please ?
Edited to show a message
Renamed everything to All the other stuff has been fixed, at least based on the tests I ran locally. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code looks good, did some tests locally as well.
src/User/Command/EditUserHandler.php
Outdated
if (isset($attributes['password'])) { | ||
$actor->assertPermission($canEdit); | ||
$actor->assertPermission($actor->can('editCredentials', $user)); | ||
$user->changePassword($attributes['password']); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
off topic observation: we force users to use the change password button (reset password email) on the frontend but not on the backend, so they could just send a payload and directly edit it.
I don't know if this was done on purpose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't disagree, but this PR already contains a lot so I'll leave this alone for now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, I wouldn't want that changed here anyway :P was an observation as it looked odd.
$newGroupIds = []; | ||
foreach ($relationships['groups']['data'] as $group) { | ||
if ($id = Arr::get($group, 'id')) { | ||
$newGroupIds[] = $id; | ||
} | ||
} | ||
|
||
$adminChanged = in_array('1', array_diff($oldGroupIds, $newGroupIds)) || in_array('1', array_diff($newGroupIds, $oldGroupIds)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't feel strongly about this, but I wouldn't remove the SelfDemotionGuard either, maybe just leave things as is ?
**Fixes #1965 **
Changes proposed in this pull request:
Reviewers should focus on:
Screenshot
Confirmed
composer test
).