/** * Builds the API UserGroupRoleAssignment object from provided SPI RoleAssignment object. * * @param \eZ\Publish\SPI\Persistence\User\RoleAssignment $spiRoleAssignment * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup * @param \eZ\Publish\API\Repository\Values\User\Role $role * * @return \eZ\Publish\API\Repository\Values\User\UserGroupRoleAssignment */ public function buildDomainUserGroupRoleAssignmentObject(SPIRoleAssignment $spiRoleAssignment, UserGroup $userGroup, APIRole $role) { $limitation = null; if (!empty($spiRoleAssignment->limitationIdentifier)) { $limitation = $this->limitationService->getLimitationType($spiRoleAssignment->limitationIdentifier)->buildValue($spiRoleAssignment->values); } return new UserGroupRoleAssignment(array('limitation' => $limitation, 'role' => $role, 'userGroup' => $userGroup)); }
/** * Validates Policy context: Limitations on a module and function. * * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException If the same limitation is repeated or if * limitation is not allowed on module/function * * @param string $module * @param string $function * @param \eZ\Publish\API\Repository\Values\User\Limitation[] $limitations * * @return \eZ\Publish\Core\FieldType\ValidationError[][] */ protected function validatePolicy($module, $function, array $limitations) { if ($module !== '*' && $function !== '*' && !empty($limitations)) { $limitationSet = array(); foreach ($limitations as $limitation) { if (isset($limitationSet[$limitation->getIdentifier()])) { throw new InvalidArgumentException('limitations', "'{$limitation->getIdentifier()}' was found several times among the limitations"); } if (!isset($this->settings['policyMap'][$module][$function][$limitation->getIdentifier()])) { throw new InvalidArgumentException('policy', "The limitation '{$limitation->getIdentifier()}' is not applicable on '{$module}/{$function}'"); } $limitationSet[$limitation->getIdentifier()] = true; } } return $this->limitationService->validateLimitations($limitations); }
/** * Get content-read Permission criteria if needed and return false if no access at all. * * @uses \eZ\Publish\API\Repository\PermissionResolver::hasAccess() * * @throws \RuntimeException If empty array of limitations are provided from hasAccess() * * @param string $module * @param string $function * * @return bool|\eZ\Publish\API\Repository\Values\Content\Query\Criterion */ public function getPermissionsCriterion($module = 'content', $function = 'read') { $permissionSets = $this->permissionResolver->hasAccess($module, $function); if ($permissionSets === false || $permissionSets === true) { return $permissionSets; } if (empty($permissionSets)) { throw new RuntimeException("Got an empty array of limitations from hasAccess( '{$module}', '{$function}' )"); } /* * RoleAssignment is a OR condition, so is policy, while limitations is a AND condition * * If RoleAssignment has limitation then policy OR conditions are wrapped in a AND condition with the * role limitation, otherwise it will be merged into RoleAssignment's OR condition. */ $currentUserRef = $this->permissionResolver->getCurrentUserReference(); $roleAssignmentOrCriteria = array(); foreach ($permissionSets as $permissionSet) { // $permissionSet is a RoleAssignment, but in the form of role limitation & role policies hash $policyOrCriteria = array(); /** * @var \eZ\Publish\API\Repository\Values\User\Policy */ foreach ($permissionSet['policies'] as $policy) { $limitations = $policy->getLimitations(); if ($limitations === '*' || empty($limitations)) { // Given policy gives full access, optimize away all role policies (but not role limitation if any) // This should be optimized on create/update of Roles, however we keep this here for bc with older data $policyOrCriteria = []; break; } $limitationsAndCriteria = array(); foreach ($limitations as $limitation) { $type = $this->limitationService->getLimitationType($limitation->getIdentifier()); $limitationsAndCriteria[] = $type->getCriterion($limitation, $currentUserRef); } $policyOrCriteria[] = isset($limitationsAndCriteria[1]) ? new LogicalAnd($limitationsAndCriteria) : $limitationsAndCriteria[0]; } /** * Apply role limitations if there is one. * * @var \eZ\Publish\API\Repository\Values\User\Limitation[] */ if ($permissionSet['limitation'] instanceof Limitation) { // We need to match both the limitation AND *one* of the policies, aka; roleLimit AND policies(OR) $type = $this->limitationService->getLimitationType($permissionSet['limitation']->getIdentifier()); if (!empty($policyOrCriteria)) { $roleAssignmentOrCriteria[] = new LogicalAnd(array($type->getCriterion($permissionSet['limitation'], $currentUserRef), isset($policyOrCriteria[1]) ? new LogicalOr($policyOrCriteria) : $policyOrCriteria[0])); } else { $roleAssignmentOrCriteria[] = $type->getCriterion($permissionSet['limitation'], $currentUserRef); } } elseif (!empty($policyOrCriteria)) { // Otherwise merge $policyOrCriteria into $roleAssignmentOrCriteria // There is no role limitation, so any of the policies can globally match in the returned OR criteria $roleAssignmentOrCriteria = empty($roleAssignmentOrCriteria) ? $policyOrCriteria : array_merge($roleAssignmentOrCriteria, $policyOrCriteria); } } if (empty($roleAssignmentOrCriteria)) { return false; } return isset($roleAssignmentOrCriteria[1]) ? new LogicalOr($roleAssignmentOrCriteria) : $roleAssignmentOrCriteria[0]; }
public function canUser($module, $function, ValueObject $object, array $targets = []) { $permissionSets = $this->hasAccess($module, $function); if ($permissionSets === false || $permissionSets === true) { return $permissionSets; } if (empty($targets)) { $targets = null; } $currentUserRef = $this->getCurrentUserReference(); foreach ($permissionSets as $permissionSet) { /** * First deal with Role limitation if any. * * Here we accept ACCESS_GRANTED and ACCESS_ABSTAIN, the latter in cases where $object and $targets * are not supported by limitation. * * @var \eZ\Publish\API\Repository\Values\User\Limitation[] */ if ($permissionSet['limitation'] instanceof Limitation) { $type = $this->limitationService->getLimitationType($permissionSet['limitation']->getIdentifier()); $accessVote = $type->evaluate($permissionSet['limitation'], $currentUserRef, $object, $targets); if ($accessVote === LimitationType::ACCESS_DENIED) { continue; } } /** * Loop over all policies. * * These are already filtered by hasAccess and given hasAccess did not return boolean * there must be some, so only return true if one of them says yes. * * @var \eZ\Publish\API\Repository\Values\User\Policy */ foreach ($permissionSet['policies'] as $policy) { $limitations = $policy->getLimitations(); /* * Return true if policy gives full access (aka no limitations) */ if ($limitations === '*') { return true; } /* * Loop over limitations, all must return ACCESS_GRANTED for policy to pass. * If limitations was empty array this means same as '*' */ $limitationsPass = true; foreach ($limitations as $limitation) { $type = $this->limitationService->getLimitationType($limitation->getIdentifier()); $accessVote = $type->evaluate($limitation, $currentUserRef, $object, $targets); /* * For policy limitation atm only support ACCESS_GRANTED * * Reasoning: Right now, use of a policy limitation not valid for a policy is per definition a * BadState. To reach this you would have to configure the "policyMap" wrongly, like using * Node (Location) limitation on state/assign. So in this case Role Limitations will return * ACCESS_ABSTAIN (== no access here), and other limitations will throw InvalidArgument above, * both cases forcing dev to investigate to find miss configuration. This might be relaxed in * the future if valid use cases for ACCESS_ABSTAIN on policy limitations becomes known. */ if ($accessVote !== LimitationType::ACCESS_GRANTED) { $limitationsPass = false; break; // Break to next policy, all limitations must pass } } if ($limitationsPass) { return true; } } } return false; // None of the limitation sets wanted to let you in, sorry! }