@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class PhabricatorPolicyExplainController
4 extends PhabricatorPolicyController {
5
6 public function shouldAllowPublic() {
7 return true;
8 }
9
10 public function handleRequest(AphrontRequest $request) {
11 $viewer = $this->getViewer();
12
13 $phid = $request->getURIData('phid');
14 $capability = $request->getURIData('capability');
15
16 $object = id(new PhabricatorObjectQuery())
17 ->setViewer($viewer)
18 ->withPHIDs(array($phid))
19 ->executeOne();
20 if (!$object) {
21 return new Aphront404Response();
22 }
23
24 $policies = PhabricatorPolicyQuery::loadPolicies(
25 $viewer,
26 $object);
27
28 $policy = idx($policies, $capability);
29 if (!$policy) {
30 return new Aphront404Response();
31 }
32
33 $handle = id(new PhabricatorHandleQuery())
34 ->setViewer($viewer)
35 ->withPHIDs(array($phid))
36 ->executeOne();
37
38 $object_name = $handle->getName();
39 $object_uri = nonempty($handle->getURI(), '/');
40
41 $dialog = id(new AphrontDialogView())
42 ->setUser($viewer)
43 ->setClass('aphront-access-dialog aphront-policy-explain-dialog')
44 ->setTitle(pht('Policy Details: %s', $object_name))
45 ->addCancelButton($object_uri, pht('Done'));
46
47 $space_section = $this->buildSpaceSection(
48 $object,
49 $policy,
50 $capability);
51
52 $extended_section = $this->buildExtendedSection(
53 $object,
54 $capability);
55
56 $exceptions_section = $this->buildExceptionsSection(
57 $object,
58 $capability);
59
60 $object_section = $this->buildObjectSection(
61 $object,
62 $policy,
63 $capability,
64 $handle);
65
66 $dialog->appendChild(
67 array(
68 $space_section,
69 $extended_section,
70 $exceptions_section,
71 $object_section,
72 ));
73
74
75 return $dialog;
76 }
77
78 private function buildSpaceSection(
79 PhabricatorPolicyInterface $object,
80 PhabricatorPolicy $policy,
81 $capability) {
82 $viewer = $this->getViewer();
83
84 if (!($object instanceof PhabricatorSpacesInterface)) {
85 return null;
86 }
87
88 if (!PhabricatorSpacesNamespaceQuery::getSpacesExist()) {
89 return null;
90 }
91
92 $space_phid = PhabricatorSpacesNamespaceQuery::getObjectSpacePHID(
93 $object);
94
95 $spaces = PhabricatorSpacesNamespaceQuery::getViewerSpaces($viewer);
96 $space = idx($spaces, $space_phid);
97 if (!$space) {
98 return null;
99 }
100
101 $space_policies = PhabricatorPolicyQuery::loadPolicies($viewer, $space);
102 $space_policy = idx($space_policies, PhabricatorPolicyCapability::CAN_VIEW);
103 if (!$space_policy) {
104 return null;
105 }
106
107 $doc_href = PhabricatorEnv::getDoclink('Spaces User Guide');
108 $capability_name = $this->getCapabilityName($capability);
109
110 $space_section = id(new PHUIPolicySectionView())
111 ->setViewer($viewer)
112 ->setIcon('fa-th-large bluegrey')
113 ->setHeader(pht('Space'))
114 ->setDocumentationLink(pht('Spaces Documentation'), $doc_href)
115 ->appendList(
116 array(
117 array(
118 phutil_tag('strong', array(), pht('Space:')),
119 ' ',
120 $viewer->renderHandle($space_phid)->setAsTag(true),
121 ),
122 array(
123 phutil_tag('strong', array(), pht('%s:', $capability_name)),
124 ' ',
125 $space_policy->getShortName(),
126 ),
127 ))
128 ->appendParagraph(
129 pht(
130 'This object is in %s and can only be seen or edited by users '.
131 'with access to view objects in the space.',
132 $viewer->renderHandle($space_phid)));
133
134 $space_explanation = PhabricatorPolicy::getPolicyExplanation(
135 $viewer,
136 $space_policy->getPHID());
137 $items = array();
138 $items[] = $space_explanation;
139
140 $space_section
141 ->appendParagraph(pht('Users who can see objects in this space:'))
142 ->appendList($items);
143
144 $view_capability = PhabricatorPolicyCapability::CAN_VIEW;
145 if ($capability == $view_capability) {
146 $stronger = $space_policy->isStrongerThan($policy);
147 if ($stronger) {
148 $space_section->appendHint(
149 pht(
150 'The space this object is in has a more restrictive view '.
151 'policy ("%s") than the object does ("%s"), so the space\'s '.
152 'view policy is shown as a hint instead of the object policy.',
153 $space_policy->getShortName(),
154 $policy->getShortName()));
155 }
156 }
157
158 $space_section->appendHint(
159 pht(
160 'After a user passes space policy checks, they must still pass '.
161 'object policy checks.'));
162
163 return $space_section;
164 }
165
166 private function getCapabilityName($capability) {
167 $capability_name = $capability;
168 $capobj = PhabricatorPolicyCapability::getCapabilityByKey($capability);
169 if ($capobj) {
170 $capability_name = $capobj->getCapabilityName();
171 }
172
173 return $capability_name;
174 }
175
176 private function buildExtendedSection(
177 PhabricatorPolicyInterface $object,
178 $capability) {
179 $viewer = $this->getViewer();
180
181 if (!($object instanceof PhabricatorExtendedPolicyInterface)) {
182 return null;
183 }
184
185 $extended_rules = $object->getExtendedPolicy($capability, $viewer);
186 if (!$extended_rules) {
187 return null;
188 }
189
190 $items = array();
191 foreach ($extended_rules as $extended_rule) {
192 $extended_target = $extended_rule[0];
193 $extended_capabilities = (array)$extended_rule[1];
194 if (is_object($extended_target)) {
195 $extended_target = $extended_target->getPHID();
196 }
197
198 foreach ($extended_capabilities as $extended_capability) {
199 $ex_name = $this->getCapabilityName($extended_capability);
200 $items[] = array(
201 phutil_tag('strong', array(), pht('%s:', $ex_name)),
202 ' ',
203 $viewer->renderHandle($extended_target)->setAsTag(true),
204 );
205 }
206 }
207
208 return id(new PHUIPolicySectionView())
209 ->setViewer($viewer)
210 ->setIcon('fa-link')
211 ->setHeader(pht('Required Capabilities on Other Objects'))
212 ->appendParagraph(
213 pht(
214 'To access this object, users must have first have access '.
215 'capabilities on these other objects:'))
216 ->appendList($items);
217 }
218
219 private function buildExceptionsSection(
220 PhabricatorPolicyInterface $object,
221 $capability) {
222 $viewer = $this->getViewer();
223
224 $exceptions = PhabricatorPolicy::getSpecialRules(
225 $object,
226 $viewer,
227 $capability,
228 false);
229
230 if (!$exceptions) {
231 return null;
232 }
233
234 return id(new PHUIPolicySectionView())
235 ->setViewer($viewer)
236 ->setIcon('fa-unlock-alt red')
237 ->setHeader(pht('Special Rules'))
238 ->appendParagraph(
239 pht(
240 'This object has special rules which override normal object '.
241 'policy rules:'))
242 ->appendList($exceptions);
243 }
244
245 private function buildObjectSection(
246 PhabricatorPolicyInterface $object,
247 PhabricatorPolicy $policy,
248 $capability,
249 PhabricatorObjectHandle $handle) {
250
251 $viewer = $this->getViewer();
252 $capability_name = $this->getCapabilityName($capability);
253
254 $object_section = id(new PHUIPolicySectionView())
255 ->setViewer($viewer)
256 ->setIcon($handle->getIcon().' bluegrey')
257 ->setHeader(pht('Object Policy'))
258 ->appendParagraph(
259 array(
260 array(
261 phutil_tag('strong', array(), pht('%s:', $capability_name)),
262 ' ',
263 $policy->getShortName(),
264 ),
265 ))
266 ->appendParagraph(
267 pht(
268 'In detail, this means that these users can take this action, '.
269 'provided they pass all of the checks described above first:'))
270 ->appendList(
271 array(
272 PhabricatorPolicy::getPolicyExplanation(
273 $viewer,
274 $policy->getPHID()),
275 ));
276
277 if ($policy->isCustomPolicy()) {
278 $rules_view = id(new PhabricatorPolicyRulesView())
279 ->setViewer($viewer)
280 ->setPolicy($policy);
281 $object_section->appendRulesView($rules_view);
282 }
283
284 return $object_section;
285 }
286
287}