first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-06-08 17:09:23 -04:00
commit df3a033196
17887 changed files with 8637778 additions and 0 deletions
@@ -0,0 +1,97 @@
<?php
/**
* @file classes/security/authorization/internal/ApiAuthorizationMiddleware.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class ApiAuthorizationMiddleware
*
* @ingroup security_authorization
*
* @brief Slim middleware which enforces authorization policies
*/
namespace PKP\security\authorization\internal;
use PKP\core\APIResponse;
use PKP\core\JSONMessage;
use PKP\handler\APIHandler;
use Slim\Http\Request as SlimRequest;
class ApiAuthorizationMiddleware
{
/** @var APIHandler $handler Reference to api handler */
protected $_handler = null;
/**
* Constructor
*
*/
public function __construct(APIHandler $handler)
{
$this->_handler = $handler;
}
/**
* Handles authorization
*
* @param SlimRequest $slimRequest
*
* @return bool|string
*/
protected function _authorize($slimRequest)
{
// share SlimRequest with Handler
$this->_handler->setSlimRequest($slimRequest);
$request = $this->_handler->getRequest();
$args = [$slimRequest];
if (!$slimRequest->getAttribute('route')) {
return $request->getRouter()->handleAuthorizationFailure($request, 'api.404.endpointNotFound');
} elseif ($this->_handler->authorize($request, $args, $this->_handler->getRoleAssignments())) {
$this->_handler->validate(null, $request);
$this->_handler->initialize($request);
return true;
} else {
$authorizationMessage = $this->_handler->getLastAuthorizationMessage();
if ($authorizationMessage == '') {
$authorizationMessage = 'api.403.unauthorized';
}
$router = $request->getRouter();
$result = $router->handleAuthorizationFailure($request, $authorizationMessage);
switch (1) {
case is_string($result): return $result;
case $result instanceof JSONMessage: return $result->getString();
default:
assert(false);
return null;
}
}
}
/**
* Middleware invokable function
*
* @param SlimRequest $request request
* @param APIResponse $response response
* @param callable $next Next middleware
*
* @return bool|string|APIResponse
*/
public function __invoke($request, $response, $next)
{
$result = $this->_authorize($request);
if ($result !== true) {
return $result;
}
$response = $next($request, $response);
return $response;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\ApiAuthorizationMiddleware', '\ApiAuthorizationMiddleware');
}
@@ -0,0 +1,96 @@
<?php
/**
* @file classes/security/authorization/internal/ApiCsrfMiddleware.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class ApiCsrfMiddleware
*
* @ingroup security_authorization
*
* @brief Slim middleware which requires a CSRF token for POST, PUT and DELETE
* operations whenever an API Token is not in use.
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use PKP\core\APIResponse;
use PKP\handler\APIHandler;
use Slim\Http\Request as SlimRequest;
class ApiCsrfMiddleware
{
/** @var APIHandler $handler Reference to api handler */
protected $_handler = null;
/**
* Constructor
*
*/
public function __construct(APIHandler $handler)
{
$this->_handler = $handler;
}
/**
* Middleware invokable function
*
* @param SlimRequest $slimRequest request
* @param APIResponse $response response
* @param callable $next Next middleware
*
* @return APIResponse
*/
public function __invoke($slimRequest, $response, $next)
{
if ($this->_isCSRFRequired($slimRequest) && !$this->_isCSRFValid($slimRequest)) {
return $response->withJson([
'error' => 'form.csrfInvalid',
'errorMessage' => __('form.csrfInvalid'),
], 403);
}
$response = $next($slimRequest, $response);
return $response;
}
/**
* Check if a CSRF token is required
*
* @param SlimRequest $slimRequest
*
* @return bool
*/
protected function _isCSRFRequired($slimRequest)
{
if ($this->_handler->getApiToken()) {
return false;
}
$server = $slimRequest->getServerParams();
return !empty($server['REQUEST_METHOD']) && in_array($server['REQUEST_METHOD'], ['POST', 'PUT', 'DELETE']);
}
/**
* Check if the CSRF token is present and valid
*
* @param SlimRequest $slimRequest
*
* @return bool
*/
protected function _isCSRFValid($slimRequest)
{
$server = $slimRequest->getServerParams();
if (empty($server['HTTP_X_CSRF_TOKEN'])) {
return false;
}
$session = Application::get()->getRequest()->getSession();
return $session && $session->getCSRFToken() === $server['HTTP_X_CSRF_TOKEN'];
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\ApiCsrfMiddleware', '\ApiCsrfMiddleware');
}
@@ -0,0 +1,171 @@
<?php
/**
* @file classes/security/authorization/internal/ApiTokenDecodingMiddleware.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class ApiTokenDecodingMiddleware
*
* @ingroup security_authorization
*
* @brief Slim middleware which decodes and validates JSON Web Tokens
*/
namespace PKP\security\authorization\internal;
use Exception;
use Firebase\JWT\JWT;
use Firebase\JWT\SignatureInvalidException;
use PKP\config\Config;
use PKP\core\APIResponse;
use PKP\handler\APIHandler;
use Slim\Http\Request as SlimRequest;
class ApiTokenDecodingMiddleware
{
/** @var APIHandler $handler Reference to api handler */
protected $_handler = null;
/**
* Constructor
*
*/
public function __construct(APIHandler $handler)
{
$this->_handler = $handler;
}
/**
* Decodes the request's JSON Web Token
*
* @param SlimRequest $slimRequest
*
* @return bool|string
*/
protected function _decode($slimRequest)
{
try {
$jwt = $this->getJWT($slimRequest);
} catch (Exception $e) {
$request = $this->_handler->getRequest();
return $request->getRouter()
->handleAuthorizationFailure(
$request,
$e->getMessage()
);
}
if (!$jwt) {
/**
* If we don't have a token, it's for the authentication logic to handle if it's a problem.
*/
return true;
}
$secret = Config::getVar('security', 'api_key_secret', '');
if (!$secret) {
$request = $this->_handler->getRequest();
return $request->getRouter()
->handleAuthorizationFailure(
$request,
'api.500.apiSecretKeyMissing'
);
}
try {
$apiToken = JWT::decode($jwt, $secret, ['HS256']);
/**
* Compatibility with old API keys
*
* @link https://github.com/pkp/pkp-lib/issues/6462
*/
if (substr($apiToken, 0, 2) === '""') {
$apiToken = json_decode($apiToken);
}
$this->_handler->setApiToken($apiToken);
} catch (Exception $e) {
/**
* If JWT decoding fails, it throws an 'UnexpectedValueException'.
* If JSON decoding fails (of the JWT payload), it throws a 'DomainException'.
* If token couldn't verified, it throws a 'SignatureInvalidException'.
*/
if ($e instanceof SignatureInvalidException) {
$request = $this->_handler->getRequest();
return $request->getRouter()
->handleAuthorizationFailure(
$request,
'api.400.invalidApiToken'
);
}
if ($e instanceof \UnexpectedValueException ||
$e instanceof \DomainException
) {
$request = $this->_handler->getRequest();
return $request->getRouter()
->handleAuthorizationFailure(
$request,
'api.400.tokenCouldNotBeDecoded',
[
'error' => $e->getMessage()
]
);
}
throw $e;
}
return true;
}
/**
* Middleware invokable function
*
* @param SlimRequest $request request
* @param APIResponse $response response
* @param callable $next Next middleware
*
* @return bool|string|APIResponse
*/
public function __invoke($request, $response, $next)
{
$result = $this->_decode($request);
if ($result !== true) {
return $result;
}
$response = $next($request, $response);
return $response;
}
protected function getJWT(SlimRequest $slimRequest)
{
$authHeader = $slimRequest->getHeader('Authorization');
if (!count($authHeader) || empty($authHeader[0])) {
// DEPRECATED as of 3.4.0. Use the Authorization header.
return $slimRequest->getQueryParam('apiToken');
}
// Several authorization methods may be supplied with commas between them.
// For example: Basic basic_auth_string_here, Bearer api_key_here
// JWT uses the Bearer scheme with an API key. Ignore the others.
$clauses = explode(',', $authHeader[0]);
foreach ($clauses as $clause) {
// Split the authorization scheme and parameters and look for the Bearer scheme.
$parts = explode(' ', trim($clause));
if (count($parts) == 2 && $parts[0] == 'Bearer') {
// Found bearer authorization; return the token.
return $parts[1];
}
}
return null;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\ApiTokenDecodingMiddleware', '\ApiTokenDecodingMiddleware');
}
@@ -0,0 +1,41 @@
<?php
/**
* @file classes/security/authorization/internal/ContextPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class ContextPolicy
*
* @ingroup security_authorization_internal
*
* @brief Basic policy that ensures availability of a context in
* the request context and a valid user group. All context based policies
* extend this policy.
*/
namespace PKP\security\authorization\internal;
use PKP\security\authorization\ContextRequiredPolicy;
use PKP\security\authorization\PolicySet;
class ContextPolicy extends PolicySet
{
/**
* Constructor
*
* @param \PKP\core\PKPRequest $request
*/
public function __construct($request)
{
parent::__construct();
// Ensure we're in a context
$this->addPolicy(new ContextRequiredPolicy($request, 'user.authorization.noContext'));
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\ContextPolicy', '\ContextPolicy');
}
@@ -0,0 +1,104 @@
<?php
/**
* @file classes/security/authorization/internal/DecisionAllowedPolicy.php
*
* Copyright (c) 2014-2022 Simon Fraser University
* Copyright (c) 2000-2022 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class DecisionAllowedPolicy
*
* @ingroup security_authorization_internal
*
* @brief Checks whether a user is allowed to take the authorized decision
* on the authorized submission. Also relies on authorized roles.
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use APP\facades\Repo;
use PKP\db\DAORegistry;
use PKP\security\authorization\AuthorizationPolicy;
use PKP\security\Role;
use PKP\stageAssignment\StageAssignmentDAO;
use PKP\user\User;
class DecisionAllowedPolicy extends AuthorizationPolicy
{
protected ?User $user;
/**
* Constructor
*
*/
public function __construct(?User $user)
{
parent::__construct('editor.submission.workflowDecision.disallowedDecision');
$this->user = $user;
}
/**
* @see AuthorizationPolicy::effect()
*/
public function effect()
{
if (!$this->user) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
$decisionType = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_DECISION_TYPE);
$stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO'); /** @var StageAssignmentDAO $stageAssignmentDao */
$result = $stageAssignmentDao->getBySubmissionAndUserIdAndStageId($submission->getId(), $this->user->getId(), $submission->getData('stageId'));
$stageAssignments = $result->toArray();
if (empty($stageAssignments)) {
$userRoles = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_USER_ROLES);
$canAccessUnassignedSubmission = !empty(array_intersect([Role::ROLE_ID_SITE_ADMIN, Role::ROLE_ID_MANAGER], $userRoles));
if ($canAccessUnassignedSubmission) {
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
} else {
$this->setAdvice(self::AUTHORIZATION_ADVICE_DENY_MESSAGE, 'editor.submission.workflowDecision.noUnassignedDecisions');
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
} else {
$isAllowed = false;
foreach ($stageAssignments as $stageAssignment) {
$userGroup = Repo::userGroup()->get($stageAssignment->getUserGroupId());
if (!in_array($userGroup->getRoleId(), [Role::ROLE_ID_MANAGER, Role::ROLE_ID_SUB_EDITOR])) {
continue;
}
if (Repo::decision()->isRecommendation($decisionType->getDecision()) && $stageAssignment->getRecommendOnly()) {
$isAllowed = true;
} elseif (!$stageAssignment->getRecommendOnly()) {
$isAllowed = true;
}
// Check whether there is a decision that a recommending role can make on the stage the submission is in.
$recommendatorsAvailableDecisions = Repo::decision()
->getDecisionTypesMadeByRecommendingUsers($submission->getData('stageId'));
// if there is any decision that the recommending role is allowed to make, check if the current decision is within the allowed ones
if (!empty($recommendatorsAvailableDecisions)) {
$matches = array_filter($recommendatorsAvailableDecisions, function ($decisionInArray) use ($decisionType) {
return $decisionInArray->getDecision() === $decisionType->getDecision();
});
if (!empty($matches)) {
$isAllowed = true;
}
}
}
if ($isAllowed) {
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
}
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\DecisionAllowedPolicy', '\DecisionAllowedPolicy');
}
@@ -0,0 +1,51 @@
<?php
/**
* @file classes/security/authorization/internal/DecisionStageValidPolicy.php
*
* Copyright (c) 2014-2022 Simon Fraser University
* Copyright (c) 2000-2022 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class DecisionStageValidPolicy
*
* @ingroup security_authorization_internal
*
* @brief Checks whether the authorized submission is in the correct stage
* to take the authorized decision
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use PKP\security\authorization\AuthorizationPolicy;
class DecisionStageValidPolicy extends AuthorizationPolicy
{
/**
* Constructor
*
*/
public function __construct()
{
parent::__construct('editor.submission.workflowDecision.invalidStage');
}
/**
* @see AuthorizationPolicy::effect()
*/
public function effect()
{
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
$decisionType = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_DECISION_TYPE);
if ($submission->getData('stageId') === $decisionType->getStageId()) {
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\DecisionStageValidPolicy', '\DecisionStageValidPolicy');
}
@@ -0,0 +1,65 @@
<?php
/**
* @file classes/security/authorization/internal/DecisionTypeRequiredPolicy.php
*
* Copyright (c) 2014-2022 Simon Fraser University
* Copyright (c) 2000-2022 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class DecisionTypeRequiredPolicy
*
* @ingroup security_authorization_internal
*
* @brief Policy that ensures that the requested decision type is valid
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use APP\facades\Repo;
use PKP\core\PKPRequest;
use PKP\decision\DecisionType;
use PKP\security\authorization\AuthorizationPolicy;
use PKP\security\authorization\DataObjectRequiredPolicy;
class DecisionTypeRequiredPolicy extends DataObjectRequiredPolicy
{
protected int $decision;
/**
* Constructor
*
* @param PKPRequest $request
* @param array $args request parameters
* @param int $decision The decision constant to check
*/
public function __construct($request, &$args, int $decision)
{
parent::__construct($request, $args, '', 'editor.submission.workflowDecision.typeInvalid');
$this->decision = $decision;
}
//
// Implement template methods from AuthorizationPolicy
//
/**
* @see DataObjectRequiredPolicy::dataObjectEffect()
*/
public function dataObjectEffect()
{
/** @var ?DecisionType $decisionType */
$decisionType = Repo::decision()->getDecisionType($this->decision);
if (!$decisionType) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
$this->addAuthorizedContextObject(Application::ASSOC_TYPE_DECISION_TYPE, $decisionType);
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\DecisionTypeRequiredPolicy', '\DecisionTypeRequiredPolicy');
}
@@ -0,0 +1,63 @@
<?php
/**
* @file classes/security/authorization/internal/PluginLevelRequiredPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class PluginLevelRequiredPolicy
*
* @ingroup security_authorization_internal
*
* @brief Class to test the plugin level.
*
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use PKP\core\PKPRequest;
use PKP\security\authorization\AuthorizationPolicy;
class PluginLevelRequiredPolicy extends AuthorizationPolicy
{
/** @var bool */
public $_contextPresent;
/**
* Constructor
*
* @param PKPRequest $request
* @param bool $contextPresent
*/
public function __construct($request, $contextPresent)
{
parent::__construct('user.authorization.pluginLevel');
$this->_contextPresent = $contextPresent;
}
//
// Implement template methods from AuthorizationPolicy
//
/**
* @see AuthorizationPolicy::effect()
*/
public function effect()
{
// Get the plugin.
$plugin = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_PLUGIN);
if (!$plugin instanceof \PKP\plugins\Plugin) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
if (!$this->_contextPresent) { // Site context
return $plugin->isSitePlugin() ? AuthorizationPolicy::AUTHORIZATION_PERMIT : AuthorizationPolicy::AUTHORIZATION_DENY;
}
return $plugin->isSitePlugin() ? AuthorizationPolicy::AUTHORIZATION_DENY : AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\PluginLevelRequiredPolicy', '\PluginLevelRequiredPolicy');
}
@@ -0,0 +1,75 @@
<?php
/**
* @file classes/security/authorization/internal/PluginRequiredPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class PluginRequiredPolicy
*
* @ingroup security_authorization_internal
*
* @brief Class to make sure we have a valid plugin in request.
*
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use APP\core\Request;
use PKP\core\PKPRequest;
use PKP\plugins\Plugin;
use PKP\plugins\PluginRegistry;
use PKP\security\authorization\AuthorizationPolicy;
class PluginRequiredPolicy extends AuthorizationPolicy
{
/** @var Request */
public $_request;
/**
* Constructor
*
* @param PKPRequest $request
*/
public function __construct($request)
{
parent::__construct('user.authorization.pluginRequired');
$this->_request = $request;
}
//
// Implement template methods from AuthorizationPolicy
//
/**
* @see AuthorizationPolicy::effect()
*/
public function effect()
{
// Get the plugin request data.
$category = $this->_request->getUserVar('category');
$pluginName = $this->_request->getUserVar('plugin');
// Load the plugin.
$plugins = PluginRegistry::loadCategory($category);
$foundPlugin = null;
foreach ($plugins as $plugin) { /** @var Plugin $plugin */
if ($plugin->getName() == $pluginName) {
$foundPlugin = $plugin;
break;
}
}
if (!$foundPlugin instanceof Plugin) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Add the plugin to the authorized context.
$this->addAuthorizedContextObject(Application::ASSOC_TYPE_PLUGIN, $foundPlugin);
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\PluginRequiredPolicy', '\PluginRequiredPolicy');
}
@@ -0,0 +1,54 @@
<?php
/**
* @file classes/security/authorization/internal/PublicationCanBeEditedPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class PublicationCanBeEditedPolicy
*
* @ingroup security_authorization_internal
*
* @brief Policy to ensure the authorized publication is editable by the given user
*
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use APP\core\Request;
use APP\facades\Repo;
use APP\submission\Submission;
use PKP\security\authorization\AuthorizationPolicy;
use PKP\security\Role;
class PublicationCanBeEditedPolicy extends AuthorizationPolicy
{
/** @var \User */
private $_currentUser;
public function __construct(Request $request, string $message)
{
parent::__construct($message);
$currentUser = $request->getUser();
$this->_currentUser = $currentUser;
}
/**
* @see AuthorizationPolicy::effect()
*/
public function effect()
{
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION); /** @var Submission $submission */
// Prevent users from editing publications if they do not have permission. Except for admins.
$userRoles = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_USER_ROLES);
if (in_array(Role::ROLE_ID_SITE_ADMIN, $userRoles) || Repo::submission()->canEditPublication($submission->getId(), $this->_currentUser->getId())) {
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
}
@@ -0,0 +1,42 @@
<?php
/**
* @file classes/security/authorization/internal/PublicationIsSubmissionPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class PublicationIsSubmissionPolicy
*
* @ingroup security_authorization_internal
*
* @brief Policy to ensure the authorized publication is related to the authorized submission.
*
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use PKP\security\authorization\AuthorizationPolicy;
class PublicationIsSubmissionPolicy extends AuthorizationPolicy
{
/**
* @see AuthorizationPolicy::effect()
*/
public function effect()
{
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
$publication = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_PUBLICATION);
if ($submission && $publication && $submission->getId() === $publication->getData('submissionId')) {
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\PublicationIsSubmissionPolicy', '\PublicationIsSubmissionPolicy');
}
@@ -0,0 +1,82 @@
<?php
/**
* @file classes/security/authorization/internal/PublicationRequiredPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class PublicationRequiredPolicy
*
* @ingroup security_authorization_internal
*
* @brief Policy that ensures that the request contains a valid publication id.
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use APP\facades\Repo;
use PKP\core\PKPRequest;
use PKP\security\authorization\AuthorizationPolicy;
use PKP\security\authorization\DataObjectRequiredPolicy;
class PublicationRequiredPolicy extends DataObjectRequiredPolicy
{
protected ?string $publicationParameterName;
/**
* Constructor
*
* @param PKPRequest $request
* @param array $args request parameters
* @param ?string $publicationParameterName the request parameter we expect
* the submission id in. Pass null to look for the current publication in an authorized
* submission object instead.
* @param null|mixed $operations
*/
public function __construct($request, &$args, $publicationParameterName = null, $operations = null)
{
parent::__construct($request, $args, $publicationParameterName, 'user.authorization.invalidPublication', $operations);
$this->publicationParameterName = $publicationParameterName;
$callOnDeny = [$request->getDispatcher(), 'handle404', []];
$this->setAdvice(
AuthorizationPolicy::AUTHORIZATION_ADVICE_CALL_ON_DENY,
$callOnDeny
);
}
//
// Implement template methods from AuthorizationPolicy
//
/**
* @see DataObjectRequiredPolicy::dataObjectEffect()
*/
public function dataObjectEffect()
{
// Get the publication id from the policy or, if
// no parameter name is passed, look for the current
// publication in an authorized submission object
if ($this->publicationParameterName) {
$publication = Repo::publication()->get((int) $this->getDataObjectId());
} else {
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
if ($submission) {
$publication = $submission->getCurrentPublication();
}
}
if (!isset($publication)) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Save the publication to the authorization context.
$this->addAuthorizedContextObject(Application::ASSOC_TYPE_PUBLICATION, $publication);
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\PublicationRequiredPolicy', '\PublicationRequiredPolicy');
}
@@ -0,0 +1,83 @@
<?php
/**
* @file classes/security/authorization/internal/QueryAssignedToUserAccessPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class QueryAssignedToUserAccessPolicy
*
* @ingroup security_authorization_internal
*
* @brief Class to control access to a query that is assigned to the current user
*
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use PKP\core\PKPRequest;
use PKP\db\DAORegistry;
use PKP\query\QueryDAO;
use PKP\security\authorization\AuthorizationPolicy;
use PKP\security\Role;
class QueryAssignedToUserAccessPolicy extends AuthorizationPolicy
{
/** @var PKPRequest */
public $_request;
/**
* Constructor
*
* @param PKPRequest $request
*/
public function __construct($request)
{
parent::__construct('user.authorization.submissionQuery');
$this->_request = $request;
}
//
// Implement template methods from AuthorizationPolicy
//
/**
* @see AuthorizationPolicy::effect()
*/
public function effect()
{
// A query should already be in the context.
$query = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_QUERY);
if (!$query instanceof \PKP\query\Query) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Check that there is a currently logged in user.
$user = $this->_request->getUser();
if (!$user instanceof \PKP\user\User) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Determine if the query is assigned to the user.
$queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */
if ($queryDao->getParticipantIds($query->getId(), $user->getId())) {
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
// Managers are allowed to access discussions they are not participants in
// as long as they have Manager-level access to the workflow stage
$accessibleWorkflowStages = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES);
$managerAssignments = array_intersect([Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN], $accessibleWorkflowStages[$query->getStageId()] ?? []);
if (!empty($managerAssignments)) {
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
// Otherwise, deny.
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\QueryAssignedToUserAccessPolicy', '\QueryAssignedToUserAccessPolicy');
}
@@ -0,0 +1,82 @@
<?php
/**
* @file classes/security/authorization/internal/QueryRequiredPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class QueryRequiredPolicy
*
* @ingroup security_authorization_internal
*
* @brief Policy that ensures that the request contains a valid query.
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use APP\submission\Submission;
use PKP\core\PKPRequest;
use PKP\db\DAORegistry;
use PKP\query\Query;
use PKP\query\QueryDAO;
use PKP\security\authorization\AuthorizationPolicy;
use PKP\security\authorization\DataObjectRequiredPolicy;
class QueryRequiredPolicy extends DataObjectRequiredPolicy
{
/**
* Constructor
*
* @param PKPRequest $request
* @param array $args request parameters
* @param null|mixed $operations
*/
public function __construct($request, &$args, $parameterName = 'queryId', $operations = null)
{
parent::__construct($request, $args, $parameterName, 'user.authorization.invalidQuery', $operations);
}
//
// Implement template methods from AuthorizationPolicy
//
/**
* @see DataObjectRequiredPolicy::dataObjectEffect()
*/
public function dataObjectEffect()
{
$queryId = (int)$this->getDataObjectId();
if (!$queryId) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Make sure the query belongs to the submission.
$queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */
$query = $queryDao->getById($queryId);
if (!$query instanceof Query) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
switch ($query->getAssocType()) {
case Application::ASSOC_TYPE_SUBMISSION:
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
if (!$submission instanceof Submission) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
if ($query->getAssocId() != $submission->getId()) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
break;
default:
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Save the query to the authorization context.
$this->addAuthorizedContextObject(Application::ASSOC_TYPE_QUERY, $query);
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\QueryRequiredPolicy', '\QueryRequiredPolicy');
}
@@ -0,0 +1,69 @@
<?php
/**
* @file classes/security/authorization/internal/QueryUserAccessibleWorkflowStageRequiredPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class QueryUserAccessibleWorkflowStageRequiredPolicy
*
* @ingroup security_authorization_internal
*
* @brief Policy to extend access to queries to assigned reviewers.
*
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use PKP\db\DAORegistry;
use PKP\security\authorization\AuthorizationPolicy;
use PKP\security\Role;
use PKP\submission\reviewAssignment\ReviewAssignmentDAO;
class QueryUserAccessibleWorkflowStageRequiredPolicy extends UserAccessibleWorkflowStageRequiredPolicy
{
//
// Private helper methods.
//
/**
* @see AuthorizationPolicy::effect()
*/
public function effect()
{
$result = parent::effect();
if ($result === AuthorizationPolicy::AUTHORIZATION_PERMIT) {
return $result;
}
if (!in_array(Role::ROLE_ID_REVIEWER, $this->getAuthorizedContextObject(Application::ASSOC_TYPE_USER_ROLES) ?? [])) {
return $result;
}
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
$reviewAssignmentDao = DAORegistry::getDAO('ReviewAssignmentDAO'); /** @var ReviewAssignmentDAO $reviewAssignmentDao */
$reviewAssignments = $reviewAssignmentDao->getBySubmissionId($submission->getId());
foreach ($reviewAssignments as $reviewAssignment) {
if ($reviewAssignment->getReviewerId() == $this->_request->getUser()->getId()) {
$accessibleWorkflowStages = (array) $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES);
$accessibleWorkflowStages[WORKFLOW_STAGE_ID_INTERNAL_REVIEW] = array_merge(
$accessibleWorkflowStages[WORKFLOW_STAGE_ID_INTERNAL_REVIEW] ?? [],
[Role::ROLE_ID_REVIEWER]
);
$accessibleWorkflowStages[WORKFLOW_STAGE_ID_EXTERNAL_REVIEW] = array_merge(
$accessibleWorkflowStages[WORKFLOW_STAGE_ID_EXTERNAL_REVIEW] ?? [],
[Role::ROLE_ID_REVIEWER]
);
$this->addAuthorizedContextObject(Application::ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES, $accessibleWorkflowStages);
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
}
return $result;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\QueryUserAccessibleWorkflowStageRequiredPolicy', '\QueryUserAccessibleWorkflowStageRequiredPolicy');
}
@@ -0,0 +1,80 @@
<?php
/**
* @file classes/security/authorization/internal/RepresentationRequiredPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class RepresentationRequiredPolicy
*
* @ingroup security_authorization_internal
*
* @brief Policy that ensures that the request contains a valid representation.
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use APP\publication\Publication;
use APP\submission\Submission;
use PKP\core\PKPRequest;
use PKP\security\authorization\AuthorizationPolicy;
use PKP\security\authorization\DataObjectRequiredPolicy;
use PKP\submission\Representation;
class RepresentationRequiredPolicy extends DataObjectRequiredPolicy
{
/**
* Constructor
*
* @param PKPRequest $request
* @param array $args request parameters
* @param null|mixed $operations
*/
public function __construct($request, &$args, $parameterName = 'representationId', $operations = null)
{
parent::__construct($request, $args, $parameterName, 'user.authorization.invalidRepresentation', $operations);
}
//
// Implement template methods from AuthorizationPolicy
//
/**
* @see DataObjectRequiredPolicy::dataObjectEffect()
*/
public function dataObjectEffect()
{
$representationId = (int)$this->getDataObjectId();
if (!$representationId) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Need a valid submission in request.
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
if (!$submission instanceof Submission) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Need a valid publication in request
$publication = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_PUBLICATION);
if (!$publication instanceof Publication) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Make sure the representation belongs to the submission.
$representationDao = Application::getRepresentationDAO();
$representation = $representationDao->getById($representationId, $publication->getId());
if (!$representation instanceof Representation) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Save the representation to the authorization context.
$this->addAuthorizedContextObject(Application::ASSOC_TYPE_REPRESENTATION, $representation);
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\RepresentationRequiredPolicy', '\RepresentationRequiredPolicy');
}
@@ -0,0 +1,105 @@
<?php
/**
* @file classes/security/authorization/internal/RepresentationUploadAccessPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class RepresentationUploadAccessPolicy
*
* @ingroup security_authorization_internal
*
* @brief Policy that checks whether a file can be uploaded to a representation.
* It checks whether the user is allowed to access the representation file stage,
* whether the representation exists, whether it matches the authorized submission,
* and whether it is not part of a published publication. This policy expects an
* authorized submission in the authorization context.
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use APP\facades\Repo;
use PKP\core\PKPRequest;
use PKP\security\authorization\AuthorizationPolicy;
use PKP\security\authorization\DataObjectRequiredPolicy;
use PKP\submission\PKPSubmission;
use PKP\submissionFile\SubmissionFile;
class RepresentationUploadAccessPolicy extends DataObjectRequiredPolicy
{
/** @var int */
public $_representationId;
/**
* Constructor
*
* @param PKPRequest $request
* @param array $args request parameters
* @param int $representationId
*/
public function __construct($request, &$args, $representationId)
{
parent::__construct($request, $args, 'user.authorization.accessDenied');
$this->_representationId = $representationId;
}
//
// Implement template methods from AuthorizationPolicy
//
/**
* @see DataObjectRequiredPolicy::dataObjectEffect()
*/
public function dataObjectEffect()
{
$assignedFileStages = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ACCESSIBLE_FILE_STAGES);
if (empty($assignedFileStages) || !in_array(SubmissionFile::SUBMISSION_FILE_PROOF, $assignedFileStages)) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
if (empty($this->_representationId)) {
$this->setAdvice(AuthorizationPolicy::AUTHORIZATION_ADVICE_DENY_MESSAGE, 'user.authorization.representationNotFound');
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
$representationDao = Application::get()->getRepresentationDAO();
$representation = $representationDao->getById($this->_representationId);
if (!$representation) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
if (!$submission) {
$this->setAdvice(AuthorizationPolicy::AUTHORIZATION_ADVICE_DENY_MESSAGE, 'user.authorization.invalidSubmission');
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
$publication = Repo::publication()->get($representation->getData('publicationId'));
if (!$publication) {
$this->setAdvice(AuthorizationPolicy::AUTHORIZATION_ADVICE_DENY_MESSAGE, 'galley.publicationNotFound');
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Publication and submission must match
if ($publication->getData('submissionId') !== $submission->getId()) {
$this->setAdvice(AuthorizationPolicy::AUTHORIZATION_ADVICE_DENY_MESSAGE, 'user.authorization.invalidPublication');
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Representations can not be modified on published publications
if ($publication->getData('status') === PKPSubmission::STATUS_PUBLISHED) {
$this->setAdvice(AuthorizationPolicy::AUTHORIZATION_ADVICE_DENY_MESSAGE, 'galley.editPublishedDisabled');
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
$this->addAuthorizedContextObject(Application::ASSOC_TYPE_REPRESENTATION, $representation);
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\RepresentationUploadAccessPolicy', '\RepresentationUploadAccessPolicy');
}
@@ -0,0 +1,97 @@
<?php
/**
* @file classes/security/authorization/internal/ReviewAssignmentAccessPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class ReviewAssignmentAccessPolicy
*
* @ingroup security_authorization_internal
*
* @brief Class to control access to a submission based on whether the user is an assigned reviewer.
*
* NB: This policy expects a previously authorized submission in the
* authorization context.
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use APP\submission\Submission;
use PKP\core\PKPRequest;
use PKP\db\DAORegistry;
use PKP\security\authorization\AuthorizationPolicy;
use PKP\submission\reviewAssignment\ReviewAssignmentDAO;
use PKP\user\User;
class ReviewAssignmentAccessPolicy extends AuthorizationPolicy
{
/** @var PKPRequest */
public $_request;
/** @var bool */
public $_permitDeclined;
/**
* Constructor
*
* @param PKPRequest $request
* @param bool $permitDeclined True if declined or cancelled reviews are acceptable.
*/
public function __construct($request, $permitDeclined = false)
{
parent::__construct('user.authorization.submissionReviewer');
$this->_request = $request;
$this->_permitDeclined = $permitDeclined;
}
//
// Implement template methods from AuthorizationPolicy
//
/**
* @see AuthorizationPolicy::effect()
*/
public function effect()
{
// Get the user
$user = $this->_request->getUser();
if (!$user instanceof User) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Get the submission
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
if (!$submission instanceof Submission) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Check if a review assignment exists between the submission and the user
$reviewAssignmentDao = DAORegistry::getDAO('ReviewAssignmentDAO'); /** @var ReviewAssignmentDAO $reviewAssignmentDao */
$reviewAssignment = $reviewAssignmentDao->getLastReviewRoundReviewAssignmentByReviewer($submission->getId(), $user->getId());
// Ensure a valid review assignment was fetched from the database
if (!($reviewAssignment instanceof \PKP\submission\reviewAssignment\ReviewAssignment)) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// If the assignment has been cancelled, deny access.
if ($reviewAssignment->getCancelled()) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Ensure that the assignment isn't declined, unless that's permitted
if (!$this->_permitDeclined && $reviewAssignment->getDeclined()) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Save the review assignment to the authorization context.
$this->addAuthorizedContextObject(Application::ASSOC_TYPE_REVIEW_ASSIGNMENT, $reviewAssignment);
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\ReviewAssignmentAccessPolicy', '\ReviewAssignmentAccessPolicy');
}
@@ -0,0 +1,96 @@
<?php
/**
* @file classes/security/authorization/internal/ReviewAssignmentRequiredPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class ReviewAssignmentRequiredPolicy
*
* @ingroup security_authorization_internal
*
* @brief Policy that ensures that the request contains a valid review assignment.
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use APP\submission\Submission;
use PKP\core\PKPRequest;
use PKP\db\DAORegistry;
use PKP\security\authorization\AuthorizationPolicy;
use PKP\security\authorization\DataObjectRequiredPolicy;
use PKP\submission\reviewAssignment\ReviewAssignmentDAO;
class ReviewAssignmentRequiredPolicy extends DataObjectRequiredPolicy
{
/** @var array Allowed review methods */
public $_reviewMethods = [];
/**
* Constructor
*
* @param PKPRequest $request
* @param array $args request parameters
* @param string $parameterName the request parameter we
* expect the submission id in.
* @param array|string $operations either a single operation or a list of operations that
* this policy is targeting.
* @param array $reviewMethods limit the policy to specific review methods
*/
public function __construct($request, &$args, $parameterName = 'reviewAssignmentId', $operations = null, $reviewMethods = null)
{
parent::__construct($request, $args, $parameterName, 'user.authorization.invalidReviewAssignment', $operations);
$this->_reviewMethods = $reviewMethods;
}
//
// Implement template methods from AuthorizationPolicy
//
/**
* @see DataObjectRequiredPolicy::dataObjectEffect()
*/
public function dataObjectEffect()
{
$reviewId = (int)$this->getDataObjectId();
if (!$reviewId) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
$reviewAssignmentDao = DAORegistry::getDAO('ReviewAssignmentDAO'); /** @var ReviewAssignmentDAO $reviewAssignmentDao */
$reviewAssignment = $reviewAssignmentDao->getById($reviewId);
if (!($reviewAssignment instanceof \PKP\submission\reviewAssignment\ReviewAssignment)) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// If reviewMethods is defined, check that the assignment uses the defined method(s)
if ($this->_reviewMethods) {
if (!in_array($reviewAssignment->getReviewMethod(), $this->_reviewMethods)) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
}
// Ensure that the review assignment actually belongs to the
// authorized submission.
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
assert($submission instanceof Submission);
if ($reviewAssignment->getSubmissionId() != $submission->getId()) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Ensure that the review assignment is for this workflow stage
$stageId = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_WORKFLOW_STAGE);
if ($reviewAssignment->getStageId() != $stageId) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Save the review Assignment to the authorization context.
$this->addAuthorizedContextObject(Application::ASSOC_TYPE_REVIEW_ASSIGNMENT, $reviewAssignment);
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\ReviewAssignmentRequiredPolicy', '\ReviewAssignmentRequiredPolicy');
}
@@ -0,0 +1,93 @@
<?php
/**
* @file classes/security/authorization/internal/ReviewRoundRequiredPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class ReviewRoundRequiredPolicy
*
* @ingroup security_authorization_internal
*
* @brief Policy that ensures that the request contains a valid review round.
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use PKP\core\PKPRequest;
use PKP\db\DAORegistry;
use PKP\security\authorization\AuthorizationPolicy;
use PKP\security\authorization\DataObjectRequiredPolicy;
use PKP\submission\reviewRound\ReviewRound;
use PKP\submission\reviewRound\ReviewRoundDAO;
class ReviewRoundRequiredPolicy extends DataObjectRequiredPolicy
{
/** @var int Review round id. */
public $_reviewRoundId;
/**
* Constructor
*
* @param PKPRequest $request
* @param array $args request parameters
* @param string $parameterName the request parameter we expect
* the submission id in.
* @param array $operations Optional list of operations for which this check takes effect. If specified, operations outside this set will not be checked against this policy.
* @param int $reviewRoundId Optionally pass the review round id directly. If passed, the $parameterName will be ignored.
*/
public function __construct($request, &$args, $parameterName = 'reviewRoundId', $operations = null, $reviewRoundId = null)
{
parent::__construct($request, $args, $parameterName, 'user.authorization.invalidReviewRound', $operations);
if ($reviewRoundId) {
$this->_reviewRoundId = $reviewRoundId;
}
}
//
// Implement template methods from AuthorizationPolicy
//
/**
* @see DataObjectRequiredPolicy::dataObjectEffect()
*/
public function dataObjectEffect()
{
// Get the review round id.
if (!$this->_reviewRoundId) {
$this->_reviewRoundId = $this->getDataObjectId();
}
if ($this->_reviewRoundId === false) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Validate the review round id.
$reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /** @var ReviewRoundDAO $reviewRoundDao */
$reviewRound = $reviewRoundDao->getById($this->_reviewRoundId);
if (!$reviewRound instanceof ReviewRound) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Ensure that the review round actually belongs to the
// authorized submission.
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
if ($reviewRound->getSubmissionId() != $submission->getId()) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Ensure that the review round is for this workflow stage
$stageId = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_WORKFLOW_STAGE);
if ($reviewRound->getStageId() != $stageId) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Save the review round to the authorization context.
$this->addAuthorizedContextObject(Application::ASSOC_TYPE_REVIEW_ROUND, $reviewRound);
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\ReviewRoundRequiredPolicy', '\ReviewRoundRequiredPolicy');
}
@@ -0,0 +1,93 @@
<?php
/**
* @file classes/security/authorization/internal/SubmissionAuthorPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class SubmissionAuthorPolicy
*
* @ingroup security_authorization_internal
*
* @brief Class to control access to a submission based on authorship.
*
* NB: This policy expects a previously authorized submission in the
* authorization context.
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use APP\facades\Repo;
use APP\submission\Submission;
use PKP\core\PKPApplication;
use PKP\core\PKPRequest;
use PKP\security\authorization\AuthorizationPolicy;
use PKP\security\Role;
use PKP\user\User;
class SubmissionAuthorPolicy extends AuthorizationPolicy
{
/** @var PKPRequest */
public $_request;
/**
* Constructor
*
* @param PKPRequest $request
*/
public function __construct($request)
{
parent::__construct('user.authorization.submissionAuthor');
$this->_request = $request;
}
//
// Implement template methods from AuthorizationPolicy
//
/**
* @see AuthorizationPolicy::effect()
*/
public function effect()
{
// Get the user
$user = $this->_request->getUser();
if (!$user instanceof User) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Get the submission
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
if (!$submission instanceof Submission) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
$context = $this->_request->getContext();
// Check authorship of the submission. Any ROLE_ID_AUTHOR assignment will do.
$accessibleWorkflowStages = Repo::user()->getAccessibleWorkflowStages(
$user->getId(),
$context->getId(),
$submission,
$this->getAuthorizedContextObject(PKPApplication::ASSOC_TYPE_USER_ROLES)
);
if (empty($accessibleWorkflowStages)) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
foreach ($accessibleWorkflowStages as $roles) {
if (in_array(Role::ROLE_ID_AUTHOR, $roles)) {
$this->addAuthorizedContextObject(Application::ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES, $accessibleWorkflowStages);
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
}
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\SubmissionAuthorPolicy', '\SubmissionAuthorPolicy');
}
@@ -0,0 +1,80 @@
<?php
/**
* @file classes/security/authorization/internal/SubmissionFileAssignedQueryAccessPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class SubmissionFileAssignedQueryAccessPolicy
*
* @ingroup security_authorization_internal
*
* @brief Submission file policy to check if the current user is a participant
* in a query the file belongs to.
*
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use PKP\db\DAORegistry;
use PKP\note\NoteDAO;
use PKP\query\QueryDAO;
use PKP\security\authorization\AuthorizationPolicy;
class SubmissionFileAssignedQueryAccessPolicy extends SubmissionFileBaseAccessPolicy
{
//
// Implement template methods from AuthorizationPolicy
//
/**
* @see AuthorizationPolicy::effect()
*/
public function effect()
{
$request = $this->getRequest();
// Get the user
$user = $request->getUser();
if (!$user instanceof \PKP\user\User) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Get the submission file
$submissionFile = $this->getSubmissionFile($request);
if (!$submissionFile instanceof \PKP\submissionFile\SubmissionFile) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Check if it's associated with a note.
if ($submissionFile->getData('assocType') != Application::ASSOC_TYPE_NOTE) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
$noteDao = DAORegistry::getDAO('NoteDAO'); /** @var NoteDAO $noteDao */
$note = $noteDao->getById($submissionFile->getData('assocId'));
if (!$note instanceof \PKP\note\Note) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
if ($note->getAssocType() != Application::ASSOC_TYPE_QUERY) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
$queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */
$query = $queryDao->getById($note->getAssocId());
if (!$query) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
if ($queryDao->getParticipantIds($note->getAssocId(), $user->getId())) {
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\SubmissionFileAssignedQueryAccessPolicy', '\SubmissionFileAssignedQueryAccessPolicy');
}
@@ -0,0 +1,90 @@
<?php
/**
* @file classes/security/authorization/internal/SubmissionFileAssignedReviewerAccessPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class SubmissionFileAssignedReviewerAccessPolicy
*
* @ingroup security_authorization_internal
*
* @brief Submission file policy to check if the current user is an assigned
* reviewer of the file.
*
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use Exception;
use PKP\db\DAORegistry;
use PKP\security\authorization\AuthorizationPolicy;
use PKP\submission\reviewAssignment\ReviewAssignmentDAO;
use PKP\submission\ReviewFilesDAO;
use PKP\submissionFile\SubmissionFile;
class SubmissionFileAssignedReviewerAccessPolicy extends SubmissionFileBaseAccessPolicy
{
//
// Implement template methods from AuthorizationPolicy
//
/**
* @see AuthorizationPolicy::effect()
*/
public function effect()
{
$request = $this->getRequest();
// Get the user
$user = $request->getUser();
if (!$user instanceof \PKP\user\User) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Get the submission file
$submissionFile = $this->getSubmissionFile($request);
if (!$submissionFile instanceof SubmissionFile) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
$context = $request->getContext();
$reviewAssignmentDao = DAORegistry::getDAO('ReviewAssignmentDAO'); /** @var ReviewAssignmentDAO $reviewAssignmentDao */
$reviewAssignments = $reviewAssignmentDao->getByUserId($user->getId());
$reviewFilesDao = DAORegistry::getDAO('ReviewFilesDAO'); /** @var ReviewFilesDAO $reviewFilesDao */
foreach ($reviewAssignments as $reviewAssignment) {
if ($context->getData('restrictReviewerFileAccess') && !$reviewAssignment->getDateConfirmed()) {
continue;
}
// Determine which file stage the requested file should be in.
$reviewFileStage = null;
switch ($reviewAssignment->getStageId()) {
case WORKFLOW_STAGE_ID_INTERNAL_REVIEW:
$reviewFileStage = SubmissionFile::SUBMISSION_FILE_INTERNAL_REVIEW_FILE;
break;
case WORKFLOW_STAGE_ID_EXTERNAL_REVIEW:
$reviewFileStage = SubmissionFile::SUBMISSION_FILE_REVIEW_FILE;
break;
default: throw new Exception('Unknown review workflow stage ID!');
}
if (
$submissionFile->getData('submissionId') == $reviewAssignment->getSubmissionId() &&
$submissionFile->getData('fileStage') == $reviewFileStage &&
$reviewFilesDao->check($reviewAssignment->getId(), $submissionFile->getId())
) {
$this->addAuthorizedContextObject(Application::ASSOC_TYPE_REVIEW_ASSIGNMENT, $reviewAssignment);
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
}
// If a pass condition wasn't found above, deny access.
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\SubmissionFileAssignedReviewerAccessPolicy', '\SubmissionFileAssignedReviewerAccessPolicy');
}
@@ -0,0 +1,68 @@
<?php
/**
* @file classes/security/authorization/internal/SubmissionFileAuthorEditorPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class SubmissionFileAuthorEditorPolicy
*
* @ingroup security_authorization_internal
*
* @brief Submission file policy to ensure that an editor is denied access to
* anonymous review files when they are also assigned to the submission as an
* author.
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use PKP\db\DAORegistry;
use PKP\security\authorization\AuthorizationPolicy;
use PKP\security\Role;
use PKP\submission\reviewAssignment\ReviewAssignment;
use PKP\submission\reviewAssignment\ReviewAssignmentDAO;
use PKP\submissionFile\SubmissionFile;
class SubmissionFileAuthorEditorPolicy extends SubmissionFileBaseAccessPolicy
{
/**
* @copydoc AuthorizationPolicy::effect()
*/
public function effect()
{
$request = $this->getRequest();
// Get the submission file.
$submissionFile = $this->getSubmissionFile($request);
if (!$submissionFile instanceof SubmissionFile) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Allow if this is not a file submitted with a review
if ($submissionFile->getFileStage() != SubmissionFile::SUBMISSION_FILE_REVIEW_ATTACHMENT) {
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
// Deny if the user is assigned as an author to any stage, and this file is
// attached to an anonymous review
$userRoles = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES);
foreach ($userRoles as $stageRoles) {
if (in_array(Role::ROLE_ID_AUTHOR, $stageRoles)) {
$reviewAssignmentDao = DAORegistry::getDAO('ReviewAssignmentDAO'); /** @var ReviewAssignmentDAO $reviewAssignmentDao */
$reviewAssignment = $reviewAssignmentDao->getById((int) $submissionFile->getData('assocId'));
if ($reviewAssignment && $reviewAssignment->getReviewMethod() != ReviewAssignment::SUBMISSION_REVIEW_METHOD_OPEN) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
break;
}
}
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\SubmissionFileAuthorEditorPolicy', '\SubmissionFileAuthorEditorPolicy');
}
@@ -0,0 +1,99 @@
<?php
/**
* @file classes/security/authorization/internal/SubmissionFileBaseAccessPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class SubmissionFileBaseAccessPolicy
*
* @ingroup security_authorization_internal
*
* @brief Abstract class for submission file access policies.
*
*/
namespace PKP\security\authorization\internal;
use APP\facades\Repo;
use PKP\core\PKPRequest;
use PKP\security\authorization\AuthorizationPolicy;
use PKP\submissionFile\SubmissionFile;
class SubmissionFileBaseAccessPolicy extends AuthorizationPolicy
{
/** @var PKPRequest */
public $_request;
/** @var int Submission file id */
public $_submissionFileId;
/**
* Constructor
*
* @param PKPRequest $request
* @param int $submissionFileId If passed, this policy will try to
* get the submission file from this data.
*/
public function __construct($request, $submissionFileId = null)
{
parent::__construct('user.authorization.submissionFile');
$this->_request = $request;
$this->_submissionFileId = $submissionFileId;
}
//
// Private methods
//
/**
* Get a cache of submission files. Used because many policy subclasses
* may be combined to fetch a single submission file.
*
* @return array
*/
public function &_getCache()
{
static $cache = [];
return $cache;
}
//
// Protected methods
//
/**
* Get the requested submission file.
*
* @param PKPRequest $request
*
* @return SubmissionFile
*/
public function getSubmissionFile($request)
{
// Get the identifying info from the request
if (is_null($this->_submissionFileId)) {
$this->_submissionFileId = (int) $request->getUserVar('submissionFileId');
assert($this->_submissionFileId > 0);
}
// Fetch the object, caching if possible
$cache = & $this->_getCache();
return $cache[$this->_submissionFileId] ??= Repo::submissionFile()->get($this->_submissionFileId);
}
/**
* Get the current request object.
*
* @return PKPRequest
*/
public function getRequest()
{
return $this->_request;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\SubmissionFileBaseAccessPolicy', '\SubmissionFileBaseAccessPolicy');
}
@@ -0,0 +1,71 @@
<?php
/**
* @file classes/security/authorization/internal/SubmissionFileMatchesSubmissionPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class SubmissionFileMatchesSubmissionPolicy
*
* @ingroup security_authorization_internal
*
* @brief Submission file policy to check if the file belongs to the submission
*
* NB: This policy expects a previously authorized submission in the
* authorization context.
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use APP\submission\Submission;
use PKP\security\authorization\AuthorizationPolicy;
use PKP\submissionFile\SubmissionFile;
class SubmissionFileMatchesSubmissionPolicy extends SubmissionFileBaseAccessPolicy
{
//
// Implement template methods from AuthorizationPolicy
//
/**
* @see AuthorizationPolicy::effect()
*/
public function effect()
{
// Get the submission file
$request = $this->getRequest();
$submissionFile = $this->getSubmissionFile($request);
if (!$submissionFile instanceof SubmissionFile) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Get the submission
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
if (!$submission instanceof Submission) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Check if the submission file belongs to the submission.
if ($submissionFile->getData('submissionId') == $submission->getId()) {
// We add this submission file to the context submission files array.
$submissionFilesArray = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION_FILES);
if (is_null($submissionFilesArray)) {
$submissionFilesArray = [];
}
array_push($submissionFilesArray, $submissionFile);
$this->addAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION_FILES, $submissionFilesArray);
// Save the submission file to the authorization context.
$this->addAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION_FILE, $submissionFile);
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
} else {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\SubmissionFileMatchesSubmissionPolicy', '\SubmissionFileMatchesSubmissionPolicy');
}
@@ -0,0 +1,70 @@
<?php
/**
* @file classes/security/authorization/internal/SubmissionFileMatchesWorkflowStageIdPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class SubmissionFileMatchesWorkflowStageIdPolicy
*
* @ingroup security_authorization_internal
*
* @brief Submission file policy to check if the file belongs to the specified workflow stage ID
*/
namespace PKP\security\authorization\internal;
use APP\facades\Repo;
use PKP\core\PKPRequest;
use PKP\security\authorization\AuthorizationPolicy;
use PKP\submissionFile\SubmissionFile;
class SubmissionFileMatchesWorkflowStageIdPolicy extends SubmissionFileBaseAccessPolicy
{
/** @var int|null Workflow stage ID (WORKFLOW_STAGE_ID_...) */
protected $_stageId = null;
/**
* Constructor
*
* @param PKPRequest $request
* @param int $stageId Workflow stage ID (WORKFLOW_STAGE_ID_...)
* @param null|mixed $submissionFileId
*/
public function __construct($request, $submissionFileId = null, $stageId = null)
{
parent::__construct($request, $submissionFileId);
$this->_stageId = (int) $stageId;
}
//
// Implement template methods from AuthorizationPolicy
//
/**
* @see AuthorizationPolicy::effect()
*/
public function effect()
{
// Get the submission file
$request = $this->getRequest();
$submissionFile = $this->getSubmissionFile($request);
if (!$submissionFile instanceof SubmissionFile) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
$workflowStageId = Repo::submissionFile()->getWorkflowStageId($submissionFile);
// Check if the submission file belongs to the specified workflow stage.
if ($workflowStageId != $this->_stageId) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\SubmissionFileMatchesWorkflowStageIdPolicy', '\SubmissionFileMatchesWorkflowStageIdPolicy');
}
@@ -0,0 +1,58 @@
<?php
/**
* @file classes/security/authorization/internal/SubmissionFileNotQueryAccessPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class SubmissionFileNotQueryAccessPolicy
*
* @ingroup security_authorization_internal
*
* @brief Submission file policy to check if the requested file is not attached
* to a query. This returns AUTHORIZATION_PERMIT for _any_ file that is not
* attached to a query note.
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use PKP\db\DAORegistry;
use PKP\note\NoteDAO;
use PKP\security\authorization\AuthorizationPolicy;
class SubmissionFileNotQueryAccessPolicy extends SubmissionFileBaseAccessPolicy
{
/**
* @see AuthorizationPolicy::effect()
*/
public function effect()
{
$request = $this->getRequest();
// Get the submission file
$submissionFile = $this->getSubmissionFile($request);
if (!$submissionFile instanceof \PKP\submissionFile\SubmissionFile) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Check if it's associated with a note.
if ($submissionFile->getData('assocType') != Application::ASSOC_TYPE_NOTE) {
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
// Check if that note is associated with a query
$noteDao = DAORegistry::getDAO('NoteDAO'); /** @var NoteDAO $noteDao */
$note = $noteDao->getById($submissionFile->getData('assocId'));
if ($note->getAssocType() != Application::ASSOC_TYPE_QUERY) {
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\SubmissionFileNotQueryAccessPolicy', '\SubmissionFileNotQueryAccessPolicy');
}
@@ -0,0 +1,102 @@
<?php
/**
* @file classes/security/authorization/internal/SubmissionFileRequestedRevisionRequiredPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class SubmissionFileRequestedRevisionRequiredPolicy
*
* @ingroup security_authorization_internal
*
* @brief Base Submission file policy to ensure we have a viewable file that is part of
* a review round with the requested revision decision.
*
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use APP\decision\Decision;
use APP\facades\Repo;
use PKP\db\DAORegistry;
use PKP\security\authorization\AuthorizationPolicy;
use PKP\submission\reviewRound\ReviewRound;
use PKP\submission\reviewRound\ReviewRoundDAO;
use PKP\submissionFile\SubmissionFile;
class SubmissionFileRequestedRevisionRequiredPolicy extends SubmissionFileBaseAccessPolicy
{
//
// Implement template methods from AuthorizationPolicy
// Note: This class is subclassed in each Application, so that Policies have the opportunity to add
// constraints to the effect() method. See e.g. SubmissionFileRequestedRevisionRequiredPolicy.php in OMP.
//
/**
* @see AuthorizationPolicy::effect()
*/
public function effect()
{
$request = $this->getRequest();
$reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /** @var ReviewRoundDAO $reviewRoundDao */
// Get the submission file.
$submissionFile = $this->getSubmissionFile($request);
if (!$submissionFile instanceof SubmissionFile) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Make sure the file is part of a review round
// with a requested revision decision.
$reviewRound = $reviewRoundDao->getBySubmissionFileId($submissionFile->getId());
if (!$reviewRound instanceof ReviewRound) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
$countRevisionDecisions = Repo::decision()->getCollector()
->filterBySubmissionIds([$submissionFile->getData('submissionId)')])
->filterByReviewRoundIds([$reviewRound->getId()])
->filterByDecisionTypes([Decision::PENDING_REVISIONS])
->getCount();
if (!$countRevisionDecisions) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Make sure review round stage is the same of the current stage in request.
$stageId = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_WORKFLOW_STAGE);
if ($reviewRound->getStageId() != $stageId) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Make sure the file stage is SubmissionFile::SUBMISSION_FILE_REVIEW_REVISION.
if ($submissionFile->getData('fileStage') != SubmissionFile::SUBMISSION_FILE_REVIEW_REVISION) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
$reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /** @var ReviewRoundDAO $reviewRoundDao */
// Make sure that the last review round editor decision is request revisions.
$reviewRoundDecisions = Repo::decision()->getCollector()
->filterBySubmissionIds([$submissionFile->getData('submissionId')])
->filterByStageIds([$reviewRound->getStageId()])
->filterByReviewRoundIds([$reviewRound->getId()])
->getMany();
if ($reviewRoundDecisions->isEmpty()) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
$lastEditorDecision = $reviewRoundDecisions->last();
if ($lastEditorDecision->getData('decision') != Decision::PENDING_REVISIONS) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Made it through -- permit access.
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\SubmissionFileRequestedRevisionRequiredPolicy', '\SubmissionFileRequestedRevisionRequiredPolicy');
}
@@ -0,0 +1,138 @@
<?php
/**
* @file classes/security/authorization/internal/SubmissionFileStageAccessPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class SubmissionFileStageAccessPolicy
*
* @ingroup security_authorization_internal
*
* @brief Submission file policy to ensure that the user can read or write to a particular
* file stage based on their stage assignments. This policy expects submission, user roles
* and workflow stage assignments in the authorized context.
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use APP\decision\Decision;
use APP\facades\Repo;
use PKP\db\DAORegistry;
use PKP\security\authorization\AuthorizationPolicy;
use PKP\security\authorization\SubmissionFileAccessPolicy;
use PKP\security\Role;
use PKP\submission\reviewRound\ReviewRoundDAO;
use PKP\submissionFile\SubmissionFile;
class SubmissionFileStageAccessPolicy extends AuthorizationPolicy
{
/** @var int SubmissionFile::SUBMISSION_FILE_... */
public $_fileStage;
/** @var int SubmissionFileAccessPolicy::SUBMISSION_FILE_ACCESS_READ... */
public $_action;
/**
* Constructor
*
* @param int $fileStage SubmissionFile::SUBMISSION_FILE_...
* @param int $action SubmissionFileAccessPolicy::SUBMISSION_FILE_ACCESS_READ or SubmissionFileAccessPolicy::SUBMISSION_FILE_ACCESS_MODIFY
* @param string $message The message to display when authorization is denied
*/
public function __construct($fileStage, $action, $message)
{
parent::__construct($message);
$this->_fileStage = $fileStage;
$this->_action = $action;
}
//
// Implement template methods from AuthorizationPolicy
//
/**
* @see AuthorizationPolicy::effect()
*/
public function effect()
{
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
$userRoles = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_USER_ROLES);
$stageAssignments = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES);
// File stage required
if (empty($this->_fileStage)) {
$this->setAdvice(AuthorizationPolicy::AUTHORIZATION_ADVICE_DENY_MESSAGE, $msg = 'api.submissionFiles.400.noFileStageId');
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Managers and site admins can access file stages when not assigned or when assigned as a manager
if (empty($stageAssignments)) {
if (count(array_intersect([Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN], $userRoles))) {
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Determine the allowed file stages
$assignedFileStages = Repo::submissionFile()
->getAssignedFileStages(
$stageAssignments,
$this->_action
);
// Authors may write to the submission files stage if the submission
// is not yet complete
if ($this->_fileStage === SubmissionFile::SUBMISSION_FILE_SUBMISSION && $this->_action === SubmissionFileAccessPolicy::SUBMISSION_FILE_ACCESS_MODIFY) {
if (!empty($stageAssignments[WORKFLOW_STAGE_ID_SUBMISSION])
&& count($stageAssignments[WORKFLOW_STAGE_ID_SUBMISSION]) === 1
&& in_array(Role::ROLE_ID_AUTHOR, $stageAssignments[WORKFLOW_STAGE_ID_SUBMISSION])
&& $submission->getData('submissionProgress')) {
$assignedFileStages[] = SubmissionFile::SUBMISSION_FILE_SUBMISSION;
}
}
// Authors may write to the revision files stage if an accept or request revisions
// decision has been made in the latest round
if (in_array($this->_fileStage, [SubmissionFile::SUBMISSION_FILE_INTERNAL_REVIEW_REVISION, SubmissionFile::SUBMISSION_FILE_REVIEW_REVISION]) && $this->_action === SubmissionFileAccessPolicy::SUBMISSION_FILE_ACCESS_MODIFY) {
$reviewStage = $this->_fileStage === SubmissionFile::SUBMISSION_FILE_INTERNAL_REVIEW_REVISION
? WORKFLOW_STAGE_ID_INTERNAL_REVIEW
: WORKFLOW_STAGE_ID_EXTERNAL_REVIEW;
if (count($stageAssignments[$reviewStage]) === 1 && in_array(Role::ROLE_ID_AUTHOR, $stageAssignments[$reviewStage])) {
$reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /** @var ReviewRoundDAO $reviewRoundDao */
$reviewRound = $reviewRoundDao->getLastReviewRoundBySubmissionId($submission->getId(), $reviewStage);
if ($reviewRound) {
$countDecisions = Repo::decision()->getCollector()
->filterBySubmissionIds([$submission->getId()])
->filterByStageIds([$reviewRound->getStageId()])
->filterByReviewRoundIds([$reviewRound->getId()])
->filterByDecisionTypes([
Decision::ACCEPT,
Decision::PENDING_REVISIONS,
Decision::NEW_EXTERNAL_ROUND,
Decision::RESUBMIT
])
->getCount();
if ($countDecisions) {
$assignedFileStages[] = $this->_fileStage;
}
}
}
}
if (in_array($this->_fileStage, $assignedFileStages)) {
$this->addAuthorizedContextObject(Application::ASSOC_TYPE_ACCESSIBLE_FILE_STAGES, $assignedFileStages);
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\SubmissionFileStageAccessPolicy', '\SubmissionFileStageAccessPolicy');
}
@@ -0,0 +1,94 @@
<?php
/**
* @file classes/security/authorization/internal/SubmissionFileStageRequiredPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class SubmissionFileStageRequiredPolicy
*
* @ingroup security_authorization_internal
*
* @brief Submission file policy to ensure that we have a file at a required stage.
*
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use PKP\core\PKPRequest;
use PKP\db\DAORegistry;
use PKP\security\authorization\AuthorizationPolicy;
use PKP\submission\reviewAssignment\ReviewAssignment;
use PKP\submission\reviewAssignment\ReviewAssignmentDAO;
class SubmissionFileStageRequiredPolicy extends SubmissionFileBaseAccessPolicy
{
/** @var int SubmissionFile::SUBMISSION_FILE_... */
public $_fileStage;
/** @var bool Whether the file has to be viewable */
public $_viewable;
/**
* Constructor
*
* @param PKPRequest $request
* @param int $submissionFileId This policy will try to
* get the submission file from this data.
* @param int $fileStage SubmissionFile::SUBMISSION_FILE_...
* @param bool $viewable Whether the file has to be viewable
*/
public function __construct($request, $submissionFileId, $fileStage, $viewable = false)
{
parent::__construct($request, $submissionFileId);
$this->_fileStage = $fileStage;
$this->_viewable = $viewable;
}
//
// Implement template methods from AuthorizationPolicy
//
/**
* @see AuthorizationPolicy::effect()
*/
public function effect()
{
$request = $this->getRequest();
// Get the submission file.
$submissionFile = $this->getSubmissionFile($request);
if (!$submissionFile instanceof \PKP\submissionFile\SubmissionFile) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Make sure that it's in the required stage
if ($submissionFile->getFileStage() != $this->_fileStage) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
if ($this->_viewable) {
// Make sure the file is visible. Unless file is included in an open review.
if (!$submissionFile->getViewable()) {
if ($submissionFile->getData('assocType') === Application::ASSOC_TYPE_REVIEW_ASSIGNMENT) {
$reviewAssignmentDao = DAORegistry::getDAO('ReviewAssignmentDAO'); /** @var ReviewAssignmentDAO $reviewAssignmentDao */
$reviewAssignment = $reviewAssignmentDao->getById((int) $submissionFile->getData('assocId'));
if ($reviewAssignment->getReviewMethod() != ReviewAssignment::SUBMISSION_REVIEW_METHOD_OPEN) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
} else {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
}
}
// Made it through -- permit access.
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\SubmissionFileStageRequiredPolicy', '\SubmissionFileStageRequiredPolicy');
}
@@ -0,0 +1,57 @@
<?php
/**
* @file classes/security/authorization/internal/SubmissionFileUploaderAccessPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class SubmissionFileUploaderAccessPolicy
*
* @ingroup security_authorization_internal
*
* @brief Submission file policy to check if the current user is the uploader.
*
*/
namespace PKP\security\authorization\internal;
use PKP\security\authorization\AuthorizationPolicy;
class SubmissionFileUploaderAccessPolicy extends SubmissionFileBaseAccessPolicy
{
//
// Implement template methods from AuthorizationPolicy
//
/**
* @see AuthorizationPolicy::effect()
*/
public function effect()
{
$request = $this->getRequest();
// Get the user
$user = $request->getUser();
if (!$user instanceof \PKP\user\User) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Get the submission file
$submissionFile = $this->getSubmissionFile($request);
if (!$submissionFile instanceof \PKP\submissionFile\SubmissionFile) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Check if the uploader is the current user.
if ($submissionFile->getUploaderUserId() == $user->getId()) {
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
} else {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\SubmissionFileUploaderAccessPolicy', '\SubmissionFileUploaderAccessPolicy');
}
@@ -0,0 +1,81 @@
<?php
/**
* @file classes/security/authorization/internal/SubmissionRequiredPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class SubmissionRequiredPolicy
*
* @ingroup security_authorization_internal
*
* @brief Policy that ensures that the request contains a valid submission.
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use APP\facades\Repo;
use APP\submission\Submission;
use PKP\core\PKPRequest;
use PKP\security\authorization\AuthorizationPolicy;
use PKP\security\authorization\DataObjectRequiredPolicy;
class SubmissionRequiredPolicy extends DataObjectRequiredPolicy
{
/**
* Constructor
*
* @param PKPRequest $request
* @param array $args request parameters
* @param string $submissionParameterName the request parameter we expect
* the submission id in.
* @param null|mixed $operations
*/
public function __construct($request, &$args, $submissionParameterName = 'submissionId', $operations = null)
{
parent::__construct($request, $args, $submissionParameterName, 'user.authorization.invalidSubmission', $operations);
$callOnDeny = [$request->getDispatcher(), 'handle404', []];
$this->setAdvice(
AuthorizationPolicy::AUTHORIZATION_ADVICE_CALL_ON_DENY,
$callOnDeny
);
}
//
// Implement template methods from AuthorizationPolicy
//
/**
* @see DataObjectRequiredPolicy::dataObjectEffect()
*/
public function dataObjectEffect()
{
// Get the submission id.
$submissionId = $this->getDataObjectId();
if ($submissionId === false) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Validate the submission id.
$submission = Repo::submission()->get((int) $submissionId);
if (!$submission instanceof Submission) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Validate that this submission belongs to the current context.
$context = $this->_request->getContext();
if ($context->getId() != $submission->getData('contextId')) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Save the submission to the authorization context.
$this->addAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION, $submission);
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\SubmissionRequiredPolicy', '\SubmissionRequiredPolicy');
}
@@ -0,0 +1,81 @@
<?php
/**
* @file classes/security/authorization/internal/UserAccessibleWorkflowStagePolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class UserAccessibleWorkflowStagePolicy
*
* @ingroup security_authorization_internal
*
* @brief Class to control access to a
*
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use PKP\security\authorization\AuthorizationPolicy;
class UserAccessibleWorkflowStagePolicy extends AuthorizationPolicy
{
/** @var int */
public $_stageId;
/** @var string Workflow type. One of PKPApplication::WORKFLOW_TYPE_... */
public $_workflowType;
/**
* Constructor
*
* @param int $stageId The one that will be checked against accessible
* user workflow stages.
* @param string $workflowType Which workflow the stage access must be granted
* for. One of PKPApplication::WORKFLOW_TYPE_*.
*/
public function __construct($stageId, $workflowType = null)
{
parent::__construct('user.authorization.accessibleWorkflowStage');
$this->_stageId = $stageId;
if (!is_null($workflowType)) {
$this->_workflowType = $workflowType;
}
}
//
// Implement template methods from AuthorizationPolicy
//
/**
* @see AuthorizationPolicy::effect()
*/
public function effect()
{
$userAccessibleStages = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES);
// User has no access to any stage in any workflow
if (empty($userAccessibleStages)) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
// Does user have access to this stage in the requested workflow?
} elseif (!is_null($this->_workflowType)) {
$workflowTypeRoles = Application::getWorkflowTypeRoles();
if (array_key_exists($this->_stageId, $userAccessibleStages) && array_intersect($workflowTypeRoles[$this->_workflowType], $userAccessibleStages[$this->_stageId])) {
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
return AuthorizationPolicy::AUTHORIZATION_DENY;
// The user has access to this stage in any workflow
} elseif (array_key_exists($this->_stageId, $userAccessibleStages)) {
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\UserAccessibleWorkflowStagePolicy', '\UserAccessibleWorkflowStagePolicy');
}
@@ -0,0 +1,93 @@
<?php
/**
* @file classes/security/authorization/internal/UserAccessibleWorkflowStageRequiredPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class UserAccessibleWorkflowStageRequiredPolicy
*
* @ingroup security_authorization_internal
*
* @brief Policy to deny access if an user assigned workflow stage is not found.
*
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use APP\facades\Repo;
use PKP\core\PKPApplication;
use PKP\core\PKPRequest;
use PKP\security\authorization\AuthorizationPolicy;
class UserAccessibleWorkflowStageRequiredPolicy extends AuthorizationPolicy
{
/** @var PKPRequest */
public $_request;
/** @var string Workflow type. One of PKPApplication::WORKFLOW_TYPE_... */
public $_workflowType;
/**
* Constructor
*
* @param PKPRequest $request
* @param string $workflowType Which workflow the stage access must be granted
* for. One of PKPApplication::WORKFLOW_TYPE_*.
*/
public function __construct($request, $workflowType = null)
{
parent::__construct('user.authorization.accessibleWorkflowStage');
$this->_request = $request;
$this->_workflowType = $workflowType;
}
//
// Implement template methods from AuthorizationPolicy
//
/**
* @see AuthorizationPolicy::effect()
*/
public function effect()
{
$request = $this->_request;
$context = $request->getContext();
$contextId = $context->getId();
$user = $request->getUser();
if (!$user instanceof \PKP\user\User) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
$accessibleWorkflowStages = Repo::user()->getAccessibleWorkflowStages(
$user->getId(),
$contextId,
$this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION),
$this->getAuthorizedContextObject(PKPApplication::ASSOC_TYPE_USER_ROLES)
);
$this->addAuthorizedContextObject(Application::ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES, $accessibleWorkflowStages);
// Does the user have a role which matches the requested workflow?
if (!is_null($this->_workflowType)) {
$workflowTypeRoles = Application::getWorkflowTypeRoles();
foreach ($accessibleWorkflowStages as $stageId => $roles) {
if (array_intersect($workflowTypeRoles[$this->_workflowType], $roles)) {
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
}
return AuthorizationPolicy::AUTHORIZATION_DENY;
// User has at least one role in any stage in any workflow
} elseif (!empty($accessibleWorkflowStages)) {
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\UserAccessibleWorkflowStageRequiredPolicy', '\UserAccessibleWorkflowStageRequiredPolicy');
}
@@ -0,0 +1,60 @@
<?php
/**
* @file classes/security/authorization/internal/WorkflowStageRequiredPolicy.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class WorkflowStageRequiredPolicy
*
* @ingroup security_authorization_internal
*
* @brief Policy that ensures that the given workflow stage is valid.
*/
namespace PKP\security\authorization\internal;
use APP\core\Application;
use PKP\security\authorization\AuthorizationPolicy;
class WorkflowStageRequiredPolicy extends AuthorizationPolicy
{
/** @var int */
public $_stageId;
/**
* Constructor
*
* @param int $stageId One of the WORKFLOW_STAGE_ID_* constants.
*/
public function __construct($stageId)
{
parent::__construct('user.authorization.workflowStageRequired');
$this->_stageId = $stageId;
}
//
// Implement template methods from AuthorizationPolicy
//
/**
* @see AuthorizationPolicy::effect()
*/
public function effect()
{
// Check the stage id.
$validAppStages = Application::getApplicationStages();
if (!in_array($this->_stageId, array_values($validAppStages))) {
return AuthorizationPolicy::AUTHORIZATION_DENY;
}
// Save the workflow stage to the authorization context.
$this->addAuthorizedContextObject(Application::ASSOC_TYPE_WORKFLOW_STAGE, $this->_stageId);
return AuthorizationPolicy::AUTHORIZATION_PERMIT;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\PKP\security\authorization\internal\WorkflowStageRequiredPolicy', '\WorkflowStageRequiredPolicy');
}