Closed ajnyga closed 1 year ago
(ps. in the long run it would be maybe good if the code would refer to Context Managers and Section Managers instead of journal specific lingo)
I think this does not work as expected: https://github.com/pkp/pkp-lib/blob/main/classes/security/authorization/StageRolePolicy.inc.php#L62
When I click Publish / Post in the UI that condition is never true, because ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES
does not seem to be set at all. $userAccessibleStages
is an empty array.
I can fix that by doing this:
if (in_array($routeName, $this->requiresProductionStageAccess)) { import('lib.pkp.classes.security.authorization.internal.UserAccessibleWorkflowStageRequiredPolicy'); $this->addPolicy(new UserAccessibleWorkflowStageRequiredPolicy($request)); import('lib.pkp.classes.security.authorization.StageRolePolicy'); $this->addPolicy(new StageRolePolicy($this->productionStageAccessRoles, WORKFLOW_STAGE_ID_PRODUCTION, false)); }
Here: https://github.com/pkp/pkp-lib/blob/main/api/v1/submissions/PKPSubmissionHandler.inc.php#L173-L176
Basically setting the missing ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES
constant. Probably there are other ways of solving this as well.
Thanks @ajnyga, I've been able to reproduce the problem that you describe.
Subeditors (section editors, series editors, moderators) should be allowed to publish as long as their user group is assigned to the production stage (yes by default). It looks like the problem lies in SubmissionAccessPolicy
, which is gets the accessible workflow stages for subeditors but not for managers. There seems to be a problem when there are dual roles, although looking at the code it should apply both policies.
Just a quick note: the fix in this comment will not work because it prevents unassigned managers from publishing.
OJS Authorization Policies ❤️
Does https://github.com/pkp/pkp-lib/blob/main/classes/security/authorization/StageRolePolicy.inc.php#L82 require the empty check? But I am maybe looking at the wrong place for the solution anyway. I got a bit confused first because I had the "can only recommend" setting on for the section editor assigment I first tested this with and that hides the Publish button altogether. Makes sense that by default they can Publish, no problem there.
edit: ah ok, I see SubmissionAccessPolicy is called in the API but as you said, but it does not set the constant properly.
Does https://github.com/pkp/pkp-lib/blob/main/classes/security/authorization/StageRolePolicy.inc.php#L82 require the empty check?
Yes. A manager may be assigned to a submission in a less-than-manager role, so we always apply authorization based on assigned role OR if no role let managers do things.
I've spent some time on this today with no luck. You're right that the problem lies in the fact that ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES
has not been set when StageRolePolicy
is called. I suspect that SubmissionAccessPolicy
is exiting after the first successful policy, which is the manager's policy, and so UserAccessibleWorkflowStageRequiredPolicy
(added for subeditors) is never called. But adding UserAccessibleWorkflowStageRequiredPolicy
to the manager's policy didn't seem to work.
We have started to encounter this problem with several journals after upgrading to 3.2.1. Is there any potential solution for this?
I think there are two problems:
A code-level problem, where ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES
is not available in the authorized context list. As you found, @ajnyga, this is happening because StageRolePolicy
depends on UserAccessibleWorkflowStageRequiredPolicy
but that policy has not been brought into the policy list in api/v1/submissions/PKPSubmissionHandler.inc.php
. I agree with the part of your proposal that adds UserAccessibleWorkflowStageRequiredPolicy
, but @NateWr mentions a follow-up problem which I think can be solved as follows:
diff --git a/classes/security/authorization/SubmissionAccessPolicy.inc.php b/classes/security/authorization/SubmissionAccessPolicy.inc.php
index ee562a821..0302cd7f2 100644
--- a/classes/security/authorization/SubmissionAccessPolicy.inc.php
+++ b/classes/security/authorization/SubmissionAccessPolicy.inc.php
@@ -43,8 +43,13 @@ class SubmissionAccessPolicy extends ContextPolicy {
// Managerial role
//
if (isset($roleAssignments[ROLE_ID_MANAGER])) {
+ $managerSubmissionAccessPolicy = new PolicySet(COMBINING_DENY_OVERRIDES);
+ import('lib.pkp.classes.security.authorization.internal.UserAccessibleWorkflowStageRequiredPolicy');
+ $managerSubmissionAccessPolicy->addPolicy(new UserAccessibleWorkflowStageRequiredPolicy($request));
+
// Managers have access to all submissions.
- $submissionAccessPolicy->addPolicy(new RoleBasedHandlerOperationPolicy($request, ROLE_ID_MANAGER, $roleAssignments[ROLE_ID_MANAGER]));
+ $managerSubmissionAccessPolicy->addPolicy(new RoleBasedHandlerOperationPolicy($request, ROLE_ID_MANAGER, $roleAssignments[ROLE_ID_MANAGER]));
+ $submissionAccessPolicy->addPolicy($managerSubmissionAccessPolicy);
}
//
(@NateWr, I think this is what you were working towards -- but I suspect the missing piece was the additional policy set with COMBINING_DENY_OVERRIDES
, which forces all component policies to agree on a GRANT, rather than picking the first and bailing.)
A business-level problem relating to automatic assignments: because automatic sub editor assignments are always made using the sub editor role, and that role is intentionally of limited functionality, automatic assignments can implicitly limit an editor's ability to work with a submission. I can think of several solutions:
That last solution is obviously bigger in scope but given the amount of confusion about automatic assignments I think it makes sense to solve it all together. See also: https://github.com/pkp/pkp-lib/issues/7103, https://github.com/pkp/pkp-lib/issues/4457 -- especially Nate's comment:
At the risk of blowing this up into something bigger than it needs to be: one of the things I'd like to see is the ability to configure automatic assignments during the editorial workflow. It would be nice if journals could configure, for example, that a Layout Editor is automatically assigned when a submission reaches the Production stage. This would help automate the hand-off, especially to managers/assistants who often need to intervene at some step of the process.
Let's continue that discussion over there but I'm 100% in favour. Without that, automatic assignments are going to be a continued source of confusion and inflexibility.
@ajnyga, just a reminder that this one is assigned to you; it likely won't make 3.3.0-9, FWIW.
@asmecher did some testing with this.
Using COMBINING_DENY_OVERRIDES
like you suggested does not seem to work. I will look closer what is happening there in a couple of days...
Basically the outcome is:
So I could not figure out how to make that example work. However, I have an alternative solution here that seems to work with all the above scenarios. Do you see a problem with that?
https://github.com/pkp/pkp-lib/compare/main...ajnyga:f6875?expand=1
Basically in StageRolePolicy, if userAccessibleStages
is empty AND the user is a manager, you actually already have a check there for cases where managers may have a stage assigment but no accessible stages. The code currently checks if there is a manager assignment and I just added also a check for sub_editor assignments. https://github.com/pkp/pkp-lib/blob/main/classes/security/authorization/StageRolePolicy.php#L106-L108
This seems like a safe change, because you can not even access that loop if you are not a manager or a site admin.
ping @asmecher since you probably know the authorization stuff the best
ping @asmecher one of our journals bumped again to this issue, did you check the solution linked above? If it seems safe, would like to fix it in our server.
Note that this probably can also happen if a journal manager is assigned to a submission using an assistant role, like layout editor. You see the same behaviour: buttons for publishing are visible, but the use of the actions is blocked by the policies.
@ajnyga, sorry for the wait; your proposed change is low-risk as you've noted, and it looks like you've already agreed with @NateWr about the workflow implications. I'm OK to go ahead with https://github.com/pkp/pkp-lib/commit/b1d0f13be698a740e1bd44aeff742ccefe53e0ec.
what do you think about assistant roles? The thing is that I am not 100% what the purpose of the loop is where I am suggesting the changes to. I mean what is the original purpose of it.
Not sure if this is answering your question -- but assistant roles are the catch-all for copyeditors, layout editors, proofreaders, etc. They are press/journal/server "insiders" (i.e. they can't self-register, are expected to be a select few, and can be considered to have a paid/professional relationship with the journal/server/press) but may also be contract roles, i.e. their work may be provided by an external business entity. [edited: yep, this was not helpful :) -AS]
@asmecher @NateWr
I am a bit worried with changing code that to me has an unknown purpose.
I mean these lines: https://github.com/pkp/pkp-lib/blob/main/classes/security/authorization/StageRolePolicy.php#L88-L112
What we are doing now is that since ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES
is not set here
https://github.com/pkp/pkp-lib/blob/main/classes/security/authorization/StageRolePolicy.php#L68
We are basically running $stageAssignmentDao->getBySubmissionAndUserIdAndStageId()
again here
https://github.com/pkp/pkp-lib/blob/main/classes/security/authorization/StageRolePolicy.php#L96
Which is just doing the same what happens when you set ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES
https://github.com/pkp/pkp-lib/blob/d0cd5cc32f94906c5bcac0a59e7ea2b6a03889d9/classes/user/Repository.php#L199
My question is that what is the situation described here? https://github.com/pkp/pkp-lib/blob/main/classes/security/authorization/StageRolePolicy.php#L93-L94
// Managers may have a stage assignment but no $userAccessibleStages, so they will
// not be caught by the earlier code that checks stage assignments.
I am not sure when that happens? In this case it happens just because ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES
is not set but this is clearly not the original purpose of that code?
Also, what does this exception mean: https://github.com/pkp/pkp-lib/blob/main/classes/security/authorization/StageRolePolicy.php#L90-L92
Why do we permit access when allowRecommendOnly
is true?
However, this would fix the situation where a manager is assigned as a section editor or as an assistant. Meaning that for example when a manager is assigned as a layout editor, they can publish as usual. This is also how the UI works, it shows the buttons by overriding based on the manager role instead of the stage assigment role.
I also changed the commenting a bit
// Managers may have a stage assignment but $userAccessibleStages is not set so they will
// not be caught by the earlier code that checks stage assignments.
if (empty($userAccessibleStages) && count(array_intersect([Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN], $this->getAuthorizedContextObject(ASSOC_TYPE_USER_ROLES)))) {
if ($this->_allowRecommendOnly) {
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
// If manager has a stage assignment as an manager, section editor or assistant, allow access with manager permissions
$stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO'); /** @var StageAssignmentDAO $stageAssignmentDao */
$result = $stageAssignmentDao->getBySubmissionAndUserIdAndStageId(
$this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION)->getId(),
Application::get()->getRequest()->getUser()->getId(),
$this->_stageId
);
$userGroupDao = DAORegistry::getDAO('UserGroupDAO'); /** @var UserGroupDAO $userGroupDao */
$noResults = true;
while ($stageAssignment = $result->next()) {
$noResults = false;
$userGroup = $userGroupDao->getById($stageAssignment->getUserGroupId());
if (in_array($userGroup->getRoleId(), [Role::ROLE_ID_MANAGER, Role::ROLE_ID_SUB_EDITOR, Role::ROLE_ID_ASSISTANT]) && !$stageAssignment->getRecommendOnly()) {
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
}
// A manager is granted access when they are not assigned in any other role
if ($noResults) {
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
}
Hi all -- I'm deferring this to 3.3.0-13 as I think pressure to get it solved will probably lead us to breaking either OJS or OPS, which will be an unpleasant surprise for a minor release. I'm going to move ahead with the 3.3.0-12 release, and we can come back to this under a little less time pressure and figure out both an expedient solution to the original OPS problem, and a longer term solution for all apps.
Ok, just want to note that we encounter this in OJS as well.
I've read through this whole issue and I'm not confident that we will find a suitable change to the authorization policies that does not have some side effect. Am I right in thinking that there are three problems?
In OPS, moderators (ROLE_ID_SUBEDITOR
) can not post preprints.
Is this true? It doesn't seem right, since Scielo is making heavy use of moderators right now.
In OJS, journal managers assigned as section editors (ROLE_ID_SUBEDITOR
) without access to the production stage can not publish submissions.
Can someone describe the use case here more clearly? Usually, section editors have access to the production stage so I'm curious what use case you're running into regularly @ajnyga.
In OJS, journals want to configure automated editorial assignments, so people who should be journal managers are assigned as section editors, limiting their permissions. This is a solved problem in 3.4, since it will be possible to configure automated editorial assignments in any editorial role (including assistants).
_1. In OPS, moderators (ROLE_ID_SUBEDITOR) can not post preprints. Is this true? It doesn't seem right, since Scielo is making heavy use of moderators right now._
Yes, this is true. We've been dealing with a manual "workaround" in order to post preprints: a) We have to remove ourselves from the submission as Moderator b) We have to assign ourselves as Server Administrator on the same submission
If we don't do this, we cannot post the preprint. We've been doing this for almost 2 years. It's a bit frustrating.
I tested this in OPS (stable-3_3_0
) and when logged in as a moderator I was able to post a preprint. @alexxxmendonca or @ajnyga can you provide reproduction steps for this problem which include any configuration settings that are necessary to reproduce it?
@NateWr does your user include both moderator and server administrator roles?
The main SciELO user account we use has multiple roles:
For reference, the roles we have are:
It's my understanding that this problem is caused by different permissions, causing conflicts.
The reproduction steps would be:
Thanks, I was able to reproduce this by assigning a moderator who also has a manager role in the context.
I think I may have found a simple solution to this. As @ajnyga figured out, the root of the problem is that when StageRolePolicy
is called, the UserAccessibleWorkflowStageRequiredPolicy
has not been called and therefore $this->getAuthorizedContextObject(ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES)
returns null
. This happens because the PolicySet
in SubmissionAccessPolicy
stops as soon as the first policy passes, which is the managerial override.
To fix this, I moved the check for managers to the bottom of the PolicySet
in SubmissionAccessPolicy
. This PolicySet
uses COMBINING_PERMIT_OVERRIDES
, so it will stop as soon as one policy returns true
. By moving it to the end, we ensure that other policies are checked first. That means UserAccessibleWorkflowStageRequiredPolicy
will be called in every case, except when the user has no stage assignment.
@asmecher can you double-check my logic here? Am I right in thinking it shouldn't cause any side effects to move the manager check lower in the PolicySet
? If this sounds correct to you, I'll do some more thorough testing across multiple apps and permission configurations before merging.
PR:
stable-3_3_0
)main
)Tests:
stable-3_3_0
)main
)Our underlying policy implementation seems to be based on XACML, and unfortunately I don't know much about that standard.
I don't think moving the manager check lower in the PolicySet
will have negative side effects, but only because they have been coded so far (I think) so that the order doesn't matter. Starting to introduce ordering adds a whole new layer to an already byzantine structure, so while it might be a good expedient fix for stable-3_3_0
, I'd like to keep thinking for main
.
How about separating the creation of ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES
from the enforcement of conditions on it? We already have a bit of a hierarchy:
UserAccessibleWorkflowStageRequiredPolicy
, which
ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES
andUserAccessibleWorkflowStagePolicy
, which kind of reiterates UserAccessibleWorkflowStageRequiredPolicy
but checking a specific stageQueryUserAccessibleWorkflowStageRequiredPolicy
, which extends UserAccessibleWorkflowStageRequiredPolicy
's requirements by adding checks on review assignments.If UserAccessibleWorkflowStageRequiredPolicy
got split into two classes, one creating ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES
and another implementing checks on it, then the first could be added to the manager check to make the dependency explicit. (main
also has a case for ROLE_ID_SITE_ADMIN
that presumably would need this too.) The other couple of classes could extend the appropriate base class, I suppose.
Did you imagine this for 3.3 and 3.4? Or a change to implement early in the 3.5 dev cycle? Personally, I don't feel comfortable making this kind of change to the policies this late in the 3.4 dev cycle. When I worked on the dual-role access permissions in #3130 I got burned by the SubmissionAccessPolicy
in half a dozen unexpected ways.
Fair enough; do you want to test a bit further with the rearrangement of policies for stable-3_3_0
and 3.4 main
, and file some of this detail for work in 3.5? (Watch out for the admin condition in main
-- presumably it'll require the same treatment.)
Thanks, I've done some testing today and not found any side effects. I've prepared PRs for the minimal change and filed an issue with the more comprehensive refactor at https://github.com/pkp/pkp-lib/issues/8557.
Merged to main
and stable-3_3_0
. This fix will be included in 3.3.0-14 and later. Thanks everyone for helping solve a tricky one! :tada:
A shared problem in all applications 3.3.0 but most visible in OPS. This was reported by @alexxxmendonca and their team (feel free to add any additional information that might be missing here).
In 3.3.0 a Journal editor / Server manager can be blocked from publishing a new submission if she has a stage assigment as a Section editor / Moderator. Because of the Manager role she can still see the Publish / Post button in the UI but the API will return a user.authorization.accessibleWorkflowStage error. This is because the Section Editor stage assigment does not allow the user to publish.
This has to work differently in OPS where we definitely want to allow Moderators the ability to Post new submission. That is their main functionality in the software. I think this was possible in 3.2 but has been removed somewhere along the way.
So fix number 1 would be: allow OPS moderators to post.
We also need to reconsider how this works in OJS. Many journals use the Section editor role for Journal editors as well, because of the automatic assignments functionality (see https://github.com/pkp/pkp-lib/issues/4457). This will probably lead to a lot of situations where the Publishing functionality is shown for the Journal editor but blocked because of the Section editor assignment. This will definitely cause a lot of confusion when journals start to upgrade to 3.3. in bigger numbers.
Fix number 2 would be: in OJS allow the Journal Manager role override the "can the user publish" check that is being applied here.
OR do we expect that Section Editors in OJS can also Publish? I think in 3.1. only editors could do that?
ping @NateWr @asmecher