Changeset View
Changeset View
Standalone View
Standalone View
src/applications/policy/filter/PhabricatorPolicyFilter.php
| Show First 20 Lines • Show All 237 Lines • ▼ Show 20 Lines | if ($extended) { | ||||
| // Put results back in the original order. | // Put results back in the original order. | ||||
| $results = array_select_keys($results, array_keys($filtered)); | $results = array_select_keys($results, array_keys($filtered)); | ||||
| } | } | ||||
| return $results; | return $results; | ||||
| } | } | ||||
| private function applyExtendedPolicyChecks(array $extended_objects) { | private function applyExtendedPolicyChecks(array $extended_objects) { | ||||
| // First, we're going to detect cycles and reject any objects which are | |||||
| // part of a cycle. We don't want to loop forever if an object has a | |||||
| // self-referential or nonsense policy. | |||||
| static $in_flight = array(); | |||||
| $all_phids = array(); | |||||
| foreach ($extended_objects as $key => $object) { | |||||
| $phid = $object->getPHID(); | |||||
| if (isset($in_flight[$phid])) { | |||||
| // TODO: This could be more user-friendly. | |||||
| $this->rejectObject($extended_objects[$key], false, '<cycle>'); | |||||
| unset($extended_objects[$key]); | |||||
| continue; | |||||
| } | |||||
| // We might throw from rejectObject(), so we don't want to actually mark | |||||
| // anything as in-flight until we survive this entire step. | |||||
| $all_phids[$phid] = $phid; | |||||
| } | |||||
| foreach ($all_phids as $phid) { | |||||
| $in_flight[$phid] = true; | |||||
| } | |||||
| $caught = null; | |||||
| try { | |||||
| $extended_objects = $this->executeExtendedPolicyChecks($extended_objects); | |||||
| } catch (Exception $ex) { | |||||
| $caught = $ex; | |||||
| } | |||||
| foreach ($all_phids as $phid) { | |||||
| unset($in_flight[$phid]); | |||||
| } | |||||
| if ($caught) { | |||||
| throw $caught; | |||||
| } | |||||
| return $extended_objects; | |||||
| } | |||||
| private function executeExtendedPolicyChecks(array $extended_objects) { | |||||
| $viewer = $this->viewer; | $viewer = $this->viewer; | ||||
| $filter_capabilities = $this->capabilities; | $filter_capabilities = $this->capabilities; | ||||
| // Iterate over the objects we need to filter and pull all the nonempty | // Iterate over the objects we need to filter and pull all the nonempty | ||||
| // policies into a flat, structured list. | // policies into a flat, structured list. | ||||
| $all_structs = array(); | $all_structs = array(); | ||||
| foreach ($extended_objects as $key => $extended_object) { | foreach ($extended_objects as $key => $extended_object) { | ||||
| foreach ($filter_capabilities as $extended_capability) { | foreach ($filter_capabilities as $extended_capability) { | ||||
| ▲ Show 20 Lines • Show All 93 Lines • ▼ Show 20 Lines | foreach ($groups as $structs) { | ||||
| $phid = $object->getPHID(); | $phid = $object->getPHID(); | ||||
| $key_map[$phid][] = $extended_key; | $key_map[$phid][] = $extended_key; | ||||
| $objects_in[$phid] = $object; | $objects_in[$phid] = $object; | ||||
| } | } | ||||
| if ($objects_in) { | if ($objects_in) { | ||||
| $objects_out = id(new PhabricatorPolicyFilter()) | $objects_out = $this->executeExtendedPolicyChecks( | ||||
| ->setViewer($viewer) | $viewer, | ||||
| ->requireCapabilities($capabilities) | $capabilities, | ||||
| ->apply($objects_in); | $objects_in, | ||||
| $key_map); | |||||
| $objects_out = mpull($objects_out, null, 'getPHID'); | $objects_out = mpull($objects_out, null, 'getPHID'); | ||||
| } else { | } else { | ||||
| $objects_out = array(); | $objects_out = array(); | ||||
| } | } | ||||
| // If any objects were removed by filtering, we're going to reject all | // If any objects were removed by filtering, we're going to reject all | ||||
| // of the original objects which needed them. | // of the original objects which needed them. | ||||
| foreach ($objects_in as $phid => $object_in) { | foreach ($objects_in as $phid => $object_in) { | ||||
| Show All 19 Lines | foreach ($groups as $structs) { | ||||
| $this->rejectObject($reject, false, '<extended>'); | $this->rejectObject($reject, false, '<extended>'); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| return $extended_objects; | return $extended_objects; | ||||
| } | } | ||||
| private function executeExtendedPolicyChecks( | |||||
| PhabricatorUser $viewer, | |||||
| array $capabilities, | |||||
| array $objects, | |||||
| array $key_map) { | |||||
| // Do crude cycle detection by seeing if we have a huge stack depth. | |||||
| // Although more sophisticated cycle detection is possible in theory, | |||||
| // it is difficult with hierarchical objects like subprojects. Many other | |||||
| // checks make it difficult to create cycles normally, so just do a | |||||
| // simple check here to limit damage. | |||||
| static $depth; | |||||
| $depth++; | |||||
| if ($depth > 32) { | |||||
| foreach ($objects as $key => $object) { | |||||
| $this->rejectObject($objects[$key], false, '<cycle>'); | |||||
| unset($objects[$key]); | |||||
| continue; | |||||
| } | |||||
| } | |||||
| if (!$objects) { | |||||
| return array(); | |||||
| } | |||||
| $caught = null; | |||||
| try { | |||||
| $result = id(new PhabricatorPolicyFilter()) | |||||
| ->setViewer($viewer) | |||||
| ->requireCapabilities($capabilities) | |||||
| ->apply($objects); | |||||
| } catch (Exception $ex) { | |||||
| $caught = $ex; | |||||
| } | |||||
| $depth--; | |||||
| if ($caught) { | |||||
| throw $caught; | |||||
| } | |||||
| return $result; | |||||
| } | |||||
| private function checkCapability( | private function checkCapability( | ||||
| PhabricatorPolicyInterface $object, | PhabricatorPolicyInterface $object, | ||||
| $capability) { | $capability) { | ||||
| $policy = $this->getObjectPolicy($object, $capability); | $policy = $this->getObjectPolicy($object, $capability); | ||||
| if (!$policy) { | if (!$policy) { | ||||
| // TODO: Formalize this somehow? | // TODO: Formalize this somehow? | ||||
| ▲ Show 20 Lines • Show All 403 Lines • Show Last 20 Lines | |||||