4 * @task objectpolicy Implementing Object Policies
6 abstract class PhabricatorPolicyRule
extends Phobject
{
8 const CONTROL_TYPE_TEXT
= 'text';
9 const CONTROL_TYPE_SELECT
= 'select';
10 const CONTROL_TYPE_TOKENIZER
= 'tokenizer';
11 const CONTROL_TYPE_NONE
= 'none';
13 abstract public function getRuleDescription();
15 public function willApplyRules(
16 PhabricatorUser
$viewer,
22 abstract public function applyRule(
23 PhabricatorUser
$viewer,
25 PhabricatorPolicyInterface
$object);
27 public function getValueControlType() {
28 return self
::CONTROL_TYPE_TEXT
;
31 public function getValueControlTemplate() {
36 * Return `true` if this rule can be applied to the given object.
38 * Some policy rules may only operation on certain kinds of objects. For
39 * example, a "task author" rule can only operate on tasks.
41 public function canApplyToObject(PhabricatorPolicyInterface
$object) {
45 protected function getDatasourceTemplate(
46 PhabricatorTypeaheadDatasource
$datasource) {
49 'markup' => new AphrontTokenizerTemplateView(),
50 'uri' => $datasource->getDatasourceURI(),
51 'placeholder' => $datasource->getPlaceholderText(),
52 'browseURI' => $datasource->getBrowseURI(),
56 public function getRuleOrder() {
60 public function getValueForStorage($value) {
64 public function getValueForDisplay(PhabricatorUser
$viewer, $value) {
68 public function getRequiredHandlePHIDsForSummary($value) {
71 switch ($this->getValueControlType()) {
72 case self
::CONTROL_TYPE_TOKENIZER
:
75 case self
::CONTROL_TYPE_TEXT
:
76 case self
::CONTROL_TYPE_SELECT
:
77 case self
::CONTROL_TYPE_NONE
:
79 if (phid_get_type($value) !=
80 PhabricatorPHIDConstants
::PHID_TYPE_UNKNOWN
) {
81 $phids = array($value);
92 * Return `true` if the given value creates a rule with a meaningful effect.
93 * An example of a rule with no meaningful effect is a "users" rule with no
96 * @return bool True if the value creates a meaningful rule.
98 public function ruleHasEffect($value) {
103 /* -( Transaction Hints )-------------------------------------------------- */
107 * Tell policy rules about upcoming transaction effects.
109 * Before transaction effects are applied, we try to stop users from making
110 * edits which will lock them out of objects. We can't do this perfectly,
111 * since they can set a policy to "the moon is full" moments before it wanes,
112 * but we try to prevent as many mistakes as possible.
114 * Some policy rules depend on complex checks against object state which
115 * we can't set up ahead of time. For example, subscriptions require database
118 * In cases like this, instead of doing writes, you can pass a hint about an
119 * object to a policy rule. The rule can then look for hints and use them in
120 * rendering a verdict about whether the user will be able to see the object
121 * or not after applying the policy change.
123 * @param PhabricatorPolicyInterface Object to pass a hint about.
124 * @param PhabricatorPolicyRule Rule to pass hint to.
128 public static function passTransactionHintToRule(
129 PhabricatorPolicyInterface
$object,
130 PhabricatorPolicyRule
$rule,
133 $cache = PhabricatorCaches
::getRequestCache();
134 $cache->setKey(self
::getObjectPolicyCacheKey($object, $rule), $hint);
137 final protected function getTransactionHint(
138 PhabricatorPolicyInterface
$object) {
140 $cache = PhabricatorCaches
::getRequestCache();
141 return $cache->getKey(self
::getObjectPolicyCacheKey($object, $this));
144 private static function getObjectPolicyCacheKey(
145 PhabricatorPolicyInterface
$object,
146 PhabricatorPolicyRule
$rule) {
148 // NOTE: This is quite a bit of a hack, but we don't currently have a
149 // better way to carry hints from the TransactionEditor into PolicyRules
150 // about pending policy changes.
152 // Put some magic secret unique value on each object so we can pass
153 // information about it by proxy. This allows us to test if pending
154 // edits to an object will cause policy violations or not, before allowing
155 // those edits to go through.
157 // Some better approaches might be:
158 // - Use traits to give `PhabricatorPolicyInterface` objects real
159 // storage (requires PHP 5.4.0).
160 // - Wrap policy objects in a container with extra storage which the
161 // policy filter knows how to unbox (lots of work).
163 // When this eventually gets cleaned up, the corresponding hack in
164 // LiskDAO->__set() should also be cleaned up.
166 if (!isset($object->_hashKey
)) {
167 @$object->_hashKey
= 'object.id('.(++
$id).')';
170 return $object->_hashKey
;
174 /* -( Implementing Object Policies )--------------------------------------- */
178 * Return a unique string like "maniphest.author" to expose this rule as an
181 * Object policy rules, like "Task Author", are more advanced than basic
182 * policy rules (like "All Users") but not as powerful as custom rules.
184 * @return string Unique identifier for this rule.
187 public function getObjectPolicyKey() {
191 final public function getObjectPolicyFullKey() {
192 $key = $this->getObjectPolicyKey();
197 'This policy rule (of class "%s") does not have an associated '.
198 'object policy key.',
202 return PhabricatorPolicyQuery
::OBJECT_POLICY_PREFIX
.$key;
205 public function getObjectPolicyName() {
206 throw new PhutilMethodNotImplementedException();
209 public function getObjectPolicyShortName() {
210 return $this->getObjectPolicyName();
213 public function getObjectPolicyIcon() {
217 public function getPolicyExplanation() {
218 throw new PhutilMethodNotImplementedException();