Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/diffusion/worker/DiffusionUpdateObjectAfterCommitWorker.php
12241 views
1
<?php
2
3
final class DiffusionUpdateObjectAfterCommitWorker
4
extends PhabricatorWorker {
5
6
private $properties;
7
8
protected function getViewer() {
9
return PhabricatorUser::getOmnipotentUser();
10
}
11
12
protected function doWork() {
13
$viewer = $this->getViewer();
14
$data = $this->getTaskData();
15
16
$commit_phid = idx($data, 'commitPHID');
17
if (!$commit_phid) {
18
throw new PhabricatorWorkerPermanentFailureException(
19
pht('No "commitPHID" in task data.'));
20
}
21
22
$commit = id(new DiffusionCommitQuery())
23
->setViewer($viewer)
24
->withPHIDs(array($commit_phid))
25
->needIdentities(true)
26
->executeOne();
27
if (!$commit) {
28
throw new PhabricatorWorkerPermanentFailureException(
29
pht(
30
'Unable to load commit "%s".',
31
$commit_phid));
32
}
33
34
$object_phid = idx($data, 'objectPHID');
35
if (!$object_phid) {
36
throw new PhabricatorWorkerPermanentFailureException(
37
pht('No "objectPHID" in task data.'));
38
}
39
40
$object = id(new PhabricatorObjectQuery())
41
->setViewer($viewer)
42
->withPHIDs(array($object_phid))
43
->executeOne();
44
if (!$object) {
45
throw new PhabricatorWorkerPermanentFailureException(
46
pht(
47
'Unable to load object "%s".',
48
$object_phid));
49
}
50
51
$properties = idx($data, 'properties', array());
52
$this->properties = $properties;
53
54
if ($object instanceof ManiphestTask) {
55
$this->updateTask($commit, $object);
56
} else if ($object instanceof DifferentialRevision) {
57
$this->updateRevision($commit, $object);
58
}
59
}
60
61
protected function getUpdateProperty($key, $default = null) {
62
return idx($this->properties, $key, $default);
63
}
64
65
protected function getActingPHID(PhabricatorRepositoryCommit $commit) {
66
if ($commit->hasCommitterIdentity()) {
67
return $commit->getCommitterIdentity()->getIdentityDisplayPHID();
68
}
69
70
if ($commit->hasAuthorIdentity()) {
71
return $commit->getAuthorIdentity()->getIdentityDisplayPHID();
72
}
73
74
return id(new PhabricatorDiffusionApplication())->getPHID();
75
}
76
77
protected function loadActingUser($acting_phid) {
78
// If we we were able to identify an author or committer for the commit, we
79
// try to act as that user when affecting other objects, like tasks marked
80
// with "Fixes Txxx".
81
82
// This helps to prevent mistakes where a user accidentally writes the
83
// wrong task IDs and affects tasks they can't see (and thus can't undo the
84
// status changes for).
85
86
// This is just a guard rail, not a security measure. An attacker can still
87
// forge another user's identity trivially by forging author or committer
88
// email addresses.
89
90
// We also let commits with unrecognized authors act on any task to make
91
// behavior less confusing for new installs, and any user can craft a
92
// commit with an unrecognized author and committer.
93
94
$viewer = $this->getViewer();
95
96
$user_type = PhabricatorPeopleUserPHIDType::TYPECONST;
97
if (phid_get_type($acting_phid) === $user_type) {
98
$acting_user = id(new PhabricatorPeopleQuery())
99
->setViewer($viewer)
100
->withPHIDs(array($acting_phid))
101
->executeOne();
102
if ($acting_user) {
103
return $acting_user;
104
}
105
}
106
107
return $viewer;
108
}
109
110
private function updateTask(
111
PhabricatorRepositoryCommit $commit,
112
ManiphestTask $task) {
113
114
$acting_phid = $this->getActingPHID($commit);
115
$acting_user = $this->loadActingUser($acting_phid);
116
117
$commit_phid = $commit->getPHID();
118
119
$xactions = array();
120
121
$xactions[] = $this->newEdgeTransaction(
122
$task,
123
$commit,
124
ManiphestTaskHasCommitEdgeType::EDGECONST);
125
126
$status = $this->getUpdateProperty('status');
127
if ($status) {
128
$xactions[] = $task->getApplicationTransactionTemplate()
129
->setTransactionType(ManiphestTaskStatusTransaction::TRANSACTIONTYPE)
130
->setMetadataValue('commitPHID', $commit_phid)
131
->setNewValue($status);
132
}
133
134
$content_source = $this->newContentSource();
135
136
$editor = $task->getApplicationTransactionEditor()
137
->setActor($acting_user)
138
->setActingAsPHID($acting_phid)
139
->setContentSource($content_source)
140
->setContinueOnNoEffect(true)
141
->setContinueOnMissingFields(true)
142
->addUnmentionablePHIDs(array($commit_phid));
143
144
$editor->applyTransactions($task, $xactions);
145
}
146
147
private function updateRevision(
148
PhabricatorRepositoryCommit $commit,
149
DifferentialRevision $revision) {
150
151
$acting_phid = $this->getActingPHID($commit);
152
$acting_user = $this->loadActingUser($acting_phid);
153
154
// See T13625. The "Acting User" is the author of the commit based on the
155
// author string, or the Diffusion application PHID if we could not
156
// identify an author.
157
158
// This user may not be able to view the commit or the revision, and may
159
// also be unable to make API calls. Here, we execute queries and apply
160
// transactions as the omnipotent user.
161
162
// It would probably be better to use the acting user everywhere here, and
163
// exit gracefully if they can't see the revision (this is how the flow
164
// on tasks works). However, without a positive indicator in the UI
165
// explaining "no revision was updated because the author of this commit
166
// can't see anything", this might be fairly confusing, and break workflows
167
// which have worked historically.
168
169
// This isn't, per se, a policy violation (you can't get access to anything
170
// you don't already have access to by making commits that reference
171
// revisions, even if you can't see the commits or revisions), so just
172
// leave it for now.
173
174
$viewer = $this->getViewer();
175
176
// Reload the revision to get the active diff, which is currently required
177
// by "updateRevisionWithCommit()".
178
$revision = id(new DifferentialRevisionQuery())
179
->setViewer($viewer)
180
->withIDs(array($revision->getID()))
181
->needActiveDiffs(true)
182
->executeOne();
183
184
$xactions = array();
185
186
$xactions[] = $this->newEdgeTransaction(
187
$revision,
188
$commit,
189
DifferentialRevisionHasCommitEdgeType::EDGECONST);
190
191
$match_data = $this->getUpdateProperty('revisionMatchData');
192
193
$type_close = DifferentialRevisionCloseTransaction::TRANSACTIONTYPE;
194
$xactions[] = $revision->getApplicationTransactionTemplate()
195
->setTransactionType($type_close)
196
->setNewValue(true)
197
->setMetadataValue('isCommitClose', true)
198
->setMetadataValue('revisionMatchData', $match_data)
199
->setMetadataValue('commitPHID', $commit->getPHID());
200
201
$extraction_engine = id(new DifferentialDiffExtractionEngine())
202
->setViewer($viewer)
203
->setAuthorPHID($acting_phid);
204
205
$content_source = $this->newContentSource();
206
207
$extraction_engine->updateRevisionWithCommit(
208
$revision,
209
$commit,
210
$xactions,
211
$content_source);
212
}
213
214
private function newEdgeTransaction(
215
$object,
216
PhabricatorRepositoryCommit $commit,
217
$edge_type) {
218
219
$commit_phid = $commit->getPHID();
220
221
return $object->getApplicationTransactionTemplate()
222
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
223
->setMetadataValue('edge:type', $edge_type)
224
->setNewValue(
225
array(
226
'+' => array(
227
$commit_phid => $commit_phid,
228
),
229
));
230
}
231
232
233
}
234
235