1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-15 01:50:55 +01:00

(stable) Promote 2018 Week 4

This commit is contained in:
epriestley 2018-01-26 15:23:23 -08:00
commit 4bf1bc2563
203 changed files with 6006 additions and 1673 deletions

1
bin/bulk Symbolic link
View file

@ -0,0 +1 @@
../scripts/setup/manage_bulk.php

View file

@ -9,16 +9,16 @@ return array(
'names' => array(
'conpherence.pkg.css' => 'e68cf1fa',
'conpherence.pkg.js' => '15191c65',
'core.pkg.css' => 'fdb27ef9',
'core.pkg.css' => '075f9867',
'core.pkg.js' => '4c79d74f',
'darkconsole.pkg.js' => '1f9a31bc',
'differential.pkg.css' => '45951e9e',
'differential.pkg.js' => '500a75c5',
'differential.pkg.js' => '19ee9979',
'diffusion.pkg.css' => 'a2d17c7d',
'diffusion.pkg.js' => '6134c5a1',
'favicon.ico' => '30672e08',
'maniphest.pkg.css' => '4845691a',
'maniphest.pkg.js' => '5ab2753f',
'maniphest.pkg.js' => '4d7e79c8',
'rsrc/audio/basic/alert.mp3' => '98461568',
'rsrc/audio/basic/bing.mp3' => 'ab8603a5',
'rsrc/audio/basic/pock.mp3' => '0cc772f5',
@ -81,7 +81,6 @@ return array(
'rsrc/css/application/harbormaster/harbormaster.css' => 'f491c9f4',
'rsrc/css/application/herald/herald-test.css' => 'a52e323e',
'rsrc/css/application/herald/herald.css' => 'cd8d0134',
'rsrc/css/application/maniphest/batch-editor.css' => 'b0f0b6d5',
'rsrc/css/application/maniphest/report.css' => '9b9580b7',
'rsrc/css/application/maniphest/task-edit.css' => 'fda62a9b',
'rsrc/css/application/maniphest/task-summary.css' => '11cc5344',
@ -135,14 +134,15 @@ return array(
'rsrc/css/phui/object-item/phui-oi-color.css' => 'cd2b9b77',
'rsrc/css/phui/object-item/phui-oi-drag-ui.css' => '08f4ccc3',
'rsrc/css/phui/object-item/phui-oi-flush-ui.css' => '9d9685d6',
'rsrc/css/phui/object-item/phui-oi-list-view.css' => 'bf094950',
'rsrc/css/phui/object-item/phui-oi-list-view.css' => '6ae18df0',
'rsrc/css/phui/object-item/phui-oi-simple-ui.css' => 'a8beebea',
'rsrc/css/phui/phui-action-list.css' => 'f7f61a34',
'rsrc/css/phui/phui-action-panel.css' => 'b4798122',
'rsrc/css/phui/phui-badge.css' => '22c0cf4f',
'rsrc/css/phui/phui-basic-nav-view.css' => '98c11ab3',
'rsrc/css/phui/phui-big-info-view.css' => 'acc3492c',
'rsrc/css/phui/phui-box.css' => '9f3745fb',
'rsrc/css/phui/phui-box.css' => '4bd6cdb9',
'rsrc/css/phui/phui-bulk-editor.css' => '9a81e5d5',
'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
'rsrc/css/phui/phui-cms.css' => '504b4b23',
'rsrc/css/phui/phui-comment-form.css' => 'ac68149f',
@ -416,11 +416,10 @@ return array(
'rsrc/js/application/drydock/drydock-live-operation-status.js' => '901935ef',
'rsrc/js/application/files/behavior-icon-composer.js' => '8499b6ab',
'rsrc/js/application/files/behavior-launch-icon-composer.js' => '48086888',
'rsrc/js/application/herald/HeraldRuleEditor.js' => '2dff5579',
'rsrc/js/application/herald/HeraldRuleEditor.js' => 'dca75c0e',
'rsrc/js/application/herald/PathTypeahead.js' => 'f7fc67ec',
'rsrc/js/application/herald/herald-rule-editor.js' => '7ebaeed3',
'rsrc/js/application/maniphest/behavior-batch-editor.js' => '782ab6e7',
'rsrc/js/application/maniphest/behavior-batch-selector.js' => '0825c27a',
'rsrc/js/application/maniphest/behavior-batch-selector.js' => 'ad54037e',
'rsrc/js/application/maniphest/behavior-line-chart.js' => 'e4232876',
'rsrc/js/application/maniphest/behavior-list-edit.js' => 'a9f88de2',
'rsrc/js/application/maniphest/behavior-subpriorityeditor.js' => '71237763',
@ -444,7 +443,7 @@ return array(
'rsrc/js/application/releeph/releeph-preview-branch.js' => 'b2b4fbaf',
'rsrc/js/application/releeph/releeph-request-state-change.js' => 'a0b57eb8',
'rsrc/js/application/releeph/releeph-request-typeahead.js' => 'de2e896f',
'rsrc/js/application/repository/repository-crossreference.js' => '7fe9bc12',
'rsrc/js/application/repository/repository-crossreference.js' => '2ab10a76',
'rsrc/js/application/search/behavior-reorder-profile-menu-items.js' => 'e2e0a072',
'rsrc/js/application/search/behavior-reorder-queries.js' => 'e9581f08',
'rsrc/js/application/slowvote/behavior-slowvote-embed.js' => '887ad43f',
@ -477,6 +476,7 @@ return array(
'rsrc/js/core/behavior-audio-source.js' => '59b251eb',
'rsrc/js/core/behavior-autofocus.js' => '7319e029',
'rsrc/js/core/behavior-badge-view.js' => '8ff5e24c',
'rsrc/js/core/behavior-bulk-editor.js' => '66a6def1',
'rsrc/js/core/behavior-choose-control.js' => '327a00d1',
'rsrc/js/core/behavior-copy.js' => 'b0b8f86d',
'rsrc/js/core/behavior-detect-timezone.js' => '4c193c96',
@ -523,6 +523,7 @@ return array(
'rsrc/js/core/phtize.js' => 'd254d646',
'rsrc/js/phui/behavior-phui-dropdown-menu.js' => 'b95d6f7d',
'rsrc/js/phui/behavior-phui-file-upload.js' => 'b003d4fb',
'rsrc/js/phui/behavior-phui-selectable-list.js' => '464259a2',
'rsrc/js/phui/behavior-phui-submenu.js' => 'a6f7a73b',
'rsrc/js/phui/behavior-phui-tab-group.js' => '0a0b10e9',
'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
@ -531,7 +532,7 @@ return array(
'rsrc/js/phuix/PHUIXButtonView.js' => '8a91e1ac',
'rsrc/js/phuix/PHUIXDropdownMenu.js' => '04b2ae03',
'rsrc/js/phuix/PHUIXExample.js' => '68af71ca',
'rsrc/js/phuix/PHUIXFormControl.js' => '83e03671',
'rsrc/js/phuix/PHUIXFormControl.js' => '1dd0870c',
'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
),
'symbols' => array(
@ -579,7 +580,7 @@ return array(
'global-drag-and-drop-css' => 'b556a948',
'harbormaster-css' => 'f491c9f4',
'herald-css' => 'cd8d0134',
'herald-rule-editor' => '2dff5579',
'herald-rule-editor' => 'dca75c0e',
'herald-test-css' => 'a52e323e',
'inline-comment-summary-css' => 'f23d4e8f',
'javelin-aphlict' => 'e1d4b11a',
@ -594,6 +595,7 @@ return array(
'javelin-behavior-audio-source' => '59b251eb',
'javelin-behavior-audit-preview' => 'd835b03a',
'javelin-behavior-badge-view' => '8ff5e24c',
'javelin-behavior-bulk-editor' => '66a6def1',
'javelin-behavior-bulk-job-reload' => 'edf8a145',
'javelin-behavior-calendar-month-view' => 'fe33e256',
'javelin-behavior-choose-control' => '327a00d1',
@ -641,8 +643,7 @@ return array(
'javelin-behavior-lightbox-attachments' => '560f41da',
'javelin-behavior-line-chart' => 'e4232876',
'javelin-behavior-load-blame' => '42126667',
'javelin-behavior-maniphest-batch-editor' => '782ab6e7',
'javelin-behavior-maniphest-batch-selector' => '0825c27a',
'javelin-behavior-maniphest-batch-selector' => 'ad54037e',
'javelin-behavior-maniphest-list-editor' => 'a9f88de2',
'javelin-behavior-maniphest-subpriority-editor' => '71237763',
'javelin-behavior-owners-path-editor' => '7a68dda3',
@ -673,6 +674,7 @@ return array(
'javelin-behavior-phui-dropdown-menu' => 'b95d6f7d',
'javelin-behavior-phui-file-upload' => 'b003d4fb',
'javelin-behavior-phui-hovercards' => 'bcaccd64',
'javelin-behavior-phui-selectable-list' => '464259a2',
'javelin-behavior-phui-submenu' => 'a6f7a73b',
'javelin-behavior-phui-tab-group' => '0a0b10e9',
'javelin-behavior-phuix-example' => '68af71ca',
@ -690,7 +692,7 @@ return array(
'javelin-behavior-reorder-applications' => '76b9fc3e',
'javelin-behavior-reorder-columns' => 'e1d25dfb',
'javelin-behavior-reorder-profile-menu-items' => 'e2e0a072',
'javelin-behavior-repository-crossreference' => '7fe9bc12',
'javelin-behavior-repository-crossreference' => '2ab10a76',
'javelin-behavior-scrollbar' => '834a1173',
'javelin-behavior-search-reorder-queries' => 'e9581f08',
'javelin-behavior-select-content' => 'bf5374ef',
@ -754,7 +756,6 @@ return array(
'javelin-workboard-column' => '758b4758',
'javelin-workboard-controller' => '26167537',
'javelin-workflow' => '1e911d0f',
'maniphest-batch-editor' => 'b0f0b6d5',
'maniphest-report-css' => '9b9580b7',
'maniphest-task-edit-css' => 'fda62a9b',
'maniphest-task-summary-css' => '11cc5344',
@ -820,7 +821,8 @@ return array(
'phui-badge-view-css' => '22c0cf4f',
'phui-basic-nav-view-css' => '98c11ab3',
'phui-big-info-view-css' => 'acc3492c',
'phui-box-css' => '9f3745fb',
'phui-box-css' => '4bd6cdb9',
'phui-bulk-editor-css' => '9a81e5d5',
'phui-button-bar-css' => 'f1ff5494',
'phui-button-css' => '1863cc6e',
'phui-button-simple-css' => '8e1baf68',
@ -860,7 +862,7 @@ return array(
'phui-oi-color-css' => 'cd2b9b77',
'phui-oi-drag-ui-css' => '08f4ccc3',
'phui-oi-flush-ui-css' => '9d9685d6',
'phui-oi-list-view-css' => 'bf094950',
'phui-oi-list-view-css' => '6ae18df0',
'phui-oi-simple-ui-css' => 'a8beebea',
'phui-pager-css' => 'edcbc226',
'phui-pinboard-view-css' => '2495140e',
@ -882,7 +884,7 @@ return array(
'phuix-autocomplete' => 'e0731603',
'phuix-button-view' => '8a91e1ac',
'phuix-dropdown-menu' => '04b2ae03',
'phuix-form-control-view' => '83e03671',
'phuix-form-control-view' => '1dd0870c',
'phuix-icon-view' => 'bff6884b',
'policy-css' => '957ea14c',
'policy-edit-css' => '815c66f7',
@ -958,12 +960,6 @@ return array(
'javelin-stratcom',
'javelin-workflow',
),
'0825c27a' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-util',
),
'08f4ccc3' => array(
'phui-oi-list-view-css',
),
@ -1033,6 +1029,10 @@ return array(
'javelin-request',
'javelin-uri',
),
'1dd0870c' => array(
'javelin-install',
'javelin-dom',
),
'1e911d0f' => array(
'javelin-stratcom',
'javelin-request',
@ -1088,6 +1088,12 @@ return array(
'javelin-install',
'javelin-util',
),
'2ab10a76' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-uri',
),
'2ae077e1' => array(
'javelin-behavior',
'javelin-dom',
@ -1106,15 +1112,6 @@ return array(
'javelin-install',
'javelin-event',
),
'2dff5579' => array(
'multirow-row-manager',
'javelin-install',
'javelin-util',
'javelin-dom',
'javelin-stratcom',
'javelin-json',
'phabricator-prefab',
),
'2ee659ce' => array(
'javelin-install',
),
@ -1226,6 +1223,11 @@ return array(
'javelin-behavior',
'javelin-dom',
),
'464259a2' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
),
'469c0d9e' => array(
'javelin-behavior',
'javelin-dom',
@ -1422,6 +1424,14 @@ return array(
'javelin-workflow',
'javelin-dom',
),
'66a6def1' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'multirow-row-manager',
'javelin-json',
'phuix-form-control-view',
),
'680ea2c8' => array(
'javelin-install',
'javelin-dom',
@ -1523,14 +1533,6 @@ return array(
'javelin-request',
'javelin-util',
),
'782ab6e7' => array(
'javelin-behavior',
'javelin-dom',
'javelin-util',
'phabricator-prefab',
'multirow-row-manager',
'javelin-json',
),
'7927a7d3' => array(
'javelin-behavior',
'javelin-quicksand',
@ -1559,20 +1561,10 @@ return array(
'7f243deb' => array(
'javelin-install',
),
'7fe9bc12' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-uri',
),
'834a1173' => array(
'javelin-behavior',
'javelin-scrollbar',
),
'83e03671' => array(
'javelin-install',
'javelin-dom',
),
'8499b6ab' => array(
'javelin-behavior',
'javelin-dom',
@ -1808,6 +1800,12 @@ return array(
'phuix-autocomplete',
'javelin-mask',
),
'ad54037e' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-util',
),
'b003d4fb' => array(
'javelin-behavior',
'javelin-stratcom',
@ -2023,6 +2021,15 @@ return array(
'javelin-util',
'phabricator-shaped-request',
),
'dca75c0e' => array(
'multirow-row-manager',
'javelin-install',
'javelin-util',
'javelin-dom',
'javelin-stratcom',
'javelin-json',
'phabricator-prefab',
),
'de2e896f' => array(
'javelin-behavior',
'javelin-dom',

View file

@ -1,27 +1,13 @@
<?php
$table = new PhabricatorRepositoryVCSPassword();
$conn_w = $table->establishConnection('w');
// This migration once upgraded VCS password hashing, but the table was
// later removed in 2018 (see T13043).
echo pht('Upgrading password hashing for VCS passwords.')."\n";
// Since almost four years have passed since this migration, the cost of
// losing this data is very small (users just need to reset their passwords),
// and a version of this migration against the modern schema isn't easy to
// implement or test, just skip the migration.
$best_hasher = PhabricatorPasswordHasher::getBestHasher();
foreach (new LiskMigrationIterator($table) as $password) {
$id = $password->getID();
echo pht('Migrating VCS password %d...', $id)."\n";
$input_hash = $password->getPasswordHash();
$input_envelope = new PhutilOpaqueEnvelope($input_hash);
$storage_hash = $best_hasher->getPasswordHashForStorage($input_envelope);
queryfx(
$conn_w,
'UPDATE %T SET passwordHash = %s WHERE id = %d',
$table->getTableName(),
$storage_hash->openEnvelope(),
$id);
}
echo pht('Done.')."\n";
// This means that installs which upgrade from a version of Phabricator
// released prior to Feb 2014 to a version of Phabricator relased after
// Jan 2018 will need to have users reset VCS passwords.

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_worker.worker_bulkjob
ADD isSilent BOOL NOT NULL;

View file

@ -0,0 +1,10 @@
CREATE TABLE {$NAMESPACE}_auth.auth_password (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
phid VARBINARY(64) NOT NULL,
objectPHID VARBINARY(64) NOT NULL,
passwordType VARCHAR(64) NOT NULL COLLATE {$COLLATE_TEXT},
passwordHash VARCHAR(128) NOT NULL COLLATE {$COLLATE_TEXT},
isRevoked BOOL NOT NULL,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,19 @@
CREATE TABLE {$NAMESPACE}_auth.auth_passwordtransaction (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
phid VARBINARY(64) NOT NULL,
authorPHID VARBINARY(64) NOT NULL,
objectPHID VARBINARY(64) NOT NULL,
viewPolicy VARBINARY(64) NOT NULL,
editPolicy VARBINARY(64) NOT NULL,
commentPHID VARBINARY(64) DEFAULT NULL,
commentVersion INT UNSIGNED NOT NULL,
transactionType VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL,
oldValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
newValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
metadata LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
UNIQUE KEY `key_phid` (`phid`),
KEY `key_object` (`objectPHID`)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,6 @@
INSERT INTO {$NAMESPACE}_auth.auth_password
(objectPHID, phid, passwordType, passwordHash, isRevoked,
dateCreated, dateModified)
SELECT userPHID, CONCAT('XVCS', id), 'vcs', passwordHash, 0,
dateCreated, dateModified
FROM {$NAMESPACE}_repository.repository_vcspassword;

View file

@ -0,0 +1,24 @@
<?php
// Populate VCS passwords (which we copied from the old "VCS Password" table
// in the last migration) with new PHIDs.
$table = new PhabricatorAuthPassword();
$conn = $table->establishConnection('w');
$password_type = PhabricatorAuthPasswordPHIDType::TYPECONST;
foreach (new LiskMigrationIterator($table) as $row) {
if (phid_get_type($row->getPHID()) == $password_type) {
continue;
}
$new_phid = $row->generatePHID();
queryfx(
$conn,
'UPDATE %T SET phid = %s WHERE id = %d',
$table->getTableName(),
$new_phid,
$row->getID());
}

View file

@ -0,0 +1 @@
DROP TABLE {$NAMESPACE}_repository.repository_vcspassword;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_auth.auth_password
ADD passwordSalt VARCHAR(64) NOT NULL COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,7 @@
INSERT INTO {$NAMESPACE}_auth.auth_password
(objectPHID, phid, passwordType, passwordHash, passwordSalt, isRevoked,
dateCreated, dateModified)
SELECT phid, CONCAT('XACCOUNT', id), 'account', passwordHash, passwordSalt, 0,
dateCreated, dateModified
FROM {$NAMESPACE}_user.user
WHERE passwordHash != '';

View file

@ -0,0 +1,24 @@
<?php
// Populate account passwords (which we copied from the user table in the last
// migration) with new PHIDs.
$table = new PhabricatorAuthPassword();
$conn = $table->establishConnection('w');
$password_type = PhabricatorAuthPasswordPHIDType::TYPECONST;
foreach (new LiskMigrationIterator($table) as $row) {
if (phid_get_type($row->getPHID()) == $password_type) {
continue;
}
$new_phid = $row->generatePHID();
queryfx(
$conn,
'UPDATE %T SET phid = %s WHERE id = %d',
$table->getTableName(),
$new_phid,
$row->getID());
}

View file

@ -0,0 +1,5 @@
ALTER TABLE {$NAMESPACE}_user.user
DROP passwordSalt;
ALTER TABLE {$NAMESPACE}_user.user
DROP passwordHash;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_auth.auth_password
ADD legacyDigestFormat VARCHAR(32) COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,4 @@
UPDATE {$NAMESPACE}_auth.auth_password
SET legacyDigestFormat = 'v1'
WHERE passwordType IN ('vcs', 'account')
AND legacyDigestFormat IS NULL;

View file

@ -0,0 +1,26 @@
/* This column was previously "uint32?" with these values:
1: run every time
0: run only the first time
*/
UPDATE {$NAMESPACE}_herald.herald_rule
SET repetitionPolicy = '1'
WHERE repetitionPolicy IS NULL;
ALTER TABLE {$NAMESPACE}_herald.herald_rule
CHANGE repetitionPolicy
repetitionPolicy VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT};
/* If the old value was "0", the new value is "first". */
UPDATE {$NAMESPACE}_herald.herald_rule
SET repetitionPolicy = 'first'
WHERE repetitionPolicy = '0';
/* If the old value was anything else, the new value is "every". */
UPDATE {$NAMESPACE}_herald.herald_rule
SET repetitionPolicy = 'every'
WHERE repetitionPolicy NOT IN ('first', '0');

View file

@ -1,39 +1,8 @@
<?php
echo pht('Cleaning up old Herald rule applied rows...')."\n";
$table = new HeraldRule();
$table->openTransaction();
$table->beginReadLocking();
// Once, this migration deleted some unnecessary rows written by Herald before
// January 2012. These rows don't hurt anything, they just cluttered up the
// database a bit.
$rules = $table->loadAll();
foreach ($rules as $key => $rule) {
$first_policy = HeraldRepetitionPolicyConfig::toInt(
HeraldRepetitionPolicyConfig::FIRST);
if ($rule->getRepetitionPolicy() != $first_policy) {
unset($rules[$key]);
}
}
$conn_w = $table->establishConnection('w');
$clause = '';
if ($rules) {
$clause = qsprintf(
$conn_w,
'WHERE ruleID NOT IN (%Ld)',
mpull($rules, 'getID'));
}
echo pht('This may take a moment')."\n";
do {
queryfx(
$conn_w,
'DELETE FROM %T %Q LIMIT 1000',
HeraldRule::TABLE_RULE_APPLIED,
$clause);
echo '.';
} while ($conn_w->getAffectedRows());
$table->endReadLocking();
$table->saveTransaction();
echo "\n".pht('Done.')."\n";
// The migration was removed in January 2018 to make maintenance on rule
// repetition policies easier.

1
scripts/manage_bulk.php Symbolic link
View file

@ -0,0 +1 @@
../scripts/setup/manage_bulk.php

21
scripts/setup/manage_bulk.php Executable file
View file

@ -0,0 +1,21 @@
#!/usr/bin/env php
<?php
$root = dirname(dirname(dirname(__FILE__)));
require_once $root.'/scripts/__init_script__.php';
$args = new PhutilArgumentParser($argv);
$args->setTagline(pht('manage bulk jobs'));
$args->setSynopsis(<<<EOSYNOPSIS
**bulk** __command__ [__options__]
Manage and debug bulk jobs.
EOSYNOPSIS
);
$args->parseStandardArguments();
$workflows = id(new PhutilClassMapQuery())
->setAncestorClass('PhabricatorBulkManagementWorkflow')
->execute();
$workflows[] = new PhutilHelpArgumentWorkflow();
$args->parseWorkflows($workflows);

View file

@ -245,7 +245,7 @@ try {
}
$workflow = $parsed_args->parseWorkflows($workflows);
$workflow->setUser($user);
$workflow->setSSHUser($user);
$workflow->setOriginalArguments($original_argv);
$workflow->setIsClusterRequest($is_cluster_request);

View file

@ -112,17 +112,6 @@ if ($is_new) {
$create_email = $email;
}
$changed_pass = false;
// This disables local echo, so the user's password is not shown as they type
// it.
phutil_passthru('stty -echo');
$password = phutil_console_prompt(
pht('Enter a password for this user [blank to leave unchanged]:'));
phutil_passthru('stty echo');
if (strlen($password)) {
$changed_pass = $password;
}
$is_system_agent = $user->getIsSystemAgent();
$set_system_agent = phutil_console_confirm(
pht('Is this user a bot?'),
@ -158,10 +147,6 @@ printf($tpl, pht('Real Name'), $original->getRealName(), $user->getRealName());
if ($is_new) {
printf($tpl, pht('Email'), '', $create_email);
}
printf($tpl, pht('Password'), null,
($changed_pass !== false)
? pht('Updated')
: pht('Unchanged'));
printf(
$tpl,
@ -218,11 +203,6 @@ $user->openTransaction();
$editor->makeAdminUser($user, $set_admin);
$editor->makeSystemAgentUser($user, $set_system_agent);
if ($changed_pass !== false) {
$envelope = new PhutilOpaqueEnvelope($changed_pass);
$editor->changePassword($user, $envelope);
}
$user->saveTransaction();
echo pht('Saved changes.')."\n";

View file

@ -222,6 +222,12 @@ phutil_register_library_map(array(
'AuditConduitAPIMethod' => 'applications/audit/conduit/AuditConduitAPIMethod.php',
'AuditQueryConduitAPIMethod' => 'applications/audit/conduit/AuditQueryConduitAPIMethod.php',
'AuthManageProvidersCapability' => 'applications/auth/capability/AuthManageProvidersCapability.php',
'BulkParameterType' => 'applications/transactions/bulk/type/BulkParameterType.php',
'BulkPointsParameterType' => 'applications/transactions/bulk/type/BulkPointsParameterType.php',
'BulkRemarkupParameterType' => 'applications/transactions/bulk/type/BulkRemarkupParameterType.php',
'BulkSelectParameterType' => 'applications/transactions/bulk/type/BulkSelectParameterType.php',
'BulkStringParameterType' => 'applications/transactions/bulk/type/BulkStringParameterType.php',
'BulkTokenizerParameterType' => 'applications/transactions/bulk/type/BulkTokenizerParameterType.php',
'CalendarTimeUtil' => 'applications/calendar/util/CalendarTimeUtil.php',
'CalendarTimeUtilTestCase' => 'applications/calendar/__tests__/CalendarTimeUtilTestCase.php',
'CelerityAPI' => 'applications/celerity/CelerityAPI.php',
@ -584,6 +590,7 @@ phutil_register_library_map(array(
'DifferentialRevisionStatus' => 'applications/differential/constants/DifferentialRevisionStatus.php',
'DifferentialRevisionStatusDatasource' => 'applications/differential/typeahead/DifferentialRevisionStatusDatasource.php',
'DifferentialRevisionStatusFunctionDatasource' => 'applications/differential/typeahead/DifferentialRevisionStatusFunctionDatasource.php',
'DifferentialRevisionStatusHeraldField' => 'applications/differential/herald/DifferentialRevisionStatusHeraldField.php',
'DifferentialRevisionStatusTransaction' => 'applications/differential/xaction/DifferentialRevisionStatusTransaction.php',
'DifferentialRevisionSummaryHeraldField' => 'applications/differential/herald/DifferentialRevisionSummaryHeraldField.php',
'DifferentialRevisionSummaryTransaction' => 'applications/differential/xaction/DifferentialRevisionSummaryTransaction.php',
@ -758,6 +765,7 @@ phutil_register_library_map(array(
'DiffusionLintCountQuery' => 'applications/diffusion/query/DiffusionLintCountQuery.php',
'DiffusionLintSaveRunner' => 'applications/diffusion/DiffusionLintSaveRunner.php',
'DiffusionLocalRepositoryFilter' => 'applications/diffusion/data/DiffusionLocalRepositoryFilter.php',
'DiffusionLogController' => 'applications/diffusion/controller/DiffusionLogController.php',
'DiffusionLookSoonConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionLookSoonConduitAPIMethod.php',
'DiffusionLowLevelCommitFieldsQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelCommitFieldsQuery.php',
'DiffusionLowLevelCommitQuery' => 'applications/diffusion/query/lowlevel/DiffusionLowLevelCommitQuery.php',
@ -825,9 +833,11 @@ phutil_register_library_map(array(
'DiffusionPreCommitRefTypeHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitRefTypeHeraldField.php',
'DiffusionPreCommitUsesGitLFSHeraldField' => 'applications/diffusion/herald/DiffusionPreCommitUsesGitLFSHeraldField.php',
'DiffusionPullEventGarbageCollector' => 'applications/diffusion/garbagecollector/DiffusionPullEventGarbageCollector.php',
'DiffusionPullLogListController' => 'applications/diffusion/controller/DiffusionPullLogListController.php',
'DiffusionPullLogListView' => 'applications/diffusion/view/DiffusionPullLogListView.php',
'DiffusionPullLogSearchEngine' => 'applications/diffusion/query/DiffusionPullLogSearchEngine.php',
'DiffusionPushCapability' => 'applications/diffusion/capability/DiffusionPushCapability.php',
'DiffusionPushEventViewController' => 'applications/diffusion/controller/DiffusionPushEventViewController.php',
'DiffusionPushLogController' => 'applications/diffusion/controller/DiffusionPushLogController.php',
'DiffusionPushLogListController' => 'applications/diffusion/controller/DiffusionPushLogListController.php',
'DiffusionPushLogListView' => 'applications/diffusion/view/DiffusionPushLogListView.php',
'DiffusionPythonExternalSymbolsSource' => 'applications/diffusion/symbol/DiffusionPythonExternalSymbolsSource.php',
@ -1351,7 +1361,9 @@ phutil_register_library_map(array(
'HeraldConditionTranscript' => 'applications/herald/storage/transcript/HeraldConditionTranscript.php',
'HeraldContentSourceField' => 'applications/herald/field/HeraldContentSourceField.php',
'HeraldController' => 'applications/herald/controller/HeraldController.php',
'HeraldCoreStateReasons' => 'applications/herald/state/HeraldCoreStateReasons.php',
'HeraldDAO' => 'applications/herald/storage/HeraldDAO.php',
'HeraldDeprecatedFieldGroup' => 'applications/herald/field/HeraldDeprecatedFieldGroup.php',
'HeraldDifferentialAdapter' => 'applications/differential/herald/HeraldDifferentialAdapter.php',
'HeraldDifferentialDiffAdapter' => 'applications/differential/herald/HeraldDifferentialDiffAdapter.php',
'HeraldDifferentialRevisionAdapter' => 'applications/differential/herald/HeraldDifferentialRevisionAdapter.php',
@ -1389,7 +1401,6 @@ phutil_register_library_map(array(
'HeraldRelatedFieldGroup' => 'applications/herald/field/HeraldRelatedFieldGroup.php',
'HeraldRemarkupFieldValue' => 'applications/herald/value/HeraldRemarkupFieldValue.php',
'HeraldRemarkupRule' => 'applications/herald/remarkup/HeraldRemarkupRule.php',
'HeraldRepetitionPolicyConfig' => 'applications/herald/config/HeraldRepetitionPolicyConfig.php',
'HeraldRule' => 'applications/herald/storage/HeraldRule.php',
'HeraldRuleController' => 'applications/herald/controller/HeraldRuleController.php',
'HeraldRuleDatasource' => 'applications/herald/typeahead/HeraldRuleDatasource.php',
@ -1487,8 +1498,8 @@ phutil_register_library_map(array(
'MacroQueryConduitAPIMethod' => 'applications/macro/conduit/MacroQueryConduitAPIMethod.php',
'ManiphestAssignEmailCommand' => 'applications/maniphest/command/ManiphestAssignEmailCommand.php',
'ManiphestAssigneeDatasource' => 'applications/maniphest/typeahead/ManiphestAssigneeDatasource.php',
'ManiphestBatchEditController' => 'applications/maniphest/controller/ManiphestBatchEditController.php',
'ManiphestBulkEditCapability' => 'applications/maniphest/capability/ManiphestBulkEditCapability.php',
'ManiphestBulkEditController' => 'applications/maniphest/controller/ManiphestBulkEditController.php',
'ManiphestClaimEmailCommand' => 'applications/maniphest/command/ManiphestClaimEmailCommand.php',
'ManiphestCloseEmailCommand' => 'applications/maniphest/command/ManiphestCloseEmailCommand.php',
'ManiphestConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestConduitAPIMethod.php',
@ -1547,6 +1558,7 @@ phutil_register_library_map(array(
'ManiphestTaskAttachTransaction' => 'applications/maniphest/xaction/ManiphestTaskAttachTransaction.php',
'ManiphestTaskAuthorHeraldField' => 'applications/maniphest/herald/ManiphestTaskAuthorHeraldField.php',
'ManiphestTaskAuthorPolicyRule' => 'applications/maniphest/policyrule/ManiphestTaskAuthorPolicyRule.php',
'ManiphestTaskBulkEngine' => 'applications/maniphest/bulk/ManiphestTaskBulkEngine.php',
'ManiphestTaskCloseAsDuplicateRelationship' => 'applications/maniphest/relationship/ManiphestTaskCloseAsDuplicateRelationship.php',
'ManiphestTaskClosedStatusDatasource' => 'applications/maniphest/typeahead/ManiphestTaskClosedStatusDatasource.php',
'ManiphestTaskCoverImageTransaction' => 'applications/maniphest/xaction/ManiphestTaskCoverImageTransaction.php',
@ -1556,7 +1568,6 @@ phutil_register_library_map(array(
'ManiphestTaskDescriptionTransaction' => 'applications/maniphest/xaction/ManiphestTaskDescriptionTransaction.php',
'ManiphestTaskDetailController' => 'applications/maniphest/controller/ManiphestTaskDetailController.php',
'ManiphestTaskEdgeTransaction' => 'applications/maniphest/xaction/ManiphestTaskEdgeTransaction.php',
'ManiphestTaskEditBulkJobType' => 'applications/maniphest/bulk/ManiphestTaskEditBulkJobType.php',
'ManiphestTaskEditController' => 'applications/maniphest/controller/ManiphestTaskEditController.php',
'ManiphestTaskEditEngineLock' => 'applications/maniphest/editor/ManiphestTaskEditEngineLock.php',
'ManiphestTaskFerretEngine' => 'applications/maniphest/search/ManiphestTaskFerretEngine.php',
@ -2026,6 +2037,7 @@ phutil_register_library_map(array(
'PhabricatorAuthApplication' => 'applications/auth/application/PhabricatorAuthApplication.php',
'PhabricatorAuthAuthFactorPHIDType' => 'applications/auth/phid/PhabricatorAuthAuthFactorPHIDType.php',
'PhabricatorAuthAuthProviderPHIDType' => 'applications/auth/phid/PhabricatorAuthAuthProviderPHIDType.php',
'PhabricatorAuthChangePasswordAction' => 'applications/auth/action/PhabricatorAuthChangePasswordAction.php',
'PhabricatorAuthConduitAPIMethod' => 'applications/auth/conduit/PhabricatorAuthConduitAPIMethod.php',
'PhabricatorAuthConduitTokenRevoker' => 'applications/auth/revoker/PhabricatorAuthConduitTokenRevoker.php',
'PhabricatorAuthConfirmLinkController' => 'applications/auth/controller/PhabricatorAuthConfirmLinkController.php',
@ -2082,7 +2094,21 @@ phutil_register_library_map(array(
'PhabricatorAuthOldOAuthRedirectController' => 'applications/auth/controller/PhabricatorAuthOldOAuthRedirectController.php',
'PhabricatorAuthOneTimeLoginController' => 'applications/auth/controller/PhabricatorAuthOneTimeLoginController.php',
'PhabricatorAuthOneTimeLoginTemporaryTokenType' => 'applications/auth/tokentype/PhabricatorAuthOneTimeLoginTemporaryTokenType.php',
'PhabricatorAuthPassword' => 'applications/auth/storage/PhabricatorAuthPassword.php',
'PhabricatorAuthPasswordEditor' => 'applications/auth/editor/PhabricatorAuthPasswordEditor.php',
'PhabricatorAuthPasswordEngine' => 'applications/auth/engine/PhabricatorAuthPasswordEngine.php',
'PhabricatorAuthPasswordException' => 'applications/auth/password/PhabricatorAuthPasswordException.php',
'PhabricatorAuthPasswordHashInterface' => 'applications/auth/password/PhabricatorAuthPasswordHashInterface.php',
'PhabricatorAuthPasswordPHIDType' => 'applications/auth/phid/PhabricatorAuthPasswordPHIDType.php',
'PhabricatorAuthPasswordQuery' => 'applications/auth/query/PhabricatorAuthPasswordQuery.php',
'PhabricatorAuthPasswordResetTemporaryTokenType' => 'applications/auth/tokentype/PhabricatorAuthPasswordResetTemporaryTokenType.php',
'PhabricatorAuthPasswordRevokeTransaction' => 'applications/auth/xaction/PhabricatorAuthPasswordRevokeTransaction.php',
'PhabricatorAuthPasswordRevoker' => 'applications/auth/revoker/PhabricatorAuthPasswordRevoker.php',
'PhabricatorAuthPasswordTestCase' => 'applications/auth/__tests__/PhabricatorAuthPasswordTestCase.php',
'PhabricatorAuthPasswordTransaction' => 'applications/auth/storage/PhabricatorAuthPasswordTransaction.php',
'PhabricatorAuthPasswordTransactionQuery' => 'applications/auth/query/PhabricatorAuthPasswordTransactionQuery.php',
'PhabricatorAuthPasswordTransactionType' => 'applications/auth/xaction/PhabricatorAuthPasswordTransactionType.php',
'PhabricatorAuthPasswordUpgradeTransaction' => 'applications/auth/xaction/PhabricatorAuthPasswordUpgradeTransaction.php',
'PhabricatorAuthProvider' => 'applications/auth/provider/PhabricatorAuthProvider.php',
'PhabricatorAuthProviderConfig' => 'applications/auth/storage/PhabricatorAuthProviderConfig.php',
'PhabricatorAuthProviderConfigController' => 'applications/auth/controller/config/PhabricatorAuthProviderConfigController.php',
@ -2098,7 +2124,6 @@ phutil_register_library_map(array(
'PhabricatorAuthRevoker' => 'applications/auth/revoker/PhabricatorAuthRevoker.php',
'PhabricatorAuthSSHKey' => 'applications/auth/storage/PhabricatorAuthSSHKey.php',
'PhabricatorAuthSSHKeyController' => 'applications/auth/controller/PhabricatorAuthSSHKeyController.php',
'PhabricatorAuthSSHKeyDeactivateController' => 'applications/auth/controller/PhabricatorAuthSSHKeyDeactivateController.php',
'PhabricatorAuthSSHKeyEditController' => 'applications/auth/controller/PhabricatorAuthSSHKeyEditController.php',
'PhabricatorAuthSSHKeyEditor' => 'applications/auth/editor/PhabricatorAuthSSHKeyEditor.php',
'PhabricatorAuthSSHKeyGenerateController' => 'applications/auth/controller/PhabricatorAuthSSHKeyGenerateController.php',
@ -2106,12 +2131,15 @@ phutil_register_library_map(array(
'PhabricatorAuthSSHKeyPHIDType' => 'applications/auth/phid/PhabricatorAuthSSHKeyPHIDType.php',
'PhabricatorAuthSSHKeyQuery' => 'applications/auth/query/PhabricatorAuthSSHKeyQuery.php',
'PhabricatorAuthSSHKeyReplyHandler' => 'applications/auth/mail/PhabricatorAuthSSHKeyReplyHandler.php',
'PhabricatorAuthSSHKeyRevokeController' => 'applications/auth/controller/PhabricatorAuthSSHKeyRevokeController.php',
'PhabricatorAuthSSHKeySearchEngine' => 'applications/auth/query/PhabricatorAuthSSHKeySearchEngine.php',
'PhabricatorAuthSSHKeyTableView' => 'applications/auth/view/PhabricatorAuthSSHKeyTableView.php',
'PhabricatorAuthSSHKeyTestCase' => 'applications/auth/__tests__/PhabricatorAuthSSHKeyTestCase.php',
'PhabricatorAuthSSHKeyTransaction' => 'applications/auth/storage/PhabricatorAuthSSHKeyTransaction.php',
'PhabricatorAuthSSHKeyTransactionQuery' => 'applications/auth/query/PhabricatorAuthSSHKeyTransactionQuery.php',
'PhabricatorAuthSSHKeyViewController' => 'applications/auth/controller/PhabricatorAuthSSHKeyViewController.php',
'PhabricatorAuthSSHPublicKey' => 'applications/auth/sshkey/PhabricatorAuthSSHPublicKey.php',
'PhabricatorAuthSSHRevoker' => 'applications/auth/revoker/PhabricatorAuthSSHRevoker.php',
'PhabricatorAuthSession' => 'applications/auth/storage/PhabricatorAuthSession.php',
'PhabricatorAuthSessionEngine' => 'applications/auth/engine/PhabricatorAuthSessionEngine.php',
'PhabricatorAuthSessionEngineExtension' => 'applications/auth/engine/PhabricatorAuthSessionEngineExtension.php',
@ -2119,6 +2147,7 @@ phutil_register_library_map(array(
'PhabricatorAuthSessionGarbageCollector' => 'applications/auth/garbagecollector/PhabricatorAuthSessionGarbageCollector.php',
'PhabricatorAuthSessionInfo' => 'applications/auth/data/PhabricatorAuthSessionInfo.php',
'PhabricatorAuthSessionQuery' => 'applications/auth/query/PhabricatorAuthSessionQuery.php',
'PhabricatorAuthSessionRevoker' => 'applications/auth/revoker/PhabricatorAuthSessionRevoker.php',
'PhabricatorAuthSetPasswordController' => 'applications/auth/controller/PhabricatorAuthSetPasswordController.php',
'PhabricatorAuthSetupCheck' => 'applications/config/check/PhabricatorAuthSetupCheck.php',
'PhabricatorAuthStartController' => 'applications/auth/controller/PhabricatorAuthStartController.php',
@ -2126,6 +2155,7 @@ phutil_register_library_map(array(
'PhabricatorAuthTemporaryToken' => 'applications/auth/storage/PhabricatorAuthTemporaryToken.php',
'PhabricatorAuthTemporaryTokenGarbageCollector' => 'applications/auth/garbagecollector/PhabricatorAuthTemporaryTokenGarbageCollector.php',
'PhabricatorAuthTemporaryTokenQuery' => 'applications/auth/query/PhabricatorAuthTemporaryTokenQuery.php',
'PhabricatorAuthTemporaryTokenRevoker' => 'applications/auth/revoker/PhabricatorAuthTemporaryTokenRevoker.php',
'PhabricatorAuthTemporaryTokenType' => 'applications/auth/tokentype/PhabricatorAuthTemporaryTokenType.php',
'PhabricatorAuthTemporaryTokenTypeModule' => 'applications/auth/tokentype/PhabricatorAuthTemporaryTokenTypeModule.php',
'PhabricatorAuthTerminateSessionController' => 'applications/auth/controller/PhabricatorAuthTerminateSessionController.php',
@ -2198,6 +2228,11 @@ phutil_register_library_map(array(
'PhabricatorBuiltinFileCachePurger' => 'applications/cache/purger/PhabricatorBuiltinFileCachePurger.php',
'PhabricatorBuiltinPatchList' => 'infrastructure/storage/patch/PhabricatorBuiltinPatchList.php',
'PhabricatorBulkContentSource' => 'infrastructure/daemon/contentsource/PhabricatorBulkContentSource.php',
'PhabricatorBulkEditGroup' => 'applications/transactions/bulk/PhabricatorBulkEditGroup.php',
'PhabricatorBulkEngine' => 'applications/transactions/bulk/PhabricatorBulkEngine.php',
'PhabricatorBulkManagementMakeSilentWorkflow' => 'applications/transactions/bulk/management/PhabricatorBulkManagementMakeSilentWorkflow.php',
'PhabricatorBulkManagementWorkflow' => 'applications/transactions/bulk/management/PhabricatorBulkManagementWorkflow.php',
'PhabricatorCSVExportFormat' => 'infrastructure/export/PhabricatorCSVExportFormat.php',
'PhabricatorCacheDAO' => 'applications/cache/storage/PhabricatorCacheDAO.php',
'PhabricatorCacheEngine' => 'applications/system/engine/PhabricatorCacheEngine.php',
'PhabricatorCacheEngineExtension' => 'applications/system/engine/PhabricatorCacheEngineExtension.php',
@ -2727,6 +2762,7 @@ phutil_register_library_map(array(
'PhabricatorEdgesDestructionEngineExtension' => 'infrastructure/edges/engineextension/PhabricatorEdgesDestructionEngineExtension.php',
'PhabricatorEditEngine' => 'applications/transactions/editengine/PhabricatorEditEngine.php',
'PhabricatorEditEngineAPIMethod' => 'applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php',
'PhabricatorEditEngineBulkJobType' => 'applications/transactions/bulk/PhabricatorEditEngineBulkJobType.php',
'PhabricatorEditEngineCheckboxesCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineCheckboxesCommentAction.php',
'PhabricatorEditEngineColumnsCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineColumnsCommentAction.php',
'PhabricatorEditEngineCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineCommentAction.php',
@ -2802,12 +2838,15 @@ phutil_register_library_map(array(
'PhabricatorEnv' => 'infrastructure/env/PhabricatorEnv.php',
'PhabricatorEnvTestCase' => 'infrastructure/env/__tests__/PhabricatorEnvTestCase.php',
'PhabricatorEpochEditField' => 'applications/transactions/editfield/PhabricatorEpochEditField.php',
'PhabricatorEpochExportField' => 'infrastructure/export/PhabricatorEpochExportField.php',
'PhabricatorEvent' => 'infrastructure/events/PhabricatorEvent.php',
'PhabricatorEventEngine' => 'infrastructure/events/PhabricatorEventEngine.php',
'PhabricatorEventListener' => 'infrastructure/events/PhabricatorEventListener.php',
'PhabricatorEventType' => 'infrastructure/events/constant/PhabricatorEventType.php',
'PhabricatorExampleEventListener' => 'infrastructure/events/PhabricatorExampleEventListener.php',
'PhabricatorExecFutureFileUploadSource' => 'applications/files/uploadsource/PhabricatorExecFutureFileUploadSource.php',
'PhabricatorExportField' => 'infrastructure/export/PhabricatorExportField.php',
'PhabricatorExportFormat' => 'infrastructure/export/PhabricatorExportFormat.php',
'PhabricatorExtendedPolicyInterface' => 'applications/policy/interface/PhabricatorExtendedPolicyInterface.php',
'PhabricatorExtendingPhabricatorConfigOptions' => 'applications/config/option/PhabricatorExtendingPhabricatorConfigOptions.php',
'PhabricatorExtensionsSetupCheck' => 'applications/config/check/PhabricatorExtensionsSetupCheck.php',
@ -2927,6 +2966,7 @@ phutil_register_library_map(array(
'PhabricatorFileUploadDialogController' => 'applications/files/controller/PhabricatorFileUploadDialogController.php',
'PhabricatorFileUploadException' => 'applications/files/exception/PhabricatorFileUploadException.php',
'PhabricatorFileUploadSource' => 'applications/files/uploadsource/PhabricatorFileUploadSource.php',
'PhabricatorFileUploadSourceByteLimitException' => 'applications/files/uploadsource/PhabricatorFileUploadSourceByteLimitException.php',
'PhabricatorFileinfoSetupCheck' => 'applications/config/check/PhabricatorFileinfoSetupCheck.php',
'PhabricatorFilesApplication' => 'applications/files/application/PhabricatorFilesApplication.php',
'PhabricatorFilesApplicationStorageEnginePanel' => 'applications/files/applicationpanel/PhabricatorFilesApplicationStorageEnginePanel.php',
@ -3026,6 +3066,7 @@ phutil_register_library_map(array(
'PhabricatorHomeProfileMenuItem' => 'applications/home/menuitem/PhabricatorHomeProfileMenuItem.php',
'PhabricatorHovercardEngineExtension' => 'applications/search/engineextension/PhabricatorHovercardEngineExtension.php',
'PhabricatorHovercardEngineExtensionModule' => 'applications/search/engineextension/PhabricatorHovercardEngineExtensionModule.php',
'PhabricatorIDExportField' => 'infrastructure/export/PhabricatorIDExportField.php',
'PhabricatorIDsSearchEngineExtension' => 'applications/search/engineextension/PhabricatorIDsSearchEngineExtension.php',
'PhabricatorIDsSearchField' => 'applications/search/field/PhabricatorIDsSearchField.php',
'PhabricatorIconDatasource' => 'applications/files/typeahead/PhabricatorIconDatasource.php',
@ -3049,6 +3090,7 @@ phutil_register_library_map(array(
'PhabricatorInlineSummaryView' => 'infrastructure/diff/view/PhabricatorInlineSummaryView.php',
'PhabricatorInstructionsEditField' => 'applications/transactions/editfield/PhabricatorInstructionsEditField.php',
'PhabricatorIntConfigType' => 'applications/config/type/PhabricatorIntConfigType.php',
'PhabricatorIntExportField' => 'infrastructure/export/PhabricatorIntExportField.php',
'PhabricatorInternalSetting' => 'applications/settings/setting/PhabricatorInternalSetting.php',
'PhabricatorInternationalizationManagementExtractWorkflow' => 'infrastructure/internationalization/management/PhabricatorInternationalizationManagementExtractWorkflow.php',
'PhabricatorInternationalizationManagementWorkflow' => 'infrastructure/internationalization/management/PhabricatorInternationalizationManagementWorkflow.php',
@ -3058,6 +3100,7 @@ phutil_register_library_map(array(
'PhabricatorIteratorFileUploadSource' => 'applications/files/uploadsource/PhabricatorIteratorFileUploadSource.php',
'PhabricatorJIRAAuthProvider' => 'applications/auth/provider/PhabricatorJIRAAuthProvider.php',
'PhabricatorJSONConfigType' => 'applications/config/type/PhabricatorJSONConfigType.php',
'PhabricatorJSONExportFormat' => 'infrastructure/export/PhabricatorJSONExportFormat.php',
'PhabricatorJavelinLinter' => 'infrastructure/lint/linter/PhabricatorJavelinLinter.php',
'PhabricatorJiraIssueHasObjectEdgeType' => 'applications/doorkeeper/edge/PhabricatorJiraIssueHasObjectEdgeType.php',
'PhabricatorJumpNavHandler' => 'applications/search/engine/PhabricatorJumpNavHandler.php',
@ -3377,6 +3420,7 @@ phutil_register_library_map(array(
'PhabricatorPHDConfigOptions' => 'applications/config/option/PhabricatorPHDConfigOptions.php',
'PhabricatorPHID' => 'applications/phid/storage/PhabricatorPHID.php',
'PhabricatorPHIDConstants' => 'applications/phid/PhabricatorPHIDConstants.php',
'PhabricatorPHIDExportField' => 'infrastructure/export/PhabricatorPHIDExportField.php',
'PhabricatorPHIDInterface' => 'applications/phid/interface/PhabricatorPHIDInterface.php',
'PhabricatorPHIDListEditField' => 'applications/transactions/editfield/PhabricatorPHIDListEditField.php',
'PhabricatorPHIDListEditType' => 'applications/transactions/edittype/PhabricatorPHIDListEditType.php',
@ -3465,6 +3509,7 @@ phutil_register_library_map(array(
'PhabricatorPagerUIExample' => 'applications/uiexample/examples/PhabricatorPagerUIExample.php',
'PhabricatorPassphraseApplication' => 'applications/passphrase/application/PhabricatorPassphraseApplication.php',
'PhabricatorPasswordAuthProvider' => 'applications/auth/provider/PhabricatorPasswordAuthProvider.php',
'PhabricatorPasswordDestructionEngineExtension' => 'applications/auth/extension/PhabricatorPasswordDestructionEngineExtension.php',
'PhabricatorPasswordHasher' => 'infrastructure/util/password/PhabricatorPasswordHasher.php',
'PhabricatorPasswordHasherTestCase' => 'infrastructure/util/password/__tests__/PhabricatorPasswordHasherTestCase.php',
'PhabricatorPasswordHasherUnavailableException' => 'infrastructure/util/password/PhabricatorPasswordHasherUnavailableException.php',
@ -3915,7 +3960,6 @@ phutil_register_library_map(array(
'PhabricatorRepositoryURITestCase' => 'applications/repository/storage/__tests__/PhabricatorRepositoryURITestCase.php',
'PhabricatorRepositoryURITransaction' => 'applications/repository/storage/PhabricatorRepositoryURITransaction.php',
'PhabricatorRepositoryURITransactionQuery' => 'applications/repository/query/PhabricatorRepositoryURITransactionQuery.php',
'PhabricatorRepositoryVCSPassword' => 'applications/repository/storage/PhabricatorRepositoryVCSPassword.php',
'PhabricatorRepositoryWorkingCopyVersion' => 'applications/repository/storage/PhabricatorRepositoryWorkingCopyVersion.php',
'PhabricatorRequestExceptionHandler' => 'aphront/handler/PhabricatorRequestExceptionHandler.php',
'PhabricatorResourceSite' => 'aphront/site/PhabricatorResourceSite.php',
@ -4142,6 +4186,7 @@ phutil_register_library_map(array(
'PhabricatorStorageSchemaSpec' => 'infrastructure/storage/schema/PhabricatorStorageSchemaSpec.php',
'PhabricatorStorageSetupCheck' => 'applications/config/check/PhabricatorStorageSetupCheck.php',
'PhabricatorStringConfigType' => 'applications/config/type/PhabricatorStringConfigType.php',
'PhabricatorStringExportField' => 'infrastructure/export/PhabricatorStringExportField.php',
'PhabricatorStringListConfigType' => 'applications/config/type/PhabricatorStringListConfigType.php',
'PhabricatorStringListEditField' => 'applications/transactions/editfield/PhabricatorStringListEditField.php',
'PhabricatorStringSetting' => 'applications/settings/setting/PhabricatorStringSetting.php',
@ -4205,6 +4250,7 @@ phutil_register_library_map(array(
'PhabricatorTextAreaEditField' => 'applications/transactions/editfield/PhabricatorTextAreaEditField.php',
'PhabricatorTextConfigType' => 'applications/config/type/PhabricatorTextConfigType.php',
'PhabricatorTextEditField' => 'applications/transactions/editfield/PhabricatorTextEditField.php',
'PhabricatorTextExportFormat' => 'infrastructure/export/PhabricatorTextExportFormat.php',
'PhabricatorTextListConfigType' => 'applications/config/type/PhabricatorTextListConfigType.php',
'PhabricatorTime' => 'infrastructure/time/PhabricatorTime.php',
'PhabricatorTimeFormatSetting' => 'applications/settings/setting/PhabricatorTimeFormatSetting.php',
@ -5239,6 +5285,12 @@ phutil_register_library_map(array(
'AuditConduitAPIMethod' => 'ConduitAPIMethod',
'AuditQueryConduitAPIMethod' => 'AuditConduitAPIMethod',
'AuthManageProvidersCapability' => 'PhabricatorPolicyCapability',
'BulkParameterType' => 'Phobject',
'BulkPointsParameterType' => 'BulkParameterType',
'BulkRemarkupParameterType' => 'BulkParameterType',
'BulkSelectParameterType' => 'BulkParameterType',
'BulkStringParameterType' => 'BulkParameterType',
'BulkTokenizerParameterType' => 'BulkParameterType',
'CalendarTimeUtil' => 'Phobject',
'CalendarTimeUtilTestCase' => 'PhabricatorTestCase',
'CelerityAPI' => 'Phobject',
@ -5650,6 +5702,7 @@ phutil_register_library_map(array(
'DifferentialRevisionStatus' => 'Phobject',
'DifferentialRevisionStatusDatasource' => 'PhabricatorTypeaheadDatasource',
'DifferentialRevisionStatusFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
'DifferentialRevisionStatusHeraldField' => 'DifferentialRevisionHeraldField',
'DifferentialRevisionStatusTransaction' => 'DifferentialRevisionTransactionType',
'DifferentialRevisionSummaryHeraldField' => 'DifferentialRevisionHeraldField',
'DifferentialRevisionSummaryTransaction' => 'DifferentialRevisionTransactionType',
@ -5827,6 +5880,7 @@ phutil_register_library_map(array(
'DiffusionLintCountQuery' => 'PhabricatorQuery',
'DiffusionLintSaveRunner' => 'Phobject',
'DiffusionLocalRepositoryFilter' => 'Phobject',
'DiffusionLogController' => 'DiffusionController',
'DiffusionLookSoonConduitAPIMethod' => 'DiffusionConduitAPIMethod',
'DiffusionLowLevelCommitFieldsQuery' => 'DiffusionLowLevelQuery',
'DiffusionLowLevelCommitQuery' => 'DiffusionLowLevelQuery',
@ -5894,10 +5948,12 @@ phutil_register_library_map(array(
'DiffusionPreCommitRefTypeHeraldField' => 'DiffusionPreCommitRefHeraldField',
'DiffusionPreCommitUsesGitLFSHeraldField' => 'DiffusionPreCommitContentHeraldField',
'DiffusionPullEventGarbageCollector' => 'PhabricatorGarbageCollector',
'DiffusionPullLogListController' => 'DiffusionLogController',
'DiffusionPullLogListView' => 'AphrontView',
'DiffusionPullLogSearchEngine' => 'PhabricatorApplicationSearchEngine',
'DiffusionPushCapability' => 'PhabricatorPolicyCapability',
'DiffusionPushEventViewController' => 'DiffusionPushLogController',
'DiffusionPushLogController' => 'DiffusionController',
'DiffusionPushLogListController' => 'DiffusionPushLogController',
'DiffusionPushEventViewController' => 'DiffusionLogController',
'DiffusionPushLogListController' => 'DiffusionLogController',
'DiffusionPushLogListView' => 'AphrontView',
'DiffusionPythonExternalSymbolsSource' => 'DiffusionExternalSymbolsSource',
'DiffusionQuery' => 'PhabricatorQuery',
@ -6516,7 +6572,9 @@ phutil_register_library_map(array(
'HeraldConditionTranscript' => 'Phobject',
'HeraldContentSourceField' => 'HeraldField',
'HeraldController' => 'PhabricatorController',
'HeraldCoreStateReasons' => 'HeraldStateReasons',
'HeraldDAO' => 'PhabricatorLiskDAO',
'HeraldDeprecatedFieldGroup' => 'HeraldFieldGroup',
'HeraldDifferentialAdapter' => 'HeraldAdapter',
'HeraldDifferentialDiffAdapter' => 'HeraldDifferentialAdapter',
'HeraldDifferentialRevisionAdapter' => array(
@ -6557,7 +6615,6 @@ phutil_register_library_map(array(
'HeraldRelatedFieldGroup' => 'HeraldFieldGroup',
'HeraldRemarkupFieldValue' => 'HeraldFieldValue',
'HeraldRemarkupRule' => 'PhabricatorObjectRemarkupRule',
'HeraldRepetitionPolicyConfig' => 'Phobject',
'HeraldRule' => array(
'HeraldDAO',
'PhabricatorApplicationTransactionInterface',
@ -6678,8 +6735,8 @@ phutil_register_library_map(array(
'MacroQueryConduitAPIMethod' => 'MacroConduitAPIMethod',
'ManiphestAssignEmailCommand' => 'ManiphestEmailCommand',
'ManiphestAssigneeDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
'ManiphestBatchEditController' => 'ManiphestController',
'ManiphestBulkEditCapability' => 'PhabricatorPolicyCapability',
'ManiphestBulkEditController' => 'ManiphestController',
'ManiphestClaimEmailCommand' => 'ManiphestEmailCommand',
'ManiphestCloseEmailCommand' => 'ManiphestEmailCommand',
'ManiphestConduitAPIMethod' => 'ConduitAPIMethod',
@ -6761,6 +6818,7 @@ phutil_register_library_map(array(
'ManiphestTaskAttachTransaction' => 'ManiphestTaskTransactionType',
'ManiphestTaskAuthorHeraldField' => 'ManiphestTaskHeraldField',
'ManiphestTaskAuthorPolicyRule' => 'PhabricatorPolicyRule',
'ManiphestTaskBulkEngine' => 'PhabricatorBulkEngine',
'ManiphestTaskCloseAsDuplicateRelationship' => 'ManiphestTaskRelationship',
'ManiphestTaskClosedStatusDatasource' => 'PhabricatorTypeaheadDatasource',
'ManiphestTaskCoverImageTransaction' => 'ManiphestTaskTransactionType',
@ -6770,7 +6828,6 @@ phutil_register_library_map(array(
'ManiphestTaskDescriptionTransaction' => 'ManiphestTaskTransactionType',
'ManiphestTaskDetailController' => 'ManiphestController',
'ManiphestTaskEdgeTransaction' => 'ManiphestTaskTransactionType',
'ManiphestTaskEditBulkJobType' => 'PhabricatorWorkerBulkJobType',
'ManiphestTaskEditController' => 'ManiphestController',
'ManiphestTaskEditEngineLock' => 'PhabricatorEditEngineLock',
'ManiphestTaskFerretEngine' => 'PhabricatorFerretEngine',
@ -7284,6 +7341,7 @@ phutil_register_library_map(array(
'PhabricatorAuthApplication' => 'PhabricatorApplication',
'PhabricatorAuthAuthFactorPHIDType' => 'PhabricatorPHIDType',
'PhabricatorAuthAuthProviderPHIDType' => 'PhabricatorPHIDType',
'PhabricatorAuthChangePasswordAction' => 'PhabricatorSystemAction',
'PhabricatorAuthConduitAPIMethod' => 'ConduitAPIMethod',
'PhabricatorAuthConduitTokenRevoker' => 'PhabricatorAuthRevoker',
'PhabricatorAuthConfirmLinkController' => 'PhabricatorAuthController',
@ -7343,7 +7401,25 @@ phutil_register_library_map(array(
'PhabricatorAuthOldOAuthRedirectController' => 'PhabricatorAuthController',
'PhabricatorAuthOneTimeLoginController' => 'PhabricatorAuthController',
'PhabricatorAuthOneTimeLoginTemporaryTokenType' => 'PhabricatorAuthTemporaryTokenType',
'PhabricatorAuthPassword' => array(
'PhabricatorAuthDAO',
'PhabricatorPolicyInterface',
'PhabricatorDestructibleInterface',
'PhabricatorApplicationTransactionInterface',
),
'PhabricatorAuthPasswordEditor' => 'PhabricatorApplicationTransactionEditor',
'PhabricatorAuthPasswordEngine' => 'Phobject',
'PhabricatorAuthPasswordException' => 'Exception',
'PhabricatorAuthPasswordPHIDType' => 'PhabricatorPHIDType',
'PhabricatorAuthPasswordQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorAuthPasswordResetTemporaryTokenType' => 'PhabricatorAuthTemporaryTokenType',
'PhabricatorAuthPasswordRevokeTransaction' => 'PhabricatorAuthPasswordTransactionType',
'PhabricatorAuthPasswordRevoker' => 'PhabricatorAuthRevoker',
'PhabricatorAuthPasswordTestCase' => 'PhabricatorTestCase',
'PhabricatorAuthPasswordTransaction' => 'PhabricatorModularTransaction',
'PhabricatorAuthPasswordTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorAuthPasswordTransactionType' => 'PhabricatorModularTransactionType',
'PhabricatorAuthPasswordUpgradeTransaction' => 'PhabricatorAuthPasswordTransactionType',
'PhabricatorAuthProvider' => 'Phobject',
'PhabricatorAuthProviderConfig' => array(
'PhabricatorAuthDAO',
@ -7368,7 +7444,6 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionInterface',
),
'PhabricatorAuthSSHKeyController' => 'PhabricatorAuthController',
'PhabricatorAuthSSHKeyDeactivateController' => 'PhabricatorAuthSSHKeyController',
'PhabricatorAuthSSHKeyEditController' => 'PhabricatorAuthSSHKeyController',
'PhabricatorAuthSSHKeyEditor' => 'PhabricatorApplicationTransactionEditor',
'PhabricatorAuthSSHKeyGenerateController' => 'PhabricatorAuthSSHKeyController',
@ -7376,12 +7451,15 @@ phutil_register_library_map(array(
'PhabricatorAuthSSHKeyPHIDType' => 'PhabricatorPHIDType',
'PhabricatorAuthSSHKeyQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorAuthSSHKeyReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
'PhabricatorAuthSSHKeyRevokeController' => 'PhabricatorAuthSSHKeyController',
'PhabricatorAuthSSHKeySearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhabricatorAuthSSHKeyTableView' => 'AphrontView',
'PhabricatorAuthSSHKeyTestCase' => 'PhabricatorTestCase',
'PhabricatorAuthSSHKeyTransaction' => 'PhabricatorApplicationTransaction',
'PhabricatorAuthSSHKeyTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorAuthSSHKeyViewController' => 'PhabricatorAuthSSHKeyController',
'PhabricatorAuthSSHPublicKey' => 'Phobject',
'PhabricatorAuthSSHRevoker' => 'PhabricatorAuthRevoker',
'PhabricatorAuthSession' => array(
'PhabricatorAuthDAO',
'PhabricatorPolicyInterface',
@ -7392,6 +7470,7 @@ phutil_register_library_map(array(
'PhabricatorAuthSessionGarbageCollector' => 'PhabricatorGarbageCollector',
'PhabricatorAuthSessionInfo' => 'Phobject',
'PhabricatorAuthSessionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorAuthSessionRevoker' => 'PhabricatorAuthRevoker',
'PhabricatorAuthSetPasswordController' => 'PhabricatorAuthController',
'PhabricatorAuthSetupCheck' => 'PhabricatorSetupCheck',
'PhabricatorAuthStartController' => 'PhabricatorAuthController',
@ -7402,6 +7481,7 @@ phutil_register_library_map(array(
),
'PhabricatorAuthTemporaryTokenGarbageCollector' => 'PhabricatorGarbageCollector',
'PhabricatorAuthTemporaryTokenQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorAuthTemporaryTokenRevoker' => 'PhabricatorAuthRevoker',
'PhabricatorAuthTemporaryTokenType' => 'Phobject',
'PhabricatorAuthTemporaryTokenTypeModule' => 'PhabricatorConfigModule',
'PhabricatorAuthTerminateSessionController' => 'PhabricatorAuthController',
@ -7487,6 +7567,11 @@ phutil_register_library_map(array(
'PhabricatorBuiltinFileCachePurger' => 'PhabricatorCachePurger',
'PhabricatorBuiltinPatchList' => 'PhabricatorSQLPatchList',
'PhabricatorBulkContentSource' => 'PhabricatorContentSource',
'PhabricatorBulkEditGroup' => 'Phobject',
'PhabricatorBulkEngine' => 'Phobject',
'PhabricatorBulkManagementMakeSilentWorkflow' => 'PhabricatorBulkManagementWorkflow',
'PhabricatorBulkManagementWorkflow' => 'PhabricatorManagementWorkflow',
'PhabricatorCSVExportFormat' => 'PhabricatorExportFormat',
'PhabricatorCacheDAO' => 'PhabricatorLiskDAO',
'PhabricatorCacheEngine' => 'Phobject',
'PhabricatorCacheEngineExtension' => 'Phobject',
@ -8106,6 +8191,7 @@ phutil_register_library_map(array(
'PhabricatorPolicyInterface',
),
'PhabricatorEditEngineAPIMethod' => 'ConduitAPIMethod',
'PhabricatorEditEngineBulkJobType' => 'PhabricatorWorkerBulkJobType',
'PhabricatorEditEngineCheckboxesCommentAction' => 'PhabricatorEditEngineCommentAction',
'PhabricatorEditEngineColumnsCommentAction' => 'PhabricatorEditEngineCommentAction',
'PhabricatorEditEngineCommentAction' => 'Phobject',
@ -8182,12 +8268,15 @@ phutil_register_library_map(array(
'PhabricatorEnv' => 'Phobject',
'PhabricatorEnvTestCase' => 'PhabricatorTestCase',
'PhabricatorEpochEditField' => 'PhabricatorEditField',
'PhabricatorEpochExportField' => 'PhabricatorExportField',
'PhabricatorEvent' => 'PhutilEvent',
'PhabricatorEventEngine' => 'Phobject',
'PhabricatorEventListener' => 'PhutilEventListener',
'PhabricatorEventType' => 'PhutilEventType',
'PhabricatorExampleEventListener' => 'PhabricatorEventListener',
'PhabricatorExecFutureFileUploadSource' => 'PhabricatorFileUploadSource',
'PhabricatorExportField' => 'Phobject',
'PhabricatorExportFormat' => 'Phobject',
'PhabricatorExtendingPhabricatorConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorExtensionsSetupCheck' => 'PhabricatorSetupCheck',
'PhabricatorExternalAccount' => array(
@ -8340,6 +8429,7 @@ phutil_register_library_map(array(
'PhabricatorFileUploadDialogController' => 'PhabricatorFileController',
'PhabricatorFileUploadException' => 'Exception',
'PhabricatorFileUploadSource' => 'Phobject',
'PhabricatorFileUploadSourceByteLimitException' => 'Exception',
'PhabricatorFileinfoSetupCheck' => 'PhabricatorSetupCheck',
'PhabricatorFilesApplication' => 'PhabricatorApplication',
'PhabricatorFilesApplicationStorageEnginePanel' => 'PhabricatorApplicationConfigurationPanel',
@ -8447,6 +8537,7 @@ phutil_register_library_map(array(
'PhabricatorHomeProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorHovercardEngineExtension' => 'Phobject',
'PhabricatorHovercardEngineExtensionModule' => 'PhabricatorConfigModule',
'PhabricatorIDExportField' => 'PhabricatorExportField',
'PhabricatorIDsSearchEngineExtension' => 'PhabricatorSearchEngineExtension',
'PhabricatorIDsSearchField' => 'PhabricatorSearchField',
'PhabricatorIconDatasource' => 'PhabricatorTypeaheadDatasource',
@ -8469,6 +8560,7 @@ phutil_register_library_map(array(
'PhabricatorInlineSummaryView' => 'AphrontView',
'PhabricatorInstructionsEditField' => 'PhabricatorEditField',
'PhabricatorIntConfigType' => 'PhabricatorTextConfigType',
'PhabricatorIntExportField' => 'PhabricatorExportField',
'PhabricatorInternalSetting' => 'PhabricatorSetting',
'PhabricatorInternationalizationManagementExtractWorkflow' => 'PhabricatorInternationalizationManagementWorkflow',
'PhabricatorInternationalizationManagementWorkflow' => 'PhabricatorManagementWorkflow',
@ -8478,6 +8570,7 @@ phutil_register_library_map(array(
'PhabricatorIteratorFileUploadSource' => 'PhabricatorFileUploadSource',
'PhabricatorJIRAAuthProvider' => 'PhabricatorOAuth1AuthProvider',
'PhabricatorJSONConfigType' => 'PhabricatorTextConfigType',
'PhabricatorJSONExportFormat' => 'PhabricatorExportFormat',
'PhabricatorJavelinLinter' => 'ArcanistLinter',
'PhabricatorJiraIssueHasObjectEdgeType' => 'PhabricatorEdgeType',
'PhabricatorJumpNavHandler' => 'Phobject',
@ -8837,6 +8930,7 @@ phutil_register_library_map(array(
'PhabricatorPHDConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorPHID' => 'Phobject',
'PhabricatorPHIDConstants' => 'Phobject',
'PhabricatorPHIDExportField' => 'PhabricatorExportField',
'PhabricatorPHIDListEditField' => 'PhabricatorEditField',
'PhabricatorPHIDListEditType' => 'PhabricatorEditType',
'PhabricatorPHIDResolver' => 'Phobject',
@ -8952,6 +9046,7 @@ phutil_register_library_map(array(
'PhabricatorPagerUIExample' => 'PhabricatorUIExample',
'PhabricatorPassphraseApplication' => 'PhabricatorApplication',
'PhabricatorPasswordAuthProvider' => 'PhabricatorAuthProvider',
'PhabricatorPasswordDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
'PhabricatorPasswordHasher' => 'Phobject',
'PhabricatorPasswordHasherTestCase' => 'PhabricatorTestCase',
'PhabricatorPasswordHasherUnavailableException' => 'Exception',
@ -9530,7 +9625,6 @@ phutil_register_library_map(array(
'PhabricatorRepositoryURITestCase' => 'PhabricatorTestCase',
'PhabricatorRepositoryURITransaction' => 'PhabricatorApplicationTransaction',
'PhabricatorRepositoryURITransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorRepositoryVCSPassword' => 'PhabricatorRepositoryDAO',
'PhabricatorRepositoryWorkingCopyVersion' => 'PhabricatorRepositoryDAO',
'PhabricatorRequestExceptionHandler' => 'AphrontRequestExceptionHandler',
'PhabricatorResourceSite' => 'PhabricatorSite',
@ -9554,7 +9648,7 @@ phutil_register_library_map(array(
'PhabricatorSSHKeysSettingsPanel' => 'PhabricatorSettingsPanel',
'PhabricatorSSHLog' => 'Phobject',
'PhabricatorSSHPassthruCommand' => 'Phobject',
'PhabricatorSSHWorkflow' => 'PhabricatorManagementWorkflow',
'PhabricatorSSHWorkflow' => 'PhutilArgumentWorkflow',
'PhabricatorSavedQuery' => array(
'PhabricatorSearchDAO',
'PhabricatorPolicyInterface',
@ -9776,6 +9870,7 @@ phutil_register_library_map(array(
'PhabricatorStorageSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'PhabricatorStorageSetupCheck' => 'PhabricatorSetupCheck',
'PhabricatorStringConfigType' => 'PhabricatorTextConfigType',
'PhabricatorStringExportField' => 'PhabricatorExportField',
'PhabricatorStringListConfigType' => 'PhabricatorTextListConfigType',
'PhabricatorStringListEditField' => 'PhabricatorEditField',
'PhabricatorStringSetting' => 'PhabricatorSetting',
@ -9838,6 +9933,7 @@ phutil_register_library_map(array(
'PhabricatorTextAreaEditField' => 'PhabricatorEditField',
'PhabricatorTextConfigType' => 'PhabricatorConfigType',
'PhabricatorTextEditField' => 'PhabricatorEditField',
'PhabricatorTextExportFormat' => 'PhabricatorExportFormat',
'PhabricatorTextListConfigType' => 'PhabricatorTextConfigType',
'PhabricatorTime' => 'Phobject',
'PhabricatorTimeFormatSetting' => 'PhabricatorSelectSetting',
@ -9931,6 +10027,7 @@ phutil_register_library_map(array(
'PhabricatorFulltextInterface',
'PhabricatorFerretInterface',
'PhabricatorConduitResultInterface',
'PhabricatorAuthPasswordHashInterface',
),
'PhabricatorUserBadgesCacheType' => 'PhabricatorUserCacheType',
'PhabricatorUserBlurbField' => 'PhabricatorUserCustomField',

View file

@ -0,0 +1,208 @@
<?php
final class PhabricatorAuthPasswordTestCase extends PhabricatorTestCase {
protected function getPhabricatorTestCaseConfiguration() {
return array(
self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true,
);
}
public function testCompare() {
$password1 = new PhutilOpaqueEnvelope('hunter2');
$password2 = new PhutilOpaqueEnvelope('hunter3');
$user = $this->generateNewTestUser();
$type = PhabricatorAuthPassword::PASSWORD_TYPE_TEST;
$pass = PhabricatorAuthPassword::initializeNewPassword($user, $type)
->setPassword($password1, $user)
->save();
$this->assertTrue(
$pass->comparePassword($password1, $user),
pht('Good password should match.'));
$this->assertFalse(
$pass->comparePassword($password2, $user),
pht('Bad password should not match.'));
}
public function testPasswordEngine() {
$password1 = new PhutilOpaqueEnvelope('the quick');
$password2 = new PhutilOpaqueEnvelope('brown fox');
$user = $this->generateNewTestUser();
$test_type = PhabricatorAuthPassword::PASSWORD_TYPE_TEST;
$account_type = PhabricatorAuthPassword::PASSWORD_TYPE_ACCOUNT;
$content_source = $this->newContentSource();
$engine = id(new PhabricatorAuthPasswordEngine())
->setViewer($user)
->setContentSource($content_source)
->setPasswordType($test_type)
->setObject($user);
$account_engine = id(new PhabricatorAuthPasswordEngine())
->setViewer($user)
->setContentSource($content_source)
->setPasswordType($account_type)
->setObject($user);
// We haven't set any passwords yet, so both passwords should be
// invalid.
$this->assertFalse($engine->isValidPassword($password1));
$this->assertFalse($engine->isValidPassword($password2));
$pass = PhabricatorAuthPassword::initializeNewPassword($user, $test_type)
->setPassword($password1, $user)
->save();
// The password should now be valid.
$this->assertTrue($engine->isValidPassword($password1));
$this->assertFalse($engine->isValidPassword($password2));
// But, since the password is a "test" password, it should not be a valid
// "account" password.
$this->assertFalse($account_engine->isValidPassword($password1));
$this->assertFalse($account_engine->isValidPassword($password2));
// Both passwords are unique for the "test" engine, since an active
// password of a given type doesn't collide with itself.
$this->assertTrue($engine->isUniquePassword($password1));
$this->assertTrue($engine->isUniquePassword($password2));
// The "test" password is no longer unique for the "account" engine.
$this->assertFalse($account_engine->isUniquePassword($password1));
$this->assertTrue($account_engine->isUniquePassword($password2));
$this->revokePassword($user, $pass);
// Now that we've revoked the password, it should no longer be valid.
$this->assertFalse($engine->isValidPassword($password1));
$this->assertFalse($engine->isValidPassword($password2));
// But it should be a revoked password.
$this->assertTrue($engine->isRevokedPassword($password1));
$this->assertFalse($engine->isRevokedPassword($password2));
// It should be revoked for both roles: revoking a "test" password also
// prevents you from choosing it as a new "account" password.
$this->assertTrue($account_engine->isRevokedPassword($password1));
$this->assertFalse($account_engine->isValidPassword($password2));
// The revoked password makes this password non-unique for all account
// types.
$this->assertFalse($engine->isUniquePassword($password1));
$this->assertTrue($engine->isUniquePassword($password2));
$this->assertFalse($account_engine->isUniquePassword($password1));
$this->assertTrue($account_engine->isUniquePassword($password2));
}
public function testPasswordUpgrade() {
$weak_hasher = new PhabricatorIteratedMD5PasswordHasher();
// Make sure we have two different hashers, and that the second one is
// stronger than iterated MD5. The most common reason this would fail is
// if an install does not have bcrypt available.
$strong_hasher = PhabricatorPasswordHasher::getBestHasher();
if ($strong_hasher->getStrength() <= $weak_hasher->getStrength()) {
$this->assertSkipped(
pht(
'Multiple password hashers of different strengths are not '.
'available, so hash upgrading can not be tested.'));
}
$envelope = new PhutilOpaqueEnvelope('lunar1997');
$user = $this->generateNewTestUser();
$type = PhabricatorAuthPassword::PASSWORD_TYPE_TEST;
$content_source = $this->newContentSource();
$engine = id(new PhabricatorAuthPasswordEngine())
->setViewer($user)
->setContentSource($content_source)
->setPasswordType($type)
->setObject($user);
$password = PhabricatorAuthPassword::initializeNewPassword($user, $type)
->setPasswordWithHasher($envelope, $user, $weak_hasher)
->save();
$weak_name = $weak_hasher->getHashName();
$strong_name = $strong_hasher->getHashName();
// Since we explicitly used the weak hasher, the password should have
// been hashed with it.
$actual_hasher = $password->getHasher();
$this->assertEqual($weak_name, $actual_hasher->getHashName());
$is_valid = $engine
->setUpgradeHashers(false)
->isValidPassword($envelope, $user);
$password->reload();
// Since we disabled hasher upgrading, the password should not have been
// rehashed.
$this->assertTrue($is_valid);
$actual_hasher = $password->getHasher();
$this->assertEqual($weak_name, $actual_hasher->getHashName());
$is_valid = $engine
->setUpgradeHashers(true)
->isValidPassword($envelope, $user);
$password->reload();
// Now that we enabled hasher upgrading, the password should have been
// automatically rehashed into the stronger format.
$this->assertTrue($is_valid);
$actual_hasher = $password->getHasher();
$this->assertEqual($strong_name, $actual_hasher->getHashName());
// We should also have an "upgrade" transaction in the transaction record
// now which records the two hasher names.
$xactions = id(new PhabricatorAuthPasswordTransactionQuery())
->setViewer($user)
->withObjectPHIDs(array($password->getPHID()))
->withTransactionTypes(
array(
PhabricatorAuthPasswordUpgradeTransaction::TRANSACTIONTYPE,
))
->execute();
$this->assertEqual(1, count($xactions));
$xaction = head($xactions);
$this->assertEqual($weak_name, $xaction->getOldValue());
$this->assertEqual($strong_name, $xaction->getNewValue());
$is_valid = $engine
->isValidPassword($envelope, $user);
// Finally, the password should still be valid after all the dust has
// settled.
$this->assertTrue($is_valid);
}
private function revokePassword(
PhabricatorUser $actor,
PhabricatorAuthPassword $password) {
$content_source = $this->newContentSource();
$revoke_type = PhabricatorAuthPasswordRevokeTransaction::TRANSACTIONTYPE;
$xactions = array();
$xactions[] = $password->getApplicationTransactionTemplate()
->setTransactionType($revoke_type)
->setNewValue(true);
$editor = $password->getApplicationTransactionEditor()
->setActor($actor)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true)
->setContentSource($content_source)
->applyTransactions($password, $xactions);
}
}

View file

@ -0,0 +1,78 @@
<?php
final class PhabricatorAuthSSHKeyTestCase extends PhabricatorTestCase {
protected function getPhabricatorTestCaseConfiguration() {
return array(
self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true,
);
}
public function testRevokeSSHKey() {
$user = $this->generateNewTestUser();
$raw_key = 'ssh-rsa hunter2';
$ssh_key = PhabricatorAuthSSHKey::initializeNewSSHKey($user, $user);
// Add the key to the user's account.
$xactions = array();
$xactions[] = $ssh_key->getApplicationTransactionTemplate()
->setTransactionType(PhabricatorAuthSSHKeyTransaction::TYPE_NAME)
->setNewValue('key1');
$xactions[] = $ssh_key->getApplicationTransactionTemplate()
->setTransactionType(PhabricatorAuthSSHKeyTransaction::TYPE_KEY)
->setNewValue($raw_key);
$this->applyTransactions($user, $ssh_key, $xactions);
$ssh_key->reload();
$this->assertTrue((bool)$ssh_key->getIsActive());
// Revoke it.
$xactions = array();
$xactions[] = $ssh_key->getApplicationTransactionTemplate()
->setTransactionType(PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE)
->setNewValue(true);
$this->applyTransactions($user, $ssh_key, $xactions);
$ssh_key->reload();
$this->assertFalse((bool)$ssh_key->getIsActive());
// Try to add the revoked key back. This should fail with a validation
// error because the key was previously revoked by the user.
$revoked_key = PhabricatorAuthSSHKey::initializeNewSSHKey($user, $user);
$xactions = array();
$xactions[] = $ssh_key->getApplicationTransactionTemplate()
->setTransactionType(PhabricatorAuthSSHKeyTransaction::TYPE_NAME)
->setNewValue('key2');
$xactions[] = $ssh_key->getApplicationTransactionTemplate()
->setTransactionType(PhabricatorAuthSSHKeyTransaction::TYPE_KEY)
->setNewValue($raw_key);
$caught = null;
try {
$this->applyTransactions($user, $ssh_key, $xactions);
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$errors = $ex->getErrors();
$this->assertEqual(1, count($errors));
$caught = head($errors)->getType();
}
$this->assertEqual(PhabricatorAuthSSHKeyTransaction::TYPE_KEY, $caught);
}
private function applyTransactions(
PhabricatorUser $actor,
PhabricatorAuthSSHKey $key,
array $xactions) {
$content_source = $this->newContentSource();
$editor = $key->getApplicationTransactionEditor()
->setActor($actor)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true)
->setContentSource($content_source)
->applyTransactions($key, $xactions);
}
}

View file

@ -0,0 +1,22 @@
<?php
final class PhabricatorAuthChangePasswordAction
extends PhabricatorSystemAction {
const TYPECONST = 'auth.password';
public function getActionConstant() {
return self::TYPECONST;
}
public function getScoreThreshold() {
return 20 / phutil_units('1 hour in seconds');
}
public function getLimitExplanation() {
return pht(
'You have failed to enter the correct account password too often in '.
'a short period of time.');
}
}

View file

@ -80,8 +80,8 @@ final class PhabricatorAuthApplication extends PhabricatorApplication {
'generate/' => 'PhabricatorAuthSSHKeyGenerateController',
'upload/' => 'PhabricatorAuthSSHKeyEditController',
'edit/(?P<id>\d+)/' => 'PhabricatorAuthSSHKeyEditController',
'deactivate/(?P<id>\d+)/'
=> 'PhabricatorAuthSSHKeyDeactivateController',
'revoke/(?P<id>\d+)/'
=> 'PhabricatorAuthSSHKeyRevokeController',
'view/(?P<id>\d+)/' => 'PhabricatorAuthSSHKeyViewController',
),
'password/' => 'PhabricatorAuthSetPasswordController',

View file

@ -61,6 +61,9 @@ final class PhabricatorAuthRegisterController
$default_username = $account->getUsername();
$default_realname = $account->getRealName();
$account_type = PhabricatorAuthPassword::PASSWORD_TYPE_ACCOUNT;
$content_source = PhabricatorContentSource::newFromRequest($request);
$default_email = $account->getEmail();
if ($invite) {
@ -285,27 +288,22 @@ final class PhabricatorAuthRegisterController
if ($must_set_password) {
$value_password = $request->getStr('password');
$value_confirm = $request->getStr('confirm');
if (!strlen($value_password)) {
$e_password = pht('Required');
$errors[] = pht('You must choose a password.');
} else if ($value_password !== $value_confirm) {
$e_password = pht('No Match');
$errors[] = pht('Password and confirmation must match.');
} else if (strlen($value_password) < $min_len) {
$e_password = pht('Too Short');
$errors[] = pht(
'Password is too short (must be at least %d characters long).',
$min_len);
} else if (
PhabricatorCommonPasswords::isCommonPassword($value_password)) {
$e_password = pht('Very Weak');
$errors[] = pht(
'Password is pathologically weak. This password is one of the '.
'most common passwords in use, and is extremely easy for '.
'attackers to guess. You must choose a stronger password.');
} else {
$password_envelope = new PhutilOpaqueEnvelope($value_password);
$confirm_envelope = new PhutilOpaqueEnvelope($value_confirm);
$engine = id(new PhabricatorAuthPasswordEngine())
->setViewer($user)
->setContentSource($content_source)
->setPasswordType($account_type)
->setObject($user);
try {
$engine->checkNewPassword($password_envelope, $confirm_envelope);
$e_password = null;
} catch (PhabricatorAuthPasswordException $ex) {
$errors[] = $ex->getMessage();
$e_password = $ex->getPasswordError();
}
}
@ -408,8 +406,13 @@ final class PhabricatorAuthRegisterController
$editor->createNewUser($user, $email_obj, $allow_reassign_email);
if ($must_set_password) {
$envelope = new PhutilOpaqueEnvelope($value_password);
$editor->changePassword($user, $envelope);
$password_object = PhabricatorAuthPassword::initializeNewPassword(
$user,
$account_type);
$password_object
->setPassword($password_envelope, $user)
->save();
}
if ($is_setup) {

View file

@ -1,6 +1,6 @@
<?php
final class PhabricatorAuthSSHKeyDeactivateController
final class PhabricatorAuthSSHKeyRevokeController
extends PhabricatorAuthSSHKeyController {
public function handleRequest(AphrontRequest $request) {
@ -46,14 +46,14 @@ final class PhabricatorAuthSSHKeyDeactivateController
$name = phutil_tag('strong', array(), $key->getName());
return $this->newDialog()
->setTitle(pht('Deactivate SSH Public Key'))
->setTitle(pht('Revoke SSH Public Key'))
->appendParagraph(
pht(
'The key "%s" will be permanently deactivated, and you will no '.
'The key "%s" will be permanently revoked, and you will no '.
'longer be able to use the corresponding private key to '.
'authenticate.',
$name))
->addSubmitButton(pht('Deactivate Public Key'))
->addSubmitButton(pht('Revoke Public Key'))
->addCancelButton($cancel_uri);
}

View file

@ -35,7 +35,7 @@ final class PhabricatorAuthSSHKeyViewController
if ($ssh_key->getIsActive()) {
$header->setStatus('fa-check', 'bluegrey', pht('Active'));
} else {
$header->setStatus('fa-ban', 'dark', pht('Deactivated'));
$header->setStatus('fa-ban', 'dark', pht('Revoked'));
}
$header->addActionLink(
@ -80,7 +80,7 @@ final class PhabricatorAuthSSHKeyViewController
$id = $ssh_key->getID();
$edit_uri = $this->getApplicationURI("sshkey/edit/{$id}/");
$deactivate_uri = $this->getApplicationURI("sshkey/deactivate/{$id}/");
$revoke_uri = $this->getApplicationURI("sshkey/revoke/{$id}/");
$curtain = $this->newCurtainView($ssh_key);
@ -95,8 +95,8 @@ final class PhabricatorAuthSSHKeyViewController
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-times')
->setName(pht('Deactivate SSH Key'))
->setHref($deactivate_uri)
->setName(pht('Revoke SSH Key'))
->setHref($revoke_uri)
->setWorkflow(true)
->setDisabled(!$can_edit));

View file

@ -40,8 +40,30 @@ final class PhabricatorAuthSetPasswordController
return new Aphront404Response();
}
$min_len = PhabricatorEnv::getEnvConfig('account.minimum-password-length');
$min_len = (int)$min_len;
$content_source = PhabricatorContentSource::newFromRequest($request);
$account_type = PhabricatorAuthPassword::PASSWORD_TYPE_ACCOUNT;
$password_objects = id(new PhabricatorAuthPasswordQuery())
->setViewer($viewer)
->withObjectPHIDs(array($viewer->getPHID()))
->withPasswordTypes(array($account_type))
->withIsRevoked(false)
->execute();
if ($password_objects) {
$password_object = head($password_objects);
$has_password = true;
} else {
$password_object = PhabricatorAuthPassword::initializeNewPassword(
$viewer,
$account_type);
$has_password = false;
}
$engine = id(new PhabricatorAuthPasswordEngine())
->setViewer($viewer)
->setContentSource($content_source)
->setPasswordType($account_type)
->setObject($viewer);
$e_password = true;
$e_confirm = true;
@ -50,46 +72,23 @@ final class PhabricatorAuthSetPasswordController
$password = $request->getStr('password');
$confirm = $request->getStr('confirm');
$e_password = null;
$e_confirm = null;
$password_envelope = new PhutilOpaqueEnvelope($password);
$confirm_envelope = new PhutilOpaqueEnvelope($confirm);
if (!strlen($password)) {
$errors[] = pht('You must choose a password or skip this step.');
$e_password = pht('Required');
} else if (strlen($password) < $min_len) {
$errors[] = pht(
'The selected password is too short. Passwords must be a minimum '.
'of %s characters.',
new PhutilNumber($min_len));
$e_password = pht('Too Short');
} else if (!strlen($confirm)) {
$errors[] = pht('You must confirm the selecetd password.');
$e_confirm = pht('Required');
} else if ($password !== $confirm) {
$errors[] = pht('The password and confirmation do not match.');
$e_password = pht('Invalid');
$e_confirm = pht('Invalid');
} else if (PhabricatorCommonPasswords::isCommonPassword($password)) {
$e_password = pht('Very Weak');
$errors[] = pht(
'The selected password is very weak: it is one of the most common '.
'passwords in use. Choose a stronger password.');
try {
$engine->checkNewPassword($password_envelope, $confirm_envelope, true);
$e_password = null;
$e_confirm = null;
} catch (PhabricatorAuthPasswordException $ex) {
$errors[] = $ex->getMessage();
$e_password = $ex->getPasswordError();
$e_confirm = $ex->getConfirmError();
}
if (!$errors) {
$envelope = new PhutilOpaqueEnvelope($password);
// This write is unguarded because the CSRF token has already
// been checked in the call to $request->isFormPost() and
// the CSRF token depends on the password hash, so when it
// is changed here the CSRF token check will fail.
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
id(new PhabricatorUserEditor())
->setActor($viewer)
->changePassword($viewer, $envelope);
unset($unguarded);
$password_object
->setPassword($password_envelope, $viewer)
->save();
// Destroy the token.
$auth_token->delete();
@ -98,12 +97,15 @@ final class PhabricatorAuthSetPasswordController
}
}
$min_len = PhabricatorEnv::getEnvConfig('account.minimum-password-length');
$min_len = (int)$min_len;
$len_caption = null;
if ($min_len) {
$len_caption = pht('Minimum password length: %d characters.', $min_len);
}
if ($viewer->hasPassword()) {
if ($has_password) {
$title = pht('Reset Password');
$crumb = pht('Reset Password');
$submit = pht('Reset Password');

View file

@ -0,0 +1,33 @@
<?php
final class PhabricatorAuthPasswordEditor
extends PhabricatorApplicationTransactionEditor {
private $oldHasher;
public function setOldHasher(PhabricatorPasswordHasher $old_hasher) {
$this->oldHasher = $old_hasher;
return $this;
}
public function getOldHasher() {
return $this->oldHasher;
}
public function getEditorApplicationClass() {
return 'PhabricatorAuthApplication';
}
public function getEditorObjectsDescription() {
return pht('Passwords');
}
public function getCreateObjectTitle($author, $object) {
return pht('%s created this password.', $author);
}
public function getCreateObjectTitleForFeed($author, $object) {
return pht('%s created %s.', $author, $object);
}
}

View file

@ -3,6 +3,17 @@
final class PhabricatorAuthSSHKeyEditor
extends PhabricatorApplicationTransactionEditor {
private $isAdministrativeEdit;
public function setIsAdministrativeEdit($is_administrative_edit) {
$this->isAdministrativeEdit = $is_administrative_edit;
return $this;
}
public function getIsAdministrativeEdit() {
return $this->isAdministrativeEdit;
}
public function getEditorApplicationClass() {
return 'PhabricatorAuthApplication';
}
@ -93,6 +104,7 @@ final class PhabricatorAuthSSHKeyEditor
array $xactions) {
$errors = parent::validateTransaction($object, $type, $xactions);
$viewer = $this->requireActor();
switch ($type) {
case PhabricatorAuthSSHKeyTransaction::TYPE_NAME:
@ -138,6 +150,30 @@ final class PhabricatorAuthSSHKeyEditor
pht('Invalid'),
$ex->getMessage(),
$xaction);
continue;
}
// The database does not have a unique key on just the <keyBody>
// column because we allow multiple accounts to revoke the same
// key, so we can't rely on database constraints to prevent users
// from adding keys that are on the revocation list back to their
// accounts. Explicitly check for a revoked copy of the key.
$revoked_keys = id(new PhabricatorAuthSSHKeyQuery())
->setViewer($viewer)
->withObjectPHIDs(array($object->getObjectPHID()))
->withIsActive(0)
->withKeys(array($public_key))
->execute();
if ($revoked_keys) {
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Revoked'),
pht(
'This key has been revoked. Choose or generate a new, '.
'unique key.'),
$xaction);
continue;
}
}
}
@ -239,11 +275,13 @@ final class PhabricatorAuthSSHKeyEditor
$body = parent::buildMailBody($object, $xactions);
$body->addTextSection(
pht('SECURITY WARNING'),
pht(
'If you do not recognize this change, it may indicate your account '.
'has been compromised.'));
if (!$this->getIsAdministrativeEdit()) {
$body->addTextSection(
pht('SECURITY WARNING'),
pht(
'If you do not recognize this change, it may indicate your account '.
'has been compromised.'));
}
$detail_uri = $object->getURI();
$detail_uri = PhabricatorEnv::getProductionURI($detail_uri);
@ -253,4 +291,17 @@ final class PhabricatorAuthSSHKeyEditor
return $body;
}
protected function getCustomWorkerState() {
return array(
'isAdministrativeEdit' => $this->isAdministrativeEdit,
);
}
protected function loadCustomWorkerState(array $state) {
$this->isAdministrativeEdit = idx($state, 'isAdministrativeEdit');
return $this;
}
}

View file

@ -0,0 +1,320 @@
<?php
final class PhabricatorAuthPasswordEngine
extends Phobject {
private $viewer;
private $contentSource;
private $object;
private $passwordType;
private $upgradeHashers = true;
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
public function setContentSource(PhabricatorContentSource $content_source) {
$this->contentSource = $content_source;
return $this;
}
public function getContentSource() {
return $this->contentSource;
}
public function setObject(PhabricatorAuthPasswordHashInterface $object) {
$this->object = $object;
return $this;
}
public function getObject() {
return $this->object;
}
public function setPasswordType($password_type) {
$this->passwordType = $password_type;
return $this;
}
public function getPasswordType() {
return $this->passwordType;
}
public function setUpgradeHashers($upgrade_hashers) {
$this->upgradeHashers = $upgrade_hashers;
return $this;
}
public function getUpgradeHashers() {
return $this->upgradeHashers;
}
public function checkNewPassword(
PhutilOpaqueEnvelope $password,
PhutilOpaqueEnvelope $confirm,
$can_skip = false) {
$raw_password = $password->openEnvelope();
if (!strlen($raw_password)) {
if ($can_skip) {
throw new PhabricatorAuthPasswordException(
pht('You must choose a password or skip this step.'),
pht('Required'));
} else {
throw new PhabricatorAuthPasswordException(
pht('You must choose a password.'),
pht('Required'));
}
}
$min_len = PhabricatorEnv::getEnvConfig('account.minimum-password-length');
$min_len = (int)$min_len;
if ($min_len) {
if (strlen($raw_password) < $min_len) {
throw new PhabricatorAuthPasswordException(
pht(
'The selected password is too short. Passwords must be a minimum '.
'of %s characters long.',
new PhutilNumber($min_len)),
pht('Too Short'));
}
}
$raw_confirm = $confirm->openEnvelope();
if (!strlen($raw_confirm)) {
throw new PhabricatorAuthPasswordException(
pht('You must confirm the selected password.'),
null,
pht('Required'));
}
if ($raw_password !== $raw_confirm) {
throw new PhabricatorAuthPasswordException(
pht('The password and confirmation do not match.'),
pht('Invalid'),
pht('Invalid'));
}
if (PhabricatorCommonPasswords::isCommonPassword($raw_password)) {
throw new PhabricatorAuthPasswordException(
pht(
'The selected password is very weak: it is one of the most common '.
'passwords in use. Choose a stronger password.'),
pht('Very Weak'));
}
// If we're creating a brand new object (like registering a new user)
// and it does not have a PHID yet, it isn't possible for it to have any
// revoked passwords or colliding passwords either, so we can skip these
// checks.
if ($this->getObject()->getPHID()) {
if ($this->isRevokedPassword($password)) {
throw new PhabricatorAuthPasswordException(
pht(
'The password you entered has been revoked. You can not reuse '.
'a password which has been revoked. Choose a new password.'),
pht('Revoked'));
}
if (!$this->isUniquePassword($password)) {
throw new PhabricatorAuthPasswordException(
pht(
'The password you entered is the same as another password '.
'associated with your account. Each password must be unique.'),
pht('Not Unique'));
}
}
}
public function isValidPassword(PhutilOpaqueEnvelope $envelope) {
$this->requireSetup();
$password_type = $this->getPasswordType();
$passwords = $this->newQuery()
->withPasswordTypes(array($password_type))
->withIsRevoked(false)
->execute();
$matches = $this->getMatches($envelope, $passwords);
if (!$matches) {
return false;
}
if ($this->shouldUpgradeHashers()) {
$this->upgradeHashers($envelope, $matches);
}
return true;
}
public function isUniquePassword(PhutilOpaqueEnvelope $envelope) {
$this->requireSetup();
$password_type = $this->getPasswordType();
// To test that the password is unique, we're loading all active and
// revoked passwords for all roles for the given user, then throwing out
// the active passwords for the current role (so a password can't
// collide with itself).
// Note that two different objects can have the same password (say,
// users @alice and @bailey). We're only preventing @alice from using
// the same password for everything.
$passwords = $this->newQuery()
->execute();
foreach ($passwords as $key => $password) {
$same_type = ($password->getPasswordType() === $password_type);
$is_active = !$password->getIsRevoked();
if ($same_type && $is_active) {
unset($passwords[$key]);
}
}
$matches = $this->getMatches($envelope, $passwords);
return !$matches;
}
public function isRevokedPassword(PhutilOpaqueEnvelope $envelope) {
$this->requireSetup();
// To test if a password is revoked, we're loading all revoked passwords
// across all roles for the given user. If a password was revoked in one
// role, you can't reuse it in a different role.
$passwords = $this->newQuery()
->withIsRevoked(true)
->execute();
$matches = $this->getMatches($envelope, $passwords);
return (bool)$matches;
}
private function requireSetup() {
if (!$this->getObject()) {
throw new PhutilInvalidStateException('setObject');
}
if (!$this->getPasswordType()) {
throw new PhutilInvalidStateException('setPasswordType');
}
if (!$this->getViewer()) {
throw new PhutilInvalidStateException('setViewer');
}
if ($this->shouldUpgradeHashers()) {
if (!$this->getContentSource()) {
throw new PhutilInvalidStateException('setContentSource');
}
}
}
private function shouldUpgradeHashers() {
if (!$this->getUpgradeHashers()) {
return false;
}
if (PhabricatorEnv::isReadOnly()) {
// Don't try to upgrade hashers if we're in read-only mode, since we
// won't be able to write the new hash to the database.
return false;
}
return true;
}
private function newQuery() {
$viewer = $this->getViewer();
$object = $this->getObject();
$password_type = $this->getPasswordType();
return id(new PhabricatorAuthPasswordQuery())
->setViewer($viewer)
->withObjectPHIDs(array($object->getPHID()));
}
private function getMatches(
PhutilOpaqueEnvelope $envelope,
array $passwords) {
$object = $this->getObject();
$matches = array();
foreach ($passwords as $password) {
try {
$is_match = $password->comparePassword($envelope, $object);
} catch (PhabricatorPasswordHasherUnavailableException $ex) {
$is_match = false;
}
if ($is_match) {
$matches[] = $password;
}
}
return $matches;
}
private function upgradeHashers(
PhutilOpaqueEnvelope $envelope,
array $passwords) {
assert_instances_of($passwords, 'PhabricatorAuthPassword');
$need_upgrade = array();
foreach ($passwords as $password) {
if (!$password->canUpgrade()) {
continue;
}
$need_upgrade[] = $password;
}
if (!$need_upgrade) {
return;
}
$upgrade_type = PhabricatorAuthPasswordUpgradeTransaction::TRANSACTIONTYPE;
$viewer = $this->getViewer();
$content_source = $this->getContentSource();
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
foreach ($need_upgrade as $password) {
// This does the actual upgrade. We then apply a transaction to make
// the upgrade more visible and auditable.
$old_hasher = $password->getHasher();
$password->upgradePasswordHasher($envelope, $this->getObject());
$new_hasher = $password->getHasher();
$xactions = array();
$xactions[] = $password->getApplicationTransactionTemplate()
->setTransactionType($upgrade_type)
->setNewValue($new_hasher->getHashName());
$editor = $password->getApplicationTransactionEditor()
->setActor($viewer)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true)
->setContentSource($content_source)
->setOldHasher($old_hasher)
->applyTransactions($password, $xactions);
}
unset($unguarded);
}
}

View file

@ -0,0 +1,29 @@
<?php
final class PhabricatorPasswordDestructionEngineExtension
extends PhabricatorDestructionEngineExtension {
const EXTENSIONKEY = 'passwords';
public function getExtensionName() {
return pht('Passwords');
}
public function destroyObject(
PhabricatorDestructionEngine $engine,
$object) {
$viewer = $engine->getViewer();
$object_phid = $object->getPHID();
$passwords = id(new PhabricatorAuthPasswordQuery())
->setViewer($viewer)
->withObjectPHIDs(array($object_phid))
->execute();
foreach ($passwords as $password) {
$engine->destroyObject($password);
}
}
}

View file

@ -9,8 +9,8 @@ final class PhabricatorAuthManagementRecoverWorkflow
->setExamples('**recover** __username__')
->setSynopsis(
pht(
'Recover access to an administrative account if you have locked '.
'yourself out of Phabricator.'))
'Recover access to an account if you have locked yourself out '.
'of Phabricator.'))
->setArguments(
array(
'username' => array(
@ -21,23 +21,6 @@ final class PhabricatorAuthManagementRecoverWorkflow
}
public function execute(PhutilArgumentParser $args) {
$can_recover = id(new PhabricatorPeopleQuery())
->setViewer($this->getViewer())
->withIsAdmin(true)
->execute();
if (!$can_recover) {
throw new PhutilArgumentUsageException(
pht(
'This Phabricator installation has no recoverable administrator '.
'accounts. You can use `%s` to create a new administrator '.
'account or make an existing user an administrator.',
'bin/accountadmin'));
}
$can_recover = mpull($can_recover, 'getUsername');
sort($can_recover);
$can_recover = implode(', ', $can_recover);
$usernames = $args->getArg('username');
if (!$usernames) {
throw new PhutilArgumentUsageException(
@ -57,18 +40,8 @@ final class PhabricatorAuthManagementRecoverWorkflow
if (!$user) {
throw new PhutilArgumentUsageException(
pht(
'No such user "%s". Recoverable administrator accounts are: %s.',
$username,
$can_recover));
}
if (!$user->getIsAdmin()) {
throw new PhutilArgumentUsageException(
pht(
'You can only recover administrator accounts, but %s is not an '.
'administrator. Recoverable administrator accounts are: %s.',
$username,
$can_recover));
'No such user "%s" to recover.',
$username));
}
if (!$user->canEstablishWebSessions()) {

View file

@ -7,7 +7,8 @@ final class PhabricatorAuthManagementRevokeWorkflow
$this
->setName('revoke')
->setExamples(
"**revoke** --type __type__ --from __user__\n".
"**revoke** --list\n".
"**revoke** --type __type__ --from __@user__\n".
"**revoke** --everything --everywhere")
->setSynopsis(
pht(
@ -16,15 +17,20 @@ final class PhabricatorAuthManagementRevokeWorkflow
array(
array(
'name' => 'from',
'param' => 'user',
'param' => 'object',
'help' => pht(
'Revoke credentials for the specified user.'),
'Revoke credentials for the specified object. To revoke '.
'credentials for a user, use "@username".'),
),
array(
'name' => 'type',
'param' => 'type',
'help' => pht('Revoke credentials of the given type.'),
),
array(
'name' => 'list',
'help' => pht(
'Revoke credentials of the given type.'),
'List information about available credential revokers.'),
),
array(
'name' => 'everything',
@ -34,21 +40,37 @@ final class PhabricatorAuthManagementRevokeWorkflow
'name' => 'everywhere',
'help' => pht('Revoke from all credential owners.'),
),
array(
'name' => 'force',
'help' => pht('Revoke credentials without prompting.'),
),
));
}
public function execute(PhutilArgumentParser $args) {
$viewer = PhabricatorUser::getOmnipotentUser();
$viewer = $this->getViewer();
$all_types = PhabricatorAuthRevoker::getAllRevokers();
$is_force = $args->getArg('force');
// The "--list" flag is compatible with revoker selection flags like
// "--type" to filter the list, but not compatible with target selection
// flags like "--from".
$is_list = $args->getArg('list');
$type = $args->getArg('type');
$is_everything = $args->getArg('everything');
if (!strlen($type) && !$is_everything) {
throw new PhutilArgumentUsageException(
pht(
'Specify the credential type to revoke with "--type" or specify '.
'"--everything".'));
if ($is_list) {
// By default, "bin/revoke --list" implies "--everything".
$types = $all_types;
} else {
throw new PhutilArgumentUsageException(
pht(
'Specify the credential type to revoke with "--type" or specify '.
'"--everything". Use "--list" to list available credential '.
'types.'));
}
} else if (strlen($type) && $is_everything) {
throw new PhutilArgumentUsageException(
pht(
@ -70,6 +92,32 @@ final class PhabricatorAuthManagementRevokeWorkflow
$is_everywhere = $args->getArg('everywhere');
$from = $args->getArg('from');
if ($is_list) {
if (strlen($from) || $is_everywhere) {
throw new PhutilArgumentUsageException(
pht(
'You can not "--list" and revoke credentials (with "--from" or '.
'"--everywhere") in the same operation.'));
}
}
if ($is_list) {
$last_key = last_key($types);
foreach ($types as $key => $type) {
echo tsprintf(
"**%s** (%s)\n\n",
$type->getRevokerKey(),
$type->getRevokerName());
id(new PhutilConsoleBlock())
->addParagraph(tsprintf('%B', $type->getRevokerDescription()))
->draw();
}
return 0;
}
$target = null;
if (!strlen($from) && !$is_everywhere) {
throw new PhutilArgumentUsageException(
@ -97,7 +145,7 @@ final class PhabricatorAuthManagementRevokeWorkflow
}
}
if ($is_everywhere) {
if ($is_everywhere && !$is_force) {
echo id(new PhutilConsoleBlock())
->addParagraph(
pht(
@ -128,6 +176,13 @@ final class PhabricatorAuthManagementRevokeWorkflow
'Destroyed %s credential(s) of type "%s".',
new PhutilNumber($count),
$type->getRevokerKey()));
$guidance = $type->getRevokerNextSteps();
if ($guidance !== null) {
echo tsprintf(
"%s\n",
$guidance);
}
}
echo tsprintf(

View file

@ -0,0 +1,28 @@
<?php
final class PhabricatorAuthPasswordException
extends Exception {
private $passwordError;
private $confirmErorr;
public function __construct(
$message,
$password_error,
$confirm_error = null) {
$this->passwordError = $password_error;
$this->confirmError = $confirm_error;
parent::__construct($message);
}
public function getPasswordError() {
return $this->passwordError;
}
public function getConfirmError() {
return $this->confirmError;
}
}

View file

@ -0,0 +1,9 @@
<?php
interface PhabricatorAuthPasswordHashInterface {
public function newPasswordDigest(
PhutilOpaqueEnvelope $envelope,
PhabricatorAuthPassword $password);
}

View file

@ -0,0 +1,36 @@
<?php
final class PhabricatorAuthPasswordPHIDType extends PhabricatorPHIDType {
const TYPECONST = 'APAS';
public function getTypeName() {
return pht('Auth Password');
}
public function newObject() {
return new PhabricatorAuthPassword();
}
public function getPHIDTypeApplicationClass() {
return 'PhabricatorAuthApplication';
}
protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorAuthPasswordQuery())
->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$password = $objects[$phid];
}
}
}

View file

@ -253,6 +253,7 @@ final class PhabricatorPasswordAuthProvider extends PhabricatorAuthProvider {
$request = $controller->getRequest();
$viewer = $request->getUser();
$content_source = PhabricatorContentSource::newFromRequest($request);
$require_captcha = false;
$captcha_valid = false;
@ -285,22 +286,16 @@ final class PhabricatorPasswordAuthProvider extends PhabricatorAuthProvider {
if ($user) {
$envelope = new PhutilOpaqueEnvelope($request->getStr('password'));
if ($user->comparePassword($envelope)) {
$engine = id(new PhabricatorAuthPasswordEngine())
->setViewer($user)
->setContentSource($content_source)
->setPasswordType(PhabricatorAuthPassword::PASSWORD_TYPE_ACCOUNT)
->setObject($user);
if ($engine->isValidPassword($envelope)) {
$account = $this->loadOrCreateAccount($user->getPHID());
$log_user = $user;
// If the user's password is stored using a less-than-optimal
// hash, upgrade them to the strongest available hash.
$hash_envelope = new PhutilOpaqueEnvelope(
$user->getPasswordHash());
if (PhabricatorPasswordHasher::canUpgradeHash($hash_envelope)) {
$user->setPassword($envelope);
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$user->save();
unset($unguarded);
}
}
}
}

View file

@ -0,0 +1,114 @@
<?php
final class PhabricatorAuthPasswordQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $objectPHIDs;
private $passwordTypes;
private $isRevoked;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withObjectPHIDs(array $object_phids) {
$this->objectPHIDs = $object_phids;
return $this;
}
public function withPasswordTypes(array $types) {
$this->passwordTypes = $types;
return $this;
}
public function withIsRevoked($is_revoked) {
$this->isRevoked = $is_revoked;
return $this;
}
public function newResultObject() {
return new PhabricatorAuthPassword();
}
protected function loadPage() {
return $this->loadStandardPage($this->newResultObject());
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
'phid IN (%Ls)',
$this->phids);
}
if ($this->objectPHIDs !== null) {
$where[] = qsprintf(
$conn,
'objectPHID IN (%Ls)',
$this->objectPHIDs);
}
if ($this->passwordTypes !== null) {
$where[] = qsprintf(
$conn,
'passwordType IN (%Ls)',
$this->passwordTypes);
}
if ($this->isRevoked !== null) {
$where[] = qsprintf(
$conn,
'isRevoked = %d',
(int)$this->isRevoked);
}
return $where;
}
protected function willFilterPage(array $passwords) {
$object_phids = mpull($passwords, 'getObjectPHID');
$objects = id(new PhabricatorObjectQuery())
->setViewer($this->getViewer())
->setParentQuery($this)
->withPHIDs($object_phids)
->execute();
$objects = mpull($objects, null, 'getPHID');
foreach ($passwords as $key => $password) {
$object = idx($objects, $password->getObjectPHID());
if (!$object) {
unset($passwords[$key]);
$this->didRejectResult($password);
continue;
}
$password->attachObject($object);
}
return $passwords;
}
public function getQueryApplicationClass() {
return 'PhabricatorAuthApplication';
}
}

View file

@ -0,0 +1,10 @@
<?php
final class PhabricatorAuthPasswordTransactionQuery
extends PhabricatorApplicationTransactionQuery {
public function getTemplateApplicationTransaction() {
return new PhabricatorAuthPasswordTransaction();
}
}

View file

@ -5,6 +5,19 @@ final class PhabricatorAuthConduitTokenRevoker
const REVOKERKEY = 'conduit';
public function getRevokerName() {
return pht('Conduit API Tokens');
}
public function getRevokerDescription() {
return pht(
"Revokes all Conduit API tokens used to access the API.\n\n".
"Users will need to use `arc install-certificate` to install new ".
"API tokens before `arc` commands will work. Bots and scripts which ".
"access the API will need to have new tokens generated and ".
"installed.");
}
public function revokeAllCredentials() {
$table = id(new PhabricatorConduitToken());
$conn = $table->establishConnection('w');

View file

@ -0,0 +1,81 @@
<?php
final class PhabricatorAuthPasswordRevoker
extends PhabricatorAuthRevoker {
const REVOKERKEY = 'password';
public function getRevokerName() {
return pht('Passwords');
}
public function getRevokerDescription() {
return pht(
"Revokes all stored passwords.\n\n".
"Account passwords and VCS passwords (used to access repositories ".
"over HTTP) will both be revoked. Passwords for any third party ".
"applications which use shared password infrastructure will also ".
"be revoked.\n\n".
"Users will need to reset account passwords, possibly by using the ".
"\"Forgot Password?\" link on the login page. They will also need ".
"to reset VCS passwords.\n\n".
"Passwords are revoked, not just removed. Users will be unable to ".
"select the passwords they used previously and must choose new, ".
"unique passwords.\n\n".
"Revoking passwords will not terminate outstanding login sessions. ".
"Use the \"session\" revoker in conjunction with this revoker to force ".
"users to login again.");
}
public function getRevokerNextSteps() {
return pht(
'NOTE: Revoking passwords does not terminate existing sessions which '.
'were established using the old passwords. To terminate existing '.
'sessions, run the "session" revoker now.');
}
public function revokeAllCredentials() {
$query = new PhabricatorAuthPasswordQuery();
return $this->revokeWithQuery($query);
}
public function revokeCredentialsFrom($object) {
$query = id(new PhabricatorAuthPasswordQuery())
->withObjectPHIDs(array($object->getPHID()));
return $this->revokeWithQuery($query);
}
private function revokeWithQuery(PhabricatorAuthPasswordQuery $query) {
$viewer = $this->getViewer();
$passwords = $query
->setViewer($viewer)
->withIsRevoked(false)
->execute();
$content_source = PhabricatorContentSource::newForSource(
PhabricatorDaemonContentSource::SOURCECONST);
$revoke_type = PhabricatorAuthPasswordRevokeTransaction::TRANSACTIONTYPE;
$auth_phid = id(new PhabricatorAuthApplication())->getPHID();
foreach ($passwords as $password) {
$xactions = array();
$xactions[] = $password->getApplicationTransactionTemplate()
->setTransactionType($revoke_type)
->setNewValue(true);
$editor = $password->getApplicationTransactionEditor()
->setActor($viewer)
->setActingAsPHID($auth_phid)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true)
->setContentSource($content_source)
->applyTransactions($password, $xactions);
}
return count($passwords);
}
}

View file

@ -5,9 +5,16 @@ abstract class PhabricatorAuthRevoker
private $viewer;
abstract public function revokeAlLCredentials();
abstract public function revokeAllCredentials();
abstract public function revokeCredentialsFrom($object);
abstract public function getRevokerName();
abstract public function getRevokerDescription();
public function getRevokerNextSteps() {
return null;
}
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;

View file

@ -0,0 +1,65 @@
<?php
final class PhabricatorAuthSSHRevoker
extends PhabricatorAuthRevoker {
const REVOKERKEY = 'ssh';
public function getRevokerName() {
return pht('SSH Keys');
}
public function getRevokerDescription() {
return pht(
"Revokes all SSH public keys.\n\n".
"SSH public keys are revoked, not just removed. Users will need to ".
"generate and upload new, unique keys before they can access ".
"repositories or other services over SSH.");
}
public function revokeAllCredentials() {
$query = new PhabricatorAuthSSHKeyQuery();
return $this->revokeWithQuery($query);
}
public function revokeCredentialsFrom($object) {
$query = id(new PhabricatorAuthSSHKeyQuery())
->withObjectPHIDs(array($object->getPHID()));
return $this->revokeWithQuery($query);
}
private function revokeWithQuery(PhabricatorAuthSSHKeyQuery $query) {
$viewer = $this->getViewer();
// We're only going to revoke keys which have not already been revoked.
$ssh_keys = $query
->setViewer($viewer)
->withIsActive(true)
->execute();
$content_source = PhabricatorContentSource::newForSource(
PhabricatorDaemonContentSource::SOURCECONST);
$auth_phid = id(new PhabricatorAuthApplication())->getPHID();
foreach ($ssh_keys as $ssh_key) {
$xactions = array();
$xactions[] = $ssh_key->getApplicationTransactionTemplate()
->setTransactionType(PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE)
->setNewValue(1);
$editor = $ssh_key->getApplicationTransactionEditor()
->setActor($viewer)
->setActingAsPHID($auth_phid)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true)
->setContentSource($content_source)
->setIsAdministrativeEdit(true)
->applyTransactions($ssh_key, $xactions);
}
return count($ssh_keys);
}
}

View file

@ -0,0 +1,43 @@
<?php
final class PhabricatorAuthSessionRevoker
extends PhabricatorAuthRevoker {
const REVOKERKEY = 'session';
public function getRevokerName() {
return pht('Sessions');
}
public function getRevokerDescription() {
return pht(
"Revokes all active login sessions.\n\n".
"Affected users will be logged out and need to log in again.");
}
public function revokeAllCredentials() {
$table = new PhabricatorAuthSession();
$conn = $table->establishConnection('w');
queryfx(
$conn,
'DELETE FROM %T',
$table->getTableName());
return $conn->getAffectedRows();
}
public function revokeCredentialsFrom($object) {
$table = new PhabricatorAuthSession();
$conn = $table->establishConnection('w');
queryfx(
$conn,
'DELETE FROM %T WHERE userPHID = %s',
$table->getTableName(),
$object->getPHID());
return $conn->getAffectedRows();
}
}

View file

@ -0,0 +1,46 @@
<?php
final class PhabricatorAuthTemporaryTokenRevoker
extends PhabricatorAuthRevoker {
const REVOKERKEY = 'temporary';
public function getRevokerName() {
return pht('Temporary Tokens');
}
public function getRevokerDescription() {
return pht(
"Revokes temporary authentication tokens.\n\n".
"Temporary tokens are used in password reset mail, welcome mail, and ".
"by some other systems like Git LFS. Revoking temporary tokens will ".
"invalidate existing links in password reset and invite mail that ".
"was sent before the revocation occurred.");
}
public function revokeAllCredentials() {
$table = new PhabricatorAuthTemporaryToken();
$conn = $table->establishConnection('w');
queryfx(
$conn,
'DELETE FROM %T',
$table->getTableName());
return $conn->getAffectedRows();
}
public function revokeCredentialsFrom($object) {
$table = new PhabricatorAuthTemporaryToken();
$conn = $table->establishConnection('w');
queryfx(
$conn,
'DELETE FROM %T WHERE tokenResource = %s',
$table->getTableName(),
$object->getPHID());
return $conn->getAffectedRows();
}
}

View file

@ -0,0 +1,235 @@
<?php
final class PhabricatorAuthPassword
extends PhabricatorAuthDAO
implements
PhabricatorPolicyInterface,
PhabricatorDestructibleInterface,
PhabricatorApplicationTransactionInterface {
protected $objectPHID;
protected $passwordType;
protected $passwordHash;
protected $passwordSalt;
protected $isRevoked;
protected $legacyDigestFormat;
private $object = self::ATTACHABLE;
const PASSWORD_TYPE_ACCOUNT = 'account';
const PASSWORD_TYPE_VCS = 'vcs';
const PASSWORD_TYPE_TEST = 'test';
public static function initializeNewPassword(
PhabricatorAuthPasswordHashInterface $object,
$type) {
return id(new self())
->setObjectPHID($object->getPHID())
->attachObject($object)
->setPasswordType($type)
->setIsRevoked(0);
}
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_COLUMN_SCHEMA => array(
'passwordType' => 'text64',
'passwordHash' => 'text128',
'passwordSalt' => 'text64',
'isRevoked' => 'bool',
'legacyDigestFormat' => 'text32?',
),
self::CONFIG_KEY_SCHEMA => array(
'key_role' => array(
'columns' => array('objectPHID', 'passwordType'),
),
),
) + parent::getConfiguration();
}
public function getPHIDType() {
return PhabricatorAuthPasswordPHIDType::TYPECONST;
}
public function getObject() {
return $this->assertAttached($this->object);
}
public function attachObject($object) {
$this->object = $object;
return $this;
}
public function getHasher() {
$hash = $this->newPasswordEnvelope();
return PhabricatorPasswordHasher::getHasherForHash($hash);
}
public function canUpgrade() {
// If this password uses a legacy digest format, we can upgrade it to the
// new digest format even if a better hasher isn't available.
if ($this->getLegacyDigestFormat() !== null) {
return true;
}
$hash = $this->newPasswordEnvelope();
return PhabricatorPasswordHasher::canUpgradeHash($hash);
}
public function upgradePasswordHasher(
PhutilOpaqueEnvelope $envelope,
PhabricatorAuthPasswordHashInterface $object) {
// Before we make changes, double check that this is really the correct
// password. It could be really bad if we "upgraded" a password and changed
// the secret!
if (!$this->comparePassword($envelope, $object)) {
throw new Exception(
pht(
'Attempting to upgrade password hasher, but the password for the '.
'upgrade is not the stored credential!'));
}
return $this->setPassword($envelope, $object);
}
public function setPassword(
PhutilOpaqueEnvelope $password,
PhabricatorAuthPasswordHashInterface $object) {
$hasher = PhabricatorPasswordHasher::getBestHasher();
return $this->setPasswordWithHasher($password, $object, $hasher);
}
public function setPasswordWithHasher(
PhutilOpaqueEnvelope $password,
PhabricatorAuthPasswordHashInterface $object,
PhabricatorPasswordHasher $hasher) {
if (!strlen($password->openEnvelope())) {
throw new Exception(
pht('Attempting to set an empty password!'));
}
// Generate (or regenerate) the salt first.
$new_salt = Filesystem::readRandomCharacters(64);
$this->setPasswordSalt($new_salt);
// Clear any legacy digest format to force a modern digest.
$this->setLegacyDigestFormat(null);
$digest = $this->digestPassword($password, $object);
$hash = $hasher->getPasswordHashForStorage($digest);
$raw_hash = $hash->openEnvelope();
return $this->setPasswordHash($raw_hash);
}
public function comparePassword(
PhutilOpaqueEnvelope $password,
PhabricatorAuthPasswordHashInterface $object) {
$digest = $this->digestPassword($password, $object);
$hash = $this->newPasswordEnvelope();
return PhabricatorPasswordHasher::comparePassword($digest, $hash);
}
public function newPasswordEnvelope() {
return new PhutilOpaqueEnvelope($this->getPasswordHash());
}
private function digestPassword(
PhutilOpaqueEnvelope $password,
PhabricatorAuthPasswordHashInterface $object) {
$object_phid = $object->getPHID();
if ($this->getObjectPHID() !== $object->getPHID()) {
throw new Exception(
pht(
'This password is associated with an object PHID ("%s") for '.
'a different object than the provided one ("%s").',
$this->getObjectPHID(),
$object->getPHID()));
}
$digest = $object->newPasswordDigest($password, $this);
if (!($digest instanceof PhutilOpaqueEnvelope)) {
throw new Exception(
pht(
'Failed to digest password: object ("%s") did not return an '.
'opaque envelope with a password digest.',
$object->getPHID()));
}
return $digest;
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
public function getPolicy($capability) {
return PhabricatorPolicies::getMostOpenPolicy();
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return false;
}
/* -( PhabricatorExtendedPolicyInterface )--------------------------------- */
public function getExtendedPolicy($capability, PhabricatorUser $viewer) {
return array(
array($this->getObject(), PhabricatorPolicyCapability::CAN_VIEW),
);
}
/* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$this->delete();
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new PhabricatorAuthPasswordEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new PhabricatorAuthPasswordTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
return $timeline;
}
}

View file

@ -0,0 +1,21 @@
<?php
final class PhabricatorAuthPasswordTransaction
extends PhabricatorModularTransaction {
public function getApplicationName() {
return 'auth';
}
public function getApplicationTransactionType() {
return PhabricatorAuthPasswordPHIDType::TYPECONST;
}
public function getApplicationTransactionCommentObject() {
return null;
}
public function getBaseTransactionClass() {
return 'PhabricatorAuthPasswordTransactionType';
}
}

View file

@ -139,7 +139,7 @@ final class PhabricatorAuthSSHKey
public function describeAutomaticCapability($capability) {
if (!$this->getIsACtive()) {
return pht(
'Deactivated SSH keys can not be edited or reactivated.');
'Revoked SSH keys can not be edited or reinstated.');
}
return pht(

View file

@ -43,11 +43,11 @@ final class PhabricatorAuthSSHKeyTransaction
case self::TYPE_DEACTIVATE:
if ($new) {
return pht(
'%s deactivated this key.',
'%s revoked this key.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s activated this key.',
'%s reinstated this key.',
$this->renderHandleLink($author_phid));
}

View file

@ -0,0 +1,32 @@
<?php
final class PhabricatorAuthPasswordRevokeTransaction
extends PhabricatorAuthPasswordTransactionType {
const TRANSACTIONTYPE = 'password.revoke';
public function generateOldValue($object) {
return (bool)$object->getIsRevoked();
}
public function generateNewValue($object, $value) {
return (bool)$value;
}
public function applyInternalEffects($object, $value) {
$object->setIsRevoked((int)$value);
}
public function getTitle() {
if ($this->getNewValue()) {
return pht(
'%s revoked this password.',
$this->renderAuthor());
} else {
return pht(
'%s removed this password from the revocation list.',
$this->renderAuthor());
}
}
}

View file

@ -0,0 +1,4 @@
<?php
abstract class PhabricatorAuthPasswordTransactionType
extends PhabricatorModularTransactionType {}

View file

@ -0,0 +1,30 @@
<?php
final class PhabricatorAuthPasswordUpgradeTransaction
extends PhabricatorAuthPasswordTransactionType {
const TRANSACTIONTYPE = 'password.upgrade';
public function generateOldValue($object) {
$old_hasher = $this->getEditor()->getOldHasher();
if (!$old_hasher) {
throw new PhutilInvalidStateException('setOldHasher');
}
return $old_hasher->getHashName();
}
public function generateNewValue($object, $value) {
return $value;
}
public function getTitle() {
return pht(
'%s upgraded the hash algorithm for this password from "%s" to "%s".',
$this->renderAuthor(),
$this->renderOldValue(),
$this->renderNewValue());
}
}

View file

@ -618,10 +618,14 @@ abstract class PhabricatorApplication
')?';
}
protected function getQueryRoutePattern($base = null) {
protected function getBulkRoutePattern($base = null) {
return $base.'(?:query/(?P<queryKey>[^/]+)/)?';
}
protected function getQueryRoutePattern($base = null) {
return $base.'(?:query/(?P<queryKey>[^/]+)/(?:(?P<queryAction>[^/]+)/)?)?';
}
protected function getProfileMenuRouting($controller) {
$edit_route = $this->getEditRoutePattern();

View file

@ -49,13 +49,6 @@ final class PhabricatorCalendarEventHeraldAdapter extends HeraldAdapter {
}
}
public function getRepetitionOptions() {
return array(
HeraldRepetitionPolicyConfig::EVERY,
HeraldRepetitionPolicyConfig::FIRST,
);
}
public function getHeraldName() {
return $this->getObject()->getMonogram();
}

View file

@ -46,7 +46,7 @@ final class ConduitSSHWorkflow extends PhabricatorSSHWorkflow {
try {
$call = new ConduitCall($method, $params);
$call->setUser($this->getUser());
$call->setUser($this->getSSHUser());
$result = $call->execute();
} catch (ConduitException $ex) {
@ -77,7 +77,7 @@ final class ConduitSSHWorkflow extends PhabricatorSSHWorkflow {
$connection_id = idx($metadata, 'connectionID');
$log = id(new PhabricatorConduitMethodCallLog())
->setCallerPHID($this->getUser()->getPHID())
->setCallerPHID($this->getSSHUser()->getPHID())
->setConnectionID($connection_id)
->setMethod($method)
->setError((string)$error_code)

View file

@ -18,7 +18,8 @@ final class PhabricatorGDSetupCheck extends PhabricatorSetupCheck {
$this->newIssue('extension.gd')
->setName(pht("Missing '%s' Extension", 'gd'))
->setMessage($message);
->setMessage($message)
->addPHPExtension('gd');
} else {
$image_type_map = array(
'imagecreatefrompng' => 'PNG',

View file

@ -49,13 +49,22 @@ final class PhabricatorDaemonBulkJobMonitorController
return id(new AphrontRedirectResponse())
->setURI($job->getMonitorURI());
} else {
return $this->newDialog()
->setTitle(pht('Confirm Bulk Job'))
->appendParagraph($job->getDescriptionForConfirm())
$dialog = $this->newDialog()
->setTitle(pht('Confirm Bulk Job'));
$confirm = $job->getDescriptionForConfirm();
$confirm = (array)$confirm;
foreach ($confirm as $paragraph) {
$dialog->appendParagraph($paragraph);
}
$dialog
->appendParagraph(
pht('Start work on this bulk job?'))
->addCancelButton($job->getManageURI(), pht('Details'))
->addSubmitButton(pht('Start Work'));
return $dialog;
}
} else {
return $this->newDialog()

View file

@ -79,13 +79,20 @@ final class PhabricatorWorkerTaskDetailController
->appendChild($view);
}
private function buildPropertyListView(
PhabricatorWorkerTask $task) {
$viewer = $this->getRequest()->getUser();
private function buildPropertyListView(PhabricatorWorkerTask $task) {
$viewer = $this->getViewer();
$view = new PHUIPropertyListView();
$object_phid = $task->getObjectPHID();
if ($object_phid) {
$handles = $viewer->loadHandles(array($object_phid));
$handle = $handles[$object_phid];
if ($handle->isComplete()) {
$view->addProperty(pht('Object'), $handle->renderLink());
}
}
if ($task->isArchived()) {
switch ($task->getResult()) {
case PhabricatorWorkerArchiveTask::RESULT_SUCCESS:

View file

@ -106,9 +106,11 @@ final class DifferentialCreateCommentConduitAPIMethod
}
}
// NOTE: The legacy "silent" flag is now ignored and has no effect. See
// T13042.
$editor = id(new DifferentialTransactionEditor())
->setActor($viewer)
->setDisableEmail($request->getValue('silent'))
->setContentSource($request->newContentSource())
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true);

View file

@ -700,20 +700,26 @@ final class DifferentialTransactionEditor
->addHeader('Thread-Topic', $thread_topic);
}
protected function buildMailBody(
protected function getTransactionsForMail(
PhabricatorLiskDAO $object,
array $xactions) {
$viewer = $this->requireActor();
// If this is the first time we're sending mail about this revision, we
// generate mail for all prior transactions, not just whatever is being
// applied now. This gets the "added reviewers" lines and other relevant
// information into the mail.
if ($this->isFirstBroadcast()) {
$xactions = $this->loadUnbroadcastTransactions($object);
return $this->loadUnbroadcastTransactions($object);
}
return $xactions;
}
protected function buildMailBody(
PhabricatorLiskDAO $object,
array $xactions) {
$viewer = $this->requireActor();
$body = new PhabricatorMetaMTAMailBody();
$body->setViewer($this->requireActor());
@ -1565,9 +1571,29 @@ final class DifferentialTransactionEditor
protected function didApplyTransactions($object, array $xactions) {
// In a moment, we're going to try to publish draft revisions which have
// completed all their builds. However, we only want to do that if the
// actor is either the revision author or an omnipotent user (generally,
// the Harbormaster application).
// If we let any actor publish the revision as a side effect of other
// changes then an unlucky third party who innocently comments on the draft
// can end up racing Harbormaster and promoting the revision. At best, this
// is confusing. It can also run into validation problems with the "Request
// Review" transaction. See PHI309 for some discussion.
$author_phid = $object->getAuthorPHID();
$viewer = $this->requireActor();
$can_undraft =
($this->getActingAsPHID() === $author_phid) ||
($viewer->isOmnipotent());
// If a draft revision has no outstanding builds and we're automatically
// making drafts public after builds finish, make the revision public.
$auto_undraft = !$object->getHoldAsDraft();
if ($can_undraft) {
$auto_undraft = !$object->getHoldAsDraft();
} else {
$auto_undraft = false;
}
if ($object->isDraft() && $auto_undraft) {
$active_builds = $this->hasActiveBuilds($object);
@ -1575,7 +1601,6 @@ final class DifferentialTransactionEditor
// When Harbormaster moves a revision out of the draft state, we
// attribute the action to the revision author since this is more
// natural and more useful.
$author_phid = $object->getAuthorPHID();
// Additionally, we change the acting PHID for the transaction set
// to the author if it isn't already a user so that mail comes from

View file

@ -0,0 +1,29 @@
<?php
final class DifferentialRevisionStatusHeraldField
extends DifferentialRevisionHeraldField {
const FIELDCONST = 'revision.status';
public function getHeraldFieldName() {
return pht('Revision status');
}
public function getHeraldFieldValue($object) {
return $object->getStatus();
}
protected function getHeraldFieldStandardType() {
return self::STANDARD_PHID;
}
protected function getDatasource() {
return new DifferentialRevisionStatusDatasource();
}
protected function getDatasourceValueMap() {
$map = DifferentialRevisionStatus::getAll();
return mpull($map, 'getDisplayName', 'getKey');
}
}

View file

@ -53,12 +53,6 @@ final class HeraldDifferentialDiffAdapter extends HeraldDifferentialAdapter {
}
}
public function getRepetitionOptions() {
return array(
HeraldRepetitionPolicyConfig::FIRST,
);
}
public function getHeraldName() {
return pht('New Diff');
}

View file

@ -69,13 +69,6 @@ final class HeraldDifferentialRevisionAdapter
}
}
public function getRepetitionOptions() {
return array(
HeraldRepetitionPolicyConfig::EVERY,
HeraldRepetitionPolicyConfig::FIRST,
);
}
public static function newLegacyAdapter(
DifferentialRevision $revision,
DifferentialDiff $diff) {

View file

@ -173,7 +173,7 @@ final class DifferentialChangeset
}
public function getAnchorName() {
return 'change-'.PhabricatorHash::digestForIndex($this->getFilename());
return 'change-'.PhabricatorHash::digestForAnchor($this->getFilename());
}
public function getAbsoluteRepositoryPath(

View file

@ -100,6 +100,13 @@ final class DifferentialTransaction
return true;
}
break;
case DifferentialRevisionRequestReviewTransaction::TRANSACTIONTYPE:
// Don't hide the initial "X requested review: ..." transaction from
// mail or feed even when it occurs during creation. We need this
// transaction to survive so we'll generate mail and feed stories when
// revisions immediately leave the draft state. See T13035 for
// discussion.
return false;
}
return parent::shouldHide();
@ -111,12 +118,6 @@ final class DifferentialTransaction
// Don't hide the initial "X added reviewers: ..." transaction during
// object creation from mail. See T12118 and PHI54.
return false;
case DifferentialRevisionRequestReviewTransaction::TRANSACTIONTYPE:
// Don't hide the initial "X requested review: ..." transaction from
// mail even when it occurs during creation. We need this transaction
// to survive so we'll generate mail when revisions immediately leave
// the draft state. See T13035 for discussion.
return false;
}
return parent::shouldHideForMail($xactions);

View file

@ -204,6 +204,7 @@ final class DifferentialChangesetDetailView extends AphrontView {
'loaded' => $this->getLoaded(),
'undoTemplates' => hsprintf('%s', $renderer->renderUndoTemplates()),
'displayPath' => hsprintf('%s', $display_parts),
'path' => $display_filename,
'icon' => $display_icon,
),
'class' => $class,

View file

@ -124,6 +124,9 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication {
'(?:query/(?P<queryKey>[^/]+)/)?' => 'DiffusionPushLogListController',
'view/(?P<id>\d+)/' => 'DiffusionPushEventViewController',
),
'pulllog/' => array(
$this->getQueryRoutePattern() => 'DiffusionPullLogListController',
),
'(?P<repositoryCallsign>[A-Z]+)' => $repository_routes,
'(?P<repositoryID>[1-9]\d*)' => $repository_routes,

View file

@ -54,6 +54,12 @@ final class DiffusionSearchQueryConduitAPIMethod
$limit = $request->getValue('limit');
$offset = $request->getValue('offset');
// Starting with Git 2.16.0, Git assumes passing an empty argument is
// an error and recommends you pass "." instead.
if (!strlen($path)) {
$path = '.';
}
$results = array();
$future = $repository->getLocalCommandFuture(
// NOTE: --perl-regexp is available only with libpcre compiled in.

View file

@ -658,6 +658,11 @@ final class DiffusionBrowseController extends DiffusionController {
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($corpus)
->addClass('diffusion-mobile-view')
->addSigil('diffusion-file-content-view')
->setMetadata(
array(
'path' => $this->getDiffusionRequest()->getPath(),
))
->setCollapsed(true);
$messages = array();
@ -947,6 +952,10 @@ final class DiffusionBrowseController extends DiffusionController {
}
foreach ($revision_ids as $commit_phid => $revision_id) {
// If the viewer can't actually see this revision, skip it.
if (!isset($revisions[$revision_id])) {
continue;
}
$revision_map[$commit_map[$commit_phid]] = $revision_id;
}
}

View file

@ -1,6 +1,6 @@
<?php
abstract class DiffusionPushLogController extends DiffusionController {
abstract class DiffusionLogController extends DiffusionController {
protected function shouldLoadDiffusionRequest() {
return false;

View file

@ -0,0 +1,12 @@
<?php
final class DiffusionPullLogListController
extends DiffusionLogController {
public function handleRequest(AphrontRequest $request) {
return id(new DiffusionPullLogSearchEngine())
->setController($this)
->buildResponse();
}
}

View file

@ -1,11 +1,7 @@
<?php
final class DiffusionPushEventViewController
extends DiffusionPushLogController {
public function shouldAllowPublic() {
return true;
}
extends DiffusionLogController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();

View file

@ -1,10 +1,7 @@
<?php
final class DiffusionPushLogListController extends DiffusionPushLogController {
public function shouldAllowPublic() {
return true;
}
final class DiffusionPushLogListController
extends DiffusionLogController {
public function handleRequest(AphrontRequest $request) {
return id(new PhabricatorRepositoryPushLogSearchEngine())

View file

@ -365,7 +365,7 @@ final class DiffusionRepositoryController extends DiffusionController {
if ($repository->isHosted()) {
$push_uri = $this->getApplicationURI(
'pushlog/?repositories='.$repository->getMonogram());
'pushlog/?repositories='.$repository->getPHID());
$action_view->addAction(
id(new PhabricatorActionView())
@ -374,6 +374,15 @@ final class DiffusionRepositoryController extends DiffusionController {
->setHref($push_uri));
}
$pull_uri = $this->getApplicationURI(
'pulllog/?repositories='.$repository->getPHID());
$action_view->addAction(
id(new PhabricatorActionView())
->setName(pht('View Pull Logs'))
->setIcon('fa-list-alt')
->setHref($pull_uri));
return $action_view;
}

View file

@ -104,15 +104,29 @@ final class DiffusionServeController extends DiffusionController {
try {
$remote_addr = $request->getRemoteAddress();
if ($request->isHTTPS()) {
$remote_protocol = PhabricatorRepositoryPullEvent::PROTOCOL_HTTPS;
} else {
$remote_protocol = PhabricatorRepositoryPullEvent::PROTOCOL_HTTP;
}
$pull_event = id(new PhabricatorRepositoryPullEvent())
->setEpoch(PhabricatorTime::getNow())
->setRemoteAddress($remote_addr)
->setRemoteProtocol('http');
->setRemoteProtocol($remote_protocol);
if ($response) {
$pull_event
->setResultType('wild')
->setResultCode($response->getHTTPResponseCode());
$response_code = $response->getHTTPResponseCode();
if ($response_code == 200) {
$pull_event
->setResultType(PhabricatorRepositoryPullEvent::RESULT_PULL)
->setResultCode($response_code);
} else {
$pull_event
->setResultType(PhabricatorRepositoryPullEvent::RESULT_ERROR)
->setResultCode($response_code);
}
if ($response instanceof PhabricatorVCSResponse) {
$pull_event->setProperties(
@ -122,7 +136,7 @@ final class DiffusionServeController extends DiffusionController {
}
} else {
$pull_event
->setResultType('exception')
->setResultType(PhabricatorRepositoryPullEvent::RESULT_EXCEPTION)
->setResultCode(500)
->setProperties(
array(
@ -715,30 +729,19 @@ final class DiffusionServeController extends DiffusionController {
return null;
}
$password_entry = id(new PhabricatorRepositoryVCSPassword())
->loadOneWhere('userPHID = %s', $user->getPHID());
if (!$password_entry) {
// User doesn't have a password set.
$request = $this->getRequest();
$content_source = PhabricatorContentSource::newFromRequest($request);
$engine = id(new PhabricatorAuthPasswordEngine())
->setViewer($user)
->setContentSource($content_source)
->setPasswordType(PhabricatorAuthPassword::PASSWORD_TYPE_VCS)
->setObject($user);
if (!$engine->isValidPassword($password)) {
return null;
}
if (!$password_entry->comparePassword($password, $user)) {
// Password doesn't match.
return null;
}
// If the user's password is stored using a less-than-optimal hash, upgrade
// them to the strongest available hash.
$hash_envelope = new PhutilOpaqueEnvelope(
$password_entry->getPasswordHash());
if (PhabricatorPasswordHasher::canUpgradeHash($hash_envelope)) {
$password_entry->setPassword($password, $user);
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$password_entry->save();
unset($unguarded);
}
return $user;
}

View file

@ -22,6 +22,7 @@ final class DiffusionSymbolController extends DiffusionController {
$query->setLanguage($request->getStr('lang'));
}
$repos = array();
if ($request->getStr('repositories')) {
$phids = $request->getStr('repositories');
$phids = explode(',', $phids);
@ -33,9 +34,9 @@ final class DiffusionSymbolController extends DiffusionController {
->withPHIDs($phids)
->execute();
$repos = mpull($repos, 'getPHID');
if ($repos) {
$query->withRepositoryPHIDs($repos);
$repo_phids = mpull($repos, 'getPHID');
if ($repo_phids) {
$query->withRepositoryPHIDs($repo_phids);
}
}
}
@ -45,7 +46,6 @@ final class DiffusionSymbolController extends DiffusionController {
$symbols = $query->execute();
$external_query = id(new DiffusionExternalSymbolQuery())
->withNames(array($name));
@ -61,13 +61,51 @@ final class DiffusionSymbolController extends DiffusionController {
$external_query->withLanguages(array($request->getStr('lang')));
}
if ($request->getStr('path')) {
$external_query->withPaths(array($request->getStr('path')));
}
if ($request->getInt('line')) {
$external_query->withLines(array($request->getInt('line')));
}
if ($request->getInt('char')) {
$external_query->withCharacterPositions(
array(
$request->getInt('char'),
));
}
if ($repos) {
$external_query->withRepositories($repos);
}
$external_sources = id(new PhutilClassMapQuery())
->setAncestorClass('DiffusionExternalSymbolsSource')
->execute();
$results = array($symbols);
foreach ($external_sources as $source) {
$results[] = $source->executeQuery($external_query);
$source_results = $source->executeQuery($external_query);
if (!is_array($source_results)) {
throw new Exception(
pht(
'Expected a list of results from external symbol source "%s".',
get_class($source)));
}
try {
assert_instances_of($source_results, 'PhabricatorRepositorySymbol');
} catch (InvalidArgumentException $ex) {
throw new Exception(
pht(
'Expected a list of PhabricatorRepositorySymbol objects '.
'from external symbol source "%s".',
get_class($source)));
}
$results[] = $source_results;
}
$symbols = array_mergev($results);

View file

@ -6,7 +6,11 @@ final class DiffusionCommitReviewerHeraldField
const FIELDCONST = 'diffusion.commit.reviewer';
public function getHeraldFieldName() {
return pht('Reviewer');
return pht('Reviewer (Deprecated)');
}
public function getFieldGroupKey() {
return HeraldDeprecatedFieldGroup::FIELDGROUPKEY;
}
public function getHeraldFieldValue($object) {

View file

@ -209,7 +209,7 @@ final class HeraldCommitAdapter
}
private function loadCommitDiff() {
$viewer = PhabricatorUser::getOmnipotentUser();
$viewer = $this->getViewer();
$byte_limit = self::getEnormousByteLimit();
$time_limit = self::getEnormousTimeLimit();

View file

@ -35,13 +35,20 @@ final class DiffusionSetPasswordSettingsPanel extends PhabricatorSettingsPanel {
$request,
'/settings/');
$vcspassword = id(new PhabricatorRepositoryVCSPassword())
->loadOneWhere(
'userPHID = %s',
$user->getPHID());
if (!$vcspassword) {
$vcspassword = id(new PhabricatorRepositoryVCSPassword());
$vcspassword->setUserPHID($user->getPHID());
$vcs_type = PhabricatorAuthPassword::PASSWORD_TYPE_VCS;
$vcspasswords = id(new PhabricatorAuthPasswordQuery())
->setViewer($viewer)
->withObjectPHIDs(array($user->getPHID()))
->withPasswordTypes(array($vcs_type))
->withIsRevoked(false)
->execute();
if ($vcspasswords) {
$vcspassword = head($vcspasswords);
} else {
$vcspassword = PhabricatorAuthPassword::initializeNewPassword(
$user,
$vcs_type);
}
$panel_uri = $this->getPanelURI('?saved=true');
@ -51,6 +58,19 @@ final class DiffusionSetPasswordSettingsPanel extends PhabricatorSettingsPanel {
$e_password = true;
$e_confirm = true;
$content_source = PhabricatorContentSource::newFromRequest($request);
// NOTE: This test is against $viewer (not $user), so that the error
// message below makes sense in the case that the two are different,
// and because an admin reusing their own password is bad, while
// system agents generally do not have passwords anyway.
$engine = id(new PhabricatorAuthPasswordEngine())
->setViewer($viewer)
->setContentSource($content_source)
->setObject($viewer)
->setPasswordType($vcs_type);
if ($request->isFormPost()) {
if ($request->getBool('remove')) {
if ($vcspassword->getID()) {
@ -61,62 +81,26 @@ final class DiffusionSetPasswordSettingsPanel extends PhabricatorSettingsPanel {
$new_password = $request->getStr('password');
$confirm = $request->getStr('confirm');
if (!strlen($new_password)) {
$e_password = pht('Required');
$errors[] = pht('Password is required.');
} else {
$e_password = null;
}
if (!strlen($confirm)) {
$e_confirm = pht('Required');
$errors[] = pht('You must confirm the new password.');
} else {
$envelope = new PhutilOpaqueEnvelope($new_password);
$confirm_envelope = new PhutilOpaqueEnvelope($confirm);
try {
$engine->checkNewPassword($envelope, $confirm_envelope);
$e_password = null;
$e_confirm = null;
} catch (PhabricatorAuthPasswordException $ex) {
$errors[] = $ex->getMessage();
$e_password = $ex->getPasswordError();
$e_confirm = $ex->getConfirmError();
}
if (!$errors) {
$envelope = new PhutilOpaqueEnvelope($new_password);
$vcspassword
->setPassword($envelope, $user)
->save();
try {
// NOTE: This test is against $viewer (not $user), so that the error
// message below makes sense in the case that the two are different,
// and because an admin reusing their own password is bad, while
// system agents generally do not have passwords anyway.
$same_password = $viewer->comparePassword($envelope);
} catch (PhabricatorPasswordHasherUnavailableException $ex) {
// If we're missing the hasher, just let the user continue.
$same_password = false;
}
if ($new_password !== $confirm) {
$e_password = pht('Does Not Match');
$e_confirm = pht('Does Not Match');
$errors[] = pht('Password and confirmation do not match.');
} else if ($same_password) {
$e_password = pht('Not Unique');
$e_confirm = pht('Not Unique');
$errors[] = pht(
'This password is the same as another password associated '.
'with your account. You must use a unique password for '.
'VCS access.');
} else if (
PhabricatorCommonPasswords::isCommonPassword($new_password)) {
$e_password = pht('Very Weak');
$e_confirm = pht('Very Weak');
$errors[] = pht(
'This password is extremely weak: it is one of the most common '.
'passwords in use. Choose a stronger password.');
}
if (!$errors) {
$vcspassword->setPassword($envelope, $user);
$vcspassword->save();
return id(new AphrontRedirectResponse())->setURI($panel_uri);
}
return id(new AphrontRedirectResponse())->setURI($panel_uri);
}
}

View file

@ -27,6 +27,7 @@ final class DiffusionGitCommandEngine
$env['HOME'] = PhabricatorEnv::getEmptyCWD();
$env['GIT_SSH'] = $this->getSSHWrapper();
$env['GIT_SSH_VARIANT'] = 'ssh';
if ($this->isAnyHTTPProtocol()) {
$uri = $this->getURI();

View file

@ -88,7 +88,7 @@ abstract class DiffusionFileFutureQuery
}
final protected function executeQuery() {
$future = $this->newQueryFuture();
$future = $this->newConfiguredQueryFuture();
$drequest = $this->getRequest();
@ -105,6 +105,11 @@ abstract class DiffusionFileFutureQuery
->setViewPolicy(PhabricatorPolicies::POLICY_NOONE)
->setExecFuture($future);
$byte_limit = $this->getByteLimit();
if ($byte_limit) {
$source->setByteLimit($byte_limit);
}
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$file = $source->uploadFile();
unset($unguarded);
@ -116,18 +121,8 @@ abstract class DiffusionFileFutureQuery
$this->didHitTimeLimit = true;
$file = null;
}
$byte_limit = $this->getByteLimit();
if ($byte_limit && ($file->getByteSize() > $byte_limit)) {
} catch (PhabricatorFileUploadSourceByteLimitException $ex) {
$this->didHitByteLimit = true;
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
id(new PhabricatorDestructionEngine())
->destroyObject($file);
unset($unguarded);
$file = null;
}
@ -141,11 +136,6 @@ abstract class DiffusionFileFutureQuery
$future->setTimeout($this->getTimeout());
}
$byte_limit = $this->getByteLimit();
if ($byte_limit) {
$future->setStdoutSizeLimit($byte_limit + 1);
}
return $future;
}

View file

@ -0,0 +1,166 @@
<?php
final class DiffusionPullLogSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getResultTypeDescription() {
return pht('Pull Logs');
}
public function getApplicationClassName() {
return 'PhabricatorDiffusionApplication';
}
public function newQuery() {
return new PhabricatorRepositoryPullEventQuery();
}
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
if ($map['repositoryPHIDs']) {
$query->withRepositoryPHIDs($map['repositoryPHIDs']);
}
if ($map['pullerPHIDs']) {
$query->withPullerPHIDs($map['pullerPHIDs']);
}
return $query;
}
protected function buildCustomSearchFields() {
return array(
id(new PhabricatorSearchDatasourceField())
->setDatasource(new DiffusionRepositoryDatasource())
->setKey('repositoryPHIDs')
->setAliases(array('repository', 'repositories', 'repositoryPHID'))
->setLabel(pht('Repositories'))
->setDescription(
pht('Search for pull logs for specific repositories.')),
id(new PhabricatorUsersSearchField())
->setKey('pullerPHIDs')
->setAliases(array('puller', 'pullers', 'pullerPHID'))
->setLabel(pht('Pullers'))
->setDescription(
pht('Search for pull logs by specific users.')),
);
}
protected function newExportFields() {
return array(
id(new PhabricatorIDExportField())
->setKey('id')
->setLabel(pht('ID')),
id(new PhabricatorPHIDExportField())
->setKey('phid')
->setLabel(pht('PHID')),
id(new PhabricatorPHIDExportField())
->setKey('repositoryPHID')
->setLabel(pht('Repository PHID')),
id(new PhabricatorStringExportField())
->setKey('repository')
->setLabel(pht('Repository')),
id(new PhabricatorPHIDExportField())
->setKey('pullerPHID')
->setLabel(pht('Puller PHID')),
id(new PhabricatorStringExportField())
->setKey('puller')
->setLabel(pht('Puller')),
id(new PhabricatorStringExportField())
->setKey('protocol')
->setLabel(pht('Protocol')),
id(new PhabricatorStringExportField())
->setKey('result')
->setLabel(pht('Result')),
id(new PhabricatorIntExportField())
->setKey('code')
->setLabel(pht('Code')),
id(new PhabricatorEpochExportField())
->setKey('date')
->setLabel(pht('Date')),
);
}
public function newExport(array $events) {
$viewer = $this->requireViewer();
$phids = array();
foreach ($events as $event) {
if ($event->getPullerPHID()) {
$phids[] = $event->getPullerPHID();
}
}
$handles = $viewer->loadHandles($phids);
$export = array();
foreach ($events as $event) {
$repository = $event->getRepository();
if ($repository) {
$repository_phid = $repository->getPHID();
$repository_name = $repository->getDisplayName();
} else {
$repository_phid = null;
$repository_name = null;
}
$puller_phid = $event->getPullerPHID();
if ($puller_phid) {
$puller_name = $handles[$puller_phid]->getName();
} else {
$puller_name = null;
}
$export[] = array(
'id' => $event->getID(),
'phid' => $event->getPHID(),
'repositoryPHID' => $repository_phid,
'repository' => $repository_name,
'pullerPHID' => $puller_phid,
'puller' => $puller_name,
'protocol' => $event->getRemoteProtocol(),
'result' => $event->getResultType(),
'code' => $event->getResultCode(),
'date' => $event->getEpoch(),
);
}
return $export;
}
protected function getURI($path) {
return '/diffusion/pulllog/'.$path;
}
protected function getBuiltinQueryNames() {
return array(
'all' => pht('All Pull Logs'),
);
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function renderResultList(
array $logs,
PhabricatorSavedQuery $query,
array $handles) {
$table = id(new DiffusionPullLogListView())
->setViewer($this->requireViewer())
->setLogs($logs);
return id(new PhabricatorApplicationSearchResultView())
->setTable($table);
}
}

View file

@ -15,7 +15,7 @@ final class DiffusionGitReceivePackSSHWorkflow extends DiffusionGitSSHWorkflow {
protected function executeRepositoryOperations() {
$repository = $this->getRepository();
$viewer = $this->getUser();
$viewer = $this->getSSHUser();
$device = AlmanacKeys::getLiveDevice();
// This is a write, and must have write access.

View file

@ -15,7 +15,7 @@ final class DiffusionGitUploadPackSSHWorkflow extends DiffusionGitSSHWorkflow {
protected function executeRepositoryOperations() {
$repository = $this->getRepository();
$viewer = $this->getUser();
$viewer = $this->getSSHUser();
$device = AlmanacKeys::getLiveDevice();
$skip_sync = $this->shouldSkipReadSynchronization();
@ -61,11 +61,11 @@ final class DiffusionGitUploadPackSSHWorkflow extends DiffusionGitSSHWorkflow {
if ($err) {
$pull_event
->setResultType('error')
->setResultType(PhabricatorRepositoryPullEvent::RESULT_ERROR)
->setResultCode($err);
} else {
$pull_event
->setResultType('pull')
->setResultType(PhabricatorRepositoryPullEvent::RESULT_PULL)
->setResultCode(0);
}

View file

@ -26,7 +26,7 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
public function getEnvironment() {
$env = array(
DiffusionCommitHookEngine::ENV_USER => $this->getUser()->getUsername(),
DiffusionCommitHookEngine::ENV_USER => $this->getSSHUser()->getUsername(),
DiffusionCommitHookEngine::ENV_REMOTE_PROTOCOL => 'ssh',
);
@ -122,14 +122,14 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
$key_path,
$port,
$host,
'@'.$this->getUser()->getUsername(),
'@'.$this->getSSHUser()->getUsername(),
$this->getOriginalArguments());
}
final public function execute(PhutilArgumentParser $args) {
$this->args = $args;
$viewer = $this->getUser();
$viewer = $this->getSSHUser();
$have_diffusion = PhabricatorApplication::isClassInstalledForViewer(
'PhabricatorDiffusionApplication',
$viewer);
@ -164,7 +164,7 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
}
protected function loadRepositoryWithPath($path, $vcs) {
$viewer = $this->getUser();
$viewer = $this->getSSHUser();
$info = PhabricatorRepository::parseRepositoryServicePath($path, $vcs);
if ($info === null) {
@ -214,7 +214,7 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
}
$repository = $this->getRepository();
$viewer = $this->getUser();
$viewer = $this->getSSHUser();
if ($viewer->isOmnipotent()) {
throw new Exception(
@ -252,7 +252,7 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
}
protected function shouldSkipReadSynchronization() {
$viewer = $this->getUser();
$viewer = $this->getSSHUser();
// Currently, the only case where devices interact over SSH without
// assuming user credentials is when synchronizing before a read. These
@ -265,14 +265,14 @@ abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
}
protected function newPullEvent() {
$viewer = $this->getViewer();
$viewer = $this->getSSHUser();
$repository = $this->getRepository();
$remote_address = $this->getSSHRemoteAddress();
return id(new PhabricatorRepositoryPullEvent())
->setEpoch(PhabricatorTime::getNow())
->setRemoteAddress($remote_address)
->setRemoteProtocol('ssh')
->setRemoteProtocol(PhabricatorRepositoryPullEvent::PROTOCOL_SSH)
->setPullerPHID($viewer->getPHID())
->setRepositoryPHID($repository->getPHID());
}

View file

@ -154,7 +154,7 @@ final class DiffusionSubversionServeSSHWorkflow
} else {
$command = csprintf(
'svnserve -t --tunnel-user=%s',
$this->getUser()->getUsername());
$this->getSSHUser()->getUsername());
$cwd = PhabricatorEnv::getEmptyCWD();
}

View file

@ -1,45 +1,93 @@
<?php
final class DiffusionExternalSymbolQuery extends Phobject {
private $languages = array();
private $types = array();
private $names = array();
private $contexts = array();
private $paths = array();
private $lines = array();
private $repositories = array();
private $characterPositions = array();
public function withLanguages(array $languages) {
$this->languages = $languages;
return $this;
}
public function withTypes(array $types) {
$this->types = $types;
return $this;
}
public function withNames(array $names) {
$this->names = $names;
return $this;
}
public function withContexts(array $contexts) {
$this->contexts = $contexts;
return $this;
}
public function withPaths(array $paths) {
$this->paths = $paths;
return $this;
}
public function withLines(array $lines) {
$this->lines = $lines;
return $this;
}
public function withCharacterPositions(array $positions) {
$this->characterPositions = $positions;
return $this;
}
public function withRepositories(array $repositories) {
assert_instances_of($repositories, 'PhabricatorRepository');
$this->repositories = $repositories;
return $this;
}
public function getLanguages() {
return $this->languages;
}
public function getTypes() {
return $this->types;
}
public function getNames() {
return $this->names;
}
public function getContexts() {
return $this->contexts;
}
public function getPaths() {
return $this->paths;
}
public function getLines() {
return $this->lines;
}
public function getRepositories() {
return $this->repositories;
}
public function getCharacterPositions() {
return $this->characterPositions;
}
public function matchesAnyLanguage(array $languages) {
return (!$this->languages) || array_intersect($languages, $this->languages);
}
public function matchesAnyType(array $types) {
return (!$this->types) || array_intersect($types, $this->types);
}

View file

@ -0,0 +1,115 @@
<?php
final class DiffusionPullLogListView extends AphrontView {
private $logs;
public function setLogs(array $logs) {
assert_instances_of($logs, 'PhabricatorRepositoryPullEvent');
$this->logs = $logs;
return $this;
}
public function render() {
$events = $this->logs;
$viewer = $this->getViewer();
$handle_phids = array();
foreach ($events as $event) {
if ($event->getPullerPHID()) {
$handle_phids[] = $event->getPullerPHID();
}
}
$handles = $viewer->loadHandles($handle_phids);
// Figure out which repositories are editable. We only let you see remote
// IPs if you have edit capability on a repository.
$editable_repos = array();
if ($events) {
$editable_repos = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->withPHIDs(mpull($events, 'getRepositoryPHID'))
->execute();
$editable_repos = mpull($editable_repos, null, 'getPHID');
}
$rows = array();
$any_host = false;
foreach ($events as $event) {
if ($event->getRepositoryPHID()) {
$repository = $event->getRepository();
} else {
$repository = null;
}
// Reveal this if it's valid and the user can edit the repository. For
// invalid requests you currently have to go fishing in the database.
$remote_address = '-';
if ($repository) {
if (isset($editable_repos[$event->getRepositoryPHID()])) {
$remote_address = $event->getRemoteAddress();
}
}
$event_id = $event->getID();
$repository_link = null;
if ($repository) {
$repository_link = phutil_tag(
'a',
array(
'href' => $repository->getURI(),
),
$repository->getDisplayName());
}
$puller_link = null;
if ($event->getPullerPHID()) {
$puller_link = $viewer->renderHandle($event->getPullerPHID());
}
$rows[] = array(
$event_id,
$repository_link,
$puller_link,
$remote_address,
$event->getRemoteProtocolDisplayName(),
$event->newResultIcon(),
$event->getResultCode(),
phabricator_datetime($event->getEpoch(), $viewer),
);
}
$table = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('Pull'),
pht('Repository'),
pht('Puller'),
pht('From'),
pht('Via'),
null,
pht('Code'),
pht('Date'),
))
->setColumnClasses(
array(
'n',
'',
'',
'n',
'wide',
'',
'n',
'right',
));
return $table;
}
}

View file

@ -12,6 +12,8 @@ abstract class PhabricatorFileUploadSource
private $shouldChunk;
private $didRewind;
private $totalBytesWritten = 0;
private $totalBytesRead = 0;
private $byteLimit = 0;
public function setName($name) {
$this->name = $name;
@ -40,6 +42,15 @@ abstract class PhabricatorFileUploadSource
return $this->viewPolicy;
}
public function setByteLimit($byte_limit) {
$this->byteLimit = $byte_limit;
return $this;
}
public function getByteLimit() {
return $this->byteLimit;
}
public function uploadFile() {
if (!$this->shouldChunkFile()) {
return $this->writeSingleFile();
@ -81,8 +92,15 @@ abstract class PhabricatorFileUploadSource
return false;
}
$read_bytes = $data->current();
$this->totalBytesRead += strlen($read_bytes);
if ($this->byteLimit && ($this->totalBytesRead > $this->byteLimit)) {
throw new PhabricatorFileUploadSourceByteLimitException();
}
$rope = $this->getRope();
$rope->append($data->current());
$rope->append($read_bytes);
return true;
}
@ -160,8 +178,10 @@ abstract class PhabricatorFileUploadSource
}
}
// If we have extra bytes at the end, write them.
if ($rope->getByteLength()) {
// If we have extra bytes at the end, write them. Note that it's possible
// that we have more than one chunk of bytes left if the read was very
// fast.
while ($rope->getByteLength()) {
$this->writeChunk($file, $engine);
}

View file

@ -0,0 +1,4 @@
<?php
final class PhabricatorFileUploadSourceByteLimitException
extends Exception {}

View file

@ -38,6 +38,7 @@ abstract class HeraldAdapter extends Phobject {
private $actionMap;
private $edgeCache = array();
private $forbiddenActions = array();
private $viewer;
public function getEmailPHIDs() {
return array_values($this->emailPHIDs);
@ -55,10 +56,29 @@ abstract class HeraldAdapter extends Phobject {
return $this;
}
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
// See PHI276. Normally, Herald runs without regard for policy checks.
// However, we use a real viewer during test console runs: this makes
// intracluster calls to Diffusion APIs work even if web nodes don't
// have privileged credentials.
if ($this->viewer) {
return $this->viewer;
}
return PhabricatorUser::getOmnipotentUser();
}
public function setContentSource(PhabricatorContentSource $content_source) {
$this->contentSource = $content_source;
return $this;
}
public function getContentSource() {
return $this->contentSource;
}
@ -764,9 +784,20 @@ abstract class HeraldAdapter extends Phobject {
public function getRepetitionOptions() {
return array(
HeraldRepetitionPolicyConfig::EVERY,
);
$options = array();
$options[] = HeraldRule::REPEAT_EVERY;
// Some rules, like pre-commit rules, only ever fire once. It doesn't
// make sense to use state-based repetition policies like "only the first
// time" for these rules.
if (!$this->isSingleEventAdapter()) {
$options[] = HeraldRule::REPEAT_FIRST;
$options[] = HeraldRule::REPEAT_CHANGE;
}
return $options;
}
protected function initializeNewAdapter() {
@ -887,15 +918,15 @@ abstract class HeraldAdapter extends Phobject {
));
}
$integer_code_for_every = HeraldRepetitionPolicyConfig::toInt(
HeraldRepetitionPolicyConfig::EVERY);
if ($rule->getRepetitionPolicy() == $integer_code_for_every) {
$action_text =
pht('Take these actions every time this rule matches:');
if ($rule->isRepeatFirst()) {
$action_text = pht(
'Take these actions the first time this rule matches:');
} else if ($rule->isRepeatOnChange()) {
$action_text = pht(
'Take these actions if this rule did not match the last time:');
} else {
$action_text =
pht('Take these actions the first time this rule matches:');
$action_text = pht(
'Take these actions every time this rule matches:');
}
$action_title = phutil_tag(

View file

@ -1,28 +0,0 @@
<?php
final class HeraldRepetitionPolicyConfig extends Phobject {
const FIRST = 'first'; // only execute the first time (no repeating)
const EVERY = 'every'; // repeat every time
private static $policyIntMap = array(
self::FIRST => 0,
self::EVERY => 1,
);
public static function getMap() {
return array(
self::EVERY => pht('every time'),
self::FIRST => pht('only the first time'),
);
}
public static function toInt($str) {
return idx(self::$policyIntMap, $str, self::$policyIntMap[self::EVERY]);
}
public static function toString($int) {
return idx(array_flip(self::$policyIntMap), $int, self::EVERY);
}
}

View file

@ -218,7 +218,7 @@ final class HeraldRuleController extends HeraldController {
),
pht('New Action')))
->setDescription(pht(
'Take these actions %s this rule matches:',
'Take these actions %s',
$repetition_selector))
->setContent(javelin_tag(
'table',
@ -373,8 +373,7 @@ final class HeraldRuleController extends HeraldController {
// mutate current rule, so it would be sent to the client in the right state
$rule->setMustMatchAll((int)$match_all);
$rule->setName($new_name);
$rule->setRepetitionPolicy(
HeraldRepetitionPolicyConfig::toInt($repetition_policy_param));
$rule->setRepetitionPolicyStringConstant($repetition_policy_param);
$rule->attachConditions($conditions);
$rule->attachActions($actions);
@ -594,11 +593,10 @@ final class HeraldRuleController extends HeraldController {
* time) this rule matches..." element.
*/
private function renderRepetitionSelector($rule, HeraldAdapter $adapter) {
$repetition_policy = HeraldRepetitionPolicyConfig::toString(
$rule->getRepetitionPolicy());
$repetition_policy = $rule->getRepetitionPolicyStringConstant();
$repetition_options = $adapter->getRepetitionOptions();
$repetition_names = HeraldRepetitionPolicyConfig::getMap();
$repetition_names = HeraldRule::getRepetitionPolicySelectOptionMap();
$repetition_map = array_select_keys($repetition_names, $repetition_options);
if (count($repetition_map) < 2) {

View file

@ -39,7 +39,9 @@ final class HeraldTestConsoleController extends HeraldController {
$object = $this->getTestObject();
$adapter = $this->getTestAdapter();
$adapter->setIsNewObject(false);
$adapter
->setIsNewObject(false)
->setViewer($viewer);
$rules = id(new HeraldRuleQuery())
->setViewer($viewer)

View file

@ -66,8 +66,10 @@ final class HeraldRuleEditor
$object->setMustMatchAll((int)$new_state['match_all']);
$object->attachConditions($new_state['conditions']);
$object->attachActions($new_state['actions']);
$object->setRepetitionPolicy(
HeraldRepetitionPolicyConfig::toInt($new_state['repetition_policy']));
$new_repetition = $new_state['repetition_policy'];
$object->setRepetitionPolicyStringConstant($new_repetition);
return $object;
}

View file

@ -9,7 +9,7 @@ final class HeraldRuleSerializer extends Phobject {
(bool)$rule->getMustMatchAll(),
$rule->getConditions(),
$rule->getActions(),
HeraldRepetitionPolicyConfig::toString($rule->getRepetitionPolicy()));
$rule->getRepetitionPolicyStringConstant());
}
public function serializeRuleComponents(

View file

@ -14,6 +14,7 @@ final class HeraldEngine extends Phobject {
private $forbiddenFields = array();
private $forbiddenActions = array();
private $skipEffects = array();
public function setDryRun($dry_run) {
$this->dryRun = $dry_run;
@ -68,9 +69,7 @@ final class HeraldEngine extends Phobject {
foreach ($rules as $phid => $rule) {
$this->stack = array();
$policy_first = HeraldRepetitionPolicyConfig::FIRST;
$policy_first_int = HeraldRepetitionPolicyConfig::toInt($policy_first);
$is_first_only = ($rule->getRepetitionPolicy() == $policy_first_int);
$is_first_only = $rule->isRepeatFirst();
try {
if (!$this->getDryRun() &&
@ -173,15 +172,31 @@ final class HeraldEngine extends Phobject {
return;
}
$rules = mpull($rules, null, 'getID');
$applied_ids = array();
$first_policy = HeraldRepetitionPolicyConfig::toInt(
HeraldRepetitionPolicyConfig::FIRST);
// Update the "applied" state table. How this table works depends on the
// repetition policy for the rule.
//
// REPEAT_EVERY: We delete existing rows for the rule, then write nothing.
// This policy doesn't use any state.
//
// REPEAT_FIRST: We keep existing rows, then write additional rows for
// rules which fired. This policy accumulates state over the life of the
// object.
//
// REPEAT_CHANGE: We delete existing rows, then write all the rows which
// matched. This policy only uses the state from the previous run.
// Mark all the rules that have had their effects applied as having been
// executed for the current object.
$rules = mpull($rules, null, 'getID');
$rule_ids = mpull($xscripts, 'getRuleID');
$delete_ids = array();
foreach ($rules as $rule_id => $rule) {
if ($rule->isRepeatFirst()) {
continue;
}
$delete_ids[] = $rule_id;
}
$applied_ids = array();
foreach ($rule_ids as $rule_id) {
if (!$rule_id) {
// Some apply transcripts are purely informational and not associated
@ -194,26 +209,44 @@ final class HeraldEngine extends Phobject {
continue;
}
if ($rule->getRepetitionPolicy() == $first_policy) {
if ($rule->isRepeatFirst() || $rule->isRepeatOnChange()) {
$applied_ids[] = $rule_id;
}
}
if ($applied_ids) {
// Also include "only if this rule did not match the last time" rules
// which matched but were skipped in the "applied" list.
foreach ($this->skipEffects as $rule_id => $ignored) {
$applied_ids[] = $rule_id;
}
if ($delete_ids || $applied_ids) {
$conn_w = id(new HeraldRule())->establishConnection('w');
$sql = array();
foreach ($applied_ids as $id) {
$sql[] = qsprintf(
if ($delete_ids) {
queryfx(
$conn_w,
'(%s, %d)',
'DELETE FROM %T WHERE phid = %s AND ruleID IN (%Ld)',
HeraldRule::TABLE_RULE_APPLIED,
$adapter->getPHID(),
$id);
$delete_ids);
}
if ($applied_ids) {
$sql = array();
foreach ($applied_ids as $id) {
$sql[] = qsprintf(
$conn_w,
'(%s, %d)',
$adapter->getPHID(),
$id);
}
queryfx(
$conn_w,
'INSERT IGNORE INTO %T (phid, ruleID) VALUES %Q',
HeraldRule::TABLE_RULE_APPLIED,
implode(', ', $sql));
}
queryfx(
$conn_w,
'INSERT IGNORE INTO %T (phid, ruleID) VALUES %Q',
HeraldRule::TABLE_RULE_APPLIED,
implode(', ', $sql));
}
}
@ -315,6 +348,30 @@ final class HeraldEngine extends Phobject {
}
}
// If this rule matched, and is set to run "if it did not match the last
// time", and we matched the last time, we're going to return a match in
// the transcript but set a flag so we don't actually apply any effects.
// We need the rule to match so that storage gets updated properly. If we
// just pretend the rule didn't match it won't cause any effects (which
// is correct), but it also won't set the "it matched" flag in storage,
// so the next run after this one would incorrectly trigger again.
$is_dry_run = $this->getDryRun();
if ($result && !$is_dry_run) {
$is_on_change = $rule->isRepeatOnChange();
if ($is_on_change) {
$did_apply = $rule->getRuleApplied($object->getPHID());
if ($did_apply) {
$reason = pht(
'This rule matched, but did not take any actions because it '.
'is configured to act only if it did not match the last time.');
$this->skipEffects[$rule->getID()] = true;
}
}
}
$this->newRuleTranscript($rule)
->setResult($result)
->setReason($reason);
@ -367,6 +424,11 @@ final class HeraldEngine extends Phobject {
HeraldRule $rule,
HeraldAdapter $object) {
$rule_id = $rule->getID();
if (isset($this->skipEffects[$rule_id])) {
return array();
}
$effects = array();
foreach ($rule->getActions() as $action) {
$effect = id(new HeraldEffect())

Some files were not shown because too many files have changed in this diff Show more