@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 AlmanacDevice
4 extends AlmanacDAO
5 implements
6 PhabricatorPolicyInterface,
7 PhabricatorApplicationTransactionInterface,
8 PhabricatorProjectInterface,
9 PhabricatorSSHPublicKeyInterface,
10 AlmanacPropertyInterface,
11 PhabricatorDestructibleInterface,
12 PhabricatorNgramsInterface,
13 PhabricatorConduitResultInterface,
14 PhabricatorExtendedPolicyInterface {
15
16 protected $name;
17 protected $nameIndex;
18 protected $viewPolicy;
19 protected $editPolicy;
20 protected $status;
21 protected $isBoundToClusterService;
22
23 private $almanacProperties = self::ATTACHABLE;
24
25 public static function initializeNewDevice() {
26 return id(new AlmanacDevice())
27 ->setViewPolicy(PhabricatorPolicies::POLICY_USER)
28 ->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN)
29 ->setStatus(AlmanacDeviceStatus::ACTIVE)
30 ->attachAlmanacProperties(array())
31 ->setIsBoundToClusterService(0);
32 }
33
34 protected function getConfiguration() {
35 return array(
36 self::CONFIG_AUX_PHID => true,
37 self::CONFIG_COLUMN_SCHEMA => array(
38 'name' => 'text128',
39 'nameIndex' => 'bytes12',
40 'status' => 'text32',
41 'isBoundToClusterService' => 'bool',
42 ),
43 self::CONFIG_KEY_SCHEMA => array(
44 'key_name' => array(
45 'columns' => array('nameIndex'),
46 'unique' => true,
47 ),
48 'key_nametext' => array(
49 'columns' => array('name'),
50 ),
51 ),
52 ) + parent::getConfiguration();
53 }
54
55 public function getPHIDType() {
56 return AlmanacDevicePHIDType::TYPECONST;
57 }
58
59 public function save() {
60 AlmanacNames::validateName($this->getName());
61
62 $this->nameIndex = PhabricatorHash::digestForIndex($this->getName());
63
64 return parent::save();
65 }
66
67 public function getURI() {
68 return urisprintf(
69 '/almanac/device/view/%s/',
70 $this->getName());
71 }
72
73 public function rebuildClusterBindingStatus() {
74 $services = id(new AlmanacServiceQuery())
75 ->setViewer(PhabricatorUser::getOmnipotentUser())
76 ->withDevicePHIDs(array($this->getPHID()))
77 ->execute();
78
79 $is_cluster = false;
80 foreach ($services as $service) {
81 if ($service->isClusterService()) {
82 $is_cluster = true;
83 break;
84 }
85 }
86
87 if ($is_cluster != $this->getIsBoundToClusterService()) {
88 $this->setIsBoundToClusterService((int)$is_cluster);
89 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
90 queryfx(
91 $this->establishConnection('w'),
92 'UPDATE %R SET isBoundToClusterService = %d WHERE id = %d',
93 $this,
94 $this->getIsBoundToClusterService(),
95 $this->getID());
96 unset($unguarded);
97 }
98
99 return $this;
100 }
101
102 public function isClusterDevice() {
103 return $this->getIsBoundToClusterService();
104 }
105
106 public function getStatusObject() {
107 return $this->newStatusObject();
108 }
109
110 private function newStatusObject() {
111 return AlmanacDeviceStatus::newStatusFromValue($this->getStatus());
112 }
113
114 public function isDisabled() {
115 return $this->getStatusObject()->isDisabled();
116 }
117
118
119/* -( AlmanacPropertyInterface )------------------------------------------- */
120
121
122 /**
123 * @param array<AlmanacProperty> $properties
124 */
125 public function attachAlmanacProperties(array $properties) {
126 assert_instances_of($properties, AlmanacProperty::class);
127 $this->almanacProperties = mpull($properties, null, 'getFieldName');
128 return $this;
129 }
130
131 public function getAlmanacProperties() {
132 return $this->assertAttached($this->almanacProperties);
133 }
134
135 public function hasAlmanacProperty($key) {
136 $this->assertAttached($this->almanacProperties);
137 return isset($this->almanacProperties[$key]);
138 }
139
140 public function getAlmanacProperty($key) {
141 return $this->assertAttachedKey($this->almanacProperties, $key);
142 }
143
144 public function getAlmanacPropertyValue($key, $default = null) {
145 if ($this->hasAlmanacProperty($key)) {
146 return $this->getAlmanacProperty($key)->getFieldValue();
147 } else {
148 return $default;
149 }
150 }
151
152 public function getAlmanacPropertyFieldSpecifications() {
153 return array();
154 }
155
156 public function newAlmanacPropertyEditEngine() {
157 return new AlmanacDevicePropertyEditEngine();
158 }
159
160 public function getAlmanacPropertySetTransactionType() {
161 return AlmanacDeviceSetPropertyTransaction::TRANSACTIONTYPE;
162 }
163
164 public function getAlmanacPropertyDeleteTransactionType() {
165 return AlmanacDeviceDeletePropertyTransaction::TRANSACTIONTYPE;
166 }
167
168
169/* -( PhabricatorPolicyInterface )----------------------------------------- */
170
171
172 public function getCapabilities() {
173 return array(
174 PhabricatorPolicyCapability::CAN_VIEW,
175 PhabricatorPolicyCapability::CAN_EDIT,
176 );
177 }
178
179 public function getPolicy($capability) {
180 switch ($capability) {
181 case PhabricatorPolicyCapability::CAN_VIEW:
182 return $this->getViewPolicy();
183 case PhabricatorPolicyCapability::CAN_EDIT:
184 return $this->getEditPolicy();
185 }
186 }
187
188 public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
189 return false;
190 }
191
192
193/* -( PhabricatorExtendedPolicyInterface )--------------------------------- */
194
195
196 public function getExtendedPolicy($capability, PhabricatorUser $viewer) {
197 switch ($capability) {
198 case PhabricatorPolicyCapability::CAN_EDIT:
199 if ($this->isClusterDevice()) {
200 return array(
201 array(
202 new PhabricatorAlmanacApplication(),
203 AlmanacManageClusterServicesCapability::CAPABILITY,
204 ),
205 );
206 }
207 break;
208 }
209
210 return array();
211 }
212
213
214/* -( PhabricatorApplicationTransactionInterface )------------------------- */
215
216
217 public function getApplicationTransactionEditor() {
218 return new AlmanacDeviceEditor();
219 }
220
221 public function getApplicationTransactionTemplate() {
222 return new AlmanacDeviceTransaction();
223 }
224
225
226/* -( PhabricatorSSHPublicKeyInterface )----------------------------------- */
227
228
229 public function getSSHPublicKeyManagementURI(PhabricatorUser $viewer) {
230 return $this->getURI();
231 }
232
233 public function getSSHKeyDefaultName() {
234 return $this->getName();
235 }
236
237 public function getSSHKeyNotifyPHIDs() {
238 // Devices don't currently have anyone useful to notify about SSH key
239 // edits, and they're usually a difficult vector to attack since you need
240 // access to a cluster host. However, it would be nice to make them
241 // subscribable at some point.
242 return array();
243 }
244
245
246/* -( PhabricatorDestructibleInterface )----------------------------------- */
247
248
249 public function destroyObjectPermanently(
250 PhabricatorDestructionEngine $engine) {
251
252 $interfaces = id(new AlmanacInterfaceQuery())
253 ->setViewer($engine->getViewer())
254 ->withDevicePHIDs(array($this->getPHID()))
255 ->execute();
256 foreach ($interfaces as $interface) {
257 $engine->destroyObject($interface);
258 }
259
260 $this->delete();
261 }
262
263
264/* -( PhabricatorNgramsInterface )----------------------------------------- */
265
266
267 public function newNgrams() {
268 return array(
269 id(new AlmanacDeviceNameNgrams())
270 ->setValue($this->getName()),
271 );
272 }
273
274
275/* -( PhabricatorConduitResultInterface )---------------------------------- */
276
277
278 public function getFieldSpecificationsForConduit() {
279 return array(
280 id(new PhabricatorConduitSearchFieldSpecification())
281 ->setKey('name')
282 ->setType('string')
283 ->setDescription(pht('The name of the device.')),
284 id(new PhabricatorConduitSearchFieldSpecification())
285 ->setKey('status')
286 ->setType('map<string, wild>')
287 ->setDescription(pht('Device status information.')),
288 id(new PhabricatorConduitSearchFieldSpecification())
289 ->setKey('disabled')
290 ->setType('bool')
291 ->setDescription(pht('True if device is disabled.')),
292 );
293 }
294
295 public function getFieldValuesForConduit() {
296 $status = $this->getStatusObject();
297
298 return array(
299 'name' => $this->getName(),
300 'status' => array(
301 'value' => $status->getValue(),
302 'name' => $status->getName(),
303 ),
304 'disabled' => $this->isDisabled(),
305 );
306 }
307
308 public function getConduitSearchAttachments() {
309 return array(
310 id(new AlmanacPropertiesSearchEngineAttachment())
311 ->setAttachmentKey('properties'),
312 );
313 }
314
315}