Skip to content
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

Merged
merged 16 commits into from Mar 1, 2021
Merged

User edit permission split #2620

merged 16 commits into from Mar 1, 2021

Conversation

tankerkiller125
Copy link
Contributor

**Fixes #1965 **

Changes proposed in this pull request:

  • Currently only prevents non-admins from editing admin
  • Splits the edit.user permission into three permissions (username, credentials, groups)

Reviewers should focus on:

  • Is this the best way of doing this?

Screenshot

image

Confirmed

  • Frontend changes: tested on a local Flarum installation.
  • Backend changes: tests are green (run composer test).

@tankerkiller125 tankerkiller125 changed the title Initial user permission split User edit permission split Feb 21, 2021
Copy link
Sponsor Member

@askvortsov1 askvortsov1 left a 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

js/src/admin/components/PermissionGrid.js Outdated Show resolved Hide resolved
src/Api/Serializer/UserSerializer.php Outdated Show resolved Hide resolved
src/User/Command/EditUserHandler.php Outdated Show resolved Hide resolved
js/src/admin/components/PermissionGrid.js Show resolved Hide resolved
locale/core.yml Outdated Show resolved Hide resolved
locale/core.yml Outdated Show resolved Hide resolved
js/src/common/models/User.js Outdated Show resolved Hide resolved
Copy link
Sponsor Member

@askvortsov1 askvortsov1 left a 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

Copy link
Sponsor Member

@askvortsov1 askvortsov1 left a 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

@@ -82,7 +82,8 @@ public function handle(EditUser $command)
}
}

if ($actor->isAdmin() && ! empty($attributes['isEmailConfirmed'])) {
if (!empty($attributes['isEmailConfirmed'])) {
Copy link
Sponsor Member

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));
Copy link
Sponsor Member

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?

Copy link
Member

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(),
]
[
Copy link
Sponsor Member

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

@askvortsov1
Copy link
Sponsor Member

We also added restrictions that stop non-admins from editing credentials of admins, and stop non-admins from promoting/demoting admins

Copy link
Sponsor Member

@askvortsov1 askvortsov1 left a 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),
Copy link
Sponsor Member

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.

Copy link
Contributor Author

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.

Copy link
Sponsor Member

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.

Copy link
Member

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 ?

Copy link
Sponsor Member

@askvortsov1 askvortsov1 left a 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.

Copy link
Member

@SychO9 SychO9 left a 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.
    flarum lan_u_SychO (1)

  • 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).
    flarum lan_u_simonisbailey

  • 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).

locale/core.yml Outdated Show resolved Hide resolved
'joinTime' => $this->formatDate($user->joined_at),
'discussionCount' => (int) $user->discussion_count,
'commentCount' => (int) $user->comment_count,
'canEdit' => $this->actor->can('edit', $user),
Copy link
Member

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 ?

@askvortsov1
Copy link
Sponsor Member

  • The EditUserModal is empty if the only permission given to the user is edit attributes.

Edited to show a message

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 ?

Renamed everything to editUser for consistency.

All the other stuff has been fixed, at least based on the tests I ran locally.

@askvortsov1 askvortsov1 requested a review from SychO9 March 1, 2021 03:43
Copy link
Member

@SychO9 SychO9 left a 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.

Comment on lines 90 to 92
if (isset($attributes['password'])) {
$actor->assertPermission($canEdit);
$actor->assertPermission($actor->can('editCredentials', $user));
$user->changePassword($attributes['password']);
Copy link
Member

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.

Copy link
Sponsor Member

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

Copy link
Member

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));
Copy link
Member

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 ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Users with edit user permission can make themselves admins
4 participants