Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php b/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php
index fec7c1f8af..767cfe5290 100644
--- a/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php
+++ b/src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php
@@ -1,372 +1,417 @@
<?php
abstract class PhabricatorRepositoryCommitMessageParserWorker
extends PhabricatorRepositoryCommitParserWorker {
protected function getImportStepFlag() {
return PhabricatorRepositoryCommit::IMPORTED_MESSAGE;
}
abstract protected function getFollowupTaskClass();
final protected function parseCommit(
PhabricatorRepository $repository,
PhabricatorRepositoryCommit $commit) {
if (!$this->shouldSkipImportStep()) {
$viewer = PhabricatorUser::getOmnipotentUser();
$refs_raw = DiffusionQuery::callConduitWithDiffusionRequest(
$viewer,
DiffusionRequest::newFromDictionary(
array(
'repository' => $repository,
'user' => $viewer,
)),
'diffusion.querycommits',
array(
'repositoryPHID' => $repository->getPHID(),
'phids' => array($commit->getPHID()),
'bypassCache' => true,
'needMessages' => true,
));
if (empty($refs_raw['data'])) {
throw new Exception(
pht(
'Unable to retrieve details for commit "%s"!',
$commit->getPHID()));
}
$ref = DiffusionCommitRef::newFromConduitResult(head($refs_raw['data']));
$this->updateCommitData($ref);
}
if ($this->shouldQueueFollowupTasks()) {
$this->queueTask(
$this->getFollowupTaskClass(),
array(
'commitID' => $commit->getID(),
),
array(
// We queue followup tasks at default priority so that the queue
// finishes work it has started before starting more work. If
// followups are queued at the same priority level, we do all
// message parses first, then all change parses, etc. This makes
// progress uneven. See T11677 for discussion.
'priority' => PhabricatorWorker::PRIORITY_DEFAULT,
));
}
}
final protected function updateCommitData(DiffusionCommitRef $ref) {
$commit = $this->commit;
$author = $ref->getAuthor();
$message = $ref->getMessage();
$committer = $ref->getCommitter();
$hashes = $ref->getHashes();
$data = id(new PhabricatorRepositoryCommitData())->loadOneWhere(
'commitID = %d',
$commit->getID());
if (!$data) {
$data = new PhabricatorRepositoryCommitData();
}
$data->setCommitID($commit->getID());
$data->setAuthorName(id(new PhutilUTF8StringTruncator())
->setMaximumBytes(255)
->truncateString((string)$author));
$data->setCommitDetail('authorEpoch', $ref->getAuthorEpoch());
$data->setCommitDetail('authorName', $ref->getAuthorName());
$data->setCommitDetail('authorEmail', $ref->getAuthorEmail());
$data->setCommitDetail(
'authorPHID',
$this->resolveUserPHID($commit, $author));
$data->setCommitMessage($message);
if (strlen($committer)) {
$data->setCommitDetail('committer', $committer);
$data->setCommitDetail('committerName', $ref->getCommitterName());
$data->setCommitDetail('committerEmail', $ref->getCommitterEmail());
$data->setCommitDetail(
'committerPHID',
$this->resolveUserPHID($commit, $committer));
}
$repository = $this->repository;
$author_phid = $data->getCommitDetail('authorPHID');
$committer_phid = $data->getCommitDetail('committerPHID');
$user = new PhabricatorUser();
if ($author_phid) {
$user = $user->loadOneWhere(
'phid = %s',
$author_phid);
}
$differential_app = 'PhabricatorDifferentialApplication';
$revision_id = null;
$low_level_query = null;
if (PhabricatorApplication::isClassInstalled($differential_app)) {
$low_level_query = id(new DiffusionLowLevelCommitFieldsQuery())
->setRepository($repository)
->withCommitRef($ref);
$field_values = $low_level_query->execute();
$revision_id = idx($field_values, 'revisionID');
if (!empty($field_values['reviewedByPHIDs'])) {
$data->setCommitDetail(
'reviewerPHID',
reset($field_values['reviewedByPHIDs']));
}
$data->setCommitDetail('differential.revisionID', $revision_id);
}
if ($author_phid != $commit->getAuthorPHID()) {
$commit->setAuthorPHID($author_phid);
}
$commit->setSummary($data->getSummary());
$commit->save();
// Figure out if we're going to try to "autoclose" related objects (e.g.,
// close linked tasks and related revisions) and, if not, record why we
// aren't. Autoclose can be disabled for various reasons at the repository
// or commit levels.
$force_autoclose = idx($this->getTaskData(), 'forceAutoclose', false);
if ($force_autoclose) {
$autoclose_reason = PhabricatorRepository::BECAUSE_AUTOCLOSE_FORCED;
} else {
$autoclose_reason = $repository->shouldSkipAutocloseCommit($commit);
}
$data->setCommitDetail('autocloseReason', $autoclose_reason);
$should_autoclose = $force_autoclose ||
$repository->shouldAutocloseCommit($commit);
// When updating related objects, we'll act under an omnipotent user to
// ensure we can see them, but take actions as either the committer or
// author (if we recognize their accounts) or the Diffusion application
// (if we do not).
$actor = PhabricatorUser::getOmnipotentUser();
$acting_as_phid = nonempty(
$committer_phid,
$author_phid,
id(new PhabricatorDiffusionApplication())->getPHID());
+ $acting_user = $this->loadActingUser($actor, $acting_as_phid);
+
$conn_w = id(new DifferentialRevision())->establishConnection('w');
// NOTE: The `differential_commit` table has a unique ID on `commitPHID`,
// preventing more than one revision from being associated with a commit.
// Generally this is good and desirable, but with the advent of hash
// tracking we may end up in a situation where we match several different
// revisions. We just kind of ignore this and pick one, we might want to
// revisit this and do something differently. (If we match several revisions
// someone probably did something very silly, though.)
$revision = null;
if ($revision_id) {
$revision_query = id(new DifferentialRevisionQuery())
->withIDs(array($revision_id))
->setViewer($actor)
->needReviewers(true)
->needActiveDiffs(true);
$revision = $revision_query->executeOne();
if ($revision) {
if (!$data->getCommitDetail('precommitRevisionStatus')) {
$data->setCommitDetail(
'precommitRevisionStatus',
$revision->getStatus());
}
$commit_drev = DiffusionCommitHasRevisionEdgeType::EDGECONST;
id(new PhabricatorEdgeEditor())
->addEdge($commit->getPHID(), $commit_drev, $revision->getPHID())
->save();
queryfx(
$conn_w,
'INSERT IGNORE INTO %T (revisionID, commitPHID) VALUES (%d, %s)',
DifferentialRevision::TABLE_COMMIT,
$revision->getID(),
$commit->getPHID());
$status_closed = ArcanistDifferentialRevisionStatus::CLOSED;
$should_close = ($revision->getStatus() != $status_closed) &&
$should_autoclose;
if ($should_close) {
$commit_close_xaction = id(new DifferentialTransaction())
->setTransactionType(DifferentialTransaction::TYPE_ACTION)
->setNewValue(DifferentialAction::ACTION_CLOSE)
->setMetadataValue('isCommitClose', true);
$commit_close_xaction->setMetadataValue(
'commitPHID',
$commit->getPHID());
$commit_close_xaction->setMetadataValue(
'committerPHID',
$committer_phid);
$commit_close_xaction->setMetadataValue(
'committerName',
$data->getCommitDetail('committer'));
$commit_close_xaction->setMetadataValue(
'authorPHID',
$author_phid);
$commit_close_xaction->setMetadataValue(
'authorName',
$data->getAuthorName());
if ($low_level_query) {
$commit_close_xaction->setMetadataValue(
'revisionMatchData',
$low_level_query->getRevisionMatchData());
$data->setCommitDetail(
'revisionMatchData',
$low_level_query->getRevisionMatchData());
}
$extraction_engine = id(new DifferentialDiffExtractionEngine())
->setViewer($actor)
->setAuthorPHID($acting_as_phid);
$content_source = $this->newContentSource();
$update_data = $extraction_engine->updateRevisionWithCommit(
$revision,
$commit,
array(
$commit_close_xaction,
),
$content_source);
foreach ($update_data as $key => $value) {
$data->setCommitDetail($key, $value);
}
}
}
}
if ($should_autoclose) {
$this->closeTasks(
$actor,
$acting_as_phid,
$repository,
$commit,
- $message);
+ $message,
+ $acting_user);
}
$data->save();
$commit->writeImportStatusFlag(
PhabricatorRepositoryCommit::IMPORTED_MESSAGE);
}
private function resolveUserPHID(
PhabricatorRepositoryCommit $commit,
$user_name) {
return id(new DiffusionResolveUserQuery())
->withCommit($commit)
->withName($user_name)
->execute();
}
private function closeTasks(
PhabricatorUser $actor,
$acting_as,
PhabricatorRepository $repository,
PhabricatorRepositoryCommit $commit,
- $message) {
+ $message,
+ PhabricatorUser $acting_user = null) {
+
+ // If we we were able to identify an author for the commit, we try to act
+ // as that user when loading tasks marked with "Fixes Txxx". This prevents
+ // mistakes where a user accidentally writes the wrong task IDs and affects
+ // tasks they can't see (and thus can't undo the status changes for).
+
+ // This is just a guard rail, not a security measure. An attacker can still
+ // forge another user's identity trivially by forging author or committer
+ // emails. We also let commits with unrecognized authors act on any task to
+ // make behavior less confusing for new installs.
+
+ if (!$acting_user) {
+ $acting_user = $actor;
+ }
$maniphest = 'PhabricatorManiphestApplication';
if (!PhabricatorApplication::isClassInstalled($maniphest)) {
return;
}
$prefixes = ManiphestTaskStatus::getStatusPrefixMap();
$suffixes = ManiphestTaskStatus::getStatusSuffixMap();
$matches = id(new ManiphestCustomFieldStatusParser())
->parseCorpus($message);
$task_statuses = array();
foreach ($matches as $match) {
$prefix = phutil_utf8_strtolower($match['prefix']);
$suffix = phutil_utf8_strtolower($match['suffix']);
$status = idx($suffixes, $suffix);
if (!$status) {
$status = idx($prefixes, $prefix);
}
foreach ($match['monograms'] as $task_monogram) {
$task_id = (int)trim($task_monogram, 'tT');
$task_statuses[$task_id] = $status;
}
}
if (!$task_statuses) {
return;
}
$tasks = id(new ManiphestTaskQuery())
- ->setViewer($actor)
+ ->setViewer($acting_user)
->withIDs(array_keys($task_statuses))
->needProjectPHIDs(true)
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
->execute();
foreach ($tasks as $task_id => $task) {
$xactions = array();
$edge_type = ManiphestTaskHasCommitEdgeType::EDGECONST;
$edge_xaction = id(new ManiphestTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $edge_type)
->setNewValue(
array(
'+' => array(
$commit->getPHID() => $commit->getPHID(),
),
));
$status = $task_statuses[$task_id];
if ($status) {
if ($task->getStatus() != $status) {
$xactions[] = id(new ManiphestTransaction())
->setTransactionType(ManiphestTransaction::TYPE_STATUS)
->setMetadataValue('commitPHID', $commit->getPHID())
->setNewValue($status);
$edge_xaction->setMetadataValue('commitPHID', $commit->getPHID());
}
}
$xactions[] = $edge_xaction;
$content_source = $this->newContentSource();
$editor = id(new ManiphestTransactionEditor())
->setActor($actor)
->setActingAsPHID($acting_as)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true)
->setUnmentionablePHIDMap(
array($commit->getPHID() => $commit->getPHID()))
->setContentSource($content_source);
$editor->applyTransactions($task, $xactions);
}
}
+ private function loadActingUser(PhabricatorUser $viewer, $user_phid) {
+ if (!$user_phid) {
+ return null;
+ }
+
+ $user_type = PhabricatorPeopleUserPHIDType::TYPECONST;
+ if (phid_get_type($user_phid) != $user_type) {
+ return null;
+ }
+
+ $user = id(new PhabricatorPeopleQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($user_phid))
+ ->executeOne();
+ if (!$user) {
+ return null;
+ }
+
+ return $user;
+ }
+
+
}

File Metadata

Mime Type
text/x-diff
Expires
Fri, Feb 7, 6:28 AM (1 d, 13 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34063
Default Alt Text
(14 KB)

Event Timeline