-
Notifications
You must be signed in to change notification settings - Fork 452
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
pkp/pkp-lib#10290 whether current user can log in as reviewer/participant #10477
pkp/pkp-lib#10290 whether current user can log in as reviewer/participant #10477
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.
Thanks! Looks good. I left 2 comments.
classes/security/Validation.php
Outdated
|
||
if ($unmanagedContextsExist) { | ||
// check if partial administration is allowed in the current context | ||
$isManagerInCurrentContext = $contextId !== null && |
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.
Have you tested this? in my case $contextId
is null here. Steps to debug:
- Use test dataset
- Create a second journal
- Assign a user with a sub editor role another role in the second journal
- Go to settings -> users in the first journal by a user that has only manager role in that journal.
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.
@Vitaliy-1 what is the concern here ? I tested it and seems works fine . So I have create a new Editor and Reviewer and also assigned to a second journal as Editor and Reviewer . Then from the first journal, As JM only assigned to first journal , logged in and they are visible with following restriction
- Can not disable as these 2 user has association to multi context .
- can only edit the role assignment for the first journal as JM is only associated with the first journal .
I am bit confused about the part in my case $contextId is null here
. Also I did not test with the fresh datasets .
@Hafsa-Naeem can you test this behaviour with importing the latest datasets ?
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.
Thanks, @touhidurabir. What is current business logic behind login as
and partial administration? Who can administer whom and what additional actions could/couldn't be done, e.g., changing the role, merging users... Is there a good description somewhere?
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.
@Vitaliy-1 please see this #10477 (comment) .
classes/security/Validation.php
Outdated
->filterByContextIds([\PKP\core\PKPApplication::SITE_CONTEXT_ID]) | ||
->filterByRoleIds([Role::ROLE_ID_SITE_ADMIN]) | ||
->filterByUserIds([$administratorUserId]) | ||
->getCount() > 0; |
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.
Can we use here and in other places exists
?
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.
@Hafsa-Naeem I agree with @Vitaliy-1 . As you are refactoring this method implementation , we can replace all of the getCount()
with exists
as following manner
Repo::userGroup()
->getCollector()
->filterByContextIds([\PKP\core\PKPApplication::SITE_CONTEXT_ID])
->filterByRoleIds([Role::ROLE_ID_SITE_ADMIN])
->filterByUserIds([$administeredUserId])
->getQueryBuilder()
->exists();
exists
is much more performant that getCount or count
as when querying if SQL records using exists instead of using count. exists is much more efficient and breaks out of the loop when first record is found.
@touhidurabir, you are probably the most familiar with this new code regarding |
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.
@Hafsa-Naeem I have added few reviews , please see those . But most importantly, the proposed changes for Login As
in this PR, I am not sure if that the right behaviour we want to have .
Also I think what we really need is someway to let the frontend now if the current user can do the login as
for few given users in reviewers grid or stage participants grid. I think we should aim to provide those details via API to allow frontend to show the right options .
classes/security/Validation.php
Outdated
->filterByContextIds([\PKP\core\PKPApplication::SITE_CONTEXT_ID]) | ||
->filterByRoleIds([Role::ROLE_ID_SITE_ADMIN]) | ||
->filterByUserIds([$administratorUserId]) | ||
->getCount() > 0; |
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.
@Hafsa-Naeem I agree with @Vitaliy-1 . As you are refactoring this method implementation , we can replace all of the getCount()
with exists
as following manner
Repo::userGroup()
->getCollector()
->filterByContextIds([\PKP\core\PKPApplication::SITE_CONTEXT_ID])
->filterByRoleIds([Role::ROLE_ID_SITE_ADMIN])
->filterByUserIds([$administeredUserId])
->getQueryBuilder()
->exists();
exists
is much more performant that getCount or count
as when querying if SQL records using exists instead of using count. exists is much more efficient and breaks out of the loop when first record is found.
( | ||
Validation::isSiteAdmin() || | ||
$currentUser->hasRole([Role::ROLE_ID_MANAGER], $contextId) | ||
) |
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.
@Hafsa-Naeem as you sure this is working as it's intended to ? In my case it does not allow me logged in as that user .
Also I think this is kind if dangerous as it's allow a user form a context to logged in as another user who has role in multiple context which goes against the logic of \PKP\security\Validation::getAdministrationLevel()
. So I am really concern about this change .
@asmecher @Vitaliy-1 your thought on this ?
Specially @asmecher , I am pretty sure that when I worked on #7391, we both agreed that except for a Role::ROLE_ID_SITE_ADMIN
, one with only role in particular context can not do the following
- disable
- edit user information except the role management to current journal
- login as
when the target user has role associated with multiple context .
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.
@Hafsa-Naeem after some discussion on this with @asmecher , I think this is dangerous and it will break the user administration restriction when a user as association in multiple context .
I think we should allow to pass this information via API by deciding if the user can administrate the target users by using query like what has been proposed by @asmecher at #10290 (comment) . The API can accept an optional param as to decide if response need this details and enable front end to show the proper options based on the response .
( | ||
Validation::isSiteAdmin() || | ||
$currentUser->hasRole([Role::ROLE_ID_MANAGER], $contextId) | ||
) && |
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 same concern as above
( | ||
Validation::isSiteAdmin() || | ||
$user->hasRole([Role::ROLE_ID_MANAGER], $contextId) | ||
) |
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.
Again , the same concern as above as a JM only role in one context can logged in as another user who as role association in multiple context .
pages/login/LoginHandler.php
Outdated
@@ -413,7 +413,7 @@ public function signInAsUser($args, $request) | |||
$templateMgr->assign([ | |||
'pageTitle' => 'manager.people', | |||
'errorMsg' => 'manager.people.noAdministrativeRights', | |||
'backLink' => $request->url(null, null, 'people', 'all'), | |||
'backLink' => $request->url(null, null, 'people', ['all']), |
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 backLink
url here is wrong as it's generate the url as BASE_URL/index.php/publicknowledge/en/login/people/all
. It's not that important but if we want to show a link to get back to all enrolled users , it should be something like BASE_URL/index.php/publicknowledge/en/management/settings/access
.
I see that we currently have $request->url(null, null, 'people', 'all')
which is also wrong . However this login as can happens a few places and we need to construct the backLink
of that page from where the Login As
action initiated .
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.
@Hafsa-Naeem I have added 2 comments . Please those .
However, I am not sure if this enough to accommodate the requirement of what the issue describe . If I am not mistaken, currently frontend can not determine from the API response if the current logged in user has certain permissions (e.g. log in as
, merge user
etc) to perform actions on a user if has multiple context association . We need some way to pass that optional information with response at certain end points only when necessary for frontend to make the decisions to show those options .
classes/security/Validation.php
Outdated
Repo::userGroup() | ||
->getCollector() | ||
->filterByContextIds([$contextId]) | ||
->filterByUserIds([$administratorUserId]) | ||
->filterByRoleIds([Role::ROLE_ID_MANAGER]) | ||
->getCount()) { | ||
->getCount() > 0; |
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.
Here can we also use exists
?
pages/login/LoginHandler.php
Outdated
@@ -413,7 +413,7 @@ public function signInAsUser($args, $request) | |||
$templateMgr->assign([ | |||
'pageTitle' => 'manager.people', | |||
'errorMsg' => 'manager.people.noAdministrativeRights', | |||
'backLink' => $request->url(null, null, 'people', 'all'), | |||
'backLink' => $request->url(null, 'management', 'settings', ['access']), |
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 is ok but ideally if we can, we would like to redirect back to url where where it's redirected here . so for example, this login in as can also happens from a submission workflow page (like dashboard/editorial?currentViewId=active&workflowSubmissionId=78
) and the back link should take back to there . Here we are setting the back link always to user's management access page . Though I am not sure if this is possible with our current page routing infrastructure .
$currentUser = $request->getUser(); | ||
$targetUserId = $this->getId(); | ||
$context = $request->getContext(); | ||
$contextId = $context ? $context->getId() : null; // Use null if context is not available |
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.
instead of null
, maybe we should use Application::SITE_CONTEXT_ID
|
||
// Get the context (journal) to check for the Manager role | ||
$context = $request->getContext(); | ||
$contextId = $context ? $context->getId() : 0; |
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.
instead of 0
, we should use Application::SITE_CONTEXT_ID
that point to a site as there can be no context with ID 0
.
@@ -109,10 +109,13 @@ public function initialize($request, $template = null) | |||
$this->addAction(new NotifyLinkAction($request, $submission, $stageId, $userId)); | |||
|
|||
$user = $request->getUser(); | |||
$contextId = $context ? $context->getId() : 0; // 0 in case context is not available |
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.
@touhidurabir I am also working on this issue#10637. I think it's related to your comment. Could you please have a look at this PR too? |
fb2dce8
to
2f83076
Compare
13ba70f
to
1f1962a
Compare
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.
Thanks! I left just minor comments
classes/security/Validation.php
Outdated
->exists(); | ||
foreach ($allUserGroups as $userGroup) { | ||
$roleId = $userGroup->roleId; | ||
$ctxId = $userGroup->contextId ?? 0; |
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.
Let's name it $userGroupContextId
.
Also, what does 0
mean here? Should it be PKPApplication::SITE_CONTEXT_ID
?
classes/security/Validation.php
Outdated
->withActive(); | ||
// single query to fetch user groups assigned to either user | ||
$allUserGroups = UserGroup::query() | ||
->whereHas('userUserGroups', function ($q) use ($administratorUserId, $administeredUserId) { |
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 can replace this anonymous function and the one below with a shorthand fn() => ...
classes/security/Validation.php
Outdated
$adminUserRoles = [ | ||
'siteAdmin' => false, | ||
'managerContexts' => [], | ||
'anyRoleContexts' => [], |
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.
It isn't used anywhere
|
||
if ($isAdministratorUserSiteAdmin) { | ||
// then each user assignment row | ||
foreach ($userGroup->userUserGroups as $uug) { |
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 we can do it shorter. Let's say we define 2 arrays:
/*
* [
* context ID => $roles
* ]
*/
$administeredUserRoles = [];
$administratorUserRoles = [];
foreach ($allUserGroups as $userGroup) {
foreach ($userGroup->userUserGroups as $uug) {
if ($uug->userId === $administratorUserId) {
$administratorUserRoles[inval($userGroup->contextId)][] => $userGroup->roleId
} elseif ($uug->userId === $administeredUserId) {
$administeredUserRoles[inval($userGroup->contextId)][] => $userGroup->roleId
}
}
}
// check if administered user is an admin
// check if administrator user is an admin
// check if administrator user is a manager and conflicting contexts
classes/submission/maps/Schema.php
Outdated
$reviewerId, | ||
$currentUser->getId(), | ||
// pass null for a site-wide check, or submission->getData('contextId') for a context check | ||
null |
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.
Null is passed as a default argument here
classes/submission/maps/Schema.php
Outdated
return []; | ||
} | ||
|
||
// collect all users assigned to this submission |
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.
Multiline comment
/*
*
*/
classes/submission/maps/Schema.php
Outdated
$usersIterator = Repo::user() | ||
->getCollector() | ||
->filterByContextIds([$context->getId()]) | ||
->assignedTo($submission->getId(), null /* or pass a stageId if desired */) |
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.
Null is passed by default as a second argument
one-line comment
//
classes/user/maps/Schema.php
Outdated
$canAdminister = Validation::getAdministrationLevel($targetUser->getId(), $currentUser->getId()) === Validation::ADMINISTRATION_FULL; | ||
|
||
// allow merging if the current user is a site admin or has full admin rights over the target user | ||
return Validation::isSiteAdmin() || $canAdminister; |
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 site admin role be already covered by the Validation::getAdministrationLevel()
check?
{ | ||
// You can administer yourself | ||
if ($administeredUserId == $administratorUserId) { | ||
public static function getAdministrationLevel( |
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.
Can you check if indent on empty lines in your environment is the same as in the project?
1f1962a
to
daecb7b
Compare
1b23cb3
to
a58446c
Compare
…anGossip, canMergeUser to schemas for a consistent API response
a58446c
to
e8cfd4c
Compare
for #10290