diff --git a/CHANGELOG.md b/CHANGELOG.md index 1eba8aa9a..2b7a9dcdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,10 @@ 1. [](#bugfix) * Grav 1.7: Fixed error on page initialization [#2753](https://github.com/getgrav/grav/issues/2753) 1. [](#improved) - * Twig filter `|yaml_serialize`: added support for `JsonSerializable` objects + * Twig filter `|yaml_serialize`: added support for `JsonSerializable` objects and other array-like objects + * Added support for returning Flex Page specific permissions for admin and testing +1. [](#bugfix) + * Fixed checking ACL for another user (who is not currently logged in) in a Flex Object or Directory # v1.7.0-rc.2 ## 12/04/2019 diff --git a/system/src/Grav/Common/Flex/Users/UserObject.php b/system/src/Grav/Common/Flex/Users/UserObject.php index 3d681a801..66155d02f 100644 --- a/system/src/Grav/Common/Flex/Users/UserObject.php +++ b/system/src/Grav/Common/Flex/Users/UserObject.php @@ -223,6 +223,7 @@ class UserObject extends FlexObject implements UserInterface, MediaManipulationI return $authorized; } + // If specific rule isn't hit, check if user is super user. if ($access->authorize('admin.super') === true) { return true; } @@ -521,16 +522,17 @@ class UserObject extends FlexObject implements UserInterface, MediaManipulationI * @param UserInterface $user * @param string $action * @param string $scope + * @param bool $isMe * @return bool|null */ - protected function isAuthorizedOverride(UserInterface $user, string $action, string $scope): ?bool + protected function isAuthorizedOverride(UserInterface $user, string $action, string $scope, bool $isMe = false): ?bool { if ($user instanceof self && $user->getStorageKey() === $this->getStorageKey()) { // User cannot delete his own account, otherwise he has full access. return $action !== 'delete'; } - return parent::isAuthorizedOverride($user, $action, $scope); + return parent::isAuthorizedOverride($user, $action, $scope, $isMe); } /** diff --git a/system/src/Grav/Common/User/Access.php b/system/src/Grav/Common/User/Access.php index 2327f5ebf..d3c5687e0 100644 --- a/system/src/Grav/Common/User/Access.php +++ b/system/src/Grav/Common/User/Access.php @@ -48,7 +48,7 @@ class Access implements \JsonSerializable, \IteratorAggregate, \Countable public function authorize(string $action, string $scope = null): ?bool { if (null !== $scope) { - $action = "{$scope}.{$action}"; + $action = $scope !== 'test' ? "{$scope}.{$action}" : $action; } return $this->acl[$action] ?? null; diff --git a/system/src/Grav/Framework/Flex/Pages/FlexPageObject.php b/system/src/Grav/Framework/Flex/Pages/FlexPageObject.php index 7bb474283..8d163a27a 100644 --- a/system/src/Grav/Framework/Flex/Pages/FlexPageObject.php +++ b/system/src/Grav/Framework/Flex/Pages/FlexPageObject.php @@ -17,6 +17,7 @@ use Grav\Common\Page\Traits\PageFormTrait; use Grav\Common\User\Interfaces\UserCollectionInterface; use Grav\Framework\File\Formatter\YamlFormatter; use Grav\Framework\Flex\FlexObject; +use Grav\Framework\Flex\Interfaces\FlexCollectionInterface; use Grav\Framework\Flex\Interfaces\FlexObjectInterface; use Grav\Framework\Flex\Interfaces\FlexTranslateInterface; use Grav\Framework\Flex\Pages\Traits\PageAuthorsTrait; @@ -424,10 +425,10 @@ class FlexPageObject extends FlexObject implements PageInterface, MediaManipulat } /** - * @return UserCollectionInterface|null + * @return UserCollectionInterface|FlexCollectionInterface|null * @internal */ - protected function loadAccounts(): ?UserCollectionInterface + protected function loadAccounts() { return Grav::instance()['accounts'] ?? null; } diff --git a/system/src/Grav/Framework/Flex/Pages/Traits/PageAuthorsTrait.php b/system/src/Grav/Framework/Flex/Pages/Traits/PageAuthorsTrait.php index f429e28ab..b32c02eb4 100644 --- a/system/src/Grav/Framework/Flex/Pages/Traits/PageAuthorsTrait.php +++ b/system/src/Grav/Framework/Flex/Pages/Traits/PageAuthorsTrait.php @@ -54,12 +54,17 @@ trait PageAuthorsTrait public function getAuthors(): array { if (null === $this->_authors) { - $this->_authors = (array)$this->loadAuthors($this->getNestedProperty('header.authors')); + $this->_authors = (array)$this->loadAuthors($this->getNestedProperty('header.authors', [])); } return $this->_authors; } + public function getPermissions() + { + return $this->loadPermissions(); + } + /** * @param iterable $authors * @return array @@ -86,13 +91,14 @@ trait PageAuthorsTrait * @param UserInterface $user * @param string $action * @param string $scope + * @param bool $isMe * @return bool|null */ - protected function isAuthorizedOverride(UserInterface $user, string $action, string $scope): ?bool + protected function isAuthorizedOverride(UserInterface $user, string $action, string $scope, bool $isMe): ?bool { $authorized = $this->isAuthorizedByGroup($user, $action, $scope); - return $authorized ?? parent::isAuthorizedOverride($user, $action, $scope); + return $authorized ?? parent::isAuthorizedOverride($user, $action, $scope, $isMe); } /** @@ -110,16 +116,28 @@ trait PageAuthorsTrait protected function isAuthorizedByGroup(UserInterface $user, string $action, string $scope): ?bool { $authorized = null; + $username = $user->username; // In admin we want to check against group permissions. - $groups = $this->loadPermissions($user); + $pageGroups = $this->loadPermissions(); + $userGroups = (array)$user->groups; + /** @var Access $access */ - foreach ($groups as $access) { + foreach ($pageGroups as $group => $access) { + if ($group === 'authors') { + if (!$this->hasAuthor($username)) { + continue; + } + } elseif (!in_array($group, $userGroups, true)) { + continue; + } + $auth = $access->authorize($action, $scope); if (is_bool($auth)) { if ($auth === false) { return false; } + $authorized = true; } } @@ -128,10 +146,9 @@ trait PageAuthorsTrait } /** - * @param UserInterface $user * @return array */ - protected function loadPermissions(UserInterface $user): array + protected function loadPermissions(): array { $permissions = $this->getNestedProperty('header.permissions'); if (empty($permissions)) { @@ -143,18 +160,8 @@ trait PageAuthorsTrait if (is_string($access)) { $access = $this->resolvePermissions($access); } - if ($group === 'author') { - // Special case for authors. - if ($this->hasAuthor($user->username)) { - $list[$group] = new Access($permissions); - } - } else { - $groups = (array)$user->groups; - if (in_array($groups, $groups, true)) { - $list[$group] = new Access($permissions); - } - } - $list[$group] = $access; + + $list[$group] = new Access($access); } return $list; @@ -192,6 +199,6 @@ trait PageAuthorsTrait } abstract public function getNestedProperty($property, $default = null, $separator = null); - abstract protected function loadAccounts(): ?UserCollectionInterface; + abstract protected function loadAccounts(); } diff --git a/system/src/Grav/Framework/Flex/Traits/FlexAuthorizeTrait.php b/system/src/Grav/Framework/Flex/Traits/FlexAuthorizeTrait.php index 9040377a4..611eb3649 100644 --- a/system/src/Grav/Framework/Flex/Traits/FlexAuthorizeTrait.php +++ b/system/src/Grav/Framework/Flex/Traits/FlexAuthorizeTrait.php @@ -41,13 +41,18 @@ trait FlexAuthorizeTrait { $action = $this->getAuthorizeAction($action); $scope = $scope ?? $this->getAuthorizeScope(); - $user = $user ?? $this->getActiveUser(); + + $isMe = null === $user; + if ($isMe) { + $user = $this->getActiveUser(); + } + if (null === $user) { return false; } // Finally authorize against given action. - return $this->isAuthorizedOverride($user, $action, $scope); + return $this->isAuthorizedOverride($user, $action, $scope, $isMe); } /** @@ -56,11 +61,12 @@ trait FlexAuthorizeTrait * @param UserInterface $user * @param string $action * @param string $scope + * @param bool $isMe * @return bool|null */ - protected function isAuthorizedOverride(UserInterface $user, string $action, string $scope): ?bool + protected function isAuthorizedOverride(UserInterface $user, string $action, string $scope, bool $isMe): ?bool { - return $this->isAuthorizedAction($user, $action, $scope); + return $this->isAuthorizedAction($user, $action, $scope, $isMe); } /** @@ -69,9 +75,10 @@ trait FlexAuthorizeTrait * @param UserInterface $user * @param string $action * @param string $scope + * @param bool $isMe * @return bool|null */ - protected function isAuthorizedAction(UserInterface $user, string $action, string $scope): ?bool + protected function isAuthorizedAction(UserInterface $user, string $action, string $scope, bool $isMe): ?bool { // Check if the action has been denied in the flex type configuration. $directory = $this instanceof FlexDirectory ? $this : $this->getFlexDirectory(); @@ -88,7 +95,7 @@ trait FlexAuthorizeTrait } // Finally authorize the action. - return $user->authorize($this->getAuthorizeRule($scope, $action)); + return $user->authorize($this->getAuthorizeRule($scope, $action), !$isMe ? 'test' : null); } /**