@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 HeraldWebhook
4 extends HeraldDAO
5 implements
6 PhabricatorPolicyInterface,
7 PhabricatorApplicationTransactionInterface,
8 PhabricatorDestructibleInterface,
9 PhabricatorProjectInterface {
10
11 protected $name;
12 protected $webhookURI;
13 protected $viewPolicy;
14 protected $editPolicy;
15 protected $status;
16 protected $hmacKey;
17
18 const HOOKSTATUS_FIREHOSE = 'firehose';
19 const HOOKSTATUS_ENABLED = 'enabled';
20 const HOOKSTATUS_DISABLED = 'disabled';
21
22 protected function getConfiguration() {
23 return array(
24 self::CONFIG_AUX_PHID => true,
25 self::CONFIG_COLUMN_SCHEMA => array(
26 'name' => 'text128',
27 'webhookURI' => 'text',
28 'status' => 'text32',
29 'hmacKey' => 'text32',
30 ),
31 self::CONFIG_KEY_SCHEMA => array(
32 'key_status' => array(
33 'columns' => array('status'),
34 ),
35 ),
36 ) + parent::getConfiguration();
37 }
38
39 public function getPHIDType() {
40 return HeraldWebhookPHIDType::TYPECONST;
41 }
42
43 public static function initializeNewWebhook(PhabricatorUser $viewer) {
44 return id(new self())
45 ->setStatus(self::HOOKSTATUS_ENABLED)
46 ->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy())
47 ->setEditPolicy($viewer->getPHID())
48 ->regenerateHMACKey();
49 }
50
51 public function getURI() {
52 return '/herald/webhook/view/'.$this->getID().'/';
53 }
54
55 public function isDisabled() {
56 return ($this->getStatus() === self::HOOKSTATUS_DISABLED);
57 }
58
59 public static function getStatusDisplayNameMap() {
60 $specs = self::getStatusSpecifications();
61 return ipull($specs, 'name', 'key');
62 }
63
64 private static function getStatusSpecifications() {
65 $specs = array(
66 array(
67 'key' => self::HOOKSTATUS_FIREHOSE,
68 'name' => pht('Firehose'),
69 'color' => 'orange',
70 'icon' => 'fa-star-o',
71 ),
72 array(
73 'key' => self::HOOKSTATUS_ENABLED,
74 'name' => pht('Enabled'),
75 'color' => 'bluegrey',
76 'icon' => 'fa-check',
77 ),
78 array(
79 'key' => self::HOOKSTATUS_DISABLED,
80 'name' => pht('Disabled'),
81 'color' => 'dark',
82 'icon' => 'fa-ban',
83 ),
84 );
85
86 return ipull($specs, null, 'key');
87 }
88
89
90 private static function getSpecificationForStatus($status) {
91 $specs = self::getStatusSpecifications();
92
93 if (isset($specs[$status])) {
94 return $specs[$status];
95 }
96
97 return array(
98 'key' => $status,
99 'name' => pht('Unknown ("%s")', $status),
100 'icon' => 'fa-question',
101 'color' => 'indigo',
102 );
103 }
104
105 public static function getDisplayNameForStatus($status) {
106 $spec = self::getSpecificationForStatus($status);
107 return $spec['name'];
108 }
109
110 public static function getIconForStatus($status) {
111 $spec = self::getSpecificationForStatus($status);
112 return $spec['icon'];
113 }
114
115 public static function getColorForStatus($status) {
116 $spec = self::getSpecificationForStatus($status);
117 return $spec['color'];
118 }
119
120 public function getStatusDisplayName() {
121 $status = $this->getStatus();
122 return self::getDisplayNameForStatus($status);
123 }
124
125 public function getStatusIcon() {
126 $status = $this->getStatus();
127 return self::getIconForStatus($status);
128 }
129
130 public function getStatusColor() {
131 $status = $this->getStatus();
132 return self::getColorForStatus($status);
133 }
134
135 public function getErrorBackoffWindow() {
136 return phutil_units('5 minutes in seconds');
137 }
138
139 public function getErrorBackoffThreshold() {
140 return 10;
141 }
142
143 public function isInErrorBackoff(PhabricatorUser $viewer) {
144 $backoff_window = $this->getErrorBackoffWindow();
145 $backoff_threshold = $this->getErrorBackoffThreshold();
146
147 $now = PhabricatorTime::getNow();
148
149 $window_start = ($now - $backoff_window);
150
151 $requests = id(new HeraldWebhookRequestQuery())
152 ->setViewer($viewer)
153 ->withWebhookPHIDs(array($this->getPHID()))
154 ->withLastRequestEpochBetween($window_start, null)
155 ->withLastRequestResults(
156 array(
157 HeraldWebhookRequest::RESULT_FAIL,
158 ))
159 ->execute();
160
161 if (count($requests) >= $backoff_threshold) {
162 return true;
163 }
164
165 return false;
166 }
167
168 public function regenerateHMACKey() {
169 return $this->setHMACKey(Filesystem::readRandomCharacters(32));
170 }
171
172/* -( PhabricatorPolicyInterface )----------------------------------------- */
173
174
175 public function getCapabilities() {
176 return array(
177 PhabricatorPolicyCapability::CAN_VIEW,
178 PhabricatorPolicyCapability::CAN_EDIT,
179 );
180 }
181
182 public function getPolicy($capability) {
183 switch ($capability) {
184 case PhabricatorPolicyCapability::CAN_VIEW:
185 return $this->getViewPolicy();
186 case PhabricatorPolicyCapability::CAN_EDIT:
187 return $this->getEditPolicy();
188 }
189 }
190
191 public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
192 return false;
193 }
194
195
196/* -( PhabricatorApplicationTransactionInterface )------------------------- */
197
198
199 public function getApplicationTransactionEditor() {
200 return new HeraldWebhookEditor();
201 }
202
203 public function getApplicationTransactionTemplate() {
204 return new HeraldWebhookTransaction();
205 }
206
207
208/* -( PhabricatorDestructibleInterface )----------------------------------- */
209
210
211 public function destroyObjectPermanently(
212 PhabricatorDestructionEngine $engine) {
213
214 while (true) {
215 $requests = id(new HeraldWebhookRequestQuery())
216 ->setViewer($engine->getViewer())
217 ->withWebhookPHIDs(array($this->getPHID()))
218 ->setLimit(100)
219 ->execute();
220
221 if (!$requests) {
222 break;
223 }
224
225 foreach ($requests as $request) {
226 $request->delete();
227 }
228 }
229
230 $this->delete();
231 }
232
233
234}